예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
 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
예제 #6
0
    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)
예제 #7
0
    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
예제 #9
0
    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)
예제 #10
0
    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
예제 #11
0
    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)
예제 #12
0
    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)
예제 #13
0
    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()
예제 #14
0
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()
예제 #15
0
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))
예제 #16
0
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()
예제 #17
0
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)
예제 #18
0
    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)
예제 #19
0
    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)
예제 #20
0
    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)
예제 #21
0
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()
예제 #22
0
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()))
예제 #23
0
    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>&#177;</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
예제 #24
0
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()
예제 #25
0
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()
예제 #26
0
    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)
예제 #27
0
    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')
예제 #28
0
    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)
예제 #29
0
    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()
예제 #30
0
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"))
예제 #31
0
    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)
예제 #32
0
    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)
예제 #33
0
    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)