ui.tool_selection_page
tool_selection_page.py
This module defines the ToolChoicePage class — a PyQt6 page that allows the user
to select the next processing step in the GliAAns pipeline.
The available options include:
- Skull Stripping
- Automatic Drawing
- Deep Learning Segmentation (Linux + CUDA only)
- Full Pipeline Execution
Depending on the selected option, the next page is dynamically loaded.
1""" 2tool_selection_page.py 3 4This module defines the `ToolChoicePage` class — a PyQt6 page that allows the user 5to select the next processing step in the GliAAns pipeline. 6 7The available options include: 8- Skull Stripping 9- Automatic Drawing 10- Deep Learning Segmentation (Linux + CUDA only) 11- Full Pipeline Execution 12 13Depending on the selected option, the next page is dynamically loaded. 14""" 15 16import platform 17import torch 18from PyQt6.QtWidgets import ( 19 QVBoxLayout, QLabel, QGroupBox, QRadioButton, QButtonGroup, QSizePolicy, 20 QWidget, QHBoxLayout, QMessageBox, QApplication 21) 22from PyQt6.QtGui import QFont 23from PyQt6.QtCore import Qt, QCoreApplication 24 25from components.info_label import InfoLabel 26from ui.nifti_mask_selection_page import MaskNiftiSelectionPage 27from ui.dl_selection_page import DlNiftiSelectionPage 28from ui.pipeline_patient_selection_page import PipelinePatientSelectionPage 29from ui.skull_stripping_page import SkullStrippingPage 30from page import Page 31 32 33class ToolSelectionPage(Page): 34 """ 35 A UI page for selecting the next processing step in the workflow. 36 37 This class extends `Page` and displays a set of radio buttons representing 38 different data processing operations. The user’s selection determines which 39 next page in the pipeline will be opened. 40 41 Attributes 42 ---------- 43 context : dict 44 Shared application state, including navigation and configuration. 45 previous_page : Page 46 Reference to the previous page for backward navigation. 47 selected_option : int or None 48 The currently selected radio button ID. 49 """ 50 51 def __init__(self, context=None, previous_page=None): 52 """Initialize the ToolSelectionPage UI.""" 53 super().__init__() 54 55 self.context = context 56 self.previous_page = previous_page 57 58 # References to next pages for each option 59 self.next_skull_stripping = None 60 self.next_manual_draw = None 61 self.next_deep_learning = None 62 self.next_pipeline = None 63 64 # === Layout setup === 65 self.layout = QVBoxLayout(self) 66 self.layout.setContentsMargins(40, 40, 40, 40) 67 self.layout.setSpacing(30) 68 self.setLayout(self.layout) 69 70 # --- Title --- 71 self.title = QLabel("Select the Next Processing Step") 72 self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) 73 self.title.setFont(QFont("Arial", 18, QFont.Weight.Bold)) 74 self.title.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) 75 self.layout.addWidget(self.title) 76 77 # --- GroupBox for options --- 78 self.radio_group_box = QGroupBox("Available Processes") 79 self.radio_group_box.setStyleSheet(""" 80 QGroupBox { 81 font-size: 13px; 82 border: 1px solid #bdc3c7; 83 border-radius: 10px; 84 padding: 20px; 85 background-color: #e5e5e5; 86 } 87 """) 88 self.radio_group_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 89 self.radio_group_box.setMaximumHeight(400) 90 91 radio_layout = QVBoxLayout() 92 radio_layout.setSpacing(20) 93 radio_layout.setContentsMargins(10, 10, 10, 10) 94 95 self.radio_group = QButtonGroup(self) 96 97 # --- Radio Buttons for each processing option --- 98 self.radio_skull = QRadioButton("Skull Stripping") 99 self.radio_draw = QRadioButton("Automatic Drawing") 100 self.radio_dl = QRadioButton("Deep Learning Segmentation") 101 self.radio_analysis = QRadioButton("Full Pipeline") 102 103 self.radio_buttons = [ 104 self.radio_skull, 105 self.radio_draw, 106 self.radio_dl, 107 self.radio_analysis, 108 ] 109 110 # Apply consistent style to all buttons 111 for btn in self.radio_buttons: 112 btn.setFont(QFont("Arial", 14)) 113 btn.setStyleSheet(""" 114 QRadioButton { 115 padding: 5px; 116 border: 1px solid #bdc3c7; 117 border-radius: 8px; 118 background-color: white; 119 spacing: 10px; 120 } 121 QRadioButton::hover { 122 background-color: #dff9fb; 123 border: 1px solid #3498db; 124 } 125 QRadioButton::checked { 126 background-color: #d6eaf8; 127 border: 2px solid #2980b9; 128 } 129 """) 130 btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 131 132 # Add buttons to layout and group 133 for i, btn in enumerate(self.radio_buttons): 134 self.radio_group.addButton(btn, id=i) 135 if btn == self.radio_dl: 136 # Add info tooltip for Deep Learning option 137 dl_widget = QWidget() 138 dl_widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 139 dl_layout = QHBoxLayout(dl_widget) 140 dl_layout.setContentsMargins(0, 0, 0, 0) 141 dl_layout.setSpacing(5) 142 143 self.dl_info_label = InfoLabel( 144 text="i", 145 tooltip_text=QCoreApplication.translate( 146 "ToolSelectionPage", 147 "To use this function you need Linux and a CUDA capable GPU" 148 ) 149 ) 150 dl_layout.addWidget(self.radio_dl) 151 dl_layout.addWidget(self.dl_info_label) 152 radio_layout.addWidget(dl_widget) 153 else: 154 radio_layout.addWidget(btn) 155 156 self.radio_group.buttonToggled.connect(lambda: self.on_selection()) 157 158 self.radio_group_box.setLayout(radio_layout) 159 self.layout.addWidget(self.radio_group_box) 160 161 self.selected_option = None 162 163 # Connect language change signal if available 164 self._translate_ui() 165 if context and "language_changed" in context: 166 context["language_changed"].connect(self._translate_ui) 167 168 # ------------------------------------------------------------------------- 169 # Navigation 170 # ------------------------------------------------------------------------- 171 172 def on_selection(self): 173 """Triggered when a radio button is toggled.""" 174 self.selected_option = self.radio_group.checkedId() 175 if self.context and "update_main_buttons" in self.context: 176 self.context["update_main_buttons"]() 177 178 def is_ready_to_advance(self): 179 """Return True if an option has been selected.""" 180 return self.radio_group.checkedId() != -1 181 182 def is_ready_to_go_back(self): 183 """Always allows going back from this page.""" 184 return True 185 186 def next(self, context): 187 """ 188 Return the next page based on the selected option. 189 190 Handles platform and CUDA availability checks for Deep Learning mode. 191 """ 192 page_classes = { 193 0: ("next_skull_stripping", SkullStrippingPage), 194 1: ("next_manual_draw", MaskNiftiSelectionPage), 195 2: ("next_deep_learning", DlNiftiSelectionPage), 196 3: ("next_pipeline", PipelinePatientSelectionPage), 197 } 198 199 if self.selected_option not in page_classes: 200 return None 201 202 # Check CUDA and OS support for Deep Learning mode 203 is_linux = platform.system() == "Linux" 204 has_gpu = torch.cuda.is_available() 205 206 if self.selected_option == 2 and (not has_gpu or not is_linux): 207 QMessageBox.warning( 208 self, 209 QCoreApplication.translate("ToolSelectionPage", "Not available for this platform"), 210 QCoreApplication.translate( 211 "ToolSelectionPage", 212 "The deep learning segmentation is not available for this platform: {0}" 213 ).format(platform.system()), 214 ) 215 return self 216 217 # Create or reuse the next page instance 218 attr_name, page_class = page_classes[self.selected_option] 219 next_page = getattr(self, attr_name, None) 220 221 if not next_page: 222 next_page = page_class(context, self) 223 setattr(self, attr_name, next_page) 224 self.context["history"].append(next_page) 225 226 next_page.on_enter() 227 return next_page 228 229 def back(self): 230 """Navigate back to the previous page if available.""" 231 if self.previous_page: 232 self.previous_page.on_enter() 233 return self.previous_page 234 return None 235 236 def reset_page(self): 237 """Reset selection state when the page is revisited.""" 238 self.radio_group.setExclusive(False) 239 for button in self.radio_group.buttons(): 240 button.setChecked(False) 241 self.radio_group.setExclusive(True) 242 self.selected_option = None 243 244 def _translate_ui(self): 245 """Translate all text elements (supports multi-language UI).""" 246 self.title.setText(QApplication.translate("ToolSelectionPage", "Select the Next Processing Step")) 247 self.radio_group_box.setTitle(QApplication.translate("ToolSelectionPage", "Available Processes")) 248 self.radio_skull.setText(QApplication.translate("ToolSelectionPage", "Skull Stripping")) 249 self.radio_draw.setText(QApplication.translate("ToolSelectionPage", "Automatic Drawing")) 250 self.radio_dl.setText(QApplication.translate("ToolSelectionPage", "Deep Learning Segmentation")) 251 self.radio_analysis.setText(QApplication.translate("ToolSelectionPage", "Full Pipeline")) 252 self.dl_info_label.setToolTip(QApplication.translate("ToolSelectionPage", "To use this function you need Linux and a CUDA capable GPU"))
34class ToolSelectionPage(Page): 35 """ 36 A UI page for selecting the next processing step in the workflow. 37 38 This class extends `Page` and displays a set of radio buttons representing 39 different data processing operations. The user’s selection determines which 40 next page in the pipeline will be opened. 41 42 Attributes 43 ---------- 44 context : dict 45 Shared application state, including navigation and configuration. 46 previous_page : Page 47 Reference to the previous page for backward navigation. 48 selected_option : int or None 49 The currently selected radio button ID. 50 """ 51 52 def __init__(self, context=None, previous_page=None): 53 """Initialize the ToolSelectionPage UI.""" 54 super().__init__() 55 56 self.context = context 57 self.previous_page = previous_page 58 59 # References to next pages for each option 60 self.next_skull_stripping = None 61 self.next_manual_draw = None 62 self.next_deep_learning = None 63 self.next_pipeline = None 64 65 # === Layout setup === 66 self.layout = QVBoxLayout(self) 67 self.layout.setContentsMargins(40, 40, 40, 40) 68 self.layout.setSpacing(30) 69 self.setLayout(self.layout) 70 71 # --- Title --- 72 self.title = QLabel("Select the Next Processing Step") 73 self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) 74 self.title.setFont(QFont("Arial", 18, QFont.Weight.Bold)) 75 self.title.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) 76 self.layout.addWidget(self.title) 77 78 # --- GroupBox for options --- 79 self.radio_group_box = QGroupBox("Available Processes") 80 self.radio_group_box.setStyleSheet(""" 81 QGroupBox { 82 font-size: 13px; 83 border: 1px solid #bdc3c7; 84 border-radius: 10px; 85 padding: 20px; 86 background-color: #e5e5e5; 87 } 88 """) 89 self.radio_group_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 90 self.radio_group_box.setMaximumHeight(400) 91 92 radio_layout = QVBoxLayout() 93 radio_layout.setSpacing(20) 94 radio_layout.setContentsMargins(10, 10, 10, 10) 95 96 self.radio_group = QButtonGroup(self) 97 98 # --- Radio Buttons for each processing option --- 99 self.radio_skull = QRadioButton("Skull Stripping") 100 self.radio_draw = QRadioButton("Automatic Drawing") 101 self.radio_dl = QRadioButton("Deep Learning Segmentation") 102 self.radio_analysis = QRadioButton("Full Pipeline") 103 104 self.radio_buttons = [ 105 self.radio_skull, 106 self.radio_draw, 107 self.radio_dl, 108 self.radio_analysis, 109 ] 110 111 # Apply consistent style to all buttons 112 for btn in self.radio_buttons: 113 btn.setFont(QFont("Arial", 14)) 114 btn.setStyleSheet(""" 115 QRadioButton { 116 padding: 5px; 117 border: 1px solid #bdc3c7; 118 border-radius: 8px; 119 background-color: white; 120 spacing: 10px; 121 } 122 QRadioButton::hover { 123 background-color: #dff9fb; 124 border: 1px solid #3498db; 125 } 126 QRadioButton::checked { 127 background-color: #d6eaf8; 128 border: 2px solid #2980b9; 129 } 130 """) 131 btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 132 133 # Add buttons to layout and group 134 for i, btn in enumerate(self.radio_buttons): 135 self.radio_group.addButton(btn, id=i) 136 if btn == self.radio_dl: 137 # Add info tooltip for Deep Learning option 138 dl_widget = QWidget() 139 dl_widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 140 dl_layout = QHBoxLayout(dl_widget) 141 dl_layout.setContentsMargins(0, 0, 0, 0) 142 dl_layout.setSpacing(5) 143 144 self.dl_info_label = InfoLabel( 145 text="i", 146 tooltip_text=QCoreApplication.translate( 147 "ToolSelectionPage", 148 "To use this function you need Linux and a CUDA capable GPU" 149 ) 150 ) 151 dl_layout.addWidget(self.radio_dl) 152 dl_layout.addWidget(self.dl_info_label) 153 radio_layout.addWidget(dl_widget) 154 else: 155 radio_layout.addWidget(btn) 156 157 self.radio_group.buttonToggled.connect(lambda: self.on_selection()) 158 159 self.radio_group_box.setLayout(radio_layout) 160 self.layout.addWidget(self.radio_group_box) 161 162 self.selected_option = None 163 164 # Connect language change signal if available 165 self._translate_ui() 166 if context and "language_changed" in context: 167 context["language_changed"].connect(self._translate_ui) 168 169 # ------------------------------------------------------------------------- 170 # Navigation 171 # ------------------------------------------------------------------------- 172 173 def on_selection(self): 174 """Triggered when a radio button is toggled.""" 175 self.selected_option = self.radio_group.checkedId() 176 if self.context and "update_main_buttons" in self.context: 177 self.context["update_main_buttons"]() 178 179 def is_ready_to_advance(self): 180 """Return True if an option has been selected.""" 181 return self.radio_group.checkedId() != -1 182 183 def is_ready_to_go_back(self): 184 """Always allows going back from this page.""" 185 return True 186 187 def next(self, context): 188 """ 189 Return the next page based on the selected option. 190 191 Handles platform and CUDA availability checks for Deep Learning mode. 192 """ 193 page_classes = { 194 0: ("next_skull_stripping", SkullStrippingPage), 195 1: ("next_manual_draw", MaskNiftiSelectionPage), 196 2: ("next_deep_learning", DlNiftiSelectionPage), 197 3: ("next_pipeline", PipelinePatientSelectionPage), 198 } 199 200 if self.selected_option not in page_classes: 201 return None 202 203 # Check CUDA and OS support for Deep Learning mode 204 is_linux = platform.system() == "Linux" 205 has_gpu = torch.cuda.is_available() 206 207 if self.selected_option == 2 and (not has_gpu or not is_linux): 208 QMessageBox.warning( 209 self, 210 QCoreApplication.translate("ToolSelectionPage", "Not available for this platform"), 211 QCoreApplication.translate( 212 "ToolSelectionPage", 213 "The deep learning segmentation is not available for this platform: {0}" 214 ).format(platform.system()), 215 ) 216 return self 217 218 # Create or reuse the next page instance 219 attr_name, page_class = page_classes[self.selected_option] 220 next_page = getattr(self, attr_name, None) 221 222 if not next_page: 223 next_page = page_class(context, self) 224 setattr(self, attr_name, next_page) 225 self.context["history"].append(next_page) 226 227 next_page.on_enter() 228 return next_page 229 230 def back(self): 231 """Navigate back to the previous page if available.""" 232 if self.previous_page: 233 self.previous_page.on_enter() 234 return self.previous_page 235 return None 236 237 def reset_page(self): 238 """Reset selection state when the page is revisited.""" 239 self.radio_group.setExclusive(False) 240 for button in self.radio_group.buttons(): 241 button.setChecked(False) 242 self.radio_group.setExclusive(True) 243 self.selected_option = None 244 245 def _translate_ui(self): 246 """Translate all text elements (supports multi-language UI).""" 247 self.title.setText(QApplication.translate("ToolSelectionPage", "Select the Next Processing Step")) 248 self.radio_group_box.setTitle(QApplication.translate("ToolSelectionPage", "Available Processes")) 249 self.radio_skull.setText(QApplication.translate("ToolSelectionPage", "Skull Stripping")) 250 self.radio_draw.setText(QApplication.translate("ToolSelectionPage", "Automatic Drawing")) 251 self.radio_dl.setText(QApplication.translate("ToolSelectionPage", "Deep Learning Segmentation")) 252 self.radio_analysis.setText(QApplication.translate("ToolSelectionPage", "Full Pipeline")) 253 self.dl_info_label.setToolTip(QApplication.translate("ToolSelectionPage", "To use this function you need Linux and a CUDA capable GPU"))
A UI page for selecting the next processing step in the workflow.
This class extends Page and displays a set of radio buttons representing
different data processing operations. The user’s selection determines which
next page in the pipeline will be opened.
Attributes
context : dict Shared application state, including navigation and configuration. previous_page : Page Reference to the previous page for backward navigation. selected_option : int or None The currently selected radio button ID.
52 def __init__(self, context=None, previous_page=None): 53 """Initialize the ToolSelectionPage UI.""" 54 super().__init__() 55 56 self.context = context 57 self.previous_page = previous_page 58 59 # References to next pages for each option 60 self.next_skull_stripping = None 61 self.next_manual_draw = None 62 self.next_deep_learning = None 63 self.next_pipeline = None 64 65 # === Layout setup === 66 self.layout = QVBoxLayout(self) 67 self.layout.setContentsMargins(40, 40, 40, 40) 68 self.layout.setSpacing(30) 69 self.setLayout(self.layout) 70 71 # --- Title --- 72 self.title = QLabel("Select the Next Processing Step") 73 self.title.setAlignment(Qt.AlignmentFlag.AlignCenter) 74 self.title.setFont(QFont("Arial", 18, QFont.Weight.Bold)) 75 self.title.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) 76 self.layout.addWidget(self.title) 77 78 # --- GroupBox for options --- 79 self.radio_group_box = QGroupBox("Available Processes") 80 self.radio_group_box.setStyleSheet(""" 81 QGroupBox { 82 font-size: 13px; 83 border: 1px solid #bdc3c7; 84 border-radius: 10px; 85 padding: 20px; 86 background-color: #e5e5e5; 87 } 88 """) 89 self.radio_group_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 90 self.radio_group_box.setMaximumHeight(400) 91 92 radio_layout = QVBoxLayout() 93 radio_layout.setSpacing(20) 94 radio_layout.setContentsMargins(10, 10, 10, 10) 95 96 self.radio_group = QButtonGroup(self) 97 98 # --- Radio Buttons for each processing option --- 99 self.radio_skull = QRadioButton("Skull Stripping") 100 self.radio_draw = QRadioButton("Automatic Drawing") 101 self.radio_dl = QRadioButton("Deep Learning Segmentation") 102 self.radio_analysis = QRadioButton("Full Pipeline") 103 104 self.radio_buttons = [ 105 self.radio_skull, 106 self.radio_draw, 107 self.radio_dl, 108 self.radio_analysis, 109 ] 110 111 # Apply consistent style to all buttons 112 for btn in self.radio_buttons: 113 btn.setFont(QFont("Arial", 14)) 114 btn.setStyleSheet(""" 115 QRadioButton { 116 padding: 5px; 117 border: 1px solid #bdc3c7; 118 border-radius: 8px; 119 background-color: white; 120 spacing: 10px; 121 } 122 QRadioButton::hover { 123 background-color: #dff9fb; 124 border: 1px solid #3498db; 125 } 126 QRadioButton::checked { 127 background-color: #d6eaf8; 128 border: 2px solid #2980b9; 129 } 130 """) 131 btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 132 133 # Add buttons to layout and group 134 for i, btn in enumerate(self.radio_buttons): 135 self.radio_group.addButton(btn, id=i) 136 if btn == self.radio_dl: 137 # Add info tooltip for Deep Learning option 138 dl_widget = QWidget() 139 dl_widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) 140 dl_layout = QHBoxLayout(dl_widget) 141 dl_layout.setContentsMargins(0, 0, 0, 0) 142 dl_layout.setSpacing(5) 143 144 self.dl_info_label = InfoLabel( 145 text="i", 146 tooltip_text=QCoreApplication.translate( 147 "ToolSelectionPage", 148 "To use this function you need Linux and a CUDA capable GPU" 149 ) 150 ) 151 dl_layout.addWidget(self.radio_dl) 152 dl_layout.addWidget(self.dl_info_label) 153 radio_layout.addWidget(dl_widget) 154 else: 155 radio_layout.addWidget(btn) 156 157 self.radio_group.buttonToggled.connect(lambda: self.on_selection()) 158 159 self.radio_group_box.setLayout(radio_layout) 160 self.layout.addWidget(self.radio_group_box) 161 162 self.selected_option = None 163 164 # Connect language change signal if available 165 self._translate_ui() 166 if context and "language_changed" in context: 167 context["language_changed"].connect(self._translate_ui)
Initialize the ToolSelectionPage UI.
173 def on_selection(self): 174 """Triggered when a radio button is toggled.""" 175 self.selected_option = self.radio_group.checkedId() 176 if self.context and "update_main_buttons" in self.context: 177 self.context["update_main_buttons"]()
Triggered when a radio button is toggled.
179 def is_ready_to_advance(self): 180 """Return True if an option has been selected.""" 181 return self.radio_group.checkedId() != -1
Return True if an option has been selected.
183 def is_ready_to_go_back(self): 184 """Always allows going back from this page.""" 185 return True
Always allows going back from this page.
187 def next(self, context): 188 """ 189 Return the next page based on the selected option. 190 191 Handles platform and CUDA availability checks for Deep Learning mode. 192 """ 193 page_classes = { 194 0: ("next_skull_stripping", SkullStrippingPage), 195 1: ("next_manual_draw", MaskNiftiSelectionPage), 196 2: ("next_deep_learning", DlNiftiSelectionPage), 197 3: ("next_pipeline", PipelinePatientSelectionPage), 198 } 199 200 if self.selected_option not in page_classes: 201 return None 202 203 # Check CUDA and OS support for Deep Learning mode 204 is_linux = platform.system() == "Linux" 205 has_gpu = torch.cuda.is_available() 206 207 if self.selected_option == 2 and (not has_gpu or not is_linux): 208 QMessageBox.warning( 209 self, 210 QCoreApplication.translate("ToolSelectionPage", "Not available for this platform"), 211 QCoreApplication.translate( 212 "ToolSelectionPage", 213 "The deep learning segmentation is not available for this platform: {0}" 214 ).format(platform.system()), 215 ) 216 return self 217 218 # Create or reuse the next page instance 219 attr_name, page_class = page_classes[self.selected_option] 220 next_page = getattr(self, attr_name, None) 221 222 if not next_page: 223 next_page = page_class(context, self) 224 setattr(self, attr_name, next_page) 225 self.context["history"].append(next_page) 226 227 next_page.on_enter() 228 return next_page
Return the next page based on the selected option.
Handles platform and CUDA availability checks for Deep Learning mode.
230 def back(self): 231 """Navigate back to the previous page if available.""" 232 if self.previous_page: 233 self.previous_page.on_enter() 234 return self.previous_page 235 return None
Navigate back to the previous page if available.
237 def reset_page(self): 238 """Reset selection state when the page is revisited.""" 239 self.radio_group.setExclusive(False) 240 for button in self.radio_group.buttons(): 241 button.setChecked(False) 242 self.radio_group.setExclusive(True) 243 self.selected_option = None
Reset selection state when the page is revisited.