def __init__(self, settings: PartSettings): super().__init__() self.settings = settings self.save_translate_dict: typing.Dict[str, SaveBase] = {x.get_short_name(): x for x in save_dict.values()} self.plan = PlanPreview(self) self.save_plan_btn = QPushButton("Save") self.clean_plan_btn = QPushButton("Remove all") self.remove_btn = QPushButton("Remove") self.update_element_chk = QCheckBox("Update element") self.change_root = EnumComboBox(RootType) self.save_choose = QComboBox() self.save_choose.addItem("<none>") self.save_choose.addItems(list(self.save_translate_dict.keys())) self.save_btn = QPushButton("Save") self.segment_profile = SearchableListWidget() self.pipeline_profile = SearchableListWidget() self.segment_stack = QTabWidget() self.segment_stack.addTab(self.segment_profile, "Profile") self.segment_stack.addTab(self.pipeline_profile, "Pipeline") self.generate_mask_btn = QPushButton("Add mask") self.generate_mask_btn.setToolTip("Mask need to have unique name") self.mask_name = QLineEdit() self.mask_operation = EnumComboBox(MaskOperation) self.chanel_num = QSpinBox() self.choose_channel_for_measurements = QComboBox() self.choose_channel_for_measurements.addItems( ["Same as segmentation"] + [str(x + 1) for x in range(MAX_CHANNEL_NUM)] ) self.units_choose = EnumComboBox(Units) self.units_choose.set_value(self.settings.get("units_value", Units.nm)) self.chanel_num.setRange(0, 10) self.expected_node_type = None self.save_constructor = None self.chose_profile_btn = QPushButton("Add Profile") self.get_big_btn = QPushButton("Leave the biggest") self.get_big_btn.hide() self.get_big_btn.setDisabled(True) self.measurements_list = SearchableListWidget(self) self.measurement_name_prefix = QLineEdit(self) self.add_calculation_btn = QPushButton("Add measurement calculation") self.information = QTextEdit() self.information.setReadOnly(True) self.protect = False self.mask_set = set() self.calculation_plan = CalculationPlan() self.plan.set_plan(self.calculation_plan) self.segmentation_mask = MaskWidget(settings) self.file_mask = FileMask() self.change_root.currentIndexChanged.connect(self.change_root_type) self.save_choose.currentTextChanged.connect(self.save_changed) self.measurements_list.currentTextChanged.connect(self.show_measurement) self.segment_profile.currentTextChanged.connect(self.show_segment) self.measurements_list.currentTextChanged.connect(self.show_measurement_info) self.segment_profile.currentTextChanged.connect(self.show_segment_info) self.pipeline_profile.currentTextChanged.connect(self.show_segment_info) self.pipeline_profile.currentTextChanged.connect(self.show_segment) self.mask_name.textChanged.connect(self.mask_name_changed) self.generate_mask_btn.clicked.connect(self.create_mask) self.clean_plan_btn.clicked.connect(self.clean_plan) self.remove_btn.clicked.connect(self.remove_element) self.mask_name.textChanged.connect(self.mask_text_changed) self.chose_profile_btn.clicked.connect(self.add_segmentation) self.get_big_btn.clicked.connect(self.add_leave_biggest) self.add_calculation_btn.clicked.connect(self.add_measurement) self.save_plan_btn.clicked.connect(self.add_calculation_plan) # self.forgot_mask_btn.clicked.connect(self.forgot_mask) # self.cmap_save_btn.clicked.connect(self.save_to_cmap) self.save_btn.clicked.connect(self.add_save_to_project) self.update_element_chk.stateChanged.connect(self.mask_text_changed) self.update_element_chk.stateChanged.connect(self.show_measurement) self.update_element_chk.stateChanged.connect(self.show_segment) self.update_element_chk.stateChanged.connect(self.update_names) self.segment_stack.currentChanged.connect(self.change_segmentation_table) plan_box = QGroupBox("Prepare workflow:") lay = QVBoxLayout() lay.addWidget(self.plan) bt_lay = QGridLayout() bt_lay.setSpacing(1) bt_lay.addWidget(self.save_plan_btn, 0, 0) bt_lay.addWidget(self.clean_plan_btn, 0, 1) bt_lay.addWidget(self.remove_btn, 1, 0) bt_lay.addWidget(self.update_element_chk, 1, 1) lay.addLayout(bt_lay) plan_box.setLayout(lay) plan_box.setStyleSheet(group_sheet) other_box = QGroupBox("Other operations:") other_box.setContentsMargins(0, 0, 0, 0) bt_lay = QVBoxLayout() bt_lay.setSpacing(0) bt_lay.addWidget(QLabel("Root type:")) bt_lay.addWidget(self.change_root) bt_lay.addStretch(1) bt_lay.addWidget(QLabel("Saving:")) bt_lay.addWidget(self.save_choose) bt_lay.addWidget(self.save_btn) other_box.setLayout(bt_lay) other_box.setStyleSheet(group_sheet) mask_box = QGroupBox("Use mask from:") mask_box.setStyleSheet(group_sheet) self.mask_stack = QTabWidget() self.mask_stack.addTab(stretch_widget(self.file_mask), "File") self.mask_stack.addTab(stretch_widget(self.segmentation_mask), "Current ROI") self.mask_stack.addTab(stretch_widget(self.mask_operation), "Operations on masks") self.mask_stack.setTabToolTip(2, "Allows to create mask which is based on masks previously added to plan.") lay = QGridLayout() lay.setSpacing(0) lay.addWidget(self.mask_stack, 0, 0, 1, 2) label = QLabel("Mask name:") label.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'") self.mask_name.setToolTip("Needed if you would like to reuse this mask in tab 'Operations on masks'") lay.addWidget(label, 1, 0) lay.addWidget(self.mask_name, 1, 1) lay.addWidget(self.generate_mask_btn, 2, 0, 1, 2) mask_box.setLayout(lay) segment_box = QGroupBox("ROI extraction:") segment_box.setStyleSheet(group_sheet) lay = QVBoxLayout() lay.setSpacing(0) lay.addWidget(self.segment_stack) lay.addWidget(self.chose_profile_btn) lay.addWidget(self.get_big_btn) segment_box.setLayout(lay) measurement_box = QGroupBox("Set of measurements:") measurement_box.setStyleSheet(group_sheet) lay = QGridLayout() lay.setSpacing(0) lay.addWidget(self.measurements_list, 0, 0, 1, 2) lab = QLabel("Name prefix:") lab.setToolTip("Prefix added before each column name") lay.addWidget(lab, 1, 0) lay.addWidget(self.measurement_name_prefix, 1, 1) lay.addWidget(QLabel("Channel:"), 2, 0) lay.addWidget(self.choose_channel_for_measurements, 2, 1) lay.addWidget(QLabel("Units:"), 3, 0) lay.addWidget(self.units_choose, 3, 1) lay.addWidget(self.add_calculation_btn, 4, 0, 1, 2) measurement_box.setLayout(lay) info_box = QGroupBox("Information") info_box.setStyleSheet(group_sheet) lay = QVBoxLayout() lay.addWidget(self.information) info_box.setLayout(lay) layout = QGridLayout() fst_col = QVBoxLayout() fst_col.addWidget(plan_box, 1) fst_col.addWidget(mask_box) layout.addWidget(plan_box, 0, 0, 5, 1) # layout.addWidget(plan_box, 0, 0, 3, 1) # layout.addWidget(mask_box, 3, 0, 2, 1) # layout.addWidget(segmentation_mask_box, 1, 1) layout.addWidget(mask_box, 0, 2, 1, 2) layout.addWidget(other_box, 0, 1) layout.addWidget(segment_box, 1, 1, 1, 2) layout.addWidget(measurement_box, 1, 3) layout.addWidget(info_box, 3, 1, 1, 3) self.setLayout(layout) self.generate_mask_btn.setDisabled(True) self.chose_profile_btn.setDisabled(True) self.add_calculation_btn.setDisabled(True) self.mask_allow = False self.segment_allow = False self.file_mask_allow = False self.node_type = NodeType.root self.node_name = "" self.plan_node_changed.connect(self.mask_text_changed) self.plan.changed_node.connect(self.node_type_changed) self.plan_node_changed.connect(self.show_segment) self.plan_node_changed.connect(self.show_measurement) self.plan_node_changed.connect(self.mask_stack_change) self.mask_stack.currentChanged.connect(self.mask_stack_change) self.file_mask.value_changed.connect(self.mask_stack_change) self.mask_name.textChanged.connect(self.mask_stack_change) self.node_type_changed()
class PeaksViewerView(QWidget): """Displays a table view of the PeaksWorkspace along with controls to interact with the peaks. """ TITLE_PREFIX = "Workspace: " def __init__(self, painter, sliceinfo_provider, parent=None): """ :param painter: An object responsible for draw the peaks representations :param sliceinfo_provider: An object responsible for providing access to current slice information :param parent: An optional parent widget """ super().__init__(parent) self._painter = painter self._sliceinfo_provider = sliceinfo_provider self._group_box = None self._presenter = None self._table_view = None self._setup_ui() @property def painter(self): """Return a reference to the painter used in this view""" return self._painter @property def sliceinfo(self): """Return information regarding the current slice""" return self._sliceinfo_provider.get_sliceinfo() @property def table_view(self): return self._table_view @property def selected_index(self): return self._selected_index() def set_axes_limits(self, xlim, ylim, auto_transform): """ Set the view limits on the image axes to the given extents :param xlim: 2-tuple of (xmin, xmax) :param ylim: 2-tuple of (ymin, ymax) :param auto_transform: If True, the limits are transformed into the rectilinear frame using the transform provided by the sliceinfo """ self._sliceinfo_provider.set_axes_limits(xlim, ylim, auto_transform) def set_peak_color(self, peak_color): """ Set the color of the peak represented in this view :param peak_color: A str describing the color of the displayed peak """ self._group_box.setStyleSheet(f"QGroupBox {{color: {peak_color}}};") def set_slicepoint(self, value): """ Set the slice point to the given value :param value: Float giving the current slice value """ self._sliceinfo_provider.set_slicepoint(value) def set_title(self, name): """ :param name: Set the name label for the workspace """ self._group_box.setTitle(self.TITLE_PREFIX + name) def subscribe(self, presenter): """ :param presenter: An object to handle GUI events. """ self._presenter = presenter self._table_view.subscribe(presenter) # private api def _setup_ui(self): """ Arrange the widgets on the window """ self._group_box = QGroupBox(self) self._group_box.setContentsMargins(0, 0, 0, 0) self._table_view = _PeaksWorkspaceTableView(parent=self, key_handler=self) self._table_view.setSelectionBehavior( _PeaksWorkspaceTableView.SelectRows) self._table_view.setSelectionMode( _PeaksWorkspaceTableView.SingleSelection) self._table_view.clicked.connect(self._on_row_clicked) self._table_view.verticalHeader().sectionClicked.connect( self._row_selected) group_box_layout = QVBoxLayout() group_box_layout.addWidget(self._table_view) self._group_box.setLayout(group_box_layout) widget_layout = QVBoxLayout() widget_layout.addWidget(self._group_box) self.setLayout(widget_layout) def _on_row_clicked(self, _): """ When a peak is clicked check if it is already selected and notify that this peak has been selected again. Handles the case when selecting the same peak needs to reset another view. Care is taken to avoid emitting the peak selection notification when peak selection changes as _on_row_selection_changed handles this. """ self._row_selected() def _row_selected(self): """ Notify that a different peak has been selected. It is assumed only single row selection is allowed """ self._presenter.notify(self._presenter.Event.PeakSelected) def _selected_index(self): # construction ensures we can only have 0 or 1 items selected selected = self.table_view.selectedIndexes() if not selected: return None return self.table_view.proxy_model.mapToSource(selected[0]).row()
class PeaksViewerView(QWidget): """Displays a table view of the PeaksWorkspace along with controls to interact with the peaks. """ TITLE_PREFIX = "Workspace: " def __init__(self, painter, sliceinfo_provider, parent=None): """ :param painter: An object responsible for draw the peaks representations :param sliceinfo_provider: An object responsible for providing access to current slice information :param parent: An optional parent widget """ super().__init__(parent) self._painter = painter self._sliceinfo_provider = sliceinfo_provider self._current_selection = None self._group_box = None self._presenter = None self._table_view = None self._setup_ui() @property def painter(self): """Return a reference to the painter used in this view""" return self._painter @property def sliceinfo(self): """Return information regarding the current slice""" return self._sliceinfo_provider.get_sliceinfo() @property def table_view(self): return self._table_view @property def selected_index(self): # cache current selection for checking in mouse click handler self._current_selection = self._selected_index() return self._current_selection def set_peak_color(self, peak_color): """ Set the color of the peak represented in this view :param peak_color: A str describing the color of the displayed peak """ self._group_box.setStyleSheet(f"QGroupBox {{color: {peak_color}}};") def set_slicepoint(self, value): """ Set the slice point to the given value :param value: Float giving the current slice value """ self._sliceinfo_provider.set_slicevalue(value) def set_title(self, name): """ :param name: Set the name label for the workspace """ self._group_box.setTitle(self.TITLE_PREFIX + name) def subscribe(self, presenter): """ :param presenter: An object to handle GUI events. """ self._presenter = presenter self._table_view.subscribe(presenter) # private api def _setup_ui(self): """ Arrange the widgets on the window """ self._group_box = QGroupBox(self) self._group_box.setContentsMargins(0, 0, 0, 0) self._table_view = PeaksWorkspaceTableView(parent=self) self._table_view.setSelectionBehavior( PeaksWorkspaceTableView.SelectRows) self._table_view.setSelectionMode( PeaksWorkspaceTableView.SingleSelection) # itemSelectionChanges handles selection changes from either keyboard/mouse self._table_view.itemSelectionChanged.connect( self._on_row_selection_changed) # the selection might not change when an item is clicked but we want to notify # the outside world self._table_view.itemClicked.connect(self._on_row_clicked) group_box_layout = QVBoxLayout() group_box_layout.addWidget(self._table_view) self._group_box.setLayout(group_box_layout) widget_layout = QVBoxLayout() widget_layout.addWidget(self._group_box) self.setLayout(widget_layout) def _on_row_selection_changed(self): """ Notify that a different peak has been selected. It is assumed only single row selection is allowed """ self._presenter.notify(self._presenter.Event.PeakSelected) def _on_row_clicked(self, _): """ When a peak is clicked check if it is already selected and notify that this peak has been selected again. Handles the case when selecting the same peak needs to reset another view. Care is taken to avoid emitting the peak selection notification when peak selection changes as _on_row_selection_changed handles this. """ if self._current_selection == self._selected_index(): self._presenter.notify(self._presenter.Event.PeakSelected) def _selected_index(self): # construction ensures we can only have 0 or 1 items selected selected = self.table_view.selectedItems() if not selected: return None return self.table_view.row(selected[0])