def __init__(self, parent=None): QWidget.__init__(self, parent) # Widgets, layouts and signals self._group = QButtonGroup() layout = QGridLayout() layout.setSpacing(0) ## Element for z, position in _ELEMENT_POSITIONS.items(): widget = ElementPushButton(z) widget.setCheckable(True) layout.addWidget(widget, *position) self._group.addButton(widget, z) ## Labels layout.addWidget(QLabel(''), 7, 0) # Dummy layout.addWidget(QLabel('*'), 5, 2, Qt.AlignRight) layout.addWidget(QLabel('*'), 8, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 6, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 9, 2, Qt.AlignRight) for row in [0, 1, 2, 3, 4, 5, 6, 8, 9]: layout.setRowStretch(row, 1) self.setLayout(layout) # Signals self._group.buttonClicked.connect(self.selectionChanged) # Default self.setColorFunction(_category_color_function)
def __init__(self, settings: PartSettings): super().__init__() self.settings = settings self.calculate_plans = SearchableListWidget(self) self.plan_view = PlanPreview(self) self.delete_plan_btn = QPushButton("Delete") self.edit_plan_btn = QPushButton("Edit") self.export_plans_btn = QPushButton("Export") self.import_plans_btn = QPushButton("Import") info_layout = QVBoxLayout() info_butt_layout = QGridLayout() info_butt_layout.setSpacing(1) info_butt_layout.addWidget(self.delete_plan_btn, 1, 1) info_butt_layout.addWidget(self.edit_plan_btn, 0, 1) info_butt_layout.addWidget(self.export_plans_btn, 1, 0) info_butt_layout.addWidget(self.import_plans_btn, 0, 0) info_layout.addLayout(info_butt_layout) info_chose_layout = QVBoxLayout() info_chose_layout.setSpacing(2) info_chose_layout.addWidget(QLabel("List of workflows:")) info_chose_layout.addWidget(self.calculate_plans) info_chose_layout.addWidget(QLabel("Preview:")) info_chose_layout.addWidget(self.plan_view) info_layout.addLayout(info_chose_layout) self.setLayout(info_layout) self.calculate_plans.addItems(list(sorted(self.settings.batch_plans.keys()))) self.protect = False self.plan_to_edit = None self.plan_view.header().close() self.calculate_plans.currentTextChanged.connect(self.plan_preview) self.delete_plan_btn.clicked.connect(self.delete_plan) self.edit_plan_btn.clicked.connect(self.edit_plan) self.export_plans_btn.clicked.connect(self.export_plans) self.import_plans_btn.clicked.connect(self.import_plans)
class HistogramWidget(QWidget): def __init__( self, x: np.ndarray, y: np.ndarray, xlabel: Optional[str] = None, ylabel: Optional[str] = None, parent=None, ): super(HistogramWidget, self).__init__(parent) # set the width of the histogram plot to match the napari layer control width self.setMinimumWidth(240) self.setMaximumWidth(240) self.hist_plot = Histogram(x, y, xlabel=xlabel, ylabel=ylabel, parent=self) self.hist_plot.setMaximumWidth(230) self.thresh_text = QLineEdit() self.text_layout = QHBoxLayout() self.text_layout.addWidget(QLabel("SNR threshold:")) self.text_layout.addWidget(self.thresh_text) self.text_layout.addItem(QSpacerItem(5, 1)) self.grid_layout = QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) # self.grid_layout.setColumnMinimumWidth(0, 86) # self.grid_layout.setColumnStretch(1, 1) self.grid_layout.addWidget(self.hist_plot, 0, 0, 4, 6) self.grid_layout.addLayout(self.text_layout, 4, 0) self.grid_layout.setRowStretch(5, 1) self.grid_layout.setColumnStretch(1, 1) self.setLayout(self.grid_layout) self.threshold_changed_callbacks = [] self._on_hist_thresh_change() # connect events self.hist_plot.connect_line_dragged(self._on_hist_thresh_change) self.thresh_text.returnPressed.connect(self._on_thresh_text_change) def _on_hist_thresh_change(self): hist_thresh_value = self.hist_plot._vert_line.getPos()[0] self.thresh_text.setText(f"{hist_thresh_value:.2f}") for func in self.threshold_changed_callbacks: func() def _on_thresh_text_change(self): hist_thresh_value = float(self.thresh_text.text()) self.hist_plot._vert_line.setValue(hist_thresh_value)
class QtLayerControls(QFrame): def __init__(self, layer): super().__init__() self.layer = layer layer.events.blending.connect(self._on_blending_change) layer.events.opacity.connect(self._on_opacity_change) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout() self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.setLayout(self.grid_layout) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(self.layer.opacity * 100) sld.valueChanged[int].connect( lambda value=sld: self.changeOpacity(value) ) self.opacitySilder = sld blend_comboBox = QComboBox() for blend in Blending: blend_comboBox.addItem(str(blend)) index = blend_comboBox.findText( self.layer.blending, Qt.MatchFixedString ) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect( lambda text=blend_comboBox: self.changeBlending(text) ) self.blendComboBox = blend_comboBox def changeOpacity(self, value): with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value / 100 def changeBlending(self, text): self.layer.blending = text def _on_opacity_change(self, event): with self.layer.events.opacity.blocker(): self.opacitySilder.setValue(self.layer.opacity * 100) def _on_blending_change(self, event): with self.layer.events.blending.blocker(): index = self.blendComboBox.findText( self.layer.blending, Qt.MatchFixedString ) self.blendComboBox.setCurrentIndex(index)
def comboFeatureLayout(self, feature, combo, action): # QGridLayout: https://stackoverflow.com/questions/61451279/how-does-setcolumnstretch-and-setrowstretch-works layout = QGridLayout() layout.setSpacing(5) # combo layout.addWidget(combo, 0, 0, 1, 3) # button button = QPushButton(config.thisTranslation[feature]) button.clicked.connect(action) layout.addWidget(button, 0, 3, 1, 1) return layout
def add_buttons_for_images(self): """.""" grpbx = QGroupBox('Other Graphs', self) gdl = QGridLayout(grpbx) gdl.setSpacing(2) self.hbl_nameh.addWidget(grpbx) btn = QPushButton('Corrs', grpbx) gdl.addWidget(btn, 0, 0) Window = create_window_from_widget(CorrectorsWidget, title='Correctors') _util.connect_window(btn, Window, self, device=self.device, prefix=self.prefix, acc=self.acc) if self.isring: btn = QPushButton('MTurn Orb', grpbx) gdl.addWidget(btn, 0, 1) Window = create_window_from_widget(MultiTurnWidget, title='Multi Turn') _util.connect_window(btn, Window, self, sigs=self.updater[0].raw_ref_sig, device=self.device, prefix=self.prefix, csorb=self._csorb) btn = QPushButton('MTurn Sum', grpbx) gdl.addWidget(btn, 0, 2) Window = create_window_from_widget(MultiTurnSumWidget, title='Multi Turn Sum') _util.connect_window(btn, Window, self, device=self.device, prefix=self.prefix, csorb=self._csorb) btn = QPushButton('SingPass Sum', grpbx) gdl.addWidget(btn, 0, 3) Window = create_window_from_widget(SinglePassSumWidget, title='Single Pass Sum') _util.connect_window(btn, Window, self, device=self.device, prefix=self.prefix, csorb=self._csorb)
def setupUI(self, widget): self.ui.setupUi(widget) root_widgets = [w for w in widget.children() if w.__class__.__name__ == "QWidget"] if len(root_widgets) == 0 or widget.layout() is not None: self.ui_widget = self else: self.ui_widget = root_widgets[-1] g = QGridLayout() if qtpy.API=="pyqt5": pass else: if isinstance(self, QtWidgetLoader): g.setMargin(0) g.setSpacing(0) widget.setLayout(g) g.addWidget(self.ui_widget)
def _create_workdir_manager(self): self.workdir_ledit = QLineEdit() self.workdir_ledit.setReadOnly(True) self.workdir_btn = QToolButton() self.workdir_btn.setIcon(get_icon('folder_open')) self.workdir_btn.setAutoRaise(True) self.workdir_btn.setToolTip("Browse a working directory...") self.workdir_btn.clicked.connect(self.select_working_directory) workdir_widget = QWidget() workdir_layout = QGridLayout(workdir_widget) workdir_layout.setContentsMargins(0, 0, 0, 0) workdir_layout.setSpacing(1) workdir_layout.addWidget(QLabel('Working Directory:'), 0, 0) workdir_layout.addWidget(self.workdir_ledit, 0, 1) workdir_layout.addWidget(self.workdir_btn, 0, 2) return workdir_widget
def setup_layout(self): self.instantiated = False layout = QGridLayout() self.load_atlas_button = add_button( "Load atlas", layout, self.load_atlas, 0, 0, minimum_width=200, ) self.load_reference_button = add_button( "Load reference image", layout, self.load_reference, 1, 0, visibility=False, ) self.load_annotated_button = add_button( "Load annotated image", layout, self.load_annotated, 2, 0, visibility=False, ) layout.setAlignment(QtCore.Qt.AlignTop) layout.setSpacing(4) self.status_label = QLabel() self.status_label.setText("Ready") layout.addWidget(self.status_label, 4, 0) self.info_box = QTextBrowser() self.info_box.setVisible(False) layout.addWidget(self.info_box) self.setLayout(layout)
def setupui(self): vbl = QVBoxLayout(self) lab = QLabel('<h2>BPMs List</h2>', alignment=Qt.AlignCenter) vbl.addWidget(lab) vbl.addSpacing(20) hbl = QHBoxLayout() search = QLineEdit(parent=self) search.setPlaceholderText("Search for BPMs...") search.textEdited.connect(self._filter_bpms) hbl.addWidget(search) hbl.addStretch() self.btnautorange = QPushButton('Auto Range graphics', self) hbl.addWidget(self.btnautorange) vbl.addItem(hbl) sa_class = get_custom_widget_class(QScrollArea) scarea = sa_class(self) scarea.setSizeAdjustPolicy(scarea.AdjustToContents) scarea.setWidgetResizable(True) wid = QWidget() wid.setObjectName('scrollarea') wid.setStyleSheet('#scrollarea {background-color: transparent;}') gdl = QGridLayout(wid) gdl.setSpacing(15) for i, bpm in enumerate(sorted(self.bpm_dict.keys())): widb = QWidget(wid) vbl2 = QVBoxLayout(widb) vbl2.addWidget( QLabel('<h3>' + bpm + '</h3>', alignment=Qt.AlignCenter)) wbpm = self.create_graph(widb, bpm=bpm, typ='ant') vbl2.addWidget(wbpm) gdl.addWidget(widb, i // 3, i % 3) self.bpm_dict[bpm] = widb self.gdl = gdl vbl.addWidget(scarea) scarea.setWidget(wid) self.scarea = scarea
def setup_layout(self): self.instantiated = False layout = QGridLayout() self.load_button = add_button( "Load project", layout, self.load_amap_directory, 0, 0, minimum_width=200, ) self.load_atlas_button = add_button("Load atlas", layout, self.load_atlas, 0, 1, visibility=False) self.save_button = add_button("Save", layout, self.save, 7, 1, visibility=False) self.status_label = QLabel() self.status_label.setText("Ready") layout.addWidget(self.status_label, 8, 0) layout.setAlignment(QtCore.Qt.AlignTop) layout.setSpacing(4) self.setLayout(layout) self.add_track_panel(layout) self.add_region_panel(layout) self.add_brainrender_panel(layout) self.setLayout(layout)
def __init__(self, parent=None, init_channel=None): """Init.""" super().__init__(parent) self._init_channel = init_channel layout = QGridLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) self.lineedit = SiriusLineEdit( parent=self, init_channel=init_channel) self.lineedit.setAlignment(Qt.AlignCenter) self.lineedit.setStyleSheet("SiriusLineEdit{min-height:1.29em;}") self.lineedit.setSizePolicy(QSzPol.Expanding, QSzPol.Preferred) self.scrollbar = PyDMScrollBar( parent=self, init_channel=init_channel) self.scrollbar.wheelEvent = lambda event: event.ignore() self.scrollbar.setTracking(False) self.scrollbar.setStyleSheet("PyDMScrollBar{max-height:0.7em;}") layout.addWidget(self.lineedit, 0, 0, 2, 1) layout.addWidget(self.scrollbar, 2, 0, 1, 1)
def __init__(self, settings: PartSettings): super().__init__() self._settings = settings self.export_btn = QPushButton("Export profile") self.export_btn.clicked.connect(self.export_profile) self.import_btn = QPushButton("Import profile") self.import_btn.clicked.connect(self.import_profiles) self.export_pipeline_btn = QPushButton("Export pipeline") self.export_pipeline_btn.clicked.connect(self.export_pipeline) self.import_pipeline_btn = QPushButton("Import pipeline") self.import_pipeline_btn.clicked.connect(self.import_pipeline) self.delete_btn = QPushButton("Delete profile") self.delete_btn.setDisabled(True) self.delete_btn.clicked.connect(self.delete_profile) self.multiple_files_chk = QCheckBox("Show multiple files panel") self._update_measurement_chk() self.multiple_files_chk.stateChanged.connect( self.multiple_files_visibility) self.rename_btn = QPushButton("Rename profile") self.rename_btn.clicked.connect(self.rename_profile) self.rename_btn.setDisabled(True) self.voxel_size_label = QLabel() self.info_label = QPlainTextEdit() self.info_label.setReadOnly(True) self.profile_list = SearchableListWidget() self.profile_list.currentTextChanged.connect(self.profile_chosen) self.pipeline_list = SearchableListWidget() self.pipeline_list.currentTextChanged.connect(self.profile_chosen) self.spacing = [QDoubleSpinBox() for _ in range(3)] self.lock_spacing = LockCheckBox() self.lock_spacing.stateChanged.connect(self.spacing[1].setDisabled) self.lock_spacing.stateChanged.connect(self.synchronize_spacing) # noinspection PyUnresolvedReferences self.spacing[2].valueChanged.connect(self.synchronize_spacing) self._settings.roi_profiles_changed.connect(self.update_profile_list) self._settings.roi_pipelines_changed.connect(self.update_profile_list) self._settings.connect_("multiple_files_widget", self._update_measurement_chk) units_value = self._settings.get("units_value", Units.nm) for el in self.spacing: el.setAlignment(Qt.AlignRight) el.setButtonSymbols(QAbstractSpinBox.NoButtons) el.setRange(0, 1000000) # noinspection PyUnresolvedReferences el.valueChanged.connect(self.image_spacing_change) self.units = QEnumComboBox(enum_class=Units) self.units.setCurrentEnum(units_value) # noinspection PyUnresolvedReferences self.units.currentIndexChanged.connect(self.update_spacing) spacing_layout = QHBoxLayout() spacing_layout.addWidget(self.lock_spacing) for txt, el in zip(["x", "y", "z"], self.spacing[::-1]): spacing_layout.addWidget(QLabel(txt + ":")) spacing_layout.addWidget(el) spacing_layout.addWidget(self.units) spacing_layout.addStretch(1) voxel_size_layout = QHBoxLayout() voxel_size_layout.addWidget(self.voxel_size_label) voxel_size_layout.addSpacing(30) profile_layout = QGridLayout() profile_layout.setSpacing(0) profile_layout.addWidget(QLabel("Profiles:"), 0, 0) profile_layout.addWidget(self.profile_list, 1, 0) profile_layout.addWidget(QLabel("Pipelines:"), 2, 0) profile_layout.addWidget(self.pipeline_list, 3, 0, 4, 1) profile_layout.addWidget(self.info_label, 1, 1, 3, 2) profile_layout.addWidget(self.export_btn, 4, 1) profile_layout.addWidget(self.import_btn, 4, 2) profile_layout.addWidget(self.export_pipeline_btn, 5, 1) profile_layout.addWidget(self.import_pipeline_btn, 5, 2) profile_layout.addWidget(self.delete_btn, 6, 1) profile_layout.addWidget(self.rename_btn, 6, 2) layout = QVBoxLayout() layout.addLayout(spacing_layout) layout.addLayout(voxel_size_layout) layout.addWidget(self.multiple_files_chk) layout.addLayout(profile_layout, 1) self.setLayout(layout) self.update_profile_list()
class PreferencesDialog(DialogBase): """Application preferences dialog.""" sig_urls_updated = Signal(str, str) sig_check_ready = Signal() sig_reset_ready = Signal() def __init__(self, config=CONF, **kwargs): """Application preferences dialog.""" super(PreferencesDialog, self).__init__(**kwargs) self.api = AnacondaAPI() self.widgets_changed = set() self.widgets = [] self.widgets_dic = {} self.config = config # Widgets self.button_ok = ButtonPrimary('Apply') self.button_cancel = ButtonNormal('Cancel') self.button_reset = ButtonNormal('Reset to defaults') self.row = 0 # Widget setup self.setWindowTitle("Preferences") # Layouts self.grid_layout = QGridLayout() buttons_layout = QHBoxLayout() buttons_layout.addWidget(self.button_reset) buttons_layout.addStretch() buttons_layout.addWidget(self.button_cancel) buttons_layout.addWidget(SpacerHorizontal()) buttons_layout.addWidget(self.button_ok) main_layout = QVBoxLayout() main_layout.addLayout(self.grid_layout) main_layout.addWidget(SpacerVertical()) main_layout.addWidget(SpacerVertical()) main_layout.addLayout(buttons_layout) self.setLayout(main_layout) # Signals self.button_ok.clicked.connect(self.accept) self.button_cancel.clicked.connect(self.reject) self.button_reset.clicked.connect(self.reset_to_defaults) self.button_reset.clicked.connect( lambda: self.button_ok.setEnabled(True) ) # Setup self.grid_layout.setSpacing(0) self.setup() self.button_ok.setDisabled(True) self.widgets[0].setFocus() self.button_ok.setDefault(True) self.button_ok.setAutoDefault(True) # --- Helpers # ------------------------------------------------------------------------- def get_option(self, option): """Get configuration option from `main` section.""" return self.config.get('main', option, None) def set_option(self, option, value): """Set configuration option in `main` section.""" self.config.set('main', option, value) def get_option_default(self, option): """Get configuration option default value in `main` section.""" return self.config.get_default('main', option) def set_option_default(self, option): """Set configuration option default value in `main` section.""" self.set_option(option, self.get_option_default(option)) def create_widget( self, widget=None, label=None, option=None, hint=None, check=None, info=None, ): """Create preference option widget and add to layout.""" config_value = self.get_option(option) widget._text = label widget.label = QLabel(label) widget.option = option widget.set_value(config_value) widget.label_information = QLabel() widget.label_information.setMinimumWidth(16) widget.label_information.setMaximumWidth(16) form_widget = QWidget() h_layout = QHBoxLayout() h_layout.addSpacing(4) h_layout.addWidget(widget.label_information, 0, Qt.AlignRight) h_layout.addWidget(widget, 0, Qt.AlignLeft) h_layout.addWidget(QLabel(hint or ''), 0, Qt.AlignLeft) form_widget.setLayout(h_layout) if check: widget.check_value = lambda value: check(value) else: widget.check_value = lambda value: (True, '') if info: label = widget.label_information label = PreferencesDialog.update_icon(label, INFO_ICON) label.setToolTip(info) self.widgets.append(widget) self.widgets_dic[option] = widget self.grid_layout.addWidget( widget.label, self.row, 0, Qt.AlignRight | Qt.AlignCenter ) self.grid_layout.addWidget( form_widget, self.row, 1, Qt.AlignLeft | Qt.AlignCenter ) self.row += 1 def create_textbox(self, label, option, hint=None, check=None, info=None): """Create textbox (QLineEdit) preference option.""" widget = QLineEdit() widget.setAttribute(Qt.WA_MacShowFocusRect, False) widget.setMinimumWidth(250) widget.get_value = lambda w=widget: w.text() widget.set_value = lambda value, w=widget: w.setText(value) widget.set_warning = lambda w=widget: w.setSelection(0, 1000) widget.textChanged.connect( lambda v=None, w=widget: self.options_changed(widget=w) ) self.create_widget( widget=widget, option=option, label=label, hint=hint, check=check, info=info, ) def create_checkbox(self, label, option, check=None, hint=None, info=None): """Create checkbox preference option.""" widget = QCheckBox() widget.get_value = lambda w=widget: bool(w.checkState()) widget.set_value = lambda value, w=widget: bool( w.setCheckState(Qt.Checked if value else Qt.Unchecked) ) api_widget = self.widgets_dic['anaconda_api_url'] widget.set_warning = lambda w=widget: api_widget widget.stateChanged.connect( lambda v=None, w=widget: self.options_changed(widget=w) ) self.create_widget( widget=widget, option=option, label=label, hint=hint, check=check, info=info, ) def options_changed(self, value=None, widget=None): """Callback helper triggered on preference value change.""" config_value = self.get_option(widget.option) if config_value != widget.get_value(): self.widgets_changed.add(widget) else: if widget in self.widgets_changed: self.widgets_changed.remove(widget) self.button_ok.setDisabled(not bool(len(self.widgets_changed))) def widget_for_option(self, option): """Return the widget for the given option.""" return self.widgets_dic[option] # --- API # ------------------------------------------------------------------------- def set_initial_values(self): """ Set configuration values found in other config files. Some options of configuration are found in condarc or in anaconda-client configuration. """ self.config.set( 'main', 'anaconda_api_url', self.api.client_get_api_url() ) # See https://conda.io/docs/install/central.html # ssl_verify overloads True/False/<Path to certificate> # Navigator splits that into 2 separate options for clarity ssl_verify = self.api.client_get_ssl() if isinstance(ssl_verify, bool): self.config.set('main', 'ssl_verification', ssl_verify) self.config.set('main', 'ssl_certificate', None) else: self.config.set('main', 'ssl_verification', True) self.config.set('main', 'ssl_certificate', ssl_verify) def setup(self): """Setup the preferences dialog.""" def api_url_checker(value): """ Custom checker to use selected ssl option instead of stored one. This allows to set an unsafe api url directly on the preferences dialog. Without this, one would have to first disable, click accept, then open preferences again and change api url for it to work. """ # Ssl widget ssl_widget = self.widgets_dic.get('ssl_verification') verify = ssl_widget.get_value() if ssl_widget else True # Certificate path ssl_cert_widget = self.widgets_dic.get('ssl_certificate') if ssl_cert_widget: verify = ssl_cert_widget.get_value() # Offline mode offline_widget = self.widgets_dic.get('offline_mode') if ssl_widget or ssl_cert_widget: offline_mode = offline_widget.get_value() else: offline_mode = False if offline_mode: basic_check = ( False, 'API Domain cannot be modified when ' 'working in <b>offline mode</b>.<br>', ) else: basic_check = self.is_valid_api(value, verify=verify) return basic_check def ssl_checker(value): """Counterpart to api_url_checker.""" api_url_widget = self.widgets_dic.get('anaconda_api_url') api_url = api_url_widget.get_value() return self.is_valid_api(api_url, verify=value) def ssl_certificate_checker(value): """Check if certificate path is valid/exists.""" ssl_widget = self.widgets_dic.get('ssl_verification') verify = ssl_widget.get_value() if ssl_widget else True ssl_cert_widget = self.widgets_dic.get('ssl_certificate') path = ssl_cert_widget.get_value() return self.is_valid_cert_file(path, verify) self.set_initial_values() self.create_textbox( 'Anaconda API domain', 'anaconda_api_url', check=api_url_checker, ) self.create_checkbox( 'Enable SSL verification', 'ssl_verification', check=ssl_checker, hint=( '<i>Disabling this option is not <br>' 'recommended for security reasons</i>' ), ) self.create_textbox( 'SSL certificate path (Optional)', 'ssl_certificate', check=ssl_certificate_checker, ) info = '''To help us improve Anaconda Navigator, fix bugs, and make it even easier for everyone to use Python, we gather anonymized usage information, just like most web browsers and mobile apps.''' self.create_checkbox( 'Quality improvement reporting', 'provide_analytics', info=info, ) info_offline = DialogOfflineMode.MESSAGE_PREFERENCES extra = '<br><br>' if WIN7 else '' self.create_checkbox( 'Enable offline mode', 'offline_mode', info=info_offline + extra, ) self.create_checkbox('Hide offline mode dialog', 'hide_offline_dialog') self.create_checkbox('Hide quit dialog', 'hide_quit_dialog') self.create_checkbox( 'Hide update dialog on startup', 'hide_update_dialog' ) self.create_checkbox( 'Hide running applications dialog', 'hide_running_apps_dialog' ) self.create_checkbox( 'Enable high DPI scaling', 'enable_high_dpi_scaling' ) self.create_checkbox( 'Show application startup error messages', 'show_application_launch_errors' ) ssl_ver_widget = self.widgets_dic.get('ssl_verification') ssl_ver_widget.stateChanged.connect(self.enable_disable_cert) ssl_cert_widget = self.widgets_dic.get('ssl_certificate') ssl_cert_widget.setPlaceholderText( 'Certificate to verify SSL connections' ) # Refresh enabled/disabled status of certificate textbox self.enable_disable_cert() def enable_disable_cert(self, value=None): """Refresh enabled/disabled status of certificate textbox.""" ssl_cert_widget = self.widgets_dic.get('ssl_certificate') if value: value = bool(value) else: ssl_ver_widget = self.widgets_dic.get('ssl_verification') value = bool(ssl_ver_widget.checkState()) ssl_cert_widget.setEnabled(value) @staticmethod def update_icon(label, icon): """Update icon for information or warning.""" pixmap = QPixmap(icon) label.setScaledContents(True) label.setPixmap( pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation) ) return label @staticmethod def warn(widget, text=None): """Display warning for widget in preferences.""" label = widget.label_information if text: label = PreferencesDialog.update_icon(label, WARNING_ICON) label.setToolTip(str(text)) w = widget.label_information.width() / 2 h = widget.label_information.height() / 2 position = widget.label_information.mapToGlobal(QPoint(w, h)) QCursor.setPos(position) else: label.setPixmap(QPixmap()) label.setToolTip('') # --- Checkers # ------------------------------------------------------------------------- def is_valid_url(self, url): """Check if a given URL returns a 200 code.""" output = self.api.download_is_valid_url(url, non_blocking=False) error = '' if not output: error = 'Invalid api url.' return output, error def is_valid_cert_file(self, path, verify): """"Check if ssl certificate file in given path exists.""" output = True error = '' # Only validate if it is not empty and if ssl_verification is checked if path.strip() and verify: output = os.path.isfile(path) if not output: error = 'File not found.' return output, error def is_valid_api(self, url, verify=True): """Check if a given URL is a valid anaconda api endpoint.""" output = self.api.download_is_valid_api_url( url, non_blocking=False, verify=verify, ) error = '' if not output: url_api_1 = '' url_api_2 = '' if '/api' not in url and self.is_valid_url(url)[0]: url_api_1 = url.replace('https://', 'https://api.') url_api_1 = url_api_1.replace('http://', 'http://api.') if url.endswith('/'): url_api_2 = url + 'api' else: url_api_2 = url + '/api' error = ( 'Invalid Anaconda API url. <br>' '<br>Try using:<br><b>{0}</b> or <br>' '<b>{1}</b>'.format(url_api_1, url_api_2) ) else: error = ( 'Invalid Anaconda API url.<br><br>' 'Check the url is valid and corresponds to the api ' 'endpoint.' ) return output, error def run_checks(self): """ Run all check functions on configuration options. This method checks and warns but it does not change/set values. """ checks = [] for widget in self.widgets_changed: value = widget.get_value() check, error = widget.check_value(value) checks.append(check) if check: self.warn(widget) else: self.button_ok.setDisabled(True) widget.set_warning() self.warn(widget, error) break # Emit checks ready self.sig_check_ready.emit() return checks def reset_to_defaults(self): """Reset the preferences to the default values.""" for widget in self.widgets: option = widget.option default = self.get_option_default(option) widget.set_value(default) # Flag all values as updated self.options_changed(widget=widget, value=default) self.sig_reset_ready.emit() def accept(self): """Override Qt method.""" sig_updated = False anaconda_api_url = None checks = self.run_checks() # Update values if checks and all(checks): for widget in self.widgets_changed: value = widget.get_value() self.set_option(widget.option, value) # Settings not stored on Navigator config, but taken from # anaconda-client config if widget.option == 'anaconda_api_url': anaconda_api_url = value # Store it to be emitted self.api.client_set_api_url(value) sig_updated = True # ssl_verify/verify_ssl handles True/False/<Path to cert> # On navi it is split in 2 options for clarity if widget.option in ['ssl_certificate', 'ssl_verification']: ssl_veri = self.widgets_dic.get('ssl_verification') ssl_cert = self.widgets_dic.get('ssl_certificate') verify = ssl_veri.get_value() path = ssl_cert.get_value() if path.strip() and verify: value = path else: value = verify self.api.client_set_ssl(value) if sig_updated and anaconda_api_url: def _api_info(worker, output, error): conda_url = output.get('conda_url') try: self.sig_urls_updated.emit(anaconda_api_url, conda_url) super(PreferencesDialog, self).accept() except RuntimeError: # Some tests on appveyor/circleci fail pass worker = self.api.api_urls() worker.sig_chain_finished.connect(_api_info) super(PreferencesDialog, self).accept()
class MxDataWidget(QWidget): """ Dialog for displaying and editing DataFrame and related objects. Based on the gtabview project (ExtTableView). For more information please see: https://github.com/wavexx/gtabview/blob/master/gtabview/viewer.py Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None, data=DataFrame()): QWidget.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None self.setup_and_check(data) def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self._selection_rec = False self._model = None self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) # self.resize(600, 500) self.hscroll = QScrollBar(Qt.Horizontal) self.vscroll = QScrollBar(Qt.Vertical) # Create the view for the level self.create_table_level() # Create the view for the horizontal header self.create_table_header() # Create the view for the vertical index self.create_table_index() # Create the model and view of the data self.dataModel = MxDataModel(data, parent=self) # self.dataModel.dataChanged.connect(self.save_and_close_enable) self.create_data_table() self.layout.addWidget(self.hscroll, 2, 0, 1, 2) self.layout.addWidget(self.vscroll, 0, 2, 2, 1) # autosize columns on-demand self._autosized_cols = set() self._max_autosize_ms = None self.dataTable.installEventFilter(self) avg_width = self.fontMetrics().averageCharWidth() self.min_trunc = avg_width * 8 # Minimum size for columns self.max_width = avg_width * 64 # Maximum size for columns self.setLayout(self.layout) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) self.setModel(self.dataModel) self.resizeColumnsToContents() return True def create_table_level(self): """Create the QTableView that will hold the level model.""" self.table_level = QTableView() self.table_level.setEditTriggers(QTableWidget.NoEditTriggers) self.table_level.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setFrameStyle(QFrame.Plain) self.table_level.horizontalHeader().sectionResized.connect( self._index_resized) self.table_level.verticalHeader().sectionResized.connect( self._header_resized) # self.table_level.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_level, 0, 0) self.table_level.setContentsMargins(0, 0, 0, 0) self.table_level.horizontalHeader().sectionClicked.connect( self.sortByIndex) def create_table_header(self): """Create the QTableView that will hold the header model.""" self.table_header = QTableView() self.table_header.verticalHeader().hide() self.table_header.setEditTriggers(QTableWidget.NoEditTriggers) self.table_header.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.table_header.setHorizontalScrollBar(self.hscroll) self.table_header.setFrameStyle(QFrame.Plain) self.table_header.horizontalHeader().sectionResized.connect( self._column_resized) # self.table_header.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_header, 0, 1) def create_table_index(self): """Create the QTableView that will hold the index model.""" self.table_index = QTableView() self.table_index.horizontalHeader().hide() self.table_index.setEditTriggers(QTableWidget.NoEditTriggers) self.table_index.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollMode(QTableView.ScrollPerPixel) self.table_index.setVerticalScrollBar(self.vscroll) self.table_index.setFrameStyle(QFrame.Plain) self.table_index.verticalHeader().sectionResized.connect( self._row_resized) # self.table_index.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_index, 1, 0) self.table_index.setContentsMargins(0, 0, 0, 0) def create_data_table(self): """Create the QTableView that will hold the data model.""" self.dataTable = MxDataTable(self, self.dataModel, self.table_header.horizontalHeader(), self.hscroll, self.vscroll) self.dataTable.verticalHeader().hide() self.dataTable.horizontalHeader().hide() self.dataTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setVerticalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setFrameStyle(QFrame.Plain) # self.dataTable.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.dataTable, 1, 1) self.setFocusProxy(self.dataTable) self.dataTable.sig_sort_by_column.connect(self._sort_update) self.dataTable.sig_fetch_more_columns.connect(self._fetch_more_columns) self.dataTable.sig_fetch_more_rows.connect(self._fetch_more_rows) def sortByIndex(self, index): """Implement a Index sort.""" self.table_level.horizontalHeader().setSortIndicatorShown(True) sort_order = self.table_level.horizontalHeader().sortIndicatorOrder() self.table_index.model().sort(index, sort_order) self._sort_update() def model(self): """Get the model of the dataframe.""" return self._model def _column_resized(self, col, old_width, new_width): """Update the column width.""" self.dataTable.setColumnWidth(col, new_width) self._update_layout() def _row_resized(self, row, old_height, new_height): """Update the row height.""" self.dataTable.setRowHeight(row, new_height) self._update_layout() def _index_resized(self, col, old_width, new_width): """Resize the corresponding column of the index section selected.""" self.table_index.setColumnWidth(col, new_width) self._update_layout() def _header_resized(self, row, old_height, new_height): """Resize the corresponding row of the header section selected.""" self.table_header.setRowHeight(row, new_height) self._update_layout() def _update_layout(self): """Set the width and height of the QTableViews and hide rows.""" h_width = max(self.table_level.verticalHeader().sizeHint().width(), self.table_index.verticalHeader().sizeHint().width()) self.table_level.verticalHeader().setFixedWidth(h_width) self.table_index.verticalHeader().setFixedWidth(h_width) last_row = self._model.header_shape[0] - 1 if last_row < 0: hdr_height = self.table_level.horizontalHeader().height() else: # Check if the header shape has only one row (which display the # same info than the horizontal header). if last_row == 0: self.table_level.setRowHidden(0, True) self.table_header.setRowHidden(0, True) else: self.table_level.setRowHidden(0, False) self.table_header.setRowHidden(0, False) hdr_height = self.table_level.rowViewportPosition(last_row) + \ self.table_level.rowHeight(last_row) + \ self.table_level.horizontalHeader().height() self.table_header.setFixedHeight(hdr_height) self.table_level.setFixedHeight(hdr_height) last_col = self._model.header_shape[1] - 1 if last_col < 0: idx_width = self.table_level.verticalHeader().width() else: idx_width = self.table_level.columnViewportPosition(last_col) + \ self.table_level.columnWidth(last_col) + \ self.table_level.verticalHeader().width() self.table_index.setFixedWidth(idx_width) self.table_level.setFixedWidth(idx_width) self._resizeVisibleColumnsToContents() def _reset_model(self, table, model): """Set the model in the given table.""" old_sel_model = table.selectionModel() table.setModel(model) if old_sel_model: del old_sel_model def setAutosizeLimit(self, limit_ms): """Set maximum size for columns.""" self._max_autosize_ms = limit_ms def setModel(self, model, relayout=True): """Set the model for the data, header/index and level views.""" self._model = model # sel_model = self.dataTable.selectionModel() # sel_model.currentColumnChanged.connect( # self._resizeCurrentColumnToContents) self._reset_model(self.dataTable, model) # Asociate the models (level, vertical index and horizontal header) # with its corresponding view. self._reset_model( self.table_level, DataFrameLevelModel(model, self.palette(), self.font())) self._reset_model(self.table_header, DataFrameHeaderModel(model, 0, self.palette())) self._reset_model(self.table_index, DataFrameHeaderModel(model, 1, self.palette())) # Needs to be called after setting all table models if relayout: self._update_layout() def setCurrentIndex(self, y, x): """Set current selection.""" self.dataTable.selectionModel().setCurrentIndex( self.dataTable.model().index(y, x), QItemSelectionModel.ClearAndSelect) def _sizeHintForColumn(self, table, col, limit_ms=None): """Get the size hint for a given column in a table.""" max_row = table.model().rowCount() lm_start = time.perf_counter() lm_row = 64 if limit_ms else max_row max_width = 0 for row in range(max_row): v = table.sizeHintForIndex(table.model().index(row, col)) max_width = max(max_width, v.width()) if row > lm_row: lm_now = time.perf_counter() lm_elapsed = (lm_now - lm_start) * 1000 if lm_elapsed >= limit_ms: break lm_row = int((row / lm_elapsed) * limit_ms) return max_width def _resizeColumnToContents(self, header, data, col, limit_ms): """Resize a column by its contents.""" hdr_width = self._sizeHintForColumn(header, col, limit_ms) data_width = self._sizeHintForColumn(data, col, limit_ms) if data_width > hdr_width: width = min(self.max_width, data_width) elif hdr_width > data_width * 2: width = max(min(hdr_width, self.min_trunc), min(self.max_width, data_width)) else: width = min(self.max_width, hdr_width) header.setColumnWidth(col, width) def _resizeColumnsToContents(self, header, data, limit_ms): """Resize all the colummns to its contents.""" max_col = data.model().columnCount() if limit_ms is None: max_col_ms = None else: max_col_ms = limit_ms / max(1, max_col) for col in range(max_col): self._resizeColumnToContents(header, data, col, max_col_ms) def eventFilter(self, obj, event): """Override eventFilter to catch resize event.""" if obj == self.dataTable and event.type() == QEvent.Resize: self._resizeVisibleColumnsToContents() return False def _resizeVisibleColumnsToContents(self): """Resize the columns that are in the view.""" index_column = self.dataTable.rect().topLeft().x() start = col = self.dataTable.columnAt(index_column) width = self._model.shape[1] end = self.dataTable.columnAt(self.dataTable.rect().bottomRight().x()) end = width if end == -1 else end + 1 if self._max_autosize_ms is None: max_col_ms = None else: max_col_ms = self._max_autosize_ms / max(1, end - start) while col < end: resized = False if col not in self._autosized_cols: self._autosized_cols.add(col) resized = True self._resizeColumnToContents(self.table_header, self.dataTable, col, max_col_ms) col += 1 if resized: # As we resize columns, the boundary will change index_column = self.dataTable.rect().bottomRight().x() end = self.dataTable.columnAt(index_column) end = width if end == -1 else end + 1 if max_col_ms is not None: max_col_ms = self._max_autosize_ms / max(1, end - start) def _resizeCurrentColumnToContents(self, new_index, old_index): """Resize the current column to its contents.""" if new_index.column() not in self._autosized_cols: # Ensure the requested column is fully into view after resizing self._resizeVisibleColumnsToContents() self.dataTable.scrollTo(new_index) def resizeColumnsToContents(self): """Resize the columns to its contents.""" self._autosized_cols = set() self._resizeColumnsToContents(self.table_level, self.table_index, self._max_autosize_ms) self._update_layout() self.table_level.resizeColumnsToContents() def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def _update_header_size(self): """Update the column width of the header.""" column_count = self.table_header.model().columnCount() for index in range(0, column_count): if index < column_count: column_width = self.dataTable.columnWidth(index) self.table_header.setColumnWidth(index, column_width) else: break def _sort_update(self): """ Update the model for all the QTableView objects. Uses the model of the dataTable as the base. """ self.setModel(self.dataTable.model()) def _fetch_more_columns(self): """Fetch more data for the header (columns).""" self.table_header.model().fetch_more() def _fetch_more_rows(self): """Fetch more data for the index (rows).""" self.table_index.model().fetch_more() def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() self._update_header_size() QApplication.restoreOverrideCursor() # --- mx specific --- def process_remote_view(self, data): if data is None: data = DataFrame() # Empty DataFrame self.setModel(MxDataModel(data, parent=self))
class SegmentationWidget(QWidget): def __init__( self, viewer, boundaries_string=BOUNDARIES_STRING, ): super(SegmentationWidget, self).__init__() # general variables self.viewer = viewer # Disable / overwrite napari viewer functions # that either do not make sense or should be avoided by the user disable_napari_btns(self.viewer) disable_napari_key_bindings() # overwrite_napari_roll(self.viewer) # Main layers self.base_layer = [] # Contains registered brain / reference brain self.atlas_layer = [] # Contains annotations / region information # Track variables self.track_layers = [] # Region variables self.label_layers = [] # Atlas variables self.current_atlas_name = "" self.atlas = None self.boundaries_string = boundaries_string self.directory = "" # Set up segmentation methods self.region_seg = RegionSeg(self) self.track_seg = TrackSeg(self) # Generate main layout self.setup_main_layout() if DISPLAY_REGION_INFO: @self.viewer.mouse_move_callbacks.append def display_region_info(v, event): """ Show brain region info on mouse over in status bar on the right """ assert self.viewer == v if len(v.layers) and self.atlas_layer and self.atlas: _, _, _, region_info = structure_from_viewer( self.viewer.status, self.atlas_layer, self.atlas) self.viewer.help = region_info def setup_main_layout(self): """ Construct main layout of widget """ self.layout = QGridLayout() self.layout.setContentsMargins(10, 10, 10, 10) self.layout.setAlignment(QtCore.Qt.AlignTop) self.layout.setSpacing(4) # 3 Steps: # - Loading panel # - Segmentation methods panel # -> Individual segmentation methods (which are invisible at first) # - Saving panel self.add_loading_panel(1) self.add_segmentation_methods_panel(1) self.track_seg.add_track_panel(2) # Track segmentation subpanel self.region_seg.add_region_panel(3) # Region segmentation subpanel self.add_saving_panel(4) # Take care of status label self.status_label = QLabel() self.status_label.setText("Ready") self.layout.addWidget(self.status_label, 5, 0) self.setLayout(self.layout) # PANELS ############################################################### def add_segmentation_methods_panel(self, row, column=1): """ Segmentation methods chooser panel: Toggle visibility of segmentation methods """ self.toggle_methods_panel = QGroupBox("Segmentation") self.toggle_methods_layout = QGridLayout() self.toggle_methods_layout.setContentsMargins(10, 10, 10, 10) self.toggle_methods_layout.setSpacing(5) self.toggle_methods_layout.setAlignment(QtCore.Qt.AlignBottom) self.show_trackseg_button = add_button( "Track tracing", self.toggle_methods_layout, self.track_seg.toggle_track_panel, 0, 1, minimum_width=COLUMN_WIDTH, alignment=SEGM_METHODS_PANEL_ALIGN, ) self.show_trackseg_button.setEnabled(False) self.show_regionseg_button = add_button( "Region segmentation", self.toggle_methods_layout, self.region_seg.toggle_region_panel, 1, 1, minimum_width=COLUMN_WIDTH, alignment=SEGM_METHODS_PANEL_ALIGN, ) self.show_regionseg_button.setEnabled(False) self.toggle_methods_layout.setColumnMinimumWidth(1, COLUMN_WIDTH) self.toggle_methods_panel.setLayout(self.toggle_methods_layout) self.toggle_methods_panel.setVisible(True) self.layout.addWidget(self.toggle_methods_panel, row, column, 1, 1) def add_loading_panel(self, row, column=0): """ Loading panel: - Load project (sample space) - Load project (atlas space) - Atlas chooser """ self.load_data_panel = QGroupBox("Load data") self.load_data_layout = QGridLayout() self.load_data_layout.setSpacing(15) self.load_data_layout.setContentsMargins(10, 10, 10, 10) self.load_data_layout.setAlignment(QtCore.Qt.AlignBottom) self.load_button = add_button( "Load project (sample space)", self.load_data_layout, self.load_brainreg_directory_sample, 0, 0, minimum_width=COLUMN_WIDTH, alignment=LOADING_PANEL_ALIGN, ) self.load_button_standard = add_button( "Load project (atlas space)", self.load_data_layout, self.load_brainreg_directory_standard, 1, 0, minimum_width=COLUMN_WIDTH, alignment=LOADING_PANEL_ALIGN, ) self.add_atlas_menu(self.load_data_layout) self.load_data_layout.setColumnMinimumWidth(0, COLUMN_WIDTH) self.load_data_panel.setLayout(self.load_data_layout) self.load_data_panel.setVisible(True) self.layout.addWidget(self.load_data_panel, row, column, 1, 1) def add_saving_panel(self, row): """ Saving/Export panel """ self.save_data_panel = QGroupBox() self.save_data_layout = QGridLayout() self.export_button = add_button( "To brainrender", self.save_data_layout, self.export_to_brainrender, 0, 0, visibility=False, ) self.save_button = add_button("Save", self.save_data_layout, self.save, 0, 1, visibility=False) self.save_data_layout.setColumnMinimumWidth(1, COLUMN_WIDTH) self.save_data_panel.setLayout(self.save_data_layout) self.layout.addWidget(self.save_data_panel, row, 0, 1, 2) self.save_data_panel.setVisible(False) # ATLAS INTERACTION #################################################### def add_atlas_menu(self, layout): list_of_atlasses = ["Load atlas"] available_atlases = get_available_atlases() for atlas in available_atlases.keys(): atlas_desc = f"{atlas} v{available_atlases[atlas]}" list_of_atlasses.append(atlas_desc) atlas_menu, _ = add_combobox( layout, None, list_of_atlasses, 2, 0, label_stack=True, callback=self.initialise_atlas, width=COLUMN_WIDTH, ) self.atlas_menu = atlas_menu def initialise_atlas(self): atlas_string = self.atlas_menu.currentText() atlas_name = atlas_string.split(" ")[0].strip() if atlas_name != self.current_atlas_name: status = self.remove_layers() if not status: # Something prevented deletion self.reset_atlas_menu() return else: print(f"{atlas_string} already selected for segmentation.") self.reset_atlas_menu() return # Get / set output directory self.set_output_directory() if not self.directory: self.reset_atlas_menu() return self.current_atlas_name = atlas_name # Instantiate atlas layers self.load_atlas() self.directory = self.directory / atlas_name self.paths = Paths(self.directory, atlas_space=True) self.status_label.setText("Ready") # Set window title self.viewer.title = f"Atlas: {self.current_atlas_name}" self.initialise_segmentation_interface() # Check / load previous regions and tracks self.region_seg.check_saved_region() self.track_seg.check_saved_track() self.reset_atlas_menu() def set_output_directory(self): self.status_label.setText("Loading...") options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog self.directory = QFileDialog.getExistingDirectory( self, "Select output directory", options=options, ) if self.directory != "": self.directory = Path(self.directory) def load_atlas(self): atlas = BrainGlobeAtlas(self.current_atlas_name) self.atlas = atlas self.base_layer = self.viewer.add_image( self.atlas.reference, name="Reference", ) self.atlas_layer = self.viewer.add_labels( self.atlas.annotation, name=self.atlas.atlas_name, blending="additive", opacity=0.3, visible=False, ) self.standard_space = True def reset_atlas_menu(self): # Reset menu for atlas - show initial description self.atlas_menu.blockSignals(True) self.atlas_menu.setCurrentIndex(0) self.atlas_menu.blockSignals(False) # BRAINREG INTERACTION ################################################# def load_brainreg_directory_sample(self): self.get_brainreg_directory(standard_space=False) def load_brainreg_directory_standard(self): self.get_brainreg_directory(standard_space=True) def get_brainreg_directory(self, standard_space): """ Shows file dialog to choose output directory and sets global directory info """ if standard_space: self.plugin = "brainreg_standard" self.standard_space = True else: self.plugin = "brainreg" self.standard_space = False self.status_label.setText("Loading...") options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog brainreg_directory = QFileDialog.getExistingDirectory( self, "Select brainreg directory", options=options, ) if not brainreg_directory: return if self.directory != brainreg_directory: status = self.remove_layers() if not status: return # Something prevented deletion self.directory = Path(brainreg_directory) else: print(f"{str(brainreg_directory)} already loaded.") return # Otherwise, proceed loading brainreg dir self.load_brainreg_directory() def load_brainreg_directory(self): """ Opens brainreg folder in napari. Calls initialise_loaded_data to set up layers / info. Then checks for previously loaded data. """ try: self.viewer.open(str(self.directory), plugin=self.plugin) self.paths = Paths( self.directory, standard_space=self.standard_space, ) self.initialise_loaded_data() except ValueError: print(f"The directory ({self.directory}) does not appear to be " f"a brainreg directory, please try again.") return # Check / load previous regions and tracks self.region_seg.check_saved_region() self.track_seg.check_saved_track() def initialise_loaded_data(self): """ Set up brainreg layers in napari / fill with new data and info """ try: self.viewer.layers.remove(self.boundaries_string) except KeyError: pass self.base_layer = self.viewer.layers["Registered image"] self.metadata = self.base_layer.metadata self.atlas = self.metadata["atlas_class"] self.atlas_layer = self.viewer.layers[self.metadata["atlas"]] self.initialise_segmentation_interface() # Set window title self.viewer.title = ( f"Brainreg: {self.metadata['atlas']} ({self.plugin})") self.status_label.setText("Ready") # MORE LAYOUT COMPONENTS ########################################### def initialise_segmentation_interface(self): self.reset_variables() self.initialise_image_view() self.save_data_panel.setVisible(True) self.save_button.setVisible(True) self.export_button.setVisible(self.standard_space) self.show_regionseg_button.setEnabled(True) self.show_trackseg_button.setEnabled(True) self.status_label.setText("Ready") def initialise_image_view(self): self.set_z_position() def set_z_position(self): midpoint = int(round(len(self.base_layer.data) / 2)) self.viewer.dims.set_point(0, midpoint) def reset_variables(self): """ Reset atlas scale dependent variables - point_size (Track segmentation) - spline_size (Track segmentation) - brush_size (Region segmentation) """ self.mean_voxel_size = int( np.sum(self.atlas.resolution) / len(self.atlas.resolution)) self.track_seg.point_size = (self.track_seg.point_size_default / self.mean_voxel_size) self.track_seg.spline_size = (self.track_seg.spline_size_default / self.mean_voxel_size) self.region_seg.brush_size = (self.region_seg.brush_size_default / self.mean_voxel_size) return def display_delete_warning(self): """ Display a warning in a pop up that informs about deletion of all annotation layers """ message_reply = QMessageBox.question( self, "About to remove layers", "All layers are about to be deleted. Proceed?", QMessageBox.Yes | QMessageBox.Cancel, ) if message_reply == QMessageBox.Yes: return True else: return False def remove_layers(self): """ TODO: This needs work. Runs into an error currently when switching from a annotated project to another one """ if len(self.viewer.layers) != 0: # Check with user if that is really what is wanted if self.track_layers or self.label_layers: choice = self.display_delete_warning() if not choice: print('Preventing deletion because user chose "Cancel"') return False # Remove old layers for layer in list(self.viewer.layers): try: self.viewer.layers.remove(layer) except IndexError: # no idea why this happens pass # There seems to be a napari bug trying to access previously used slider # values. Trying to circument for now self.viewer.window.qt_viewer.dims._last_used = None self.track_layers = [] self.label_layers = [] return True def save(self): if self.label_layers or self.track_layers: print("Saving") worker = save_all( self.paths.regions_directory, self.paths.tracks_directory, self.label_layers, self.track_layers, track_file_extension=TRACK_FILE_EXT, ) worker.start() def export_to_brainrender(self): print("Exporting") max_axis_2 = self.base_layer.shape[2] worker = export_all( self.paths.regions_directory, self.paths.tracks_directory, self.label_layers, self.track_seg.splines, self.track_seg.spline_names, self.atlas.resolution[0], max_axis_2, ) worker.start()
class BotnetWindow(FramelessWindow): update_ui = Signal() def __init__(self, parent=None): super(BotnetWindow, self).__init__(parent) self.setMouseTracking(True) self.setAttribute(Qt.WA_TranslucentBackground) self.resize(QSize(1500, 900)) self.main_widget = QWidget(self) self.main_widget.setMouseTracking(True) self.main_widget.setAttribute(Qt.WA_TranslucentBackground) self.main_widget.setContentsMargins(0, 0, 0, 0) self.grid_layout = QGridLayout(self.main_widget) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.main_widget.setLayout(self.grid_layout) self.spacer_left = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.grid_layout.addItem(self.spacer_left, 2, 1, 1, 1) self.spacer_top = QSpacerItem(472, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.grid_layout.addItem(self.spacer_top, 1, 2, 1, 1) self.spacer_right = QSpacerItem(472, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.grid_layout.addItem(self.spacer_right, 1, 0, 1, 1) self.spacer_bottom = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.grid_layout.addItem(self.spacer_bottom, 0, 1, 1, 1) self.sub_content_widget = QWidget(self.main_widget) self.sub_content_widget.setMouseTracking(True) self.sub_content_widget.setContentsMargins(0, 0, 0, 0) self.sub_content_layout = QGridLayout(self.sub_content_widget) self.sub_content_layout.setContentsMargins(0, 0, 0, 0) self.sub_content_widget.setLayout(self.sub_content_layout) self.sub_content_widget.setAttribute(Qt.WA_TranslucentBackground) self.grid_layout.addWidget(self.sub_content_widget, 1, 1, 1, 1) self.name_spacer = QSpacerItem(1, 16, QSizePolicy.Fixed, QSizePolicy.Fixed) self.sub_content_layout.addItem(self.name_spacer, 1, 0, 1, 1) self.app_name_label = QLabel(self) font = self.font() font.setPointSize(50) font.setWeight(QFont.Bold) self.app_name_label.setFont(font) self.app_name_label.setScaledContents(True) self.app_name_label.setAutoFillBackground(True) self.app_name_label.setAlignment(Qt.AlignCenter) self.app_name_label.setText("QtPyBotnet") self.app_name_label.setAttribute(Qt.WA_TranslucentBackground) self.app_name_label.setScaledContents(True) self.app_name_label.autoFillBackground() self.sub_content_layout.addWidget(self.app_name_label, 0, 0, 1, 1) try: self.closeClicked.disconnect() except Exception: # noqa pass self.setSubContentSpacing(16) self.addContentWidget(self.main_widget) def addSubContentWidget(self, widget: QWidget): self.sub_content_layout.addWidget(widget, 2, 0, 1, 1) def setSubContentSpacing(self, spacing: int): self.sub_content_layout.setSpacing(spacing) def showLogo(self, value: bool): self.app_name_label.setVisible(value)
def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self.set_mode) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.edge_color.connect(self._on_edge_color_change) self.layer.events.face_color.connect(self._on_face_color_change) self.layer.events.editable.connect(self._on_editable_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged[int].connect( lambda value=sld: self.changeWidth(value) ) self.widthSlider = sld face_comboBox = QComboBox() colors = self.layer._colors for c in colors: face_comboBox.addItem(c) face_comboBox.activated[str].connect( lambda text=face_comboBox: self.changeFaceColor(text) ) self.faceComboBox = face_comboBox self.faceColorSwatch = QFrame() self.faceColorSwatch.setObjectName('swatch') self.faceColorSwatch.setToolTip('Face color swatch') self._on_face_color_change(None) edge_comboBox = QComboBox() colors = self.layer._colors for c in colors: edge_comboBox.addItem(c) edge_comboBox.activated[str].connect( lambda text=edge_comboBox: self.changeEdgeColor(text) ) self.edgeComboBox = edge_comboBox self.edgeColorSwatch = QFrame() self.edgeColorSwatch.setObjectName('swatch') self.edgeColorSwatch.setToolTip('Edge color swatch') self._on_edge_color_change(None) self.select_button = QtModeButton( layer, 'select', Mode.SELECT, 'Select shapes' ) self.direct_button = QtModeButton( layer, 'direct', Mode.DIRECT, 'Select vertices' ) self.panzoom_button = QtModeButton( layer, 'zoom', Mode.PAN_ZOOM, 'Pan/zoom' ) self.rectangle_button = QtModeButton( layer, 'rectangle', Mode.ADD_RECTANGLE, 'Add rectangles' ) self.ellipse_button = QtModeButton( layer, 'ellipse', Mode.ADD_ELLIPSE, 'Add ellipses' ) self.line_button = QtModeButton( layer, 'line', Mode.ADD_LINE, 'Add lines' ) self.path_button = QtModeButton( layer, 'path', Mode.ADD_PATH, 'Add paths' ) self.polygon_button = QtModeButton( layer, 'polygon', Mode.ADD_POLYGON, 'Add polygons' ) self.vertex_insert_button = QtModeButton( layer, 'vertex_insert', Mode.VERTEX_INSERT, 'Insert vertex' ) self.vertex_remove_button = QtModeButton( layer, 'vertex_remove', Mode.VERTEX_REMOVE, 'Remove vertex' ) self.move_front_button = QtMoveFrontButton(layer) self.move_back_button = QtMoveBackButton(layer) self.delete_button = QtDeleteShapeButton(layer) self.panzoom_button.setChecked(True) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 1) button_grid.addWidget(self.vertex_insert_button, 0, 2) button_grid.addWidget(self.delete_button, 0, 3) button_grid.addWidget(self.direct_button, 0, 4) button_grid.addWidget(self.select_button, 0, 5) button_grid.addWidget(self.panzoom_button, 0, 6) button_grid.addWidget(self.move_back_button, 1, 0) button_grid.addWidget(self.move_front_button, 1, 1) button_grid.addWidget(self.ellipse_button, 1, 2) button_grid.addWidget(self.rectangle_button, 1, 3) button_grid.addWidget(self.polygon_button, 1, 4) button_grid.addWidget(self.line_button, 1, 5) button_grid.addWidget(self.path_button, 1, 6) button_grid.setColumnStretch(2, 2) button_grid.setSpacing(4) # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 3) self.grid_layout.addWidget(QLabel('opacity:'), 1, 0) self.grid_layout.addWidget(self.opacitySilder, 1, 1, 1, 2) self.grid_layout.addWidget(QLabel('edge width:'), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1, 1, 2) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2) self.grid_layout.addWidget(QLabel('face color:'), 4, 0) self.grid_layout.addWidget(self.faceComboBox, 4, 2) self.grid_layout.addWidget(self.faceColorSwatch, 4, 1) self.grid_layout.addWidget(QLabel('edge color:'), 5, 0) self.grid_layout.addWidget(self.edgeComboBox, 5, 2) self.grid_layout.addWidget(self.edgeColorSwatch, 5, 1) self.grid_layout.setRowStretch(6, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
def __init__(self, presenter: IDataViewSubscriber, dims_info, can_normalise, parent=None, conf=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots_active = False self.can_normalise = can_normalise self.nonortho_transform = None self.conf = conf self._line_plots = None self._image_info_tracker = None self._region_selection_on = False self._orig_lims = None # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect(self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self) self.image_info_widget.setToolTip("Information about the selected pixel") self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") self.dimensions_layout.setHorizontalSpacing(10) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) self.track_cursor.setChecked(True) self.track_cursor.stateChanged.connect(self.on_track_cursor_state_change) # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.canvas.mpl_connect('button_press_event', self.presenter.canvas_clicked) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) norm_scale = self.get_default_scale_norm() self.colorbar = ColorbarWidget(self, norm_scale) self.colorbar.cmap.setToolTip("Colormap options") self.colorbar.crev.setToolTip("Reverse colormap") self.colorbar.norm.setToolTip("Colormap normalisation options") self.colorbar.powerscale.setToolTip("Power colormap scale") self.colorbar.cmax.setToolTip("Colormap maximum limit") self.colorbar.cmin.setToolTip("Colormap minimum limit") self.colorbar.autoscale.setToolTip("Automatically changes colormap limits when zooming on the plot") self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.scaleNormChanged.connect(self.scale_norm_changed) # make width larger to fit image readout table self.colorbar.setMaximumWidth(200) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar(self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.regionSelectionClicked.connect(self.on_region_selection_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.nonOrthogonalClicked.connect(self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanClicked.connect(self.presenter.zoom_pan_clicked) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # Status bar self.status_bar = QStatusBar(parent=self) self.status_bar.setStyleSheet('QStatusBar::item {border: None;}') # Hide spacers between button and label self.status_bar_label = QLabel() self.help_button = QToolButton() self.help_button.setText("?") self.status_bar.addWidget(self.help_button) self.status_bar.addWidget(self.status_bar_label) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 3, 1) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addWidget(self.status_bar, 3, 0, 1, 1) layout.setRowStretch(2, 1)
def __init__(self, *args, **kwargs): super(Arboretum, self).__init__(*args, **kwargs) layout = QVBoxLayout() # add some buttons self.load_button = QPushButton('Load...', self) self.config_button = QPushButton('Configure...', self) self.localize_button = QPushButton('Localize', self) self.track_button = QPushButton('Track', self) self.save_button = QPushButton('Save...', self) # checkboxes self.optimize_checkbox = QCheckBox() self.optimize_checkbox.setChecked(True) # self.use_states_checkbox = QCheckBox() # self.use_states_checkbox.setChecked(True) # combo boxes self.tracking_mode_combobox = QComboBox() for mode in BayesianUpdates: self.tracking_mode_combobox.addItem(mode.name.lower()) default_mode = BayesianUpdates.EXACT self.tracking_mode_combobox.setCurrentIndex(default_mode.value) # # sliders self.search_radius_slider = QSlider(Qt.Horizontal) self.search_radius_slider.setFocusPolicy(Qt.NoFocus) self.search_radius_slider.setMinimum(1) self.search_radius_slider.setMaximum(300) self.search_radius_slider.setSingleStep(1) # self.search_radius_slider.setEnabled(False) # dynamic labels self.config_filename_label = QLabel() self.localizations_label = QLabel() self.tracks_label = QLabel() self.status_label = QLabel() self.search_radius_label = QLabel() self.search_radius_label.setAlignment(Qt.AlignRight) # load/save buttons io_panel = QWidget() io_layout = QHBoxLayout() io_layout.addWidget(self.load_button) io_layout.addWidget(self.save_button) io_panel.setLayout(io_layout) io_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) layout.addWidget(io_panel) # tracking panel tracking_panel = QGroupBox('tracking') tracking_layout = QGridLayout() tracking_layout.addWidget(QLabel('method: '), 0, 0) tracking_layout.addWidget(self.tracking_mode_combobox, 0, 1) tracking_layout.addWidget(self.search_radius_label, 1, 0) tracking_layout.addWidget(self.search_radius_slider, 1, 1) tracking_layout.addWidget(QLabel('optimize: '), 2, 0) tracking_layout.addWidget(self.optimize_checkbox, 2, 1) tracking_layout.addWidget(self.config_button, 3, 0) tracking_layout.addWidget(self.config_filename_label, 3, 1) tracking_layout.addWidget(self.localize_button, 4, 0) tracking_layout.addWidget(self.localizations_label, 4, 1) tracking_layout.addWidget(self.track_button, 5, 0) tracking_layout.addWidget(self.tracks_label, 5, 1) tracking_layout.setColumnMinimumWidth(1, 150) tracking_layout.setSpacing(4) tracking_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) tracking_panel.setLayout(tracking_layout) layout.addWidget(tracking_panel) # status panel status_panel = QGroupBox('status') status_layout = QHBoxLayout() status_layout.addWidget(self.status_label) status_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) status_panel.setLayout(status_layout) layout.addWidget(status_panel) # set the layout layout.setAlignment(Qt.AlignTop) layout.setSpacing(4) self.setLayout(layout) self.setMaximumHeight(GUI_MAXIMUM_HEIGHT) self.setMaximumWidth(GUI_MAXIMUM_WIDTH) # callbacks self.load_button.clicked.connect(self.load_data) self.save_button.clicked.connect(self.export_data) self.config_button.clicked.connect(self.load_config) self.tracking_mode_combobox.currentTextChanged.connect( self._on_mode_change) self.search_radius_slider.valueChanged.connect(self._on_radius_change) self._tracker_state = None self._segmentation = None self._localizations = None self._tracks = None self._btrack_cfg = None self._active_layer = None # TODO(arl): this is the working filename for the dataset self.filename = None self._search_radius = None self._on_mode_change() self.search_radius_slider.setValue(100)
class QtLayerControls(QFrame): """Superclass for all the other LayerControl classes. This class is never directly instantiated anywhere. Parameters ---------- layer : napari.layers.Layer An instance of a napari layer. Attributes ---------- blendComboBox : qtpy.QtWidgets.QComboBox Drowpdown widget to select blending mode of layer. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Layer An instance of a napari layer. opacitySlider : qtpy.QtWidgets.QSlider Slider controlling opacity of the layer. """ def __init__(self, layer): super().__init__() self.layer = layer self.layer.events.blending.connect(self._on_blending_change) self.layer.events.opacity.connect(self._on_opacity_change) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.grid_layout.setColumnMinimumWidth(0, 86) self.grid_layout.setColumnStretch(1, 1) self.setLayout(self.grid_layout) sld = QDoubleSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(1) sld.setSingleStep(0.01) sld.valueChanged.connect(self.changeOpacity) self.opacitySlider = sld self._on_opacity_change() blend_comboBox = QComboBox(self) for index, (data, text) in enumerate(BLENDING_TRANSLATIONS.items()): data = data.value blend_comboBox.addItem(text, data) if data == self.layer.blending: blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect(self.changeBlending) self.blendComboBox = blend_comboBox def changeOpacity(self, value): """Change opacity value on the layer model. Parameters ---------- value : float Opacity value for shapes. Input range 0 - 100 (transparent to fully opaque). """ with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value def changeBlending(self, text): """Change blending mode on the layer model. Parameters ---------- text : str Name of blending mode, eg: 'translucent', 'additive', 'opaque'. """ self.layer.blending = self.blendComboBox.currentData() def _on_opacity_change(self): """Receive layer model opacity change event and update opacity slider.""" with self.layer.events.opacity.blocker(): self.opacitySlider.setValue(self.layer.opacity) def _on_blending_change(self): """Receive layer model blending mode change event and update slider.""" with self.layer.events.blending.blocker(): self.blendComboBox.setCurrentIndex( self.blendComboBox.findData(self.layer.blending)) def deleteLater(self): disconnect_events(self.layer.events, self) super().deleteLater() def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.events, self) for child in self.children(): close_method = getattr(child, 'close', None) if close_method is not None: close_method() return super().close()
class ThumbnailScrollBar(QFrame): """ A widget that manages the display of the FigureThumbnails that are created when a figure is sent to the IPython console by the kernel and that controls what is displayed in the FigureViewer. """ redirect_stdio = Signal(bool) _min_scrollbar_width = 100 def __init__(self, figure_viewer, parent=None, background_color=None): super(ThumbnailScrollBar, self).__init__(parent) self._thumbnails = [] self.background_color = background_color self.current_thumbnail = None self.set_figureviewer(figure_viewer) self.setup_gui() # Because the range of Qt scrollareas is not updated immediately # after a new item is added to it, setting the scrollbar's value # to its maximum value after adding a new item will scroll down to # the penultimate item instead of the last. # So to scroll programmatically to the latest item after it # is added to the scrollarea, we need to do it instead in a slot # connected to the scrollbar's rangeChanged signal. # See spyder-ide/spyder#10914 for more details. self._new_thumbnail_added = False self.scrollarea.verticalScrollBar().rangeChanged.connect( self._scroll_to_newest_item) def setup_gui(self): """Setup the main layout of the widget.""" layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self.setup_scrollarea()) def setup_scrollarea(self): """Setup the scrollarea that will contain the FigureThumbnails.""" self.view = QWidget() self.scene = QGridLayout(self.view) self.scene.setContentsMargins(0, 0, 0, 0) # The vertical spacing between the thumbnails. # Note that we need to set this value explicitly or else the tests # are failing on macOS. See spyder-ide/spyder#11576. self.scene.setSpacing(5) self.scrollarea = QScrollArea() self.scrollarea.setWidget(self.view) self.scrollarea.setWidgetResizable(True) self.scrollarea.setFrameStyle(0) self.scrollarea.setViewportMargins(2, 2, 2, 2) self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollarea.setMinimumWidth(self._min_scrollbar_width) # Set the vertical scrollbar explicitely. # This is required to avoid a "RuntimeError: no access to protected # functions or signals for objects not created from Python" in Linux. self.scrollarea.setVerticalScrollBar(QScrollBar()) # Install an event filter on the scrollbar. self.scrollarea.installEventFilter(self) return self.scrollarea def set_figureviewer(self, figure_viewer): """Set the bamespace for the FigureViewer.""" self.figure_viewer = figure_viewer def eventFilter(self, widget, event): """ An event filter to trigger an update of the thumbnails size so that their width fit that of the scrollarea and to remap some key press events to mimick navigational behaviour of a Qt widget list. """ if event.type() == QEvent.KeyPress: key = event.key() if key == Qt.Key_Up: self.go_previous_thumbnail() return True elif key == Qt.Key_Down: self.go_next_thumbnail() return True if event.type() == QEvent.Resize: self._update_thumbnail_size() return super(ThumbnailScrollBar, self).eventFilter(widget, event) # ---- Save Figure def save_all_figures_as(self): """Save all the figures to a file.""" self.redirect_stdio.emit(False) save_dir = CONF.get('plots', 'save_dir', getcwd_or_home()) dirname = getexistingdirectory(self, 'Save all figures', save_dir) self.redirect_stdio.emit(True) if dirname: CONF.set('plots', 'save_dir', dirname) return self.save_all_figures_todir(dirname) def save_all_figures_todir(self, dirname): """Save all figure in dirname.""" fignames = [] for thumbnail in self._thumbnails: fig = thumbnail.canvas.fig fmt = thumbnail.canvas.fmt fext = { 'image/png': '.png', 'image/jpeg': '.jpg', 'image/svg+xml': '.svg' }[fmt] figname = get_unique_figname(dirname, 'Figure', fext) save_figure_tofile(fig, fmt, figname) fignames.append(figname) return fignames def save_current_figure_as(self): """Save the currently selected figure.""" if self.current_thumbnail is not None: self.save_figure_as(self.current_thumbnail.canvas.fig, self.current_thumbnail.canvas.fmt) def save_figure_as(self, fig, fmt): """Save the figure to a file.""" fext, ffilt = { 'image/png': ('.png', 'PNG (*.png)'), 'image/jpeg': ('.jpg', 'JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)'), 'image/svg+xml': ('.svg', 'SVG (*.svg);;PNG (*.png)') }[fmt] save_dir = CONF.get('plots', 'save_dir', getcwd_or_home()) figname = get_unique_figname(save_dir, 'Figure', fext) self.redirect_stdio.emit(False) fname, fext = getsavefilename(parent=self.parent(), caption='Save Figure', basedir=figname, filters=ffilt, selectedfilter='', options=None) self.redirect_stdio.emit(True) if fname: CONF.set('plots', 'save_dir', osp.dirname(fname)) save_figure_tofile(fig, fmt, fname) # ---- Thumbails Handlers def _calculate_figure_canvas_width(self): """ Calculate the width the thumbnails need to have to fit the scrollarea. """ extra_padding = 10 if sys.platform == 'darwin' else 0 figure_canvas_width = ( self.scrollarea.width() - 2 * self.lineWidth() - self.scrollarea.viewportMargins().left() - self.scrollarea.viewportMargins().right() - extra_padding - self.scrollarea.verticalScrollBar().sizeHint().width()) if is_dark_interface(): # This is required to take into account some hard-coded padding # and margin in qdarkstyle. figure_canvas_width = figure_canvas_width - 6 return figure_canvas_width def _setup_thumbnail_size(self, thumbnail): """ Scale the thumbnail's canvas size so that it fits the thumbnail scrollbar's width. """ max_canvas_size = self._calculate_figure_canvas_width() thumbnail.scale_canvas_size(max_canvas_size) def _update_thumbnail_size(self): """ Update the thumbnails size so that their width fit that of the scrollarea. """ # NOTE: We hide temporarily the thumbnails to prevent a repaint of # each thumbnail as soon as their size is updated in the loop, which # causes some flickering of the thumbnail scrollbar resizing animation. # Once the size of all the thumbnails has been updated, we show them # back so that they are repainted all at once instead of one after the # other. This is just a trick to make the resizing animation of the # thumbnail scrollbar look smoother. self.view.hide() for thumbnail in self._thumbnails: self._setup_thumbnail_size(thumbnail) self.view.show() def add_thumbnail(self, fig, fmt): """ Add a new thumbnail to that thumbnail scrollbar. """ thumbnail = FigureThumbnail(parent=self, background_color=self.background_color) thumbnail.canvas.load_figure(fig, fmt) thumbnail.sig_canvas_clicked.connect(self.set_current_thumbnail) thumbnail.sig_remove_figure.connect(self.remove_thumbnail) thumbnail.sig_save_figure.connect(self.save_figure_as) self._thumbnails.append(thumbnail) self._new_thumbnail_added = True self.scene.setRowStretch(self.scene.rowCount() - 1, 0) self.scene.addWidget(thumbnail, self.scene.rowCount() - 1, 0) self.scene.setRowStretch(self.scene.rowCount(), 100) self.set_current_thumbnail(thumbnail) thumbnail.show() self._setup_thumbnail_size(thumbnail) def remove_current_thumbnail(self): """Remove the currently selected thumbnail.""" if self.current_thumbnail is not None: self.remove_thumbnail(self.current_thumbnail) def remove_all_thumbnails(self): """Remove all thumbnails.""" for thumbnail in self._thumbnails: self.layout().removeWidget(thumbnail) thumbnail.sig_canvas_clicked.disconnect() thumbnail.sig_remove_figure.disconnect() thumbnail.sig_save_figure.disconnect() thumbnail.setParent(None) self._thumbnails = [] self.current_thumbnail = None self.figure_viewer.figcanvas.clear_canvas() def remove_thumbnail(self, thumbnail): """Remove thumbnail.""" if thumbnail in self._thumbnails: index = self._thumbnails.index(thumbnail) self._thumbnails.remove(thumbnail) self.layout().removeWidget(thumbnail) thumbnail.setParent(None) thumbnail.sig_canvas_clicked.disconnect() thumbnail.sig_remove_figure.disconnect() thumbnail.sig_save_figure.disconnect() # Select a new thumbnail if any : if thumbnail == self.current_thumbnail: if len(self._thumbnails) > 0: self.set_current_index(min(index, len(self._thumbnails) - 1)) else: self.current_thumbnail = None self.figure_viewer.figcanvas.clear_canvas() def set_current_index(self, index): """Set the currently selected thumbnail by its index.""" self.set_current_thumbnail(self._thumbnails[index]) def get_current_index(self): """Return the index of the currently selected thumbnail.""" try: return self._thumbnails.index(self.current_thumbnail) except ValueError: return -1 def set_current_thumbnail(self, thumbnail): """Set the currently selected thumbnail.""" self.current_thumbnail = thumbnail self.figure_viewer.load_figure(thumbnail.canvas.fig, thumbnail.canvas.fmt) for thumbnail in self._thumbnails: thumbnail.highlight_canvas(thumbnail == self.current_thumbnail) def go_previous_thumbnail(self): """Select the thumbnail previous to the currently selected one.""" if self.current_thumbnail is not None: index = self._thumbnails.index(self.current_thumbnail) - 1 index = index if index >= 0 else len(self._thumbnails) - 1 self.set_current_index(index) self.scroll_to_item(index) def go_next_thumbnail(self): """Select thumbnail next to the currently selected one.""" if self.current_thumbnail is not None: index = self._thumbnails.index(self.current_thumbnail) + 1 index = 0 if index >= len(self._thumbnails) else index self.set_current_index(index) self.scroll_to_item(index) def scroll_to_item(self, index): """Scroll to the selected item of ThumbnailScrollBar.""" spacing_between_items = self.scene.verticalSpacing() height_view = self.scrollarea.viewport().height() height_item = self.scene.itemAt(index).sizeHint().height() height_view_excluding_item = max(0, height_view - height_item) height_of_top_items = spacing_between_items for i in range(index): item = self.scene.itemAt(i) height_of_top_items += item.sizeHint().height() height_of_top_items += spacing_between_items pos_scroll = height_of_top_items - height_view_excluding_item // 2 vsb = self.scrollarea.verticalScrollBar() vsb.setValue(pos_scroll) def _scroll_to_newest_item(self, vsb_min, vsb_max): """ Scroll to the newest item added to the thumbnail scrollbar. Note that this method is called each time the rangeChanged signal is emitted by the scrollbar. """ if self._new_thumbnail_added: self._new_thumbnail_added = False self.scrollarea.verticalScrollBar().setValue(vsb_max) # ---- ScrollBar Handlers def go_up(self): """Scroll the scrollbar of the scrollarea up by a single step.""" vsb = self.scrollarea.verticalScrollBar() vsb.setValue(int(vsb.value() - vsb.singleStep())) def go_down(self): """Scroll the scrollbar of the scrollarea down by a single step.""" vsb = self.scrollarea.verticalScrollBar() vsb.setValue(int(vsb.value() + vsb.singleStep()))
def uicreate_groupbox(self, idx): """.""" grpbx = QGroupBox(self.line_names[idx], self) grpbx.setCheckable(True) grpbx.setChecked(not idx) grpbx.toggled.connect(self.updater[idx].set_visible) vbl = QVBoxLayout(grpbx) gdl = QGridLayout() gdl.setSpacing(4) vbl.addLayout(gdl) if self.is_orb: lbl_orb = self.uicreate_label('Show', grpbx) lbl_ref = self.uicreate_label('as diff to:', grpbx) cbx_ref = self.uicreate_combobox(grpbx, 'ref', idx) cbx_orb = self.uicreate_combobox(grpbx, 'val', idx) gdl.addWidget(lbl_orb, 0, 0) gdl.addWidget(lbl_ref, 1, 0) gdl.addWidget(cbx_orb, 0, 1) gdl.addWidget(cbx_ref, 1, 1) pb_save = QPushButton('', grpbx) pb_save.clicked.connect(_part(self._save_difference, idx)) pb_save.setObjectName('butt') pb_save.setStyleSheet('#butt {max-width: 40px; icon-size: 35px;}') pb_save.setIcon(qta.icon('fa5.save')) pb_save.setToolTip('Save diff to file') gdl.addWidget(pb_save, 0, 2, 2, 1) unit = 'm' if self.is_orb else 'rad' for pln in ('x', 'y'): wid = QWidget(grpbx) vbl.addWidget(wid) vbl.setSpacing(2) hbl = QHBoxLayout(wid) hbl.setSpacing(0) cbx = QCheckBox('{0:s}:'.format(pln.upper()), wid) cbx.setObjectName(pln + 'checkbox') cbx.setChecked(False) hbl.addWidget(cbx) lab_avg = Label(unit, '-100.00 mrad', wid) self.updater[idx].ave[pln].connect(lab_avg.setFloat) lab_avg.setStyleSheet("""min-width:4.5em;""") lab_avg.setAlignment(Qt.AlignRight) hbl.addWidget(lab_avg) hbl.addWidget( QLabel(" <html><head/><body><p>±</p></body></html> ", wid)) lab_std = Label(unit, '100.00 mrad', wid) self.updater[idx].std[pln].connect(lab_std.setFloat) lab_std.setStyleSheet("""min-width:4.5em;""") lab_std.setAlignment(Qt.AlignLeft) hbl.addWidget(lab_std) hbl.addWidget(QLabel('(pp. ', wid)) lab_p2p = Label(unit, '100.00 mrad', wid) self.updater[idx].p2p[pln].connect(lab_p2p.setFloat) lab_p2p.setStyleSheet("""min-width:4.5em;""") lab_p2p.setAlignment(Qt.AlignLeft) hbl.addWidget(lab_p2p) hbl.addWidget(QLabel(')', wid)) return grpbx
class QtLayerControls(QFrame): """Superclass for all the other LayerControl classes. This class is never directly instantiated anywhere. Parameters ---------- layer : napari.layers.Layer An instance of a napari layer. Attributes ---------- blendComboBox : qtpy.QtWidgets.QComboBox Drowpdown widget to select blending mode of layer. grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : napari.layers.Layer An instance of a napari layer. opacitySlider : qtpy.QtWidgets.QSlider Slider controlling opacity of the layer. """ def __init__(self, layer): super().__init__() self.layer = layer self.layer.events.blending.connect(self._on_blending_change) self.layer.events.opacity.connect(self._on_opacity_change) self.setAttribute(Qt.WA_DeleteOnClose) self.setObjectName('layer') self.setMouseTracking(True) self.grid_layout = QGridLayout(self) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setSpacing(2) self.grid_layout.setColumnMinimumWidth(0, 86) self.grid_layout.setColumnStretch(1, 1) self.setLayout(self.grid_layout) sld = QSlider(Qt.Horizontal, parent=self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.valueChanged.connect(self.changeOpacity) self.opacitySlider = sld self._on_opacity_change() blend_comboBox = QComboBox(self) blend_comboBox.addItems(Blending.keys()) index = blend_comboBox.findText( self.layer.blending, Qt.MatchFixedString ) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect(self.changeBlending) self.blendComboBox = blend_comboBox def changeOpacity(self, value): """Change opacity value on the layer model. Parameters ---------- value : float Opacity value for shapes. Input range 0 - 100 (transparent to fully opaque). """ with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value / 100 def changeBlending(self, text): """Change blending mode on the layer model. Parameters ---------- text : str Name of blending mode, eg: 'translucent', 'additive', 'opaque'. """ self.layer.blending = text def _on_opacity_change(self, event=None): """Receive layer model opacity change event and update opacity slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.opacity.blocker(): self.opacitySlider.setValue(int(self.layer.opacity * 100)) def _on_blending_change(self, event=None): """Receive layer model blending mode change event and update slider. Parameters ---------- event : napari.utils.event.Event, optional The napari event that triggered this method, by default None. """ with self.layer.events.blending.blocker(): index = self.blendComboBox.findText( self.layer.blending, Qt.MatchFixedString ) self.blendComboBox.setCurrentIndex(index) def close(self): """Disconnect events when widget is closing.""" disconnect_events(self.layer.events, self) for child in self.children(): close_method = getattr(child, 'close', None) if close_method is not None: close_method() super().close()
class PSContainer(QWidget): """PSContainer.""" def __init__(self, widget, parent=None): super().__init__(parent) self._widget = widget self.name = widget.devname self.bbbname = widget.bbbname self.udcname = widget.udcname self.dclinks = list() self.dclinks_type = '' self.dclink_widgets = list() self.dclinksbbbname = set() self.dclinksudcname = set() dclinks = PSSearch.conv_psname_2_dclink(self.name) if dclinks: self.dclinks = dclinks self.dclinks_type = PSSearch.conv_psname_2_psmodel(dclinks[0]) if self.dclinks_type != 'REGATRON_DCLink': for dc in dclinks: self.dclinksbbbname.add(PSSearch.conv_psname_2_bbbname(dc)) self.dclinksudcname.add(PSSearch.conv_psname_2_udc(dc)) self.all_props = get_prop2label(PVName(dclinks[0])) self.visible_props = sort_propties( ['detail', 'state', 'intlk', 'setpoint', 'monitor']) self._setup_ui() self._create_actions() self._enable_actions() self.setStyleSheet(""" #HideButton { min-width: 10px; max-width: 10px; } #DCLinkContainer { background-color: lightgrey; } """) def _setup_ui(self): """Setup widget UI.""" self._layout = QGridLayout() self._layout.setSpacing(10) self._layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self._layout) self._dclink_container = QWidget(self) self._dclink_container.setObjectName('DCLinkContainer') self._dclink_container.setLayout(QVBoxLayout()) self._dclink_is_filled = False if self.dclinks: self._hide = QPushButton(qta.icon('mdi.plus'), '', self) else: self._hide = QPushButton('', self) self._hide.setEnabled(False) self._hide.setObjectName('HideButton') self._hide.setSizePolicy(QSzPlcy.Maximum, QSzPlcy.Maximum) self._hide.setFlat(True) self._layout.addWidget(self._hide, 0, 0, Qt.AlignCenter) self._layout.addWidget(self._widget, 0, 1) self._layout.addWidget(self._dclink_container, 1, 1) # Configure self._dclink_container.setHidden(True) self._hide.clicked.connect(self._toggle_dclink) def _toggle_dclink(self): if self._dclink_container.isHidden(): if not self._dclink_is_filled: self._fill_dclink_container() self._enable_actions() self._hide.setIcon(qta.icon('mdi.minus')) self._dclink_container.setHidden(False) else: self._hide.setIcon(qta.icon('mdi.plus')) self._dclink_container.setHidden(True) def _fill_dclink_container(self): self._dclink_is_filled = True self._dclink_container.layout().addWidget( SummaryHeader(self.dclinks[0], self.visible_props, self)) for dclink_name in self.dclinks: w = SummaryWidget(dclink_name, self.visible_props, self) if self.dclinks_type == 'REGATRON_DCLink': connect_newprocess(w.detail_bt, [ 'sirius-hla-as-ps-regatron-individual', '-dev', dclink_name ], parent=self, is_pydm=True) else: connect_window(w.detail_bt, PSDetailWindow, self, psname=dclink_name) self._dclink_container.layout().addWidget(w) self.dclink_widgets.append(w) def update_visible_props(self, new_value): self.visible_props = sort_propties(new_value) self._enable_actions() # Action methods def _create_actions(self): self._turn_on_action = QAction('Turn DCLinks On', self) self._turn_on_action.triggered.connect( lambda: self._set_dclink_pwrstate(True)) self._turn_on_action.setEnabled(False) self._turn_off_action = QAction('Turn DCLinks Off', self) self._turn_off_action.triggered.connect( lambda: self._set_dclink_pwrstate(False)) self._turn_off_action.setEnabled(False) self._open_loop_action = QAction('Open DCLinks Control Loop', self) self._open_loop_action.triggered.connect( lambda: self._set_dclink_control_loop(False)) self._open_loop_action.setEnabled(False) self._close_loop_action = QAction('Close DCLinks Control Loop', self) self._close_loop_action.triggered.connect( lambda: self._set_dclink_control_loop(True)) self._close_loop_action.setEnabled(False) self._set_setpoint_action = QAction('Set DCLinks Voltage', self) self._set_setpoint_action.triggered.connect(self._set_setpoint) self._set_setpoint_action.setEnabled(False) self._reset_intlk_action = QAction('Reset DCLinks Interlocks', self) self._reset_intlk_action.triggered.connect(self._reset_intlk) self._reset_intlk_action.setEnabled(False) def _enable_actions(self): if 'state' in self.visible_props and \ not self._turn_on_action.isEnabled(): self._turn_on_action.setEnabled(True) self._turn_off_action.setEnabled(True) if 'ctrlloop' in self.visible_props and \ not self._open_loop_action.isEnabled(): self._open_loop_action.setEnabled(True) self._close_loop_action.setEnabled(True) if 'setpoint' in self.visible_props and \ not self._set_setpoint_action.isEnabled(): self._set_setpoint_action.setEnabled(True) if 'reset' in self.visible_props and \ not self._reset_intlk_action.isEnabled(): self._reset_intlk_action.setEnabled(True) def _set_dclink_pwrstate(self, value): for dclink in self.dclink_widgets: if value: dclink.turn_on() else: dclink.turn_off() def _set_dclink_control_loop(self, value): for dclink in self.dclink_widgets: btn = dclink.ctrlloop_bt if value: if btn._bit_val: btn.send_value() else: if not btn._bit_val: btn.send_value() def _set_setpoint(self): """Set current setpoint for every visible widget.""" dlg = QInputDialog(self) dlg.setLocale(QLocale(QLocale.English)) new_value, ok = dlg.getDouble(self, "New setpoint", "Value") if ok: for dclink in self.dclink_widgets: sp = dclink.setpoint.sp_lineedit sp.setText(str(new_value)) try: sp.send_value() except TypeError: pass def _reset_intlk(self): for dclink in self.dclink_widgets: dclink.reset() # Overloaded method def contextMenuEvent(self, event): """Overload to create a custom context menu.""" widget = self.childAt(event.pos()) parent = widget.parent() grand_parent = parent.parent() if widget.objectName() == 'DCLinkContainer' or \ parent.objectName() == 'DCLinkContainer' or \ grand_parent.objectName() == 'DCLinkContainer': menu = QMenu(self) menu.addAction(self._turn_on_action) menu.addAction(self._turn_off_action) menu.addSeparator() menu.addAction(self._close_loop_action) menu.addAction(self._open_loop_action) menu.addSeparator() menu.addAction(self._set_setpoint_action) menu.addSeparator() menu.addAction(self._reset_intlk_action) menu.addSeparator() action = menu.addAction('Show Connections...') action.triggered.connect(self.show_connections) menu.popup(event.globalPos()) else: super().contextMenuEvent(event) def show_connections(self, checked): """.""" _ = checked c = ConnectionInspector(self) c.show()
def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.current_edge_color.connect( self._on_current_edge_color_change) self.layer.events.current_face_color.connect( self._on_current_face_color_change) self.layer.events.editable.connect(self._on_editable_change) self.layer.text.events.visible.connect(self._on_text_visibility_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.current_edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged.connect(self.changeWidth) self.widthSlider = sld self.select_button = QtModeRadioButton(layer, 'select', Mode.SELECT, tooltip='Select shapes') self.direct_button = QtModeRadioButton(layer, 'direct', Mode.DIRECT, tooltip='Select vertices') self.panzoom_button = QtModeRadioButton(layer, 'zoom', Mode.PAN_ZOOM, tooltip='Pan/zoom', checked=True) self.rectangle_button = QtModeRadioButton(layer, 'rectangle', Mode.ADD_RECTANGLE, tooltip='Add rectangles') self.ellipse_button = QtModeRadioButton(layer, 'ellipse', Mode.ADD_ELLIPSE, tooltip='Add ellipses') self.line_button = QtModeRadioButton(layer, 'line', Mode.ADD_LINE, tooltip='Add lines') self.path_button = QtModeRadioButton(layer, 'path', Mode.ADD_PATH, tooltip='Add paths') self.polygon_button = QtModeRadioButton(layer, 'polygon', Mode.ADD_POLYGON, tooltip='Add polygons') self.vertex_insert_button = QtModeRadioButton(layer, 'vertex_insert', Mode.VERTEX_INSERT, tooltip='Insert vertex') self.vertex_remove_button = QtModeRadioButton(layer, 'vertex_remove', Mode.VERTEX_REMOVE, tooltip='Remove vertex') self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip='Move to front', ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip='Move to back', ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip='Delete selected shapes', ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip='click to set current face color', ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip='click to set current edge color', ) self._on_current_edge_color_change() self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) text_disp_cb = QCheckBox() text_disp_cb.setToolTip('toggle text visibility') text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel('opacity:'), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel('edge width:'), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel('blending:'), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel('face color:'), 4, 0) self.grid_layout.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel('edge color:'), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel('display text:'), 6, 0) self.grid_layout.addWidget(self.textDispCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
def initUI(self, path): pathLabel = QLabel('Path') filenameLabel = QLabel('Filename') timeLabel = QLabel('Time') titleLabel = QLabel('Title') keywordLabel = QLabel('Keyword') figureLabel = QLabel('Figure') htmlLabel = QLabel('HTML') bodyLabel = QLabel('Body') self.pathEdit = QLineEdit(path) self.pathEdit.setReadOnly(True) self.filenameEdit = QLineEdit() self.timeEdit = QLineEdit() self.titleEdit = QLineEdit() self.keywordEdit = QLineEdit() self.figureEdit = QLineEdit() self.htmlEdit = QLineEdit() self.bodyEdit = QTextEdit() # If more than one keyword, delimit with comma. # Same for figure and html filenames. #btnSave = QPushButton('Save') #btnSave.setToolTip('Save script to file') #btnSave.clicked.connect(self.saveFile) # Replace save button with keyboard shortcut # Save move hand from keyboard to mouse. grid = QGridLayout() grid.setSpacing(5) row = 0 grid.addWidget(pathLabel, row, 0) grid.addWidget(self.pathEdit, row, 1) row += 1 grid.addWidget(filenameLabel, row, 0) grid.addWidget(self.filenameEdit, row, 1) row += 1 grid.addWidget(figureLabel, row, 0) grid.addWidget(self.figureEdit, row, 1) row += 1 grid.addWidget(htmlLabel, row, 0) grid.addWidget(self.htmlEdit, row, 1) row += 1 grid.addWidget(timeLabel, row, 0) grid.addWidget(self.timeEdit, row, 1) row += 1 grid.addWidget(titleLabel, row, 0) grid.addWidget(self.titleEdit, row, 1) row += 1 grid.addWidget(keywordLabel, row, 0) grid.addWidget(self.keywordEdit, row, 1) row += 1 grid.addWidget(bodyLabel, row, 0) grid.addWidget(self.bodyEdit, row, 1, 6, 1) #grid.addWidget(btnSave, 11, 1) self.actOpen = QAction('Open', self) self.actOpen.setShortcut('Ctrl+O') self.actOpen.triggered.connect(self.openFile) self.filenameEdit.addAction(self.actOpen) self.actSave = QAction('Save', self) self.actSave.setShortcut('Ctrl+S') self.actSave.triggered.connect(self.saveFile) self.bodyEdit.addAction(self.actSave) self.setLayout(grid) #self.setGeometry(300, 300, 600, 400) self.setWindowTitle('Form - ZhuNote')
def __init__(self, parent, text): QWidget.__init__(self, parent) self.text_editor = QTextEdit(self) self.text_editor.setText(text) self.text_editor.setReadOnly(True) # Type frame type_layout = QHBoxLayout() type_label = QLabel(_("Import as")) type_layout.addWidget(type_label) data_btn = QRadioButton(_("data")) data_btn.setChecked(True) self._as_data = True type_layout.addWidget(data_btn) code_btn = QRadioButton(_("code")) self._as_code = False type_layout.addWidget(code_btn) txt_btn = QRadioButton(_("text")) type_layout.addWidget(txt_btn) h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) type_layout.addItem(h_spacer) type_frame = QFrame() type_frame.setLayout(type_layout) # Opts frame grid_layout = QGridLayout() grid_layout.setSpacing(0) col_label = QLabel(_("Column separator:")) grid_layout.addWidget(col_label, 0, 0) col_w = QWidget() col_btn_layout = QHBoxLayout() self.tab_btn = QRadioButton(_("Tab")) self.tab_btn.setChecked(False) col_btn_layout.addWidget(self.tab_btn) self.ws_btn = QRadioButton(_("Whitespace")) self.ws_btn.setChecked(False) col_btn_layout.addWidget(self.ws_btn) other_btn_col = QRadioButton(_("other")) other_btn_col.setChecked(True) col_btn_layout.addWidget(other_btn_col) col_w.setLayout(col_btn_layout) grid_layout.addWidget(col_w, 0, 1) self.line_edt = QLineEdit(",") self.line_edt.setMaximumWidth(30) self.line_edt.setEnabled(True) other_btn_col.toggled.connect(self.line_edt.setEnabled) grid_layout.addWidget(self.line_edt, 0, 2) row_label = QLabel(_("Row separator:")) grid_layout.addWidget(row_label, 1, 0) row_w = QWidget() row_btn_layout = QHBoxLayout() self.eol_btn = QRadioButton(_("EOL")) self.eol_btn.setChecked(True) row_btn_layout.addWidget(self.eol_btn) other_btn_row = QRadioButton(_("other")) row_btn_layout.addWidget(other_btn_row) row_w.setLayout(row_btn_layout) grid_layout.addWidget(row_w, 1, 1) self.line_edt_row = QLineEdit(";") self.line_edt_row.setMaximumWidth(30) self.line_edt_row.setEnabled(False) other_btn_row.toggled.connect(self.line_edt_row.setEnabled) grid_layout.addWidget(self.line_edt_row, 1, 2) grid_layout.setRowMinimumHeight(2, 15) other_group = QGroupBox(_("Additional options")) other_layout = QGridLayout() other_group.setLayout(other_layout) skiprows_label = QLabel(_("Skip rows:")) other_layout.addWidget(skiprows_label, 0, 0) self.skiprows_edt = QLineEdit('0') self.skiprows_edt.setMaximumWidth(30) intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), self.skiprows_edt) self.skiprows_edt.setValidator(intvalid) other_layout.addWidget(self.skiprows_edt, 0, 1) other_layout.setColumnMinimumWidth(2, 5) comments_label = QLabel(_("Comments:")) other_layout.addWidget(comments_label, 0, 3) self.comments_edt = QLineEdit('#') self.comments_edt.setMaximumWidth(30) other_layout.addWidget(self.comments_edt, 0, 4) self.trnsp_box = QCheckBox(_("Transpose")) #self.trnsp_box.setEnabled(False) other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) grid_layout.addWidget(other_group, 3, 0, 2, 0) opts_frame = QFrame() opts_frame.setLayout(grid_layout) data_btn.toggled.connect(opts_frame.setEnabled) data_btn.toggled.connect(self.set_as_data) code_btn.toggled.connect(self.set_as_code) # self.connect(txt_btn, SIGNAL("toggled(bool)"), # self, SLOT("is_text(bool)")) # Final layout layout = QVBoxLayout() layout.addWidget(type_frame) layout.addWidget(self.text_editor) layout.addWidget(opts_frame) self.setLayout(layout)
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 UiLinelistsWindow(object): # this code was taken as-is from the Designer. # Cleaning it up sounds like a lower priority # task for now. def setupUi(self, MainWindow, title): MainWindow.setWindowTitle(title) MainWindow.setObjectName("MainWindow") MainWindow.resize(600, 850) MainWindow.setMinimumSize(QSize(300, 350)) self.centralWidget = QWidget(MainWindow) self.centralWidget.setObjectName("centralWidget") self.gridLayout = QGridLayout(self.centralWidget) self.gridLayout.setContentsMargins(11, 11, 11, 11) self.gridLayout.setSpacing(6) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_5.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_5.setSpacing(6) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.lines_selected_label = QLabel(self.centralWidget) self.lines_selected_label.setObjectName("lines_selected_label") self.horizontalLayout_5.addWidget(self.lines_selected_label) self.label = QLabel(self.centralWidget) self.label.setObjectName("label") self.horizontalLayout_5.addWidget(self.label) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_5.addItem(spacerItem) self.draw_button = QPushButton(self.centralWidget) self.draw_button.setObjectName("draw_button") self.horizontalLayout_5.addWidget(self.draw_button) self.erase_button = QPushButton(self.centralWidget) self.erase_button.setObjectName("erase_button") self.horizontalLayout_5.addWidget(self.erase_button) self.dismiss_button = QPushButton(self.centralWidget) self.dismiss_button.setObjectName("dismiss_button") self.horizontalLayout_5.addWidget(self.dismiss_button) self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) self.verticalLayout_11 = QVBoxLayout() self.verticalLayout_11.setContentsMargins(11, 11, 11, 11) self.verticalLayout_11.setSpacing(6) self.verticalLayout_11.setObjectName("verticalLayout_11") self.tabWidget = QTabWidget(self.centralWidget) self.tabWidget.setObjectName("tabWidget") self.tabWidget.setTabsClosable(True) self.verticalLayout_11.addWidget(self.tabWidget) self.gridLayout.addLayout(self.verticalLayout_11, 0, 0, 1, 1) self.horizontalLayout_7 = QHBoxLayout() self.horizontalLayout_7.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_7.setSpacing(6) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_7.addItem(spacerItem) self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.gridLayout.addLayout(self.horizontalLayout_7, 2, 0, 2, 1) MainWindow.setCentralWidget(self.centralWidget) # self.menuBar = QMenuBar(MainWindow) # self.menuBar.setGeometry(QRect(0, 0, 767, 22)) # self.menuBar.setObjectName("menuBar") # # self.menuFile = QMenu(self.menuBar) # self.menuFile.setObjectName("menuFile") # # MainWindow.setMenuBar(self.menuBar) self.mainToolBar = QToolBar(MainWindow) self.mainToolBar.setMovable(False) self.mainToolBar.setFloatable(False) self.mainToolBar.setObjectName("mainToolBar") MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # self.statusBar = QStatusBar(MainWindow) # self.statusBar.setObjectName("statusBar") # MainWindow.setStatusBar(self.statusBar) self.actionOpen = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Open Folder-48.png")) self.actionOpen.setIcon(icon) self.actionOpen.setObjectName("actionOpen") self.actionExport = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Export-48.png")) self.actionExport.setIcon(icon) self.actionExport.setObjectName("actionExport") self.line_list_selector = QComboBox() self.line_list_selector.setToolTip( "Select line list from internal library") self.actionExit = QAction(MainWindow) self.actionExit.setObjectName("actionExit") self.actionRemove = QAction(MainWindow) self.actionRemove.setObjectName("actionRemove") self.actionChange_Color = QAction(MainWindow) self.actionChange_Color.setObjectName("actionChange_Color") # self.menuFile.addAction(self.actionOpen) # self.menuFile.addSeparator() # self.menuFile.addAction(self.actionExit) # self.menuBar.addAction(self.menuFile.menuAction()) self.mainToolBar.addAction(self.actionOpen) self.mainToolBar.addAction(self.actionExport) self.mainToolBar.addSeparator() self.mainToolBar.addWidget(self.line_list_selector) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QCoreApplication.translate self.lines_selected_label.setText(_translate("MainWindow", "0")) self.lines_selected_label.setToolTip( "Total number of lines selected in all sets.") self.label.setText(_translate("MainWindow", "lines selected")) self.label.setToolTip("Total number of lines selected in all sets.") self.draw_button.setText(_translate("MainWindow", "Draw")) self.draw_button.setToolTip( "Plot markers for all selected lines in all sets.") self.erase_button.setText(_translate("MainWindow", "Erase")) self.erase_button.setToolTip("Erase all markers") self.dismiss_button.setText(_translate("MainWindow", "Dismiss")) self.dismiss_button.setToolTip("Dismiss this window") # self.menuFile.setTitle(_translate("MainWindow", "File")) self.actionOpen.setText(_translate("MainWindow", "Open")) self.actionExport.setText( _translate("MainWindow", "Export plotted lines")) self.actionExit.setText(_translate("MainWindow", "Exit")) self.actionRemove.setText(_translate("MainWindow", "Remove")) self.actionRemove.setToolTip( _translate("MainWindow", "Removes the selected layer")) self.actionChange_Color.setText( _translate("MainWindow", "Change Color")) self.actionChange_Color.setToolTip( _translate("MainWindow", "Change the line color selected layer"))
def __init__(self, viewer): super().__init__() self.pool = QThreadPool() QCoreApplication.setAttribute( Qt.AA_UseStyleSheetPropagationInWidgetStyles, True) self.viewer = viewer self.dims = QtDims(self.viewer.dims) self.controls = QtControls(self.viewer) self.layers = QtLayerList(self.viewer.layers) self.layerButtons = QtLayerButtons(self.viewer) self.viewerButtons = QtViewerButtons(self.viewer) self.console = QtConsole({'viewer': self.viewer}) # This dictionary holds the corresponding vispy visual for each layer self.layer_to_visual = {} if self.console.shell is not None: self.console.style().unpolish(self.console) self.console.style().polish(self.console) self.console.hide() self.viewerButtons.consoleButton.clicked.connect( lambda: self._toggle_console()) else: self.viewerButtons.consoleButton.setEnabled(False) self.canvas = SceneCanvas(keys=None, vsync=True) self.canvas.events.ignore_callback_errors = False self.canvas.events.draw.connect(self.dims.enable_play) self.canvas.native.setMinimumSize(QSize(200, 200)) self.canvas.context.set_depth_func('lequal') self.canvas.connect(self.on_mouse_move) self.canvas.connect(self.on_mouse_press) self.canvas.connect(self.on_mouse_release) self.canvas.connect(self.on_key_press) self.canvas.connect(self.on_key_release) self.canvas.connect(self.on_draw) self.view = self.canvas.central_widget.add_view() self._update_camera() main_widget = QWidget() main_layout = QGridLayout() main_layout.setContentsMargins(15, 20, 15, 10) main_layout.addWidget(self.canvas.native, 0, 1, 3, 1) main_layout.addWidget(self.dims, 3, 1) main_layout.addWidget(self.controls, 0, 0) main_layout.addWidget(self.layerButtons, 1, 0) main_layout.addWidget(self.layers, 2, 0) main_layout.addWidget(self.viewerButtons, 3, 0) main_layout.setColumnStretch(1, 1) main_layout.setSpacing(10) main_widget.setLayout(main_layout) self.setOrientation(Qt.Vertical) self.addWidget(main_widget) if self.console.shell is not None: self.addWidget(self.console) self._last_visited_dir = str(Path.home()) self._cursors = { 'disabled': QCursor( QPixmap(':/icons/cursor/cursor_disabled.png').scaled(20, 20)), 'cross': Qt.CrossCursor, 'forbidden': Qt.ForbiddenCursor, 'pointing': Qt.PointingHandCursor, 'standard': QCursor(), } self._update_palette(viewer.palette) self._key_release_generators = {} self.viewer.events.interactive.connect(self._on_interactive) self.viewer.events.cursor.connect(self._on_cursor) self.viewer.events.reset_view.connect(self._on_reset_view) self.viewer.events.palette.connect( lambda event: self._update_palette(event.palette)) self.viewer.layers.events.reordered.connect(self._reorder_layers) self.viewer.layers.events.added.connect(self._add_layer) self.viewer.layers.events.removed.connect(self._remove_layer) self.viewer.dims.events.camera.connect( lambda event: self._update_camera()) # stop any animations whenever the layers change self.viewer.events.layers_change.connect(lambda x: self.dims.stop()) self.setAcceptDrops(True)
def __init__(self, layer): super().__init__(layer) self.layer.events.mode.connect(self._on_mode_change) self.layer.events.edge_width.connect(self._on_edge_width_change) self.layer.events.current_edge_color.connect( self._on_current_edge_color_change ) self.layer.events.current_face_color.connect( self._on_current_face_color_change ) self.layer.events.editable.connect(self._on_editable_change) self.layer.text.events.visible.connect(self._on_text_visibility_change) sld = QSlider(Qt.Horizontal) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(40) sld.setSingleStep(1) value = self.layer.current_edge_width if isinstance(value, Iterable): if isinstance(value, list): value = np.asarray(value) value = value.mean() sld.setValue(int(value)) sld.valueChanged.connect(self.changeWidth) self.widthSlider = sld def _radio_button( parent, btn_name, mode, action_name, extra_tooltip_text='', **kwargs, ): """ Convenience local function to create a RadioButton and bind it to an action at the same time. Parameters ---------- parent : Any Parent of the generated QtModeRadioButton btn_name : str name fo the button mode : Enum Value Associated to current button action_name : str Action triggered when button pressed extra_tooltip_text : str Text you want added after the automatic tooltip set by the action manager **kwargs: Passed to QtModeRadioButton Returns ------- button: QtModeRadioButton button bound (or that will be bound to) to action `action_name` Notes ----- When shortcuts are modifed/added/removed via the action manager, the tooltip will be updated to reflect the new shortcut. """ action_name = 'napari:' + action_name btn = QtModeRadioButton(parent, btn_name, mode, **kwargs) action_manager.bind_button( action_name, btn, extra_tooltip_text='', ) return btn self.select_button = _radio_button( layer, 'select', Mode.SELECT, "activate_select_mode" ) self.direct_button = _radio_button( layer, 'direct', Mode.DIRECT, "activate_direct_mode" ) self.panzoom_button = _radio_button( layer, 'zoom', Mode.PAN_ZOOM, "napari:activate_shape_pan_zoom_mode", extra_tooltip_text=trans._('(or hold Space)'), checked=True, ) self.rectangle_button = _radio_button( layer, 'rectangle', Mode.ADD_RECTANGLE, "activate_add_rectangle_mode", ) self.ellipse_button = _radio_button( layer, 'ellipse', Mode.ADD_ELLIPSE, "activate_add_ellipse_mode", ) self.line_button = _radio_button( layer, 'line', Mode.ADD_LINE, "activate_add_line_mode" ) self.path_button = _radio_button( layer, 'path', Mode.ADD_PATH, "activate_add_path_mode" ) self.polygon_button = _radio_button( layer, 'polygon', Mode.ADD_POLYGON, "activate_add_polygon_mode", ) self.vertex_insert_button = _radio_button( layer, 'vertex_insert', Mode.VERTEX_INSERT, "activate_vertex_insert_mode", ) self.vertex_remove_button = _radio_button( layer, 'vertex_remove', Mode.VERTEX_REMOVE, "activate_vertex_remove_mode", ) self.move_front_button = QtModePushButton( layer, 'move_front', slot=self.layer.move_to_front, tooltip=trans._('Move to front'), ) action_manager.bind_button( 'napari:move_shapes_selection_to_front', self.move_front_button ) self.move_back_button = QtModePushButton( layer, 'move_back', slot=self.layer.move_to_back, tooltip=trans._('Move to back'), ) action_manager.bind_button( 'napari:move_shapes_selection_to_back', self.move_back_button ) self.delete_button = QtModePushButton( layer, 'delete_shape', slot=self.layer.remove_selected, tooltip=trans._( "Delete selected shapes ({shortcut})", shortcut=Shortcut('Backspace').platform, ), ) self.button_group = QButtonGroup(self) self.button_group.addButton(self.select_button) self.button_group.addButton(self.direct_button) self.button_group.addButton(self.panzoom_button) self.button_group.addButton(self.rectangle_button) self.button_group.addButton(self.ellipse_button) self.button_group.addButton(self.line_button) self.button_group.addButton(self.path_button) self.button_group.addButton(self.polygon_button) self.button_group.addButton(self.vertex_insert_button) self.button_group.addButton(self.vertex_remove_button) button_grid = QGridLayout() button_grid.addWidget(self.vertex_remove_button, 0, 2) button_grid.addWidget(self.vertex_insert_button, 0, 3) button_grid.addWidget(self.delete_button, 0, 4) button_grid.addWidget(self.direct_button, 0, 5) button_grid.addWidget(self.select_button, 0, 6) button_grid.addWidget(self.panzoom_button, 0, 7) button_grid.addWidget(self.move_back_button, 1, 1) button_grid.addWidget(self.move_front_button, 1, 2) button_grid.addWidget(self.ellipse_button, 1, 3) button_grid.addWidget(self.rectangle_button, 1, 4) button_grid.addWidget(self.polygon_button, 1, 5) button_grid.addWidget(self.line_button, 1, 6) button_grid.addWidget(self.path_button, 1, 7) button_grid.setContentsMargins(5, 0, 0, 5) button_grid.setColumnStretch(0, 1) button_grid.setSpacing(4) self.faceColorEdit = QColorSwatchEdit( initial_color=self.layer.current_face_color, tooltip=trans._('click to set current face color'), ) self._on_current_face_color_change() self.edgeColorEdit = QColorSwatchEdit( initial_color=self.layer.current_edge_color, tooltip=trans._('click to set current edge color'), ) self._on_current_edge_color_change() self.faceColorEdit.color_changed.connect(self.changeFaceColor) self.edgeColorEdit.color_changed.connect(self.changeEdgeColor) text_disp_cb = QCheckBox() text_disp_cb.setToolTip(trans._('toggle text visibility')) text_disp_cb.setChecked(self.layer.text.visible) text_disp_cb.stateChanged.connect(self.change_text_visibility) self.textDispCheckBox = text_disp_cb # grid_layout created in QtLayerControls # addWidget(widget, row, column, [row_span, column_span]) self.grid_layout.addLayout(button_grid, 0, 0, 1, 2) self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0) self.grid_layout.addWidget(self.opacitySlider, 1, 1) self.grid_layout.addWidget(QLabel(trans._('edge width:')), 2, 0) self.grid_layout.addWidget(self.widthSlider, 2, 1) self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0) self.grid_layout.addWidget(self.blendComboBox, 3, 1) self.grid_layout.addWidget(QLabel(trans._('face color:')), 4, 0) self.grid_layout.addWidget(self.faceColorEdit, 4, 1) self.grid_layout.addWidget(QLabel(trans._('edge color:')), 5, 0) self.grid_layout.addWidget(self.edgeColorEdit, 5, 1) self.grid_layout.addWidget(QLabel(trans._('display text:')), 6, 0) self.grid_layout.addWidget(self.textDispCheckBox, 6, 1) self.grid_layout.setRowStretch(7, 1) self.grid_layout.setColumnStretch(1, 1) self.grid_layout.setSpacing(4)
def __init__(self, parent, text): QWidget.__init__(self, parent) self.text_editor = QTextEdit(self) self.text_editor.setText(text) self.text_editor.setReadOnly(True) # Type frame type_layout = QHBoxLayout() type_label = QLabel(_("Import as")) type_layout.addWidget(type_label) data_btn = QRadioButton(_("data")) data_btn.setChecked(True) self._as_data= True type_layout.addWidget(data_btn) code_btn = QRadioButton(_("code")) self._as_code = False type_layout.addWidget(code_btn) txt_btn = QRadioButton(_("text")) type_layout.addWidget(txt_btn) h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) type_layout.addItem(h_spacer) type_frame = QFrame() type_frame.setLayout(type_layout) # Opts frame grid_layout = QGridLayout() grid_layout.setSpacing(0) col_label = QLabel(_("Column separator:")) grid_layout.addWidget(col_label, 0, 0) col_w = QWidget() col_btn_layout = QHBoxLayout() self.tab_btn = QRadioButton(_("Tab")) self.tab_btn.setChecked(False) col_btn_layout.addWidget(self.tab_btn) other_btn_col = QRadioButton(_("other")) other_btn_col.setChecked(True) col_btn_layout.addWidget(other_btn_col) col_w.setLayout(col_btn_layout) grid_layout.addWidget(col_w, 0, 1) self.line_edt = QLineEdit(",") self.line_edt.setMaximumWidth(30) self.line_edt.setEnabled(True) other_btn_col.toggled.connect(self.line_edt.setEnabled) grid_layout.addWidget(self.line_edt, 0, 2) row_label = QLabel(_("Row separator:")) grid_layout.addWidget(row_label, 1, 0) row_w = QWidget() row_btn_layout = QHBoxLayout() self.eol_btn = QRadioButton(_("EOL")) self.eol_btn.setChecked(True) row_btn_layout.addWidget(self.eol_btn) other_btn_row = QRadioButton(_("other")) row_btn_layout.addWidget(other_btn_row) row_w.setLayout(row_btn_layout) grid_layout.addWidget(row_w, 1, 1) self.line_edt_row = QLineEdit(";") self.line_edt_row.setMaximumWidth(30) self.line_edt_row.setEnabled(False) other_btn_row.toggled.connect(self.line_edt_row.setEnabled) grid_layout.addWidget(self.line_edt_row, 1, 2) grid_layout.setRowMinimumHeight(2, 15) other_group = QGroupBox(_("Additional options")) other_layout = QGridLayout() other_group.setLayout(other_layout) skiprows_label = QLabel(_("Skip rows:")) other_layout.addWidget(skiprows_label, 0, 0) self.skiprows_edt = QLineEdit('0') self.skiprows_edt.setMaximumWidth(30) intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), self.skiprows_edt) self.skiprows_edt.setValidator(intvalid) other_layout.addWidget(self.skiprows_edt, 0, 1) other_layout.setColumnMinimumWidth(2, 5) comments_label = QLabel(_("Comments:")) other_layout.addWidget(comments_label, 0, 3) self.comments_edt = QLineEdit('#') self.comments_edt.setMaximumWidth(30) other_layout.addWidget(self.comments_edt, 0, 4) self.trnsp_box = QCheckBox(_("Transpose")) #self.trnsp_box.setEnabled(False) other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) grid_layout.addWidget(other_group, 3, 0, 2, 0) opts_frame = QFrame() opts_frame.setLayout(grid_layout) data_btn.toggled.connect(opts_frame.setEnabled) data_btn.toggled.connect(self.set_as_data) code_btn.toggled.connect(self.set_as_code) # self.connect(txt_btn, SIGNAL("toggled(bool)"), # self, SLOT("is_text(bool)")) # Final layout layout = QVBoxLayout() layout.addWidget(type_frame) layout.addWidget(self.text_editor) layout.addWidget(opts_frame) self.setLayout(layout)