Exemplo n.º 1
0
class PreferencesDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.contentsWidget = QListWidget()
        self.pagesWidget = QStackedWidget()

        self.plotPref = PlotPreferences(parent)
        self.dataPref = DataPreferences(parent)
        self.pagesWidget.addWidget(self.plotPref)
        self.pagesWidget.addWidget(self.dataPref)

        self.contentsWidget.addItem("Plot")
        self.contentsWidget.addItem("Data")
        self.contentsWidget.currentItemChanged.connect(self.changePage)
        self.contentsWidget.setCurrentRow(0)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                          | QDialogButtonBox.Apply
                                          | QDialogButtonBox.Close)

        okButton = self.buttonBox.button(QDialogButtonBox.Ok)
        okButton.clicked.connect(self.ok)
        applyButton = self.buttonBox.button(QDialogButtonBox.Apply)
        applyButton.clicked.connect(self.apply)
        closeButton = self.buttonBox.button(QDialogButtonBox.Close)
        closeButton.clicked.connect(self.close)

        horizontalLayout = QHBoxLayout()
        horizontalLayout.addWidget(self.contentsWidget)
        horizontalLayout.addWidget(self.pagesWidget)

        self.contentsWidget.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)

        mainLayout = QVBoxLayout()
        mainLayout.addLayout(horizontalLayout)
        mainLayout.addWidget(self.buttonBox)

        self.setLayout(mainLayout)

        self.setWindowTitle("Preferences")

    def changePage(self, current, previous):
        if not current:
            current = previous
        self.pagesWidget.setCurrentIndex(self.contentsWidget.row(current))

    def show(self):
        for n in range(self.pagesWidget.count()):
            self.pagesWidget.widget(n).setCurrentValues()
        super().show()

    def apply(self):
        self.pagesWidget.currentWidget().apply()

    def ok(self):
        for idx in range(self.pagesWidget.count()):
            self.pagesWidget.widget(idx).apply()
        self.accept()
Exemplo n.º 2
0
class Editor(QDialog):
    """Basic scene editor."""
    def __init__(self, parent: MainWindow, renderers: List[Renderer]) -> None:
        """Initialize the Editor."""
        super().__init__(parent=parent)
        self.renderers = renderers

        self.tree_widget = QTreeWidget()
        self.tree_widget.setHeaderHidden(True)
        self.stacked_widget = QStackedWidget()
        self.layout = QHBoxLayout()
        self.layout.addWidget(self.tree_widget)
        self.layout.addWidget(self.stacked_widget)

        def _selection_callback() -> None:
            for item in self.tree_widget.selectedItems():
                widget_idx = item.data(0, Qt.ItemDataRole.UserRole)
                self.stacked_widget.setCurrentIndex(widget_idx)

        self.tree_widget.itemSelectionChanged.connect(_selection_callback)

        self.setLayout(self.layout)
        self.setWindowTitle("Editor")
        self.setModal(True)

        self.update()

    def update(self) -> None:
        """Update the internal widget list."""
        self.tree_widget.clear()
        for idx, renderer in enumerate(self.renderers):
            actors = renderer._actors  # pylint: disable=protected-access
            widget_idx = self.stacked_widget.addWidget(
                _get_renderer_widget(renderer))
            top_item = QTreeWidgetItem(self.tree_widget,
                                       ["Renderer {}".format(idx)])
            top_item.setData(0, Qt.ItemDataRole.UserRole, widget_idx)
            self.tree_widget.addTopLevelItem(top_item)
            for name, actor in actors.items():
                if actor is not None:
                    widget_idx = self.stacked_widget.addWidget(
                        _get_actor_widget(actor))
                    child_item = QTreeWidgetItem(top_item, [name])
                    child_item.setData(0, Qt.ItemDataRole.UserRole, widget_idx)
                    top_item.addChild(child_item)
        self.tree_widget.expandAll()

    def toggle(self) -> None:
        """Toggle the editor visibility."""
        self.update()
        if self.isVisible():
            self.hide()
        else:
            self.show()
Exemplo n.º 3
0
class MixerPanel(QFrame):
    '''A color mixer to hook up to an image.
    You pass the image you the panel to operate on
    and it operates on that image in place. You also
    pass a callback to be called to trigger a refresh.
    This callback is called every time the mixer modifies
    your image.'''
    def __init__(self, img):
        QFrame.__init__(self)
        # self.setFrameStyle(QFrame.Box | QFrame.Sunken)

        self.img = img
        self.mixer = ColorMixer(self.img)
        self.callback = None

        #---------------------------------------------------------------
        # ComboBox
        #---------------------------------------------------------------

        self.combo_box_entries = [
            'RGB Color', 'HSV Color', 'Brightness/Contrast', 'Gamma',
            'Gamma (Sigmoidal)'
        ]
        self.combo_box = QComboBox()
        for entry in self.combo_box_entries:
            self.combo_box.addItem(entry)
        self.combo_box.currentIndexChanged.connect(self.combo_box_changed)

        #---------------------------------------------------------------
        # RGB color sliders
        #---------------------------------------------------------------

        # radio buttons
        self.rgb_add = QRadioButton('Additive')
        self.rgb_mul = QRadioButton('Multiplicative')
        self.rgb_mul.toggled.connect(self.rgb_radio_changed)
        self.rgb_add.toggled.connect(self.rgb_radio_changed)

        # sliders
        rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
        gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
        bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
        self.rs = rs
        self.gs = gs
        self.bs = bs

        self.rgb_widget = QWidget()
        self.rgb_widget.layout = QGridLayout(self.rgb_widget)
        self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rs, 2, 0)
        self.rgb_widget.layout.addWidget(self.gs, 2, 1)
        self.rgb_widget.layout.addWidget(self.bs, 2, 2)

        #---------------------------------------------------------------
        # HSV sliders
        #---------------------------------------------------------------
        # radio buttons
        self.hsv_add = QRadioButton('Additive')
        self.hsv_mul = QRadioButton('Multiplicative')
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)

        # sliders
        hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
        ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
        vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
        self.hs = hs
        self.ss = ss
        self.vs = vs

        self.hsv_widget = QWidget()
        self.hsv_widget.layout = QGridLayout(self.hsv_widget)
        self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hs, 2, 0)
        self.hsv_widget.layout.addWidget(self.ss, 2, 1)
        self.hsv_widget.layout.addWidget(self.vs, 2, 2)

        #---------------------------------------------------------------
        # Brightness/Contrast sliders
        #---------------------------------------------------------------
        # sliders
        cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
        bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
        self.cont = cont
        self.bright = bright

        # layout
        self.bright_widget = QWidget()
        self.bright_widget.layout = QGridLayout(self.bright_widget)
        self.bright_widget.layout.addWidget(self.cont, 0, 0)
        self.bright_widget.layout.addWidget(self.bright, 0, 1)

        #----------------------------------------------------------------------
        # Gamma Slider
        #----------------------------------------------------------------------
        gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
        self.gamma = gamma

        # layout
        self.gamma_widget = QWidget()
        self.gamma_widget.layout = QGridLayout(self.gamma_widget)
        self.gamma_widget.layout.addWidget(self.gamma, 0, 0)

        #---------------------------------------------------------------
        # Sigmoid Gamma sliders
        #---------------------------------------------------------------
        # sliders
        alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
        beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
        self.a_gamma = alpha
        self.b_gamma = beta

        # layout
        self.sig_gamma_widget = QWidget()
        self.sig_gamma_widget.layout = QGridLayout(self.sig_gamma_widget)
        self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
        self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)

        #---------------------------------------------------------------
        # Buttons
        #---------------------------------------------------------------
        self.commit_button = QPushButton('Commit')
        self.commit_button.clicked.connect(self.commit_changes)
        self.revert_button = QPushButton('Revert')
        self.revert_button.clicked.connect(self.revert_changes)

        #---------------------------------------------------------------
        # Mixer Layout
        #---------------------------------------------------------------
        self.sliders = QStackedWidget()
        self.sliders.addWidget(self.rgb_widget)
        self.sliders.addWidget(self.hsv_widget)
        self.sliders.addWidget(self.bright_widget)
        self.sliders.addWidget(self.gamma_widget)
        self.sliders.addWidget(self.sig_gamma_widget)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.combo_box, 0, 0)
        self.layout.addWidget(self.sliders, 1, 0)
        self.layout.addWidget(self.commit_button, 2, 0)
        self.layout.addWidget(self.revert_button, 3, 0)

        #---------------------------------------------------------------
        # State Initialization
        #---------------------------------------------------------------

        self.combo_box.setCurrentIndex(0)
        self.rgb_mul.setChecked(True)
        self.hsv_mul.setChecked(True)

    def set_callback(self, callback):
        self.callback = callback

    def combo_box_changed(self, index):
        self.sliders.setCurrentIndex(index)
        self.reset()

    def rgb_radio_changed(self):
        self.reset()

    def hsv_radio_changed(self):
        self.reset()

    def reset(self):
        self.reset_sliders()
        self.mixer.set_to_stateimg()
        if self.callback:
            self.callback()

    def reset_sliders(self):
        # handle changing the conversion factors necessary
        if self.rgb_add.isChecked():
            self.rs.set_conv_fac(0.51, -255)
            self.rs.set_value(0)
            self.gs.set_conv_fac(0.51, -255)
            self.gs.set_value(0)
            self.bs.set_conv_fac(0.51, -255)
            self.bs.set_value(0)
        else:
            self.rs.set_conv_fac(0.002, 0)
            self.rs.set_value(1.)
            self.gs.set_conv_fac(0.002, 0)
            self.gs.set_value(1.)
            self.bs.set_conv_fac(0.002, 0)
            self.bs.set_value(1.)

        self.hs.set_value(0)
        if self.hsv_add.isChecked():
            self.ss.set_conv_fac(0.002, -1)
            self.ss.set_value(0)
            self.vs.set_conv_fac(0.002, -1)
            self.vs.set_value(0)
        else:
            self.ss.set_conv_fac(0.002, 0)
            self.ss.set_value(1.)
            self.vs.set_conv_fac(0.002, 0)
            self.vs.set_value(1.)

        self.bright.set_value(0)
        self.cont.set_value(1.)

        self.gamma.set_value(1)
        self.a_gamma.set_value(1)
        self.b_gamma.set_value(0.5)

    def rgb_changed(self, name, val):
        if name == 'R':
            channel = self.mixer.RED
        elif name == 'G':
            channel = self.mixer.GREEN
        else:
            channel = self.mixer.BLUE

        if self.rgb_mul.isChecked():
            self.mixer.multiply(channel, val)
        elif self.rgb_add.isChecked():
            self.mixer.add(channel, val)
        else:
            pass

        if self.callback:
            self.callback()

    def hsv_changed(self, name, val):
        h = self.hs.val()
        s = self.ss.val()
        v = self.vs.val()

        if self.hsv_mul.isChecked():
            self.mixer.hsv_multiply(h, s, v)
        elif self.hsv_add.isChecked():
            self.mixer.hsv_add(h, s, v)
        else:
            pass

        if self.callback:
            self.callback()

    def bright_changed(self, name, val):
        b = self.bright.val()
        c = self.cont.val()
        self.mixer.brightness(c, b)

        if self.callback:
            self.callback()

    def gamma_changed(self, name, val):
        self.mixer.gamma(val)

        if self.callback:
            self.callback()

    def sig_gamma_changed(self, name, val):
        ag = self.a_gamma.val()
        bg = self.b_gamma.val()
        self.mixer.sigmoid_gamma(ag, bg)

        if self.callback:
            self.callback()

    def commit_changes(self):
        self.mixer.commit_changes()
        self.reset_sliders()

    def revert_changes(self):
        self.mixer.revert()
        self.reset_sliders()

        if self.callback:
            self.callback()
Exemplo n.º 4
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    resized = Signal(QSize)

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""
        # Because there are multiple pages, need to keep a list of values sets.
        self._values_orig_set_list = []
        self._values_set_list = []
        for _key, setting in SETTINGS.schemas().items():
            schema = json.loads(setting['json_schema'])
            # Need to remove certain properties that will not be displayed on the GUI
            properties = schema.pop('properties')
            model = setting['model']
            values = model.dict()
            napari_config = getattr(model, "NapariConfig", None)
            if napari_config is not None:
                for val in napari_config.preferences_exclude:
                    properties.pop(val)
                    values.pop(val)

            schema['properties'] = properties
            self._values_orig_set_list.append(set(values.items()))
            self._values_set_list.append(set(values.items()))

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""

        widget = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        widget.valueChanged.connect(self._reset_widgets)
        widget.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to SETTINGS."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        for n in range(self._stack.count()):
            # Must set the current row so that the proper set list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            self.check_differences(
                self._values_orig_set_list[n],
                self._values_set_list[n],
            )
        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """

        builder = WidgetBuilder()
        form = builder.create_form(schema, {})
        # set state values for widget
        form.widget.state = values
        form.widget.on_changed.connect(lambda d: self.check_differences(
            set(d.items()),
            self._values_set_list[self._list.currentIndex().row()],
        ))

        return form

    def check_differences(self, new_set, values_set):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_set : set
            The set of new values, with tuples of key value pairs for each
            setting.
        values_set : set
            The old set of values.
        """

        page = self._list.currentItem().text().split(" ")[0].lower()
        different_values = list(new_set - values_set)

        if len(different_values) > 0:
            # change the values in SETTINGS
            for val in different_values:
                try:
                    setattr(SETTINGS._settings[page], val[0], val[1])
                    self._values_set_list[
                        self._list.currentIndex().row()] = new_set
                except:  # noqa: E722
                    continue
Exemplo n.º 5
0
class MultiTurnData(BaseWidget):

    def __init__(self, parent=None, acq_type='ACQ', prefix='', bpm=''):
        self.acq_type = acq_type
        data_prefix = 'GEN_' if acq_type == 'ACQ' else 'PM_'
        super().__init__(
            parent=parent, prefix=prefix, bpm=bpm, data_prefix=data_prefix)

        self.samplespre = SiriusConnectionSignal(self.get_pvname(
            acq_type+'SamplesPre-RB', is_data=False))
        self.samplespost = SiriusConnectionSignal(self.get_pvname(
            acq_type+'SamplesPost-RB', is_data=False))
        self.shots = SiriusConnectionSignal(self.get_pvname(
            acq_type+'Shots-RB', is_data=False))

        self._chans.extend([self.samplespre, self.samplespost, self.shots])
        self.radio_buttons = []
        self.setupui()

    @property
    def nrsamplespershot(self):
        if self.samplespost.connected and self.samplespre.connected:
            return self.samplespost.getvalue() + self.samplespre.getvalue()

    @property
    def nrsamples(self):
        val = self.nrsamplespershot
        if val and self.shots.connected:
            return self.nrsamplespershot*self.shots.getvalue()

    def setupui(self):
        vbl = QVBoxLayout(self)
        self.stack = QStackedWidget(self)
        vbl.addWidget(self.stack)

        rbA = QRadioButton('Antennas', self)
        rbP = QRadioButton('Positions', self)
        rbA.toggled.connect(_part(self.toggle_button, 0))
        rbP.toggled.connect(_part(self.toggle_button, 1))
        self.radio_buttons.append(rbA)
        self.radio_buttons.append(rbP)
        rbA.setChecked(True)
        hbl = QHBoxLayout()
        hbl.addStretch()
        hbl.addWidget(rbA)
        hbl.addStretch()
        hbl.addWidget(rbP)
        hbl.addStretch()
        vbl.addItem(hbl)

        # ##### Antennas Widget ######
        stack1 = QWidget(self.stack)
        self.stack.addWidget(stack1)
        vbl = QVBoxLayout(stack1)

        graph = self.create_graph(stack1, False)
        vbl.addWidget(graph)
        stats = self.create_statistics(stack1, False)
        vbl.addWidget(stats)

        if not self.data_prefix.startswith('PM'):
            hbl2 = QHBoxLayout()
            hbl2.addStretch()
            vbl.addItem(hbl2)
            pb = QPushButton('Open FFT Data', stack1)
            hbl2.addWidget(pb)
            hbl2.addStretch()
            Window = create_window_from_widget(
                FFTData, title=self.bpm+': FFT Data')
            util.connect_window(
                pb, Window, parent=stack1, prefix=self.prefix,
                bpm=self.bpm, data_prefix=self.data_prefix, position=False)

        # ##### Position Widget ######
        stack2 = QWidget(self.stack)
        self.stack.addWidget(stack2)
        vbl = QVBoxLayout(stack2)

        graph = self.create_graph(stack2, True)
        vbl.addWidget(graph)
        stats = self.create_statistics(stack2, True)
        vbl.addWidget(stats)

        if not self.data_prefix.startswith('PM'):
            hbl2 = QHBoxLayout()
            hbl2.addStretch()
            vbl.addItem(hbl2)
            pb = QPushButton('Open FFT Data', stack2)
            hbl2.addWidget(pb)
            hbl2.addStretch()
            Window = create_window_from_widget(
                FFTData, title=self.bpm+': FFT Data')
            util.connect_window(
                pb, Window, parent=stack1, prefix=self.prefix,
                bpm=self.bpm, data_prefix=self.data_prefix, position=True)

        self.setStyleSheet("""
            #MultiTurnDataGraph{
                min-width:48em;
                min-height:24em;
            }
            QLabel{
                min-width:6em; max-width:6em;
                min-height:1.5em; max-height:1.5em;
            }""")

    def create_statistics(self, wid, position=True):
        text, unit, names, _ = self._get_properties(position)

        wid = QWidget(wid)
        hbl = QHBoxLayout(wid)
        hbl.addStretch()
        grpbx = QGroupBox('Statistics', wid)
        hbl.addWidget(grpbx)
        hbl.addStretch()

        gdl = QGridLayout(grpbx)
        gdl.setHorizontalSpacing(20)
        gdl.setVerticalSpacing(20)

        stats = ('MeanValue', 'Sigma', 'MinValue', 'MaxValue')
        for j, stat in enumerate(('Average', 'Sigma', 'Minimum', 'Maximum')):
            lab = QLabel(stat, wid)
            lab.setAlignment(Qt.AlignCenter)
            gdl.addWidget(lab, 0, j+1)
        for i, name in enumerate(names):
            lab = QLabel(text[:3] + name, wid)
            lab.setAlignment(Qt.AlignCenter)
            gdl.addWidget(lab, i+1, 0)
            for j, stat in enumerate(stats):
                lab = SiriusLabel(wid, init_channel=self.get_pvname(
                    name+'_STATS'+stat+'_RBV'))
                lab.setAlignment(Qt.AlignCenter)
                lab.unit_changed(unit)
                lab.showUnits = True
                lab.precisionFromPV = False
                lab.precision = 3
                gdl.addWidget(lab, i+1, j+1)
        return wid

    def create_graph(self, wid, position=True):
        text, unit, names, colors = self._get_properties(position)
        if position:
            unit = unit[1:]

        graph = GraphWave(
            wid, prefix=self.prefix, bpm=self.bpm,
            data_prefix=self.data_prefix)
        graph.setObjectName('MultiTurnDataGraph')
        graph.setLabel('left', text=text, units=unit)
        for name, cor in zip(names, colors):
            opts = dict(
                y_channel=name+'ArrayData',
                name=text[:3]+name,
                color=cor,
                lineStyle=1,
                lineWidth=1)  # NOTE: If > 1: very low performance
            opts['y_channel'] = self.get_pvname(opts['y_channel'])
            if position:
                graph.addChannel(add_scale=1e-9, **opts)
            else:
                graph.addChannel(**opts)
        return graph

    def _get_properties(self, position=True):
        if position:
            text = 'Positions'
            unit = 'nm'
            names = ('X', 'Y', 'Q', 'SUM')
            colors = ('blue', 'red', 'green', 'black')
        else:
            text = 'Antennas'
            unit = 'count'
            names = ('A', 'B', 'C', 'D')
            colors = ('blue', 'red', 'green', 'magenta')
        return text, unit, names, colors

    def toggle_button(self, i, tog):
        if tog:
            self.stack.setCurrentIndex(i)

    def control_visibility_buttons(self, text):
        bo = not text.startswith('adc')
        self.radio_buttons[1].setVisible(bo)
        self.radio_buttons[0].setChecked(True)
Exemplo n.º 6
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""    
    def __init__(self, parent=None):
        QDialog.__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.data = None
        self.arraywidget = None
        self.stack = None
        self.layout = None
        # Values for 3d array editor
        self.dim_indexes = [{}, {}, {}]
        self.last_dim = 0  # Adjust this for changing the startup dimension
        
    def setup_and_check(self, data, title='', readonly=False,
                        xlabels=None, ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)
        if data.size == 0:
            self.error(_("Array is empty"))
            return False
        if data.ndim > 3:
            self.error(_("Arrays with more than 3 dimensions are not supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(_("The 'xlabels' argument length do no match array "
                         "column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(_("The 'ylabels' argument length do no match array row "
                         "number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \
               and not dtn.startswith('unicode'):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False
        
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - " + _("NumPy array")
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)
        
        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(ArrayEditorWidget(self, data[name],
                                                   readonly, xlabels, ylabels))
        elif is_masked_array:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly,
                                                   xlabels, ylabels))
        elif data.ndim == 3:
            pass
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        self.stack.currentChanged.connect(self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array or data.ndim == 3:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not is_text_string(title):
                            title = repr(title)
                        text += ' - '+title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            if data.ndim == 3:
                # QSpinBox
                self.index_spin = QSpinBox(self, keyboardTracking=False)
                self.index_spin.valueChanged.connect(self.change_active_widget)
                # QComboBox
                names = [str(i) for i in range(3)]
                ra_combo = QComboBox(self)
                ra_combo.addItems(names)
                ra_combo.currentIndexChanged.connect(self.current_dim_changed)    
                # Adding the widgets to layout
                label = QLabel(_("Axis:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(ra_combo)
                self.shape_label = QLabel()
                btn_layout.addWidget(self.shape_label)
                label = QLabel(_("Index:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(self.index_spin)
                self.slicing_label = QLabel()
                btn_layout.addWidget(self.slicing_label)
                # set the widget to display when launched
                self.current_dim_changed(self.last_dim)
            else:
                ra_combo = QComboBox(self)
                ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
                ra_combo.addItems(names)
                btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(_("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)
            btn_layout.addStretch()
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        btn_layout.addWidget(bbox)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)
        
        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)
        
        return True
            
    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)     
            
    def change_active_widget(self, index):
        """
        This is implemented for handling negative values in index for
        3d arrays, to give the same behavior as slicing
        """
        string_index = [':']*3
        string_index[self.last_dim] = '<font color=red>%i</font>'
        self.slicing_label.setText((r"Slicing: [" + ", ".join(string_index) +
                                "]") % index)
        if index < 0:
            data_index = self.data.shape[self.last_dim] + index
        else:
            data_index = index
        slice_index = [slice(None)]*3
        slice_index[self.last_dim] = data_index

        stack_index = self.dim_indexes[self.last_dim].get(data_index)
        if stack_index == None:
            stack_index = self.stack.count()
            self.stack.addWidget(ArrayEditorWidget(self,
                                                   self.data[slice_index]))
            self.dim_indexes[self.last_dim][data_index] = stack_index
            self.stack.update()
        self.stack.setCurrentIndex(stack_index)

    def current_dim_changed(self, index):
        """
        This change the active axis the array editor is plotting over
        in 3D
        """
        self.last_dim = index
        string_size = ['%i']*3
        string_size[index] = '<font color=red>%i</font>'
        self.shape_label.setText(('Shape: (' + ', '.join(string_size) +
                                 ')    ') % self.data.shape)
        if self.index_spin.value() != 0:
            self.index_spin.setValue(0)
        else:
            # this is done since if the value is currently 0 it does not emit
            # currentIndexChanged(int)
            self.change_active_widget(0)
        self.index_spin.setRange(-self.data.shape[index],
                                 self.data.shape[index]-1)

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)
        
    def get_value(self):
        """Return modified array -- 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
        return self.data

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, _("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    @Slot()
    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemplo n.º 7
0
class TomographyPlugin(GUIPlugin):
    name = "Tomography"

    def __init__(self, *args, **kwargs):
        self.active_filename = ""

        self._main_view = QMainWindow()
        self._main_view.setCentralWidget(QWidget())
        self._main_view.centralWidget().hide()

        self._editors = QStackedWidget()

        self.workflow_process_selector = QComboBox()
        self.workflow_process_selector.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)

        self.workflow_process_selector.addItem("TomoPy Workflow")

        self._workflow = None

        self._tomo_workflow = TomographyWorkflow()  # Create a workflow
        self._workflow = self._tomo_workflow

        self._workflow_editor = WorkflowEditor(workflow=self._workflow)
        self._workflow_editor.sigRunWorkflow.connect(self.run_workflow)

        self._active_workflow_executor = QTreeWidget()

        self._editors.addWidget(self._workflow_editor)

        if hasLTT:
            self._alt_workflow = LTTWorkflow()
            self.workflow_process_selector.addItem("LTT Workflow")
            self._alt_workflow_editor = WorkflowEditor(self._alt_workflow)
            self._alt_workflow_editor.sigRunWorkflow.connect(self.run_workflow)
            self._editors.addWidget(self._alt_workflow_editor)

        self.workflow_process_selector.setCurrentIndex(0)
        self.workflow_process_selector.currentIndexChanged.connect(self.active_workflow_changed)

        self._active_workflow_widget = QWidget()
        self._active_layout = QVBoxLayout()

        self._active_layout.addWidget(self.workflow_process_selector)
        self._active_layout.addWidget(self._editors)
        self._active_layout.addWidget(self._active_workflow_executor)

        self._active_workflow_widget.setLayout(self._active_layout)

        # self.hdf5_viewer = HDFTreeViewer()
        # self.hdf5_viewer.load("/Users/hari/20200521_160002_heartbeat_test.h5")

        self.top_controls = QWidget()
        self.top_controls_layout = QHBoxLayout()
        self.top_controls.setLayout(self.top_controls_layout)
        self.top_controls_add_new_selector = QComboBox()
        self.top_controls_frequency_selector = QSpinBox()
        self.top_controls_frequency_selector.setValue(0)

        self.top_controls_add_new_viewer = QPushButton()
        self.top_controls_add_new_viewer.setText("Add Viewer")
        self.top_controls_add_new_viewer.clicked.connect(self.add_new_viewer_selected)

        self.top_controls_preview = QPushButton()
        self.top_controls_preview.setText("Preview")
        self.top_controls_preview.clicked.connect(self.preview_workflows)

        self.top_controls_execute = QPushButton()
        self.top_controls_execute.setText("Execute All")
        self.top_controls_execute.clicked.connect(self.execute_workflows)

        self.top_controls_add_new_selector.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.top_controls_add_new_viewer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.top_controls_execute.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.top_controls_frequency_selector.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.top_controls_layout.addWidget(self.top_controls_add_new_selector)
        self.top_controls_layout.addWidget(self.top_controls_add_new_viewer)
        self.top_controls_layout.addWidget(self.top_controls_frequency_selector)
        self.top_controls_layout.addWidget(self.top_controls_preview)
        self.top_controls_layout.addWidget(self.top_controls_execute)

        self.top_controls.setLayout(self.top_controls_layout)

        self.status_bar = QLabel("None Loaded...")

        # Create a layout to organize our widgets
        # The first argument (which corresponds to the center widget) is required.
        catalog_viewer_layout = GUILayout(self._main_view,
                                          top=self.top_controls,
                                          right=self._active_workflow_widget,
                                          bottom=self.status_bar)

        # Create a "View" stage that has the catalog viewer layout
        self.stages = {"View": catalog_viewer_layout}

        self.active_workflows = []

        # For classes derived from GUIPlugin, this super __init__ must occur at end
        super(TomographyPlugin, self).__init__(*args, **kwargs)

    def active_workflow_changed(self, index):
        self._editors.setCurrentIndex(index)

        if index == 0:
            self._workflow = self._tomo_workflow
        else:
            self._workflow = self._alt_workflow

    def preview_workflows(self):
        if len(self.active_filename) == 0:
            return

        preview_sinogram = self.top_controls_frequency_selector.value()

        workflows = []
        for aw in range(self._active_workflow_executor.topLevelItemCount()):
            widget_item = self._active_workflow_executor.topLevelItem(aw)
            workflow = widget_item.data(0, Qt.UserRole)
            workflows.append(workflow)

        active_workflows = self.active_workflows

        for workflow in workflows:
            def results_ready(workflow, *results):
                output_image = results[0]["recon"]  # We want the output_image from the last operation
                # print("RECON SHAPE", output_image.shape)
                for active_workflow in active_workflows:
                    if active_workflow[1] == workflow:
                        active_workflow[0].widget().setImage(output_image)  # Update the result view widget
            workflow.execute(callback_slot=partial(results_ready, workflow), path=self.active_filename, sinoindex=preview_sinogram)

    def execute_workflows(self):
        if len(self.active_filename) == 0:
            return

        workflows = []
        for aw in range(self._active_workflow_executor.topLevelItemCount()):
            widget_item = self._active_workflow_executor.topLevelItem(aw)
            workflow = widget_item.data(0, Qt.UserRole)
            workflows.append(workflow)

        active_workflows = self.active_workflows

        for workflow in workflows:
            def results_ready(workflow, *results):
                output_image = results[0]["recon"]  # We want the output_image from the last operation
                # print("RECON SHAPE", output_image.shape)
                for active_workflow in active_workflows:
                    if active_workflow[1] == workflow:
                        active_workflow[0].widget().setImage(output_image)  # Update the result view widget
            workflow.execute(callback_slot=partial(results_ready, workflow), path=self.active_filename)

    def add_new_viewer_selected(self, checked):
        if self.top_controls_add_new_selector.count() == 0:
            return

        selected = self.top_controls_add_new_selector.currentIndex()

        if selected < 0:
            return

        workflow = self.top_controls_add_new_selector.currentData(Qt.UserRole)
        print("WORKFLOW", workflow, workflow.name)

        dock_widget = QDockWidget()
        viewer = CatalogView()
        dock_widget.setWidget(viewer)
        dock_widget.setWindowTitle(workflow.name)

        if len(self.active_workflows) == 0:
            self._main_view.addDockWidget(Qt.RightDockWidgetArea, dock_widget)
        else:
            self._main_view.tabifyDockWidget(self.active_workflows[-1][0], dock_widget)

        self.active_workflows.append((dock_widget, workflow))

    def appendHeader(self, header, **kwargs):
        filename = header._documents["start"][0]["filename"]
        self.active_filename = filename

        self.status_bar.setText("Loading Filename:" + filename)
        
        dataset, bkgs, drks = read_als_hdf5(filename)
        slice_widget = MyCatalogView()
        dw = QDockWidget()
        dw.setWidget(slice_widget)
        dw.setWindowTitle(filename)
        slice_widget.setImage(dataset)

        dock_children = self._main_view.findChildren(QDockWidget)
        print(dock_children)

        #if len(dock_children) == 0:
        #    self._main_view.addDockWidget(Qt.LeftDockWidgetArea, dw)
        #else:
        #    self._main_view.tabifyDockWidget(dock_children[0], dw)

        self._main_view.addDockWidget(Qt.LeftDockWidgetArea, dw)
        self.status_bar.setText("Done Loading Filename:" + filename)

    def appendCatalog(self, catalog, **kwargs):
        """Re-implemented from GUIPlugin - gives us access to a catalog reference
        You MUST implement this method if you want to load catalog data into your GUIPlugin.
        """
        # Set the catalog viewer's catalog, stream, and field (so it knows what to display)
        # This is a quick and simple demonstration; stream and field should NOT be hardcoded
        # stream = "primary"
        # field = "img"
        # self._catalog_viewer.setCatalog(catalog, stream, field)
        print("CATALOG CALLED", catalog)
        pass

    def run_workflow(self):
        """Run the internal workflowi.
        In this example, this will be called whenever the "Run Workflow" in the WorkflowEditor is clicked.
        """
        item = QTreeWidgetItem()

        workflow_name = self._workflow.name + ":" + str(self._active_workflow_executor.model().rowCount())
        workflow = self._workflow.clone()
        workflow.name = workflow_name
        workflow._pretty_print()

        item.setText(0, workflow_name)
        item.setData(0, Qt.UserRole, workflow)
        self._active_workflow_executor.addTopLevelItem(item)
        self.top_controls_add_new_selector.addItem(workflow_name, userData=workflow)
Exemplo n.º 8
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    valueChanged = Signal()

    ui_schema = {
        "call_order": {
            "ui:widget": "plugins"
        },
        "highlight_thickness": {
            "ui:widget": "highlight"
        },
    }

    resized = Signal(QSize)
    closed = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def _restart_dialog(self, event=None, extra_str=""):
        """Displays the dialog informing user a restart is required.

        Paramters
        ---------
        event : Event
        extra_str : str
            Extra information to add to the message about needing a restart.
        """

        text_str = trans._(
            "napari requires a restart for image rendering changes to apply.")

        widget = ResetNapariInfoDialog(
            parent=self,
            text=text_str,
        )
        widget.exec_()

    def closeEvent(self, event):
        """Override to emit signal."""
        self.closed.emit()
        super().closeEvent(event)

    def reject(self):
        """Override to handle Escape."""
        super().reject()
        self.close()

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""
        settings = get_settings()
        # Because there are multiple pages, need to keep a dictionary of values dicts.
        # One set of keywords are for each page, then in each entry for a page, there are dicts
        # of setting and its value.
        self._values_orig_dict = {}
        self._values_dict = {}
        self._setting_changed_dict = {}

        for page, setting in settings.schemas().items():
            schema, values, properties = self.get_page_dict(setting)

            self._setting_changed_dict[page] = {}
            self._values_orig_dict[page] = values
            self._values_dict[page] = values

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def get_page_dict(self, setting):
        """Provides the schema, set of values for each setting, and the properties
        for each setting.

        Parameters
        ----------
        setting : dict
            Dictionary of settings for a page within the settings manager.

        Returns
        -------
        schema : dict
            Json schema of the setting page.
        values : dict
            Dictionary of values currently set for each parameter in the settings.
        properties : dict
            Dictionary of properties within the json schema.

        """
        schema = json.loads(setting['json_schema'])

        # Resolve allOf references
        definitions = schema.get("definitions", {})
        if definitions:
            for key, data in schema["properties"].items():
                if "allOf" in data:
                    allof = data["allOf"]
                    allof = [d["$ref"].rsplit("/")[-1] for d in allof]
                    for definition in allof:
                        local_def = definitions[definition]
                        schema["properties"][key]["enum"] = local_def["enum"]
                        schema["properties"][key]["type"] = "string"

        # Need to remove certain properties that will not be displayed on the GUI
        properties = schema.pop('properties')
        model = setting['model']
        values = model.dict()
        napari_config = getattr(model, "NapariConfig", None)
        if napari_config is not None:
            for val in napari_config.preferences_exclude:
                properties.pop(val)
                values.pop(val)

        schema['properties'] = properties

        return schema, values, properties

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""
        self._reset_dialog = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        self._reset_dialog.valueChanged.connect(self._reset_widgets)
        self._reset_dialog.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self.valueChanged.emit()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to settings."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        settings = get_settings()
        for n in range(self._stack.count()):
            # Must set the current row so that the proper list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            page = self._list.currentItem().text().split(" ")[0].lower()
            # get new values for settings.  If they were changed from values at beginning
            # of preference dialog session, change them back.
            # Using the settings value seems to be the best way to get the checkboxes right
            # on the plugin call order widget.
            setting = settings.schemas()[page]
            schema, new_values, properties = self.get_page_dict(setting)
            self.check_differences(self._values_orig_dict[page], new_values)

        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        settings = get_settings()
        builder = WidgetBuilder()
        form = builder.create_form(schema, self.ui_schema)

        # Disable widgets that loaded settings from environment variables
        section = schema["section"]
        form_layout = form.widget.layout()
        for row in range(form.widget.layout().rowCount()):
            widget = form_layout.itemAt(row, form_layout.FieldRole).widget()
            name = widget._name
            disable = bool(
                settings._env_settings.get(section, {}).get(name, None))
            widget.setDisabled(disable)
            try:
                widget.opacity.setOpacity(0.3 if disable else 1)
            except AttributeError:
                # some widgets may not have opacity (such as the QtPluginSorter)
                pass

        # set state values for widget
        form.widget.state = values

        if section == 'experimental':
            # need to disable async if octree is enabled.
            if values['octree'] is True:
                form = self._disable_async(form, values)

        form.widget.on_changed.connect(lambda d: self.check_differences(
            d,
            self._values_dict[schema["title"].lower()],
        ))

        return form

    def _disable_async(self, form, values, disable=True, state=True):
        """Disable async if octree is True."""
        settings = get_settings()
        # need to make sure that if async_ is an environment setting, that we don't
        # enable it here.
        if (settings._env_settings['experimental'].get('async_', None)
                is not None):
            disable = True

        idx = list(values.keys()).index('async_')
        form_layout = form.widget.layout()
        widget = form_layout.itemAt(idx, form_layout.FieldRole).widget()
        widget.opacity.setOpacity(0.3 if disable else 1)
        widget.setDisabled(disable)

        return form

    def _values_changed(self, page, new_dict, old_dict):
        """Loops through each setting in a page to determine if it changed.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting value
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the begining of preferences dialog session.

        """
        for setting_name, value in new_dict.items():
            if value != old_dict[setting_name]:
                self._setting_changed_dict[page][setting_name] = value
            elif (value == old_dict[setting_name]
                  and setting_name in self._setting_changed_dict[page]):
                self._setting_changed_dict[page].pop(setting_name)

    def set_current_index(self, index: int):
        """
        Set the current page on the preferences by index.

        Parameters
        ----------
        index : int
            Index of page to set as current one.
        """
        self._list.setCurrentRow(index)

    def check_differences(self, new_dict, old_dict):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting parameter
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the beginning of the preferences dialog session.
        """
        settings = get_settings()
        page = self._list.currentItem().text().split(" ")[0].lower()
        self._values_changed(page, new_dict, old_dict)
        different_values = self._setting_changed_dict[page]

        if len(different_values) > 0:
            # change the values in settings
            for setting_name, value in different_values.items():
                try:
                    setattr(settings._settings[page], setting_name, value)
                    self._values_dict[page] = new_dict

                    if page == 'experimental':

                        if setting_name == 'octree':

                            # disable/enable async checkbox
                            widget = self._stack.currentWidget()
                            cstate = True if value is True else False
                            self._disable_async(widget,
                                                new_dict,
                                                disable=cstate)

                            # need to inform user that napari restart needed.
                            self._restart_dialog()

                        elif setting_name == 'async_':
                            # need to inform user that napari restart needed.
                            self._restart_dialog()

                except:  # noqa: E722
                    continue
Exemplo n.º 9
0
class CalibrationWidget(ParameterWidget):

    def __init__(self, parent=None):
        ParameterWidget.__init__(self, _Calibration, parent)

    def _init_ui(self):
        # Widgets
        self._combobox = QComboBox()
        self._stack = QStackedWidget()

        # Layouts
        layout = ParameterWidget._init_ui(self)
        layout.addRow(self._combobox)
        layout.addRow(self._stack)

        # Register classes
        self._widget_indexes = {}

        for entry_point in iter_entry_points('pyhmsa_gui.spec.condition.calibration'):
            widget_class = entry_point.load(require=False)
            widget = widget_class()
            self._combobox.addItem(widget.accessibleName().title())
            self._widget_indexes[widget.CLASS] = self._stack.addWidget(widget)
            widget.edited.connect(self.edited)

        # Signals
        self._combobox.currentIndexChanged.connect(self._on_combo_box)
        self._combobox.currentIndexChanged.connect(self.edited)

        return layout

    def _on_combo_box(self):
        # Old values
        oldwidget = self._stack.currentWidget()
        try:
            quantity = oldwidget._txt_quantity.text()
        except:
            quantity = None
        try:
            unit = oldwidget._txt_unit.text()
        except:
            unit = None

        # Change widget
        current_index = self._combobox.currentIndex()
        self._stack.setCurrentIndex(current_index)

        # Update values
        widget = self._stack.currentWidget()
        widget._txt_quantity.setText(quantity)
        widget._txt_unit.setText(unit)

    def parameter(self):
        return self._stack.currentWidget().calibration()

    def setParameter(self, calibration):
        index = self._widget_indexes[type(calibration)]
        self._combobox.setCurrentIndex(index)
        self._stack.setCurrentIndex(index)
        self._stack.currentWidget().setParameter(calibration)

    def calibration(self):
        return self.parameter()

    def setCalibration(self, calibration):
        self.setParameter(calibration)

    def setReadOnly(self, state):
        ParameterWidget.setReadOnly(self, state)
        self._combobox.setEnabled(not state)
        self._stack.currentWidget().setReadOnly(state)

    def isReadOnly(self):
        return ParameterWidget.isReadOnly(self) and \
            not self._combobox.isEnabled() and \
            self._stack.currentWidget().isReadOnly()

    def hasAcceptableInput(self):
        return ParameterWidget.hasAcceptableInput(self) and \
            self._stack.currentWidget().hasAcceptableInput()
Exemplo n.º 10
0
class FeaturesPlotWidget(CustomWidget):
    call_manage_settings = Signal()

    def __init__(self, *args, **kwargs):

        # define status images
        self.status_icons = {
            -2:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_delay.png')),
            -1:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_in_use.png')),
            1:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_done.png')),
            0:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_off.png')),
        }

        # Settings
        self.subject_settings = None
        self.procedure_settings = None
        self.depth_settings = None
        self.features_settings = None

        # Plot options
        self.plot_config = {}
        self.y_range = uVRANGE
        self.plot_stack = QStackedWidget()
        # generate a dict {chan_label: {Feature:[stack idx, latest_datum]}}
        self.stack_dict = {}

        # shared memory to display the currently monitored electrode
        self.monitored_channel_mem = QSharedMemory()
        self.monitored_channel_mem.setKey("MonitoredChannelMemory")
        self.monitored_channel_mem.attach(QSharedMemory.ReadOnly)

        # wrap up init
        super(FeaturesPlotWidget, self).__init__(*args, **kwargs)
        self.move(WINDOWDIMS_FEATURES[0], WINDOWDIMS_FEATURES[1])
        self.resize(WINDOWDIMS_FEATURES[2], WINDOWDIMS_FEATURES[3])
        self.setMaximumWidth(WINDOWDIMS_FEATURES[2])

        # initialize plots
        self.layout().addWidget(self.plot_stack)
        self.refresh_axes()  # Extra time on purpose.

        # Define and start processes
        # will only start processes when settings are received
        self.depth_wrapper = ProcessWrapper('Depth_Process')
        self.depth_process_running = False

        self.features_wrapper = ProcessWrapper('Features_Process')
        self.features_process_running = False

    def create_control_panel(self):
        # define Qt GUI elements
        layout = QHBoxLayout()

        layout_L = QVBoxLayout()
        layout_L1 = QHBoxLayout()

        # layout_L1.addSpacing(10)
        layout_L1.addWidget(
            QLabel("Electrode: ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        # Channel selection
        self.chan_select = QComboBox()
        self.chan_select.addItem("None")
        self.chan_select.setMinimumWidth(70)
        self.chan_select.setEnabled(False)
        layout_L1.addWidget(self.chan_select)

        layout_L1.addSpacing(20)

        # features selection
        layout_L1.addWidget(
            QLabel("Feature set: ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        self.feature_select = QComboBox()
        self.feature_select.setMinimumWidth(60)
        self.feature_select.addItems(['Raw', 'Mapping'])
        self.feature_select.setCurrentIndex(0)
        layout_L1.addWidget(self.feature_select)

        layout_L.addLayout(layout_L1)
        layout_L.addSpacing(5)

        layout_L2 = QHBoxLayout()
        layout_L2.addSpacing(10)
        layout_L2.addWidget(
            QLabel("+/- ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        self.range_edit = QLineEdit("{:.2f}".format(uVRANGE))
        self.range_edit.setMaximumWidth(50)
        layout_L2.addWidget(self.range_edit)

        layout_L2.addSpacing(30)

        self.do_hp = QCheckBox('HP')
        self.do_hp.setChecked(True)
        layout_L2.addWidget(self.do_hp)

        layout_L2.addSpacing(30)

        self.sweep_control = QCheckBox("Match SweepGUI.")
        self.sweep_control.setChecked(True)
        self.sweep_control.setEnabled(True)
        layout_L2.addWidget(self.sweep_control)

        layout_L.addLayout(layout_L2)

        layout_R = QHBoxLayout()

        self.bt_refresh = QPushButton("Refresh")
        self.bt_refresh.setMaximumWidth(50)
        layout_R.addWidget(self.bt_refresh)

        layout_R.addSpacing(20)

        self.btn_settings = QPushButton("Settings")
        self.btn_settings.setMaximumWidth(50)
        layout_R.addWidget(self.btn_settings)

        layout_R.addSpacing(20)

        self.features_process_btn = QPushButton('Features')
        self.features_process_btn.setMaximumWidth(50)
        self.features_process_btn.setStyleSheet("QPushButton { color: white; "
                                                "background-color : red; "
                                                "border-color : red; "
                                                "border-width: 2px}")
        self.features_process_btn.clicked.connect(
            self.features_process_btn_callback)
        layout_R.addWidget(self.features_process_btn)

        layout_R.addSpacing(5)

        self.depth_process_btn = QPushButton('Record')
        self.depth_process_btn.setMaximumWidth(50)
        self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                             "background-color : red; "
                                             "border-color : red; "
                                             "border-width: 2px}")
        self.depth_process_btn.clicked.connect(self.depth_process_btn_callback)
        layout_R.addWidget(self.depth_process_btn)

        layout_R.addSpacing(20)

        self.status_label = QLabel()
        self.status_label.setPixmap(self.status_icons[0])
        layout_R.addWidget(self.status_label)
        layout_R.addSpacing(10)

        layout.addLayout(layout_L)
        layout.addStretch()
        layout.addLayout(layout_R)

        # layout.addSpacing(10)
        self.layout().addLayout(layout)

        # callbacks
        self.btn_settings.clicked.connect(self.call_manage_settings.emit)
        self.chan_select.currentIndexChanged.connect(
            self.manage_feat_chan_select)
        self.feature_select.currentIndexChanged.connect(
            self.manage_feat_chan_select)
        self.sweep_control.clicked.connect(self.manage_sweep_control)
        self.range_edit.editingFinished.connect(self.manage_range_edit)
        self.bt_refresh.clicked.connect(self.manage_refresh)

    def depth_process_btn_callback(self):
        # kill
        if self.depth_process_running:
            self.manage_depth_process(False)
            self.manage_nsp(False)
        else:
            # if we terminate and re-start the processes, we need to re-enable the shared memory
            self.depth_wrapper.manage_shared_memory()

            # re-send the settings
            self.depth_wrapper.send_settings(self.depth_settings)

            # start nsp recording
            if self.manage_nsp(True) == 0:
                # re-start the worker
                self.manage_depth_process(True)

    def features_process_btn_callback(self):
        # kill
        if self.features_process_running:
            self.manage_feature_process(False)
        else:
            # if we terminate and re-start the processes, we need to re-enable the shared memory
            self.features_wrapper.manage_shared_memory()

            # re-send the settings
            self.features_wrapper.send_settings(self.features_settings)

            # re-start the worker
            self.manage_feature_process(True)

    # GUI Callbacks
    def manage_feat_chan_select(self):
        self.plot_stack.setCurrentIndex(
            self.stack_dict[self.chan_select.currentText()][
                self.feature_select.currentText()][0])

    def manage_sweep_control(self):
        if self.sweep_control.isChecked(
        ) and self.monitored_channel_mem.isAttached():
            self.chan_select.setEnabled(False)
            self.do_hp.setEnabled(False)
            self.range_edit.setEnabled(False)
            self.read_from_shared_memory()
        else:
            self.chan_select.setEnabled(True)
            self.do_hp.setEnabled(True)
            self.range_edit.setEnabled(True)

    def manage_range_edit(self):
        # need to do it like this because if we simply read the QLineEdit.text() on update calls, it breaks during
        # typing the new range values.
        self.y_range = float(self.range_edit.text())

    @staticmethod
    def parse_patient_name(full_name):
        # parse the subject information
        names = full_name.split(' ')
        m_name = ''
        m_idx = -1
        l_idx = -1
        for idx, n in enumerate(names):
            if all([x.isupper() for x in n]):
                m_idx = idx
                l_idx = idx + 1
                m_name = n
                break

        f_name = str.join(' ', names[:m_idx])
        l_name = str.join(' ', names[l_idx:])
        return f_name, m_name, l_name

    def manage_nsp(self, on_off):
        f_name, m_name, l_name = self.parse_patient_name(
            self.subject_settings['name'])
        file_info = {
            'filename':
            os.path.normpath(
                os.path.join(
                    BASEPATH, self.subject_settings['id'],
                    self.procedure_settings['date'].strftime('%m%d%y') + '_' +
                    self.subject_settings['id'] + '_' +
                    self.procedure_settings['target_name'] + '_' +
                    self.procedure_settings['recording_config'])),
            'comment':
            self.subject_settings['NSP_comment'],
            'patient_info': {
                'ID': self.subject_settings['id'],
                # if only single name, returned in l_name
                'firstname': f_name if f_name else l_name,
                'middlename': m_name,  # TODO: implement MiddleName
                'lastname': l_name,
                'DOBMonth': self.subject_settings['birthday'].month,
                'DOBDay': self.subject_settings['birthday'].day,
                'DOBYear': self.subject_settings['birthday'].year
            }
        }

        if not CbSdkConnection().is_connected:
            CbSdkConnection().connect()

        return CbSdkConnection().set_recording_state(on_off, file_info)

    def manage_depth_process(self, on_off):
        # start process
        if on_off and not self.depth_process_running:
            self.depth_wrapper.start_worker()
            self.depth_process_running = True
        else:
            self.depth_wrapper.kill_worker()
            self.depth_process_running = False

    def manage_feature_process(self, on_off):
        if on_off and not self.features_process_running:
            self.features_wrapper.start_worker()
            self.features_process_running = True
        else:
            self.features_wrapper.kill_worker()
            self.features_process_running = False

    def manage_refresh(self):
        self.plot_stack.widget(self.stack_dict[self.chan_select.currentText()][
            self.feature_select.currentText()][0]).clear_plot()
        self.stack_dict[self.chan_select.currentText()][
            self.feature_select.currentText()][1] = 0

    def process_settings(self, sub_sett, proc_sett, depth_sett, feat_sett):
        self.subject_settings = dict(sub_sett)
        self.procedure_settings = dict(proc_sett)
        self.depth_settings = dict(depth_sett)
        self.features_settings = dict(feat_sett)

        # validate that we have some data in the electrode_settings. If the NSP is not connected we will have
        # to load the channel names from the DB. Also we want to keep the FeaturesGUI unaware of the DB channels.
        if len(self.depth_settings['electrode_settings']) == 0:
            self.depth_settings['electrode_settings'] = {}
            for lbl in DBWrapper().list_channel_labels():
                self.depth_settings['electrode_settings'][lbl] = DEPTHSETTINGS
            CbSdkConnection().is_simulating = True

        # set new features
        self.feature_select.setCurrentIndex(0)  # Raw
        while self.feature_select.count() > 2:  # Raw and Mapping
            self.feature_select.removeItem(2)
        self.feature_select.addItems(self.features_settings['features'].keys())

        # set new channels
        self.chan_select.setCurrentIndex(0)  # None
        while self.chan_select.count() > 1:
            self.chan_select.removeItem(1)
        self.chan_select.addItems(
            self.depth_settings['electrode_settings'].keys())

        # clear and update stacked widget
        to_delete = [
            self.plot_stack.widget(x) for x in range(self.plot_stack.count())
        ]
        for wid in to_delete:
            self.plot_stack.removeWidget(wid)
            wid.deleteLater()
            wid = None
        self.stack_dict = {}
        self.create_plots()

        self.depth_wrapper.send_settings(self.depth_settings)
        self.features_wrapper.send_settings(self.features_settings)

        if not self.features_process_running:
            self.manage_feature_process(True)

        # self.clear()
        self.read_from_shared_memory()

    def create_plots(self, theme='dark', **kwargs):
        # Collect PlotWidget configuration
        self.plot_config['theme'] = theme
        self.plot_config['color_iterator'] = -1
        self.plot_config['x_range'] = XRANGE_FEATURES
        self.plot_config['y_range'] = uVRANGE
        self.plot_config['do_hp'] = True

        labels = []
        for ii in range(0, self.chan_select.count()):
            labels.append(self.chan_select.itemText(ii))
        # labels.extend(self.channel_labels)

        features = []
        for ii in range(0, self.feature_select.count()):
            features.append(self.feature_select.itemText(ii))

        stack_idx = 0
        for lbl_idx, lbl in enumerate(labels):
            self.stack_dict[lbl] = {}
            self.plot_config['color_iterator'] = lbl_idx - 1
            self.plot_config['title'] = lbl
            for feat in features:
                self.stack_dict[lbl][feat] = [stack_idx, 0]
                # TODO: not hard-coding??
                if feat == 'Raw':
                    self.plot_stack.addWidget(RawPlots(dict(self.plot_config)))
                elif feat == 'Mapping':
                    self.plot_stack.addWidget(
                        MappingPlots(dict(self.plot_config)))
                elif feat == 'STN':
                    self.plot_stack.addWidget(STNPlots(dict(self.plot_config)))
                elif feat == 'LFP':
                    self.plot_stack.addWidget(LFPPlots(dict(self.plot_config)))
                elif feat == 'Spikes':
                    self.plot_stack.addWidget(
                        SpikePlots(dict(self.plot_config)))
                else:
                    self.plot_stack.addWidget(
                        NullPlotWidget(dict(self.plot_config)))
                stack_idx += 1

        self.plot_stack.setCurrentIndex(0)

    def refresh_axes(self):
        pass

    def clear(self):
        # set the current datum of all stacks to 0
        for lbl in self.stack_dict:
            for feat in self.stack_dict[lbl]:
                self.stack_dict[lbl][feat][1] = 0
                self.plot_stack.widget(
                    self.stack_dict[lbl][feat][0]).clear_plot()

    def update(self):
        # Depth process
        output = self.depth_wrapper.worker_status()
        self.status_label.setPixmap(self.status_icons[output])

        if self.depth_wrapper.is_running():
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : green; "
                                                 "border-color : green; "
                                                 "border-width: 2px}")
        else:
            self.depth_process_running = False
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : red; "
                                                 "border-color : red; "
                                                 "border-width: 2px}")

        if self.features_wrapper.is_running():
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : green; "
                "border-color : green; "
                "border-width: 2px}")
        else:
            self.features_process_running = False
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : red; "
                "border-color : red; "
                "border-width: 2px}")

        if self.sweep_control.isChecked():
            self.read_from_shared_memory()

        # features plot
        curr_chan_lbl = self.chan_select.currentText()
        if curr_chan_lbl != 'None':
            curr_feat = self.feature_select.currentText()
            do_hp = self.do_hp.isChecked()

            if do_hp != self.plot_stack.currentWidget().plot_config['do_hp'] or \
                    self.y_range != self.plot_stack.currentWidget().plot_config['y_range']:
                self.plot_stack.currentWidget().clear_plot()
                self.stack_dict[curr_chan_lbl][curr_feat][1] = 0
                self.plot_stack.currentWidget().plot_config['do_hp'] = do_hp
                self.plot_stack.currentWidget(
                ).plot_config['y_range'] = self.y_range

            curr_datum = self.stack_dict[curr_chan_lbl][curr_feat][1]
            if curr_feat == 'Raw':
                all_data = DBWrapper().load_depth_data(chan_lbl=curr_chan_lbl,
                                                       gt=curr_datum,
                                                       do_hp=do_hp,
                                                       return_uV=True)
            elif curr_feat == 'Mapping':
                all_data = DBWrapper().load_mapping_response(
                    chan_lbl=curr_chan_lbl, gt=curr_datum)
            else:
                all_data = DBWrapper().load_features_data(
                    category=curr_feat, chan_lbl=curr_chan_lbl, gt=curr_datum)
            if all_data:
                self.plot_stack.currentWidget().update_plot(dict(all_data))
                self.stack_dict[curr_chan_lbl][curr_feat][1] = max(
                    all_data.keys())

    def kill_processes(self):
        self.manage_depth_process(False)
        self.manage_feature_process(False)

    def read_from_shared_memory(self):
        if self.monitored_channel_mem.isAttached():
            self.monitored_channel_mem.lock()
            settings = np.frombuffer(self.monitored_channel_mem.data(),
                                     dtype=np.float)[-3:]
            self.chan_select.setCurrentIndex(int(settings[0]))
            self.range_edit.setText(str(settings[1]))
            self.manage_range_edit()
            self.do_hp.setChecked(bool(settings[2]))
            self.monitored_channel_mem.unlock()
        else:
            self.monitored_channel_mem.attach()
            # self.sweep_control.setChecked(False)
            self.manage_sweep_control()
Exemplo n.º 11
0
class ExtendedTabWidget(QFrame):
    '''
    Like QTabWidget, except the tab bar is located elsewhere. Intended for use with ExtendedTabBar.
    '''
    def __init__(self):
        super(ExtendedTabWidget, self).__init__()

        self._tab_bar = None
        self._stack = QStackedWidget()

        self._main_layout = QBoxLayout(QBoxLayout.LeftToRight)
        self._main_layout.setContentsMargins(0, 0, 0, 0)
        self._main_layout.addWidget(self._stack)
        self.setLayout(self._main_layout)

    def _move_tab(self, from_, to):
        '''
        Handles tab moves so that the tab bar indices stay aligned with the widget stack indices.
        '''
        displaced_widget = self._stack.widget(from_)
        moved_widget = self._stack.widget(to)

        self._stack.removeWidget(moved_widget)
        self._stack.removeWidget(displaced_widget)

        self._stack.insertWidget(to, displaced_widget)
        self._stack.insertWidget(from_, moved_widget)

        self._stack.setCurrentIndex(self._tab_bar.currentIndex())

    def setTabBar(self, tab_bar):
        '''
        Sets the tab bar that will be used to switch between tabs.

        tab_bar
            The tab bar to set as the controller of this widget.
        '''
        if self._tab_bar is not None:
            raise Exception('Tab bar already set.')

        self._tab_bar = tab_bar
        tab_bar.currentChanged.connect(self._stack.setCurrentIndex)
        tab_bar.tabCloseRequested.connect(self.closeTab)
        tab_bar.tabMoved.connect(self._move_tab)

    def closeTab(self, index):
        '''
        Closes a tab, removing from this widget, the tab bar, and deleting its widget.

        index
            Index of the tab to be closed.
        '''
        self._tab_bar.removeTab(index)

        widget = self._stack.widget(index)
        self._stack.removeWidget(widget)

        widget.deleteLater()

        self._stack.setCurrentIndex(self._tab_bar.currentIndex())

    def addTab(self, widget, label):
        '''
        Adds a tab.

        widget
            The widget for the tab contents.

        label
            The name of the tab to show in the tab bar.

        Returns the index of the added tab.
        '''
        index = self._tab_bar.addTab(label)
        self._stack.insertWidget(index, widget)

        return index

    def count(self):
        '''
        Returns the number of widgets.
        '''
        return self._stack.count()

    def widget(self, index):
        '''
        Returns the widget at the given index.
        '''
        return self._stack.widget(index)

    def indexOf(self, widget):
        '''
        Returns the index of the given widget.
        '''
        return self._stack.indexOf(widget)
Exemplo n.º 12
0
class MixerPanel(QFrame):
    '''A color mixer to hook up to an image.
    You pass the image you the panel to operate on
    and it operates on that image in place. You also
    pass a callback to be called to trigger a refresh.
    This callback is called every time the mixer modifies
    your image.'''
    def __init__(self, img):
        QFrame.__init__(self)
        # self.setFrameStyle(QFrame.Box | QFrame.Sunken)

        self.img = img
        self.mixer = ColorMixer(self.img)
        self.callback = None

        #---------------------------------------------------------------
        # ComboBox
        #---------------------------------------------------------------

        self.combo_box_entries = ['RGB Color', 'HSV Color',
                                  'Brightness/Contrast',
                                  'Gamma',
                                  'Gamma (Sigmoidal)']
        self.combo_box = QComboBox()
        for entry in self.combo_box_entries:
            self.combo_box.addItem(entry)
        self.combo_box.currentIndexChanged.connect(self.combo_box_changed)

        #---------------------------------------------------------------
        # RGB color sliders
        #---------------------------------------------------------------

        # radio buttons
        self.rgb_add = QRadioButton('Additive')
        self.rgb_mul = QRadioButton('Multiplicative')
        self.rgb_mul.toggled.connect(self.rgb_radio_changed)
        self.rgb_add.toggled.connect(self.rgb_radio_changed)

        # sliders
        rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
        gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
        bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
        self.rs = rs
        self.gs = gs
        self.bs = bs

        self.rgb_widget = QWidget()
        self.rgb_widget.layout = QGridLayout(self.rgb_widget)
        self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rs, 2, 0)
        self.rgb_widget.layout.addWidget(self.gs, 2, 1)
        self.rgb_widget.layout.addWidget(self.bs, 2, 2)

        #---------------------------------------------------------------
        # HSV sliders
        #---------------------------------------------------------------
        # radio buttons
        self.hsv_add = QRadioButton('Additive')
        self.hsv_mul = QRadioButton('Multiplicative')
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)

        # sliders
        hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
        ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
        vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
        self.hs = hs
        self.ss = ss
        self.vs = vs

        self.hsv_widget = QWidget()
        self.hsv_widget.layout = QGridLayout(self.hsv_widget)
        self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hs, 2, 0)
        self.hsv_widget.layout.addWidget(self.ss, 2, 1)
        self.hsv_widget.layout.addWidget(self.vs, 2, 2)

        #---------------------------------------------------------------
        # Brightness/Contrast sliders
        #---------------------------------------------------------------
        # sliders
        cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
        bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
        self.cont = cont
        self.bright = bright

        # layout
        self.bright_widget = QWidget()
        self.bright_widget.layout = QGridLayout(self.bright_widget)
        self.bright_widget.layout.addWidget(self.cont, 0, 0)
        self.bright_widget.layout.addWidget(self.bright, 0, 1)

        #----------------------------------------------------------------------
        # Gamma Slider
        #----------------------------------------------------------------------
        gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
        self.gamma = gamma

        # layout
        self.gamma_widget = QWidget()
        self.gamma_widget.layout = QGridLayout(self.gamma_widget)
        self.gamma_widget.layout.addWidget(self.gamma, 0, 0)

        #---------------------------------------------------------------
        # Sigmoid Gamma sliders
        #---------------------------------------------------------------
        # sliders
        alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
        beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
        self.a_gamma = alpha
        self.b_gamma = beta

        # layout
        self.sig_gamma_widget = QWidget()
        self.sig_gamma_widget.layout = QGridLayout(self.sig_gamma_widget)
        self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
        self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)

        #---------------------------------------------------------------
        # Buttons
        #---------------------------------------------------------------
        self.commit_button = QPushButton('Commit')
        self.commit_button.clicked.connect(self.commit_changes)
        self.revert_button = QPushButton('Revert')
        self.revert_button.clicked.connect(self.revert_changes)

        #---------------------------------------------------------------
        # Mixer Layout
        #---------------------------------------------------------------
        self.sliders = QStackedWidget()
        self.sliders.addWidget(self.rgb_widget)
        self.sliders.addWidget(self.hsv_widget)
        self.sliders.addWidget(self.bright_widget)
        self.sliders.addWidget(self.gamma_widget)
        self.sliders.addWidget(self.sig_gamma_widget)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.combo_box, 0, 0)
        self.layout.addWidget(self.sliders, 1, 0)
        self.layout.addWidget(self.commit_button, 2, 0)
        self.layout.addWidget(self.revert_button, 3, 0)

        #---------------------------------------------------------------
        # State Initialization
        #---------------------------------------------------------------

        self.combo_box.setCurrentIndex(0)
        self.rgb_mul.setChecked(True)
        self.hsv_mul.setChecked(True)

    def set_callback(self, callback):
        self.callback = callback

    def combo_box_changed(self, index):
        self.sliders.setCurrentIndex(index)
        self.reset()

    def rgb_radio_changed(self):
        self.reset()

    def hsv_radio_changed(self):
        self.reset()

    def reset(self):
        self.reset_sliders()
        self.mixer.set_to_stateimg()
        if self.callback:
            self.callback()

    def reset_sliders(self):
        # handle changing the conversion factors necessary
        if self.rgb_add.isChecked():
            self.rs.set_conv_fac(0.51, -255)
            self.rs.set_value(0)
            self.gs.set_conv_fac(0.51, -255)
            self.gs.set_value(0)
            self.bs.set_conv_fac(0.51, -255)
            self.bs.set_value(0)
        else:
            self.rs.set_conv_fac(0.002, 0)
            self.rs.set_value(1.)
            self.gs.set_conv_fac(0.002, 0)
            self.gs.set_value(1.)
            self.bs.set_conv_fac(0.002, 0)
            self.bs.set_value(1.)

        self.hs.set_value(0)
        if self.hsv_add.isChecked():
            self.ss.set_conv_fac(0.002, -1)
            self.ss.set_value(0)
            self.vs.set_conv_fac(0.002, -1)
            self.vs.set_value(0)
        else:
            self.ss.set_conv_fac(0.002, 0)
            self.ss.set_value(1.)
            self.vs.set_conv_fac(0.002, 0)
            self.vs.set_value(1.)

        self.bright.set_value(0)
        self.cont.set_value(1.)

        self.gamma.set_value(1)
        self.a_gamma.set_value(1)
        self.b_gamma.set_value(0.5)

    def rgb_changed(self, name, val):
        if name == 'R':
            channel = self.mixer.RED
        elif name == 'G':
            channel = self.mixer.GREEN
        else:
            channel = self.mixer.BLUE

        if self.rgb_mul.isChecked():
            self.mixer.multiply(channel, val)
        elif self.rgb_add.isChecked():
            self.mixer.add(channel, val)
        else:
            pass

        if self.callback:
            self.callback()

    def hsv_changed(self, name, val):
        h = self.hs.val()
        s = self.ss.val()
        v = self.vs.val()

        if self.hsv_mul.isChecked():
            self.mixer.hsv_multiply(h, s, v)
        elif self.hsv_add.isChecked():
            self.mixer.hsv_add(h, s, v)
        else:
            pass

        if self.callback:
            self.callback()

    def bright_changed(self, name, val):
        b = self.bright.val()
        c = self.cont.val()
        self.mixer.brightness(c, b)

        if self.callback:
            self.callback()

    def gamma_changed(self, name, val):
        self.mixer.gamma(val)

        if self.callback:
            self.callback()

    def sig_gamma_changed(self, name, val):
        ag = self.a_gamma.val()
        bg = self.b_gamma.val()
        self.mixer.sigmoid_gamma(ag, bg)

        if self.callback:
            self.callback()

    def commit_changes(self):
        self.mixer.commit_changes()
        self.reset_sliders()

    def revert_changes(self):
        self.mixer.revert()
        self.reset_sliders()

        if self.callback:
            self.callback()
Exemplo n.º 13
0
class VizTabs(QTabWidget):
    def __init__(self, parent=None):
        self._parent = parent
        super().__init__(parent)
        self.oneDViewer = None
        self.strainSliceViewer = None
        self.vtk3dviewer = None

        self.plot_1d = QStackedWidget()
        self.plot_2d = QStackedWidget()
        self.plot_3d = QStackedWidget()

        self.message = QLabel("Load project files")
        self.message.setAlignment(Qt.AlignCenter)
        font = self.message.font()
        font.setPointSize(20)
        self.message.setFont(font)

        self.message2 = QLabel("Load project files")
        self.message2.setAlignment(Qt.AlignCenter)
        self.message2.setFont(font)

        self.plot_2d.addWidget(self.message)
        self.plot_3d.addWidget(self.message2)

        self.addTab(self.plot_1d, "1D")
        self.addTab(self.plot_2d, "2D")
        self.addTab(self.plot_3d, "3D")

        self.set_1d_mode(False)

        self.setCornerWidget(QLabel("Visualization Pane    "),
                             corner=Qt.TopLeftCorner)

    def set_1d_mode(self, oned):
        self.setTabEnabled(0, oned)
        self.setTabEnabled(1, not oned)
        self.setTabEnabled(2, not USING_THINLINC and not DISABLE_3D
                           and not oned)

    def set_ws(self, field):

        if field is not None:
            try:
                ws = field.to_md_histo_workspace()
            except Exception as e:
                self._parent.show_failure_msg("Failed to generate field",
                                              str(e), traceback.format_exc())
                ws = None
        else:
            ws = None

        if ws:
            if len(ws.getNonIntegratedDimensions()) == 1:
                self.set_1d_mode(True)
                if self.oneDViewer:
                    self.plot_1d.removeWidget(self.oneDViewer)
                fig = Figure()
                self.oneDViewer = FigureCanvas(fig)

                # get scan direction
                for d in ('x', 'y', 'z'):
                    dim = getattr(field, d)
                    if not np.allclose(dim, dim[0], atol=0.1):
                        scan_dir = d

                # create simple 1D plot
                ax = fig.add_subplot(111)
                ax.errorbar(getattr(field, scan_dir),
                            field.values,
                            field.errors,
                            marker='o')
                ax.set_xlabel(f'{scan_dir} (mm)')

                self.plot_1d.addWidget(self.oneDViewer)
                self.plot_1d.setCurrentIndex(1)
            else:
                self.set_1d_mode(False)
                if self.strainSliceViewer:
                    self.strainSliceViewer.set_new_workspace(ws)
                else:
                    self.strainSliceViewer = StrainSliceViewer(ws, parent=self)
                    self.plot_2d.addWidget(self.strainSliceViewer.view)
                    self.plot_2d.setCurrentIndex(1)
                self.strainSliceViewer.set_new_field(
                    field,
                    bin_widths=[
                        ws.getDimension(n).getBinWidth() for n in range(3)
                    ])

                if not USING_THINLINC and not DISABLE_3D:
                    if self.vtk3dviewer:
                        self.vtk3dviewer.set_ws(ws)
                    else:
                        self.vtk3dviewer = VTK3DView(ws)
                        self.plot_3d.addWidget(self.vtk3dviewer)
                        self.plot_3d.setCurrentIndex(1)
        else:
            self.set_1d_mode(False)
            if self.oneDViewer is not None:
                self.plot_1d.removeWidget(self.oneDViewer)
                self.oneDViewer = None
            if self.strainSliceViewer is not None:
                self.plot_2d.removeWidget(self.strainSliceViewer.view)
                self.strainSliceViewer = None
            if self.vtk3dviewer:
                self.plot_3d.removeWidget(self.vtk3dviewer)
                self.vtk3dviewer = None

    def set_message(self, text):
        self.message.setText(text)
        self.message2.setText(text)
Exemplo n.º 14
0
class StackedCanvasView(CanvasView):
    """
    View that can display intents in their corresponding canvases.

    Currently, uses a stacked widget with several pages:
    one for tabview and others for different split views.

    Can be adapted as long as its internal widgets are CanvasDisplayWidgets.
    """
    def __init__(self, parent=None, model=None):
        super(StackedCanvasView, self).__init__(parent)
        if model is not None:
            self.setModel(model)

        self.canvas_display_widgets = [
            CanvasDisplayTabWidget(),
            SplitHorizontal(),
            SplitVertical(),
            SplitThreeView(),
            SplitGridView()
        ]

        ### Create stacked widget and fill pages with different canvas display widgets
        self.stackedwidget = QStackedWidget(self)
        # Create a visual layout section for the buttons that are used to switch the widgets
        self.buttonpanel = QHBoxLayout()
        self.buttonpanel.addStretch(10)
        # Create a logical button grouping that will:
        #   - show the currently selected view (button will be checked/pressed)
        #   - allow for switching the buttons/views in a mutually exclusive manner (only one can be pressed at a time)
        self.buttongroup = QButtonGroup()

        def add_canvas_display_widgets():
            for i in range(len(self.canvas_display_widgets)):
                # Add the view to the stacked widget
                self.stackedwidget.addWidget(self.canvas_display_widgets[i])
                # Create a button, using the view's recommended display icon
                button = QPushButton(self)
                button.setCheckable(True)
                button.setIcon(self.canvas_display_widgets[i].icon)
                # Add the button to the logical button group
                self.buttongroup.addButton(button, i)
                # Add the button to the visual layout section
                self.buttonpanel.addWidget(button)

        add_canvas_display_widgets()

        def set_default_canvas_display_widget():
            # The first button added to the buttongroup will be the currently selected button (and therefore view)
            self.buttongroup.button(0).setChecked(True)

        set_default_canvas_display_widget()

        # Whenever a button is switched, capture its id (corresponds to integer index in our case);
        # this will handle switching the view and displaying canvases.
        self.buttongroup.idToggled.connect(self.switch_view)

        # define outer layout & add stacked widget and button panel
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.stackedwidget)
        self.layout.addLayout(self.buttonpanel)
        self.setLayout(self.layout)

    # INTERFACING WITH TOOLBAR (see XPCSToolbar)
    def view(self):
        # from xicam.gui.canvases import ImageIntentCanvas
        view = self.stackedwidget.currentWidget()
        return view.getView()
        # return None

    # DONE INTERFACING

    def switch_view(self, id, toggled):
        # when toggled==True, the the button is the new button that was switched to.
        # when False, the button is the previous button
        view = self.canvas_display_widgets[id]
        if not toggled:
            ...
            # TODO: is there anything we need to do here (re: cleanup)?

        else:
            self.stackedwidget.setCurrentIndex(id)
            self.show_canvases()

    def show_canvases(self):
        self.stackedwidget.currentWidget().clear_canvases()
        self.stackedwidget.currentWidget().show_canvases(
            self._canvas_manager.canvases(self.model()))
Exemplo n.º 15
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    ui_schema = {
        "call_order": {
            "ui:widget": "plugins"
        },
    }

    resized = Signal(QSize)
    closed = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def closeEvent(self, event):
        """Override to emit signal."""
        self.closed.emit()
        super().closeEvent(event)

    def reject(self):
        """Override to handle Escape."""
        super().reject()
        self.close()

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""

        # Because there are multiple pages, need to keep a dictionary of values dicts.
        # One set of keywords are for each page, then in each entry for a page, there are dicts
        # of setting and its value.

        self._values_orig_dict = {}
        self._values_dict = {}
        self._setting_changed_dict = {}

        for page, setting in SETTINGS.schemas().items():
            schema, values, properties = self.get_page_dict(setting)

            self._setting_changed_dict[page] = {}
            self._values_orig_dict[page] = values
            self._values_dict[page] = values

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def get_page_dict(self, setting):
        """Provides the schema, set of values for each setting, and the properties
        for each setting.

        Parameters
        ----------
        setting : dict
            Dictionary of settings for a page within the settings manager.

        Returns
        -------
        schema : dict
            Json schema of the setting page.
        values : dict
            Dictionary of values currently set for each parameter in the settings.
        properties : dict
            Dictionary of properties within the json schema.

        """

        schema = json.loads(setting['json_schema'])
        # Need to remove certain properties that will not be displayed on the GUI
        properties = schema.pop('properties')
        model = setting['model']
        values = model.dict()
        napari_config = getattr(model, "NapariConfig", None)
        if napari_config is not None:
            for val in napari_config.preferences_exclude:
                properties.pop(val)
                values.pop(val)

        schema['properties'] = properties

        return schema, values, properties

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""

        widget = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        widget.valueChanged.connect(self._reset_widgets)
        widget.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to SETTINGS."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        for n in range(self._stack.count()):
            # Must set the current row so that the proper list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            page = self._list.currentItem().text().split(" ")[0].lower()
            # get new values for settings.  If they were changed from values at beginning
            # of preference dialog session, change them back.
            # Using the settings value seems to be the best way to get the checkboxes right
            # on the plugin call order widget.
            setting = SETTINGS.schemas()[page]
            schema, new_values, properties = self.get_page_dict(setting)
            self.check_differences(self._values_orig_dict[page], new_values)

        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """

        builder = WidgetBuilder()
        form = builder.create_form(schema, self.ui_schema)
        # set state values for widget
        form.widget.state = values
        form.widget.on_changed.connect(lambda d: self.check_differences(
            d,
            self._values_dict[schema["title"].lower()],
        ))

        return form

    def _values_changed(self, page, new_dict, old_dict):
        """Loops through each setting in a page to determine if it changed.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting value
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the begining of preferences dialog session.

        """
        for setting_name, value in new_dict.items():
            if value != old_dict[setting_name]:
                self._setting_changed_dict[page][setting_name] = value
            elif (value == old_dict[setting_name]
                  and setting_name in self._setting_changed_dict[page]):
                self._setting_changed_dict[page].pop(setting_name)

    def check_differences(self, new_dict, old_dict):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting parameter
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the beginning of the preferences dialog session.
        """
        page = self._list.currentItem().text().split(" ")[0].lower()
        self._values_changed(page, new_dict, old_dict)
        different_values = self._setting_changed_dict[page]

        if len(different_values) > 0:
            # change the values in SETTINGS
            for setting_name, value in different_values.items():
                try:
                    setattr(SETTINGS._settings[page], setting_name, value)
                    self._values_dict[page] = new_dict
                except:  # noqa: E722
                    continue
Exemplo n.º 16
0
class IMCWidget(QWidget):
    NAME = 'Imaging Mass Cytometry'
    FULL_NAME = f'napari-imc: {NAME}'
    AREA = 'right'
    ALLOWED_AREAS = ['left', 'right']

    def __init__(self,
                 napari_viewer: Viewer,
                 parent: Optional[QWidget] = None):
        super(IMCWidget, self).__init__(parent)
        self._controller = IMCController(napari_viewer, self)

        self._imc_file_tree_model = IMCFileTreeModel(self._controller,
                                                     parent=self)
        self._imc_file_tree_view = IMCFileTreeView(parent=self)
        self._imc_file_tree_view.setModel(self._imc_file_tree_model)

        self._channel_table_model = ChannelTableModel(self._controller,
                                                      parent=self)
        self._channel_table_proxy_model = QSortFilterProxyModel(self)
        self._channel_table_proxy_model.setSourceModel(
            self._channel_table_model)
        self._channel_table_proxy_model.sort(
            self._channel_table_model.LABEL_COLUMN)
        self._channel_table_view = ChannelTableView(parent=self)
        self._channel_table_view.setModel(self._channel_table_proxy_model)

        self._channel_controls_widget = ChannelControlsWidget(self._controller,
                                                              parent=self)
        self._channel_controls_container = QStackedWidget(self)
        self._channel_controls_container.setSizePolicy(QSizePolicy.Maximum,
                                                       QSizePolicy.Maximum)
        self._channel_controls_container.addWidget(QWidget(self))
        self._channel_controls_container.addWidget(
            self._channel_controls_widget)

        layout = QVBoxLayout(self)
        splitter = QSplitter(Qt.Vertical, self)
        splitter.addWidget(self._imc_file_tree_view)
        channel_panel = QWidget(self)
        channel_panel_layout = QVBoxLayout()
        channel_panel_layout.addWidget(self._channel_table_view)
        channel_panel_layout.addWidget(self._channel_controls_container)
        channel_panel.setLayout(channel_panel_layout)
        splitter.addWidget(channel_panel)
        layout.addWidget(splitter)
        self.setLayout(layout)

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @self._imc_file_tree_model.dataChanged.connect
        def on_imc_file_tree_model_data_changed(top_left: QModelIndex,
                                                bottom_right: QModelIndex,
                                                roles: Optional[int] = None):
            item = top_left.internalPointer()
            if isinstance(item, IMCFilePanoramaModel):
                if item.is_shown:
                    self._controller.hide_imc_file_panorama(item)
                else:
                    self._controller.show_imc_file_panorama(item)
            elif isinstance(item, IMCFileAcquisitionModel):
                if item.is_loaded:
                    self._controller.unload_imc_file_acquisition(item)
                else:
                    self._controller.load_imc_file_acquisition(item)

        # noinspection PyUnresolvedReferences
        @self._imc_file_tree_view.events.imc_file_closed.connect
        def on_imc_file_tree_view_imc_file_closed(imc_file: IMCFileModel):
            self._controller.close_imc_file(imc_file)

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @self._channel_table_model.dataChanged.connect
        def on_channel_table_model_data_changed(top_left: QModelIndex,
                                                bottom_right: QModelIndex,
                                                roles: Optional[int] = None):
            channel = self._controller.channels[top_left.row()]
            if channel.is_shown:
                self._controller.hide_channel(channel)
            else:
                self._controller.show_channel(channel)

        channel_table_selection_model: QItemSelectionModel = self._channel_table_view.selectionModel(
        )

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @channel_table_selection_model.selectionChanged.connect
        def on_channel_table_view_selection_changed(
                selected: QItemSelection, deselected: QItemSelection):
            selected_channels = []
            for index in self._channel_table_view.selectedIndexes():
                index = self._channel_table_proxy_model.mapToSource(index)
                channel = self._controller.channels[index.row()]
                selected_channels.append(channel)
            self._controller.selected_channels = selected_channels

    def select_channel(self, channel_index: int):
        top_left = self._channel_table_model.index(channel_index, 0)
        top_left = self._channel_table_proxy_model.mapFromSource(top_left)
        bottom_right = self._channel_table_model.index(
            channel_index,
            self._channel_table_model.columnCount() - 1)
        bottom_right = self._channel_table_proxy_model.mapFromSource(
            bottom_right)
        selection_model: QItemSelectionModel = self._channel_table_view.selectionModel(
        )
        selection_model.clearSelection(
        )  # first clear the selection, to make sure the channel controls refresh
        selection_model.select(QItemSelection(top_left, bottom_right),
                               QItemSelectionModel.Select)

    def refresh_channel_controls_widget(self):
        self._channel_controls_widget.refresh()
        if len(self._controller.selected_channels) > 0:
            self._channel_controls_container.setCurrentIndex(1)
        else:
            self._channel_controls_container.setCurrentIndex(0)

    @property
    def controller(self):
        return self._controller

    @property
    def imc_file_tree_model(self) -> IMCFileTreeModel:
        return self._imc_file_tree_model

    @property
    def channel_table_model(self) -> ChannelTableModel:
        return self._channel_table_model
Exemplo n.º 17
0
class PackagesDialog(DialogBase):
    """Package dependencies dialog."""

    sig_setup_ready = Signal()

    def __init__(
        self,
        parent=None,
        packages=None,
        pip_packages=None,
        remove_only=False,
        update_only=False,
    ):
        """About dialog."""
        super(PackagesDialog, self).__init__(parent=parent)

        # Variables
        self.api = AnacondaAPI()
        self.actions = None
        self.packages = packages or []
        self.pip_packages = pip_packages or []

        # Widgets
        self.stack = QStackedWidget()
        self.table = QTableWidget()
        self.text = QTextEdit()
        self.label_description = LabelBase()
        self.label_status = LabelBase()
        self.progress_bar = QProgressBar()
        self.button_ok = ButtonPrimary('Apply')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.text.setReadOnly(True)
        self.stack.addWidget(self.table)
        self.stack.addWidget(self.text)
        if remove_only:
            text = 'The following packages will be removed:<br>'
        else:
            text = 'The following packages will be modified:<br>'
        self.label_description.setText(text)
        self.label_description.setWordWrap(True)
        self.label_description.setWordWrap(True)
        self.label_status.setWordWrap(True)
        self.table.horizontalScrollBar().setVisible(False)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setAlternatingRowColors(True)
        self.table.setSelectionMode(QAbstractItemView.NoSelection)
        self.table.setSortingEnabled(True)
        self._hheader = self.table.horizontalHeader()
        self._vheader = self.table.verticalHeader()
        self._hheader.setStretchLastSection(True)
        self._hheader.setDefaultAlignment(Qt.AlignLeft)
        self._hheader.setSectionResizeMode(self._hheader.Fixed)
        self._vheader.setSectionResizeMode(self._vheader.Fixed)
        self.button_ok.setMinimumWidth(70)
        self.button_ok.setDefault(True)
        self.base_minimum_width = 300 if remove_only else 420
        if remove_only:
            self.setWindowTitle("Remove Packages")
        elif update_only:
            self.setWindowTitle("Update Packages")
        else:
            self.setWindowTitle("Install Packages")

        self.setMinimumWidth(self.base_minimum_width)

        # Layouts
        layout_progress = QHBoxLayout()
        layout_progress.addWidget(self.label_status)
        layout_progress.addWidget(SpacerHorizontal())
        layout_progress.addWidget(self.progress_bar)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addWidget(self.label_description)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.stack)
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_progress)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_ok.setDisabled(True)

        # Setup
        self.table.setDisabled(True)
        self.update_status('Solving package specifications',
                           value=0,
                           max_value=0)

    def setup(self, worker, output, error):
        """Setup the widget to include the list of dependencies."""
        if not isinstance(output, dict):
            output = {}

        packages = sorted(pkg.split('==')[0] for pkg in self.packages)
        success = output.get('success')
        error = output.get('error', '')
        exception_name = output.get('exception_name', '')
        actions = output.get('actions', [])
        prefix = worker.prefix

        if exception_name:
            message = exception_name
        else:
            # All requested packages already installed
            message = output.get('message', ' ')

        navi_deps_error = self.api.check_navigator_dependencies(
            actions, prefix)
        description = self.label_description.text()

        if error:
            description = 'No packages will be modified.'
            self.stack.setCurrentIndex(1)
            self.button_ok.setDisabled(True)
            if self.api.is_offline():
                error = ("Some of the functionality of Anaconda Navigator "
                         "will be limited in <b>offline mode</b>. <br><br>"
                         "Installation and upgrade actions will be subject to "
                         "the packages currently available on your package "
                         "cache.")
            self.text.setText(error)
        elif navi_deps_error:
            description = 'No packages will be modified.'
            error = ('Downgrading/removing these packages will modify '
                     'Anaconda Navigator dependencies.')
            self.text.setText(error)
            self.stack.setCurrentIndex(1)
            message = 'NavigatorDependenciesError'
            self.button_ok.setDisabled(True)
        elif success and actions:
            self.stack.setCurrentIndex(0)
            # Conda 4.3.x
            if isinstance(actions, list):
                actions_link = actions[0].get('LINK', [])
                actions_unlink = actions[0].get('UNLINK', [])
            # Conda 4.4.x
            else:
                actions_link = actions.get('LINK', [])
                actions_unlink = actions.get('UNLINK', [])

            deps = set()
            deps = deps.union({p['name'] for p in actions_link})
            deps = deps.union({p['name'] for p in actions_unlink})
            deps = deps - set(packages)
            deps = sorted(list(deps))

            count_total_packages = len(packages) + len(deps)
            plural_total = 's' if count_total_packages != 1 else ''
            plural_selected = 's' if len(packages) != 1 else ''

            self.table.setRowCount(count_total_packages)
            self.table.setColumnCount(4)
            if actions_link:
                description = '{0} package{1} will be installed'.format(
                    count_total_packages, plural_total)
                self.table.showColumn(2)
                self.table.showColumn(3)
            elif actions_unlink and not actions_link:
                self.table.hideColumn(2)
                self.table.hideColumn(3)
                self.table.setHorizontalHeaderLabels(
                    ['Name', 'Unlink', 'Link', 'Channel'])
                description = '{0} package{1} will be removed'.format(
                    count_total_packages, plural_total)

            for row, pkg in enumerate(packages + deps):
                link_item = [p for p in actions_link if p['name'] == pkg]
                if not link_item:
                    link_item = {
                        'version': '-'.center(len('link')),
                        'channel': '-'.center(len('channel')),
                    }
                else:
                    link_item = link_item[0]

                unlink_item = [p for p in actions_unlink if p['name'] == pkg]
                if not unlink_item:
                    unlink_item = {
                        'version': '-'.center(len('link')),
                    }
                else:
                    unlink_item = unlink_item[0]

                unlink_version = str(unlink_item['version'])
                link_version = str(link_item['version'])

                item_unlink_v = QTableWidgetItem(unlink_version)
                item_link_v = QTableWidgetItem(link_version)
                item_link_c = QTableWidgetItem(link_item['channel'])
                if pkg in packages:
                    item_name = QTableWidgetItem(pkg)
                else:
                    item_name = QTableWidgetItem('*' + pkg)

                items = [item_name, item_unlink_v, item_link_v, item_link_c]
                for column, item in enumerate(items):
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self.table.setItem(row, column, item)

            if deps:
                message = (
                    '<b>*</b> indicates the package is a dependency of a '
                    'selected package{0}<br>').format(plural_selected)

            self.button_ok.setEnabled(True)
            self.table.resizeColumnsToContents()
            unlink_width = self.table.columnWidth(1)
            if unlink_width < 60:
                self.table.setColumnWidth(1, 60)
            self.table.setHorizontalHeaderLabels(
                ['Name  ', 'Unlink  ', 'Link  ', 'Channel  '])

        self.table.setEnabled(True)
        self.update_status(message=message)
        self.label_description.setText(description)

        # Adjust size after data has populated the table
        self.table.resizeColumnsToContents()
        width = sum(
            self.table.columnWidth(i) for i in range(self.table.columnCount()))
        delta = (self.width() - self.table.width() +
                 self.table.verticalHeader().width() + 10)

        new_width = width + delta

        if new_width < self.base_minimum_width:
            new_width = self.base_minimum_width

        self.setMinimumWidth(new_width)
        self.setMaximumWidth(new_width)

        self.sig_setup_ready.emit()

    def update_status(self, message='', value=None, max_value=None):
        """Update status of packages dialog."""
        self.label_status.setText(message)

        if max_value is None and value is None:
            self.progress_bar.setVisible(False)
        else:
            self.progress_bar.setVisible(True)
            self.progress_bar.setMaximum(max_value)
            self.progress_bar.setValue(value)
Exemplo n.º 18
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""
    def __init__(self, parent=None):
        QDialog.__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.data = None
        self.arraywidget = None
        self.stack = None
        self.layout = None
        self.btn_save_and_close = None
        self.btn_close = None
        # Values for 3d array editor
        self.dim_indexes = [{}, {}, {}]
        self.last_dim = 0  # Adjust this for changing the startup dimension

    def setup_and_check(self,
                        data,
                        title='',
                        readonly=False,
                        xlabels=None,
                        ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        self.data.flags.writeable = True
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)

        if data.ndim > 3:
            self.error(
                _("Arrays with more than 3 dimensions are not "
                  "supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(
                _("The 'xlabels' argument length do no match array "
                  "column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(
                _("The 'ylabels' argument length do no match array row "
                  "number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \
               and not dtn.startswith('unicode'):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False

        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - " + _("NumPy array")
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)

        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(
                    ArrayEditorWidget(self, data[name], readonly, xlabels,
                                      ylabels))
        elif is_masked_array:
            self.stack.addWidget(
                ArrayEditorWidget(self, data, readonly, xlabels, ylabels))
            self.stack.addWidget(
                ArrayEditorWidget(self, data.data, readonly, xlabels, ylabels))
            self.stack.addWidget(
                ArrayEditorWidget(self, data.mask, readonly, xlabels, ylabels))
        elif data.ndim == 3:
            pass
        else:
            self.stack.addWidget(
                ArrayEditorWidget(self, data, readonly, xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        if self.arraywidget:
            self.arraywidget.model.dataChanged.connect(
                self.save_and_close_enable)
        self.stack.currentChanged.connect(self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array or data.ndim == 3:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not is_text_string(title):
                            title = repr(title)
                        text += ' - ' + title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            if data.ndim == 3:
                # QSpinBox
                self.index_spin = QSpinBox(self, keyboardTracking=False)
                self.index_spin.valueChanged.connect(self.change_active_widget)
                # QComboBox
                names = [str(i) for i in range(3)]
                ra_combo = QComboBox(self)
                ra_combo.addItems(names)
                ra_combo.currentIndexChanged.connect(self.current_dim_changed)
                # Adding the widgets to layout
                label = QLabel(_("Axis:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(ra_combo)
                self.shape_label = QLabel()
                btn_layout.addWidget(self.shape_label)
                label = QLabel(_("Index:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(self.index_spin)
                self.slicing_label = QLabel()
                btn_layout.addWidget(self.slicing_label)
                # set the widget to display when launched
                self.current_dim_changed(self.last_dim)
            else:
                ra_combo = QComboBox(self)
                ra_combo.currentIndexChanged.connect(
                    self.stack.setCurrentIndex)
                ra_combo.addItems(names)
                btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(
                    _("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)

        btn_layout.addStretch()

        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        return True

    @Slot(QModelIndex, QModelIndex)
    def save_and_close_enable(self, left_top, bottom_right):
        """Handle the data change event to enable the save and close button."""
        if self.btn_save_and_close:
            self.btn_save_and_close.setEnabled(True)
            self.btn_save_and_close.setAutoDefault(True)
            self.btn_save_and_close.setDefault(True)

    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)
        self.arraywidget.model.dataChanged.connect(self.save_and_close_enable)

    def change_active_widget(self, index):
        """
        This is implemented for handling negative values in index for
        3d arrays, to give the same behavior as slicing
        """
        string_index = [':'] * 3
        string_index[self.last_dim] = '<font color=red>%i</font>'
        self.slicing_label.setText(
            (r"Slicing: [" + ", ".join(string_index) + "]") % index)
        if index < 0:
            data_index = self.data.shape[self.last_dim] + index
        else:
            data_index = index
        slice_index = [slice(None)] * 3
        slice_index[self.last_dim] = data_index

        stack_index = self.dim_indexes[self.last_dim].get(data_index)
        if stack_index == None:
            stack_index = self.stack.count()
            try:
                self.stack.addWidget(
                    ArrayEditorWidget(self, self.data[slice_index]))
            except IndexError:  # Handle arrays of size 0 in one axis
                self.stack.addWidget(ArrayEditorWidget(self, self.data))
            self.dim_indexes[self.last_dim][data_index] = stack_index
            self.stack.update()
        self.stack.setCurrentIndex(stack_index)

    def current_dim_changed(self, index):
        """
        This change the active axis the array editor is plotting over
        in 3D
        """
        self.last_dim = index
        string_size = ['%i'] * 3
        string_size[index] = '<font color=red>%i</font>'
        self.shape_label.setText(
            ('Shape: (' + ', '.join(string_size) + ')    ') % self.data.shape)
        if self.index_spin.value() != 0:
            self.index_spin.setValue(0)
        else:
            # this is done since if the value is currently 0 it does not emit
            # currentIndexChanged(int)
            self.change_active_widget(0)
        self.index_spin.setRange(-self.data.shape[index],
                                 self.data.shape[index] - 1)

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)

    def get_value(self):
        """Return modified array -- 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
        return self.data

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, _("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    @Slot()
    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemplo n.º 19
0
class SinglePassData(BaseWidget):
    def __init__(self, parent=None, prefix='', bpm=''):
        super().__init__(parent=parent,
                         prefix=prefix,
                         bpm=bpm,
                         data_prefix='SP_')
        self.radio_buttons = []
        self.setupui()

    def setupui(self):
        vbl = QVBoxLayout(self)
        self.stack = QStackedWidget(self)
        vbl.addWidget(self.stack)

        rbA = QRadioButton('Antennas', self)
        rbP = QRadioButton('Positions', self)
        rbA.toggled.connect(_part(self.toggle_button, 0))
        rbP.toggled.connect(_part(self.toggle_button, 1))
        self.radio_buttons.append(rbA)
        self.radio_buttons.append(rbP)
        rbA.setChecked(True)
        hbl = QHBoxLayout()
        hbl.addStretch()
        hbl.addWidget(rbA)
        hbl.addStretch()
        hbl.addWidget(rbP)
        hbl.addStretch()
        vbl.addItem(hbl)

        # ##### Antennas Widget ######
        stack1 = QWidget(self.stack)
        self.stack.addWidget(stack1)
        vbl = QVBoxLayout(stack1)

        graph = self.create_graph(stack1, 'ant')
        vbl.addWidget(graph)
        stats = self.create_statistics(stack1, 'ant')
        vbl.addWidget(stats)

        # ##### Position and Amplitudes Widget ######
        stack2 = QWidget(self.stack)
        self.stack.addWidget(stack2)
        vbl = QVBoxLayout(stack2)

        graph = self.create_graph(stack2, 'pos')
        vbl.addWidget(graph)
        graph = self.create_graph(stack2, 'amp')
        vbl.addWidget(graph)

        self.setStyleSheet("""
            #SinglePassDataGraph{
                min-width:48em;
                min-height:24em;
            }
            QLabel{
                min-width:6em; max-width:6em;
                min-height:1.5em; max-height:1.5em;
            }""")

    def create_statistics(self, wid, typ='pos'):
        text, unit, names, _ = self._get_properties(typ)

        wid = QWidget(wid)
        hbl = QHBoxLayout(wid)
        hbl.addStretch()
        grpbx = QGroupBox('Statistics', wid)
        hbl.addWidget(grpbx)
        hbl.addStretch()

        gdl = QGridLayout(grpbx)
        gdl.setHorizontalSpacing(20)
        gdl.setVerticalSpacing(20)

        stats = ('MeanValue', 'Sigma', 'MinValue', 'MaxValue')
        for j, stat in enumerate(('Average', 'Sigma', 'Minimum', 'Maximum')):
            lab = QLabel(stat, wid)
            lab.setAlignment(Qt.AlignCenter)
            gdl.addWidget(lab, 0, j + 1)
        for i, name in enumerate(names):
            lab = QLabel(text[:3] + name, wid)
            lab.setAlignment(Qt.AlignCenter)
            gdl.addWidget(lab, i + 1, 0)
            for j, stat in enumerate(stats):
                lab = SiriusLabel(
                    wid,
                    init_channel=self.get_pvname(name + '_STATS' + stat +
                                                 '_RBV'))
                lab.setAlignment(Qt.AlignCenter)
                lab.unit_changed(unit)
                lab.showUnits = True
                lab.precisionFromPV = False
                lab.precision = 3
                gdl.addWidget(lab, i + 1, j + 1)
        return wid

    def create_graph(self, wid, typ='pos'):
        text, unit, names, colors = self._get_properties(typ)
        if typ == 'pos':
            unit = unit[1:]
            suff = '-Mon'
            CLASS = GraphTime
        elif typ == 'ant':
            suff = 'ArrayData'
            CLASS = GraphWave
        else:
            suff = '-Mon'
            CLASS = GraphTime

        graph = CLASS(wid,
                      prefix=self.prefix,
                      bpm=self.bpm,
                      data_prefix=self.data_prefix)
        graph.setLabel('left', text=text, units=unit)
        graph.setObjectName('SinglePassDataGraph')
        for name, cor in zip(names, colors):
            opts = dict(y_channel=name + suff,
                        name=name[2:],
                        color=cor,
                        lineStyle=1,
                        lineWidth=1)  # NOTE: If > 1: very low performance
            if typ == 'pos':
                opts['y_channel'] = self.get_pvname(opts['y_channel'],
                                                    is_data=False)
                graph.addYChannel(add_scale=1e-9, **opts)
            elif typ == 'amp':
                opts['y_channel'] = self.get_pvname(opts['y_channel'],
                                                    is_data=False)
                graph.addYChannel(**opts)
            else:
                opts['name'] = text[:3] + name
                opts['y_channel'] = self.get_pvname(opts['y_channel'])
                graph.addChannel(**opts)
        return graph

    def _get_properties(self, typ='pos'):
        if typ == 'pos':
            text = 'Positions'
            unit = 'nm'
            names = ('SPPosX', 'SPPosY', 'SPPosQ', 'SPSum')
            colors = ('blue', 'red', 'green', 'black')
        elif typ == 'ant':
            text = 'Antennas'
            unit = 'count'
            names = ('A', 'B', 'C', 'D')
            colors = ('blue', 'red', 'green', 'magenta')
        if typ == 'amp':
            text = 'Amplitudes'
            unit = 'count'
            names = ('SPAmplA', 'SPAmplB', 'SPAmplC', 'SPAmplD')
            colors = ('blue', 'red', 'green', 'magenta')
        return text, unit, names, colors

    def toggle_button(self, i, tog):
        if tog:
            self.stack.setCurrentIndex(i)