Example #1
0
    def create_file_combobox(self, text, choices, option, default=NoDefault,
                             tip=None, restart=False, filters=None,
                             adjust_to_contents=False,
                             default_line_edit=False):
        """choices: couples (name, key)"""
        combobox = FileComboBox(self, adjust_to_contents=adjust_to_contents,
                                default_line_edit=default_line_edit)
        combobox.restart_required = restart
        combobox.label_text = text
        edit = combobox.lineEdit()
        edit.label_text = text
        edit.restart_required = restart
        self.lineedits[edit] = (option, default)

        if tip is not None:
            combobox.setToolTip(tip)
        combobox.addItems(choices)

        msg = _('Invalid file path')
        self.validate_data[edit] = (osp.isfile, msg)
        browse_btn = QPushButton(ima.icon('FileIcon'), '', self)
        browse_btn.setToolTip(_("Select file"))
        browse_btn.clicked.connect(lambda: self.select_file(edit, filters))

        layout = QGridLayout()
        layout.addWidget(combobox, 0, 0, 0, 9)
        layout.addWidget(browse_btn, 0, 10)
        layout.setContentsMargins(0, 0, 0, 0)
        widget = QWidget(self)
        widget.combobox = combobox
        widget.browse_btn = browse_btn
        widget.setLayout(layout)

        return widget
Example #2
0
    def setup_gui(self):
        """Setup the main layout of the widget."""
        layout = QGridLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.canvas, 0, 1)
        layout.addLayout(self.setup_toolbar(), 0, 3, 2, 1)

        layout.setColumnStretch(0, 100)
        layout.setColumnStretch(2, 100)
        layout.setRowStretch(1, 100)
Example #3
0
    def _setupFeedbackSettings(self):
        ld_fbenbl = QLabel('Enable', self)
        pb_fbenbl = PyDMStateButton(self, self.dev_pref+':FBCTRL')

        ld_coefsel = QLabel('Coeficient Set', self)
        cb_coefsel = PyDMEnumComboBox(self, self.dev_pref+':SETSEL')

        ld_sftgain = QLabel('Shift Gain', self)
        sb_sftgain = PyDMSpinbox(self, self.dev_pref+':SHIFTGAIN')
        sb_sftgain.showStepExponent = False

        ld_downspl = QLabel('Downsampling', self)
        sb_downspl = PyDMSpinbox(self, self.dev_pref+':PROC_DS')
        sb_downspl.showStepExponent = False

        ld_satthrs = QLabel('Sat. Threshold [%]', self)
        sb_satthrs = PyDMSpinbox(self, self.dev_pref+':SAT_THRESHOLD')
        sb_satthrs.showStepExponent = False

        lay = QGridLayout()
        lay.addWidget(ld_fbenbl, 1, 0)
        lay.addWidget(pb_fbenbl, 1, 1)
        lay.addWidget(ld_coefsel, 2, 0)
        lay.addWidget(cb_coefsel, 2, 1)
        lay.addWidget(ld_sftgain, 3, 0)
        lay.addWidget(sb_sftgain, 3, 1)
        lay.addWidget(ld_downspl, 4, 0)
        lay.addWidget(sb_downspl, 4, 1)
        lay.addWidget(ld_satthrs, 5, 0)
        lay.addWidget(sb_satthrs, 5, 1)

        if self._is_resumed:
            wid = QWidget()
            wid.setLayout(lay)
            fb_label = QLabel(
                '<h4>Feedback Settings</h4>', self, alignment=Qt.AlignCenter)
            lay.setContentsMargins(0, 0, 0, 0)
            lay.setVerticalSpacing(12)
            lay.addWidget(fb_label, 0, 0, 1, 2)
        else:
            wid = QGroupBox('Feedback Settings', self)
            wid.setLayout(lay)

            ld_gensts = QLabel('Setup Status', self)
            led_gensts = SiriusLedAlert(self, self.dev_pref+':ERRSUM')
            lay.addWidget(ld_gensts, 6, 0)
            lay.addWidget(led_gensts, 6, 1)
        return wid
Example #4
0
    def setTempHeader(self, item_list, layout):
        ''' Display Temperature header labels '''
        widget = QWidget()
        hd_glay = QGridLayout()
        pos = 0
        for item in item_list:
            lbl_header = QLabel('<h4>' + item + '</h4>')
            lbl_header.setAlignment(Qt.AlignCenter)
            hd_glay.addWidget(lbl_header, 0, pos, 1, 3)
            pos += 3

        hd_glay = self.setParamLabel(hd_glay)
        hd_glay.setContentsMargins(0, 0, 0, 0)
        widget.setLayout(hd_glay)
        layout.addWidget(widget, 0, 1, 1, 6)
        return layout
Example #5
0
    def _setupUi(self):
        fbsett_wid = self._setupFeedbackSettings()
        status_wid = self._setupStatusWidget()

        lay = QGridLayout(self)
        lay.setAlignment(Qt.AlignTop | Qt.AlignCenter)
        if self._is_resumed:
            led_gensts = SiriusLedAlert(self, self.dev_pref+':ERRSUM')
            dev_label = QLabel(
                '<h3>'+self._label+'</h3>', self, alignment=Qt.AlignCenter)
            self.pb_detail = QPushButton(qta.icon('fa5s.ellipsis-v'), '', self)
            self.pb_detail.setObjectName('dtls')
            self.pb_detail.setStyleSheet(
                '#dtls{min-width:20px;max-width:20px;icon-size:15px;}')
            cmd = ['sirius-hla-si-di-bbb.py', '-dev', self.dev_pref]
            if self._prefix:
                cmd.extend(['-p', self._prefix])
            connect_newprocess(self.pb_detail, cmd, self)
            hbox_label = QHBoxLayout()
            hbox_label.setContentsMargins(0, 0, 0, 0)
            hbox_label.addWidget(led_gensts, alignment=Qt.AlignLeft)
            hbox_label.addWidget(dev_label)
            hbox_label.addWidget(self.pb_detail, alignment=Qt.AlignRight)
            hbox_label.setStretch(0, 1)
            hbox_label.setStretch(1, 10)
            hbox_label.setStretch(2, 1)

            wid = QWidget(self)
            wid.setObjectName('box')
            wid.setStyleSheet("""
                #box{border: 2px solid gray;}""")
            lay_box = QGridLayout(wid)
            lay_box.setVerticalSpacing(15)
            lay_box.addLayout(hbox_label, 0, 0)
            lay_box.addWidget(fbsett_wid, 1, 0)
            lay_box.addWidget(status_wid, 2, 0)

            lay.setContentsMargins(0, 0, 0, 0)
            lay.addWidget(wid)
        else:
            info_wid = BbBInfoWidget(self, self._prefix, self._device)
            lay.addWidget(fbsett_wid, 0, 1)
            lay.addWidget(status_wid, 0, 2)
            lay.addWidget(info_wid, 0, 3)
            lay.setColumnStretch(0, 3)
            lay.setColumnStretch(4, 3)
            lay.setRowStretch(1, 3)
Example #6
0
    def create_file_combobox(self,
                             text,
                             choices,
                             option,
                             default=NoDefault,
                             tip=None,
                             restart=False,
                             filters=None,
                             adjust_to_contents=False,
                             default_line_edit=False,
                             section=None,
                             validate_callback=None):
        """choices: couples (name, key)"""
        if section is not None and section != self.CONF_SECTION:
            self.cross_section_options[option] = section
        combobox = FileComboBox(self,
                                adjust_to_contents=adjust_to_contents,
                                default_line_edit=default_line_edit)
        combobox.restart_required = restart
        combobox.label_text = text
        edit = combobox.lineEdit()
        edit.label_text = text
        edit.restart_required = restart
        self.lineedits[edit] = (section, option, default)

        if tip is not None:
            combobox.setToolTip(tip)
        combobox.addItems(choices)
        combobox.choices = choices

        msg = _('Invalid file path')
        self.validate_data[edit] = (validate_callback
                                    if validate_callback else osp.isfile, msg)
        browse_btn = QPushButton(ima.icon('FileIcon'), '', self)
        browse_btn.setToolTip(_("Select file"))
        browse_btn.clicked.connect(lambda: self.select_file(edit, filters))

        layout = QGridLayout()
        layout.addWidget(combobox, 0, 0, 0, 9)
        layout.addWidget(browse_btn, 0, 10)
        layout.setContentsMargins(0, 0, 0, 0)
        widget = QWidget(self)
        widget.combobox = combobox
        widget.browse_btn = browse_btn
        widget.setLayout(layout)

        return widget
Example #7
0
    def _create_corr_summwidget(self, corr):
        """Create and return a corrector detail widget."""
        wid = QWidget()
        wid.setSizePolicy(QSzPlcy.Preferred, QSzPlcy.Maximum)
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.setAlignment(Qt.AlignCenter)

        propty_sp = 'Current-SP' if corr.sec == 'LI' else 'Kick-SP'
        propty_mon = propty_sp.replace('SP', 'Mon')

        led = SiriusLedState(
            self, corr.substitute(prefix=self.prefix, propty='PwrState-Sts'))
        led.setStyleSheet("max-width:1.29em;")
        lay.addWidget(led, 1, 1)

        nickname = corr.get_nickname(sec=corr.sec == 'LI', dev=True)
        pb = QPushButton(nickname, self)
        if corr.dis == 'PU':
            util.connect_window(pb, PUDetailWindow, parent=self, devname=corr)
        else:
            util.connect_window(pb, PSDetailWindow, parent=self, psname=corr)
        pb.setStyleSheet("""
            min-width:6em; max-width:6em; min-height:1.29em;""")
        lay.addWidget(pb, 1, 2)

        sp_kick = PyDMSpinboxScrollbar(
            self, corr.substitute(prefix=self.prefix, propty=propty_sp))
        sp_kick.setStyleSheet("QDoubleSpinBox{min-width:4em; max-width:4em; }"
                              "QScrollBar{max-width:4em;}")
        sp_kick.spinbox.precisionFromPV = False
        sp_kick.spinbox.precision = 1
        sp_kick.scrollbar.limitsFromPV = True
        lay.addWidget(sp_kick, 1, 3, 2, 1)

        lb_kick = PyDMLabel(
            self, corr.substitute(prefix=self.prefix, propty=propty_mon))
        lb_kick.setStyleSheet("""
            min-width:5em; max-width:5em; min-height:1.29em;""")
        lb_kick.showUnits = True
        lb_kick.precisionFromPV = False
        lb_kick.precision = 1
        lb_kick.setAlignment(Qt.AlignCenter)
        lay.addWidget(lb_kick, 1, 4)
        return wid
Example #8
0
    def __init__(self, dims: Dims, parent=None):

        super().__init__(parent=parent)

        # We keep a reference to the view:
        self.dims = dims

        # list of sliders
        self.sliders = []
        self._slider_axis = []

        # Initialises the layout:
        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)

        # Update the number of sliders now that the dims have been added
        self._update_nsliders()

        # The next lines connect events coming from the model to the Qt event
        # system: We need to go through Qt signals so that these events are run
        # in the Qt event loop thread. This is all about changing thread
        # context for thread-safety purposes

        # axis change listener
        def update_axis_listener(event):
            self.update_axis.emit(event.axis)

        self.dims.events.axis.connect(update_axis_listener)

        # What to do with the axis change events in terms of UI calls to the
        # widget
        self.update_axis.connect(self._update_slider)

        # ndim change listener
        def update_ndim_listener(event):
            self.update_ndim.emit()

        self.dims.events.ndim.connect(update_ndim_listener)

        # What to do with the ndim change events in terms of UI calls to the
        # widget
        self.update_ndim.connect(self._update_nsliders)
Example #9
0
    def _setupUi(self):
        self._label_dev = QLabel('Power supply: ', self)
        self._label_dev.setStyleSheet(
            'min-width: 8em; max-width: 8em;')

        self.cb_sec = QComboBox(self)
        for item in self._choose_sec:
            self.cb_sec.addItem(item)
        self.cb_sec.setCurrentText('SI')
        self.cb_sec.currentTextChanged.connect(
            self._handle_cb_visibility)
        self.cb_sec.currentTextChanged.connect(
            self._set_psnames)

        self.cb_sub = QComboBox(self)
        self.cb_sub.setEditable(True)
        self.cb_sub.setMaxVisibleItems(10)
        for item in self._choose_sub:
            self.cb_sub.addItem(item)
        self.cb_sub.currentTextChanged.connect(
            self._set_psnames)

        glay_choose = QGridLayout()
        glay_choose.addWidget(self.cb_sub, 0, 0)
        self.cb_dev = dict()
        for sec in self._choose_sec:
            visible = sec == 'SI'

            self.cb_dev[sec] = QComboBox(self)
            self.cb_dev[sec].setMaxVisibleItems(10)
            self.cb_dev[sec].setVisible(visible)
            for item in self._choose_dev[sec]:
                self.cb_dev[sec].addItem(item)
            self.cb_dev[sec].currentTextChanged.connect(
                self._set_psnames)

            glay_choose.addWidget(self.cb_dev[sec], 0, 1)

        lay = QGridLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self._label_dev, 0, 0)
        lay.addWidget(self.cb_sec, 0, 1)
        lay.addLayout(glay_choose, 0, 2)
    def _create_workdir_manager(self):
        self.workdir_ledit = QLineEdit()
        self.workdir_ledit.setReadOnly(True)

        self.workdir_btn = QToolButton()
        self.workdir_btn.setIcon(get_icon('folder_open'))
        self.workdir_btn.setAutoRaise(True)
        self.workdir_btn.setToolTip("Browse a working directory...")
        self.workdir_btn.clicked.connect(self.select_working_directory)

        workdir_widget = QWidget()
        workdir_layout = QGridLayout(workdir_widget)
        workdir_layout.setContentsMargins(0, 0, 0, 0)
        workdir_layout.setSpacing(1)
        workdir_layout.addWidget(QLabel('Working Directory:'), 0, 0)
        workdir_layout.addWidget(self.workdir_ledit, 0, 1)
        workdir_layout.addWidget(self.workdir_btn, 0, 2)

        return workdir_widget
Example #11
0
    def _setupUi(self):
        self._label_prop = QLabel('Properties: ', self)
        self._label_prop.setStyleSheet(
            'min-width: 8em; max-width: 8em;')

        self._label_symb = QLabel()
        icon = qta.icon('mdi.record-circle-outline')
        pixmap = icon.pixmap(icon.actualSize(QSize(20, 20)))
        self._label_symb.setPixmap(pixmap)
        self._label_symb.setSizePolicy(QSzPlcy.Fixed, QSzPlcy.Fixed)
        self.cb_prop_symb = QComboBox(self)
        self.cb_prop_symb.currentTextChanged.connect(
            self.propty_symb_changed.emit)
        self.cb_prop_symb.setSizePolicy(
            QSzPlcy.Expanding, QSzPlcy.Preferred)
        self.cb_prop_symb.setMaxVisibleItems(10)
        self.cb_prop_symb.addItems(self._choose_prop_symb)
        hbox_prop_symb = QHBoxLayout()
        hbox_prop_symb.addWidget(self._label_symb)
        hbox_prop_symb.addWidget(self.cb_prop_symb)

        self._label_line = QLabel()
        icon = qta.icon('mdi.pulse')
        pixmap = icon.pixmap(icon.actualSize(QSize(20, 20)))
        self._label_line.setPixmap(pixmap)
        self._label_line.setSizePolicy(QSzPlcy.Fixed, QSzPlcy.Fixed)
        self.cb_prop_line = QComboBox(self)
        self.cb_prop_line.currentTextChanged.connect(
            self.propty_line_changed.emit)
        self.cb_prop_line.setSizePolicy(
            QSzPlcy.Expanding, QSzPlcy.Preferred)
        self.cb_prop_line.setMaxVisibleItems(10)
        self.cb_prop_line.addItems(self._choose_prop_line)
        hbox_prop_line = QHBoxLayout()
        hbox_prop_line.addWidget(self._label_line)
        hbox_prop_line.addWidget(self.cb_prop_line)

        lay = QGridLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(self._label_prop, 0, 0)
        lay.addLayout(hbox_prop_symb, 0, 1)
        lay.addLayout(hbox_prop_line, 0, 2)
Example #12
0
class QuadHistogram(QFrame):
    '''A class which uses ColorHistogram to draw
    the 4 histograms of an image. R, G, B, and Value.

    The 4 histograms are layout out in a grid,
    and can be specified horizontal or vertical,
    and in which order ie. ['R', 'G', 'B', 'V']
    '''
    def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']):
        QFrame.__init__(self)

        r, g, b, v = histograms(img, 100)
        self.r_hist = ColorHistogram(r, (255, 0, 0))
        self.g_hist = ColorHistogram(g, (0, 255, 0))
        self.b_hist = ColorHistogram(b, (0, 0, 255))
        self.v_hist = ColorHistogram(v, (0, 0, 0))

        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.layout = QGridLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        order_map = {
            'R': self.r_hist,
            'G': self.g_hist,
            'B': self.b_hist,
            'V': self.v_hist
        }

        if layout == 'vertical':
            for i in range(len(order)):
                self.layout.addWidget(order_map[order[i]], i, 0)
        elif layout == 'horizontal':
            for i in range(len(order)):
                self.layout.addWidget(order_map[order[i]], 0, i)

    def update_hists(self, img):
        r, g, b, v = histograms(img, 100)
        self.r_hist.update_hist(r, (255, 0, 0))
        self.g_hist.update_hist(g, (0, 255, 0))
        self.b_hist.update_hist(b, (0, 0, 255))
        self.v_hist.update_hist(v, (0, 0, 0))
Example #13
0
    def _setupTopUpModeWidget(self):
        self._ld_tuperd = QLabel('Period', self)
        self._sb_tuperd = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='TopUpPeriod-SP'))
        self._sb_tuperd.showStepExponent = False
        self._lb_tuperd = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TopUpPeriod-RB'))
        self._lb_tuperd.showUnits = True

        self._ld_tumaxpu = QLabel('Max.Nr.Pulses', self)
        self._sb_tumaxpu = SiriusSpinbox(
            self, self._inj_prefix.substitute(propty='TopUpMaxNrPulses-SP'))
        self._sb_tumaxpu.showStepExponent = False
        self._lb_tumaxpu = PyDMLabel(
            self, self._inj_prefix.substitute(propty='TopUpMaxNrPulses-RB'))
        self._lb_tumaxpu.showUnits = True

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 6, 0, 0)
        lay.setAlignment(Qt.AlignTop)
        lay.addWidget(self._ld_tuperd, 0, 0)
        lay.addWidget(self._sb_tuperd, 0, 1)
        lay.addWidget(self._lb_tuperd, 0, 2)
        lay.addWidget(self._ld_tumaxpu, 1, 0)
        lay.addWidget(self._sb_tumaxpu, 1, 1)
        lay.addWidget(self._lb_tumaxpu, 1, 2)
        lay.setColumnStretch(0, 3)
        lay.setColumnStretch(1, 2)
        lay.setColumnStretch(2, 2)

        wid.setStyleSheet("""
            .QLabel{
                min-width: 6.5em; max-width: 6.5em; min-height: 1.5em;
                qproperty-alignment: 'AlignRight | AlignVCenter';
            }
            PyDMLabel{
                qproperty-alignment: AlignCenter;
            }""")
        return wid
Example #14
0
class QuadHistogram(QFrame):
    '''A class which uses ColorHistogram to draw
    the 4 histograms of an image. R, G, B, and Value.

    The 4 histograms are layout out in a grid,
    and can be specified horizontal or vertical,
    and in which order ie. ['R', 'G', 'B', 'V']
    '''

    def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']):
        QFrame.__init__(self)

        r, g, b, v = histograms(img, 100)
        self.r_hist = ColorHistogram(r, (255, 0, 0))
        self.g_hist = ColorHistogram(g, (0, 255, 0))
        self.b_hist = ColorHistogram(b, (0, 0, 255))
        self.v_hist = ColorHistogram(v, (0, 0, 0))

        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.layout = QGridLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist,
                     'V': self.v_hist}

        if layout == 'vertical':
            for i in range(len(order)):
                self.layout.addWidget(order_map[order[i]], i, 0)
        elif layout == 'horizontal':
            for i in range(len(order)):
                self.layout.addWidget(order_map[order[i]], 0, i)

    def update_hists(self, img):
        r, g, b, v = histograms(img, 100)
        self.r_hist.update_hist(r, (255, 0, 0))
        self.g_hist.update_hist(g, (0, 255, 0))
        self.b_hist.update_hist(b, (0, 0, 255))
        self.v_hist.update_hist(v, (0, 0, 0))
Example #15
0
    def __init__(self, parent=None, init_channel=None):
        """Init."""
        super().__init__(parent)
        self._init_channel = init_channel

        layout = QGridLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(2)

        self.lineedit = SiriusLineEdit(
            parent=self, init_channel=init_channel)
        self.lineedit.setAlignment(Qt.AlignCenter)
        self.lineedit.setStyleSheet("SiriusLineEdit{min-height:1.29em;}")
        self.lineedit.setSizePolicy(QSzPol.Expanding, QSzPol.Preferred)

        self.scrollbar = PyDMScrollBar(
            parent=self, init_channel=init_channel)
        self.scrollbar.wheelEvent = lambda event: event.ignore()
        self.scrollbar.setTracking(False)
        self.scrollbar.setStyleSheet("PyDMScrollBar{max-height:0.7em;}")

        layout.addWidget(self.lineedit, 0, 0, 2, 1)
        layout.addWidget(self.scrollbar, 2, 0, 1, 1)
Example #16
0
    def __init__(self, plot, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.plot = plot
        ly = QGridLayout()
        # remove annoying padding
        ly.setContentsMargins(0, 0, 0, 0)
        self.setLayout(ly)

        self.label = QLabel('Binning:')
        ly.addWidget(self.label, 0, 0)

        self.auto = QCheckBox('auto', checked=False)
        ly.addWidget(self.auto, 0, 1)

        self.spinbox = QSpinBox()
        ly.addWidget(self.spinbox, 1, 0, 1, 2)
        self.spinbox.setRange(1, 100000)
        self.spinbox.setValue(10)

        self.spinbox.valueChanged.connect(self._on_binning_change)
        self.auto.clicked.connect(self._on_auto_change)

        self.auto.click()
Example #17
0
    def setup_ui(self):
        self.host = QLineEdit(self)
        self.host.setMinimumWidth(170)
        self.port = QLineEdit(self)
        self.port.setValidator(QIntValidator(0, 99999, self))
        self.username = QLineEdit(self)
        self.password = QLineEdit(self)
        self.password.setEchoMode(QLineEdit.Password)
        self.password.editingFinished.connect(
            lambda: [self._connect() if self.password.text() else None])

        host, user, uuid, port = self.gateway.get_current()
        self.host.setText(self.gateway._host or host or "")
        self.port.setText(self.gateway._port or port or "")
        self.username.setText(self.gateway._user or user or "")
        self.password.setText(os.getenv("OMERO_PASSWORD"))
        self.status = QLabel(self)
        self.status.setWordWrap(True)
        self.connect_btn = QPushButton("connect", self)
        self.connect_btn.clicked.connect(self._connect)

        self.login_form = QWidget(self)
        login_layout = QGridLayout(self.login_form)
        login_layout.setContentsMargins(0, 0, 0, 0)
        login_layout.addWidget(QLabel("host", self), 0, 0)
        login_layout.addWidget(self.host, 0, 1)
        login_layout.addWidget(QLabel("port", self), 1, 0)
        login_layout.addWidget(self.port, 1, 1)
        login_layout.addWidget(QLabel("username", self), 2, 0)
        login_layout.addWidget(self.username, 2, 1)
        login_layout.addWidget(QLabel("password", self), 3, 0)
        login_layout.addWidget(self.password, 3, 1)
        login_layout.addWidget(self.connect_btn, 4, 1)

        layout = QVBoxLayout(self)
        layout.addWidget(self.login_form)
        layout.addWidget(self.status)
Example #18
0
    def __init__(self, parent, enable_replace=False):
        QWidget.__init__(self, parent)
        self.enable_replace = enable_replace
        self.editor = None
        self.is_code_editor = None

        glayout = QGridLayout()
        glayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(glayout)

        self.close_button = create_toolbutton(
            self, triggered=self.hide, icon=ima.icon('DialogCloseButton'))
        glayout.addWidget(self.close_button, 0, 0)

        # Find layout
        self.search_text = PatternComboBox(self,
                                           tip=_("Search string"),
                                           adjust_to_minimum=False)

        self.return_shift_pressed.connect(
            lambda: self.find(changed=False,
                              forward=False,
                              rehighlight=False,
                              multiline_replace_check=False))

        self.return_pressed.connect(
            lambda: self.find(changed=False,
                              forward=True,
                              rehighlight=False,
                              multiline_replace_check=False))

        self.search_text.lineEdit().textEdited.connect(
            self.text_has_been_edited)

        self.number_matches_text = QLabel(self)
        self.previous_button = create_toolbutton(self,
                                                 triggered=self.find_previous,
                                                 icon=ima.icon('ArrowUp'),
                                                 tip=_("Find previous"))
        self.next_button = create_toolbutton(self,
                                             triggered=self.find_next,
                                             icon=ima.icon('ArrowDown'),
                                             tip=_("Find next"))
        self.next_button.clicked.connect(self.update_search_combo)
        self.previous_button.clicked.connect(self.update_search_combo)

        self.re_button = create_toolbutton(self,
                                           icon=ima.icon('regex'),
                                           tip=_("Regular expression"))
        self.re_button.setCheckable(True)
        self.re_button.toggled.connect(lambda state: self.find())

        self.case_button = create_toolbutton(
            self, icon=ima.icon("format_letter_case"), tip=_("Case Sensitive"))
        self.case_button.setCheckable(True)
        self.case_button.toggled.connect(lambda state: self.find())

        self.words_button = create_toolbutton(self,
                                              icon=get_icon("whole_words.png"),
                                              tip=_("Whole words"))
        self.words_button.setCheckable(True)
        self.words_button.toggled.connect(lambda state: self.find())

        self.highlight_button = create_toolbutton(
            self, icon=get_icon("highlight.png"), tip=_("Highlight matches"))
        self.highlight_button.setCheckable(True)
        self.highlight_button.toggled.connect(self.toggle_highlighting)

        hlayout = QHBoxLayout()
        self.widgets = [
            self.close_button, self.search_text, self.number_matches_text,
            self.previous_button, self.next_button, self.re_button,
            self.case_button, self.words_button, self.highlight_button
        ]
        for widget in self.widgets[1:]:
            hlayout.addWidget(widget)
        glayout.addLayout(hlayout, 0, 1)

        # Replace layout
        replace_with = QLabel(_("Replace with:"))
        self.replace_text = PatternComboBox(self,
                                            adjust_to_minimum=False,
                                            tip=_('Replace string'))
        self.replace_text.valid.connect(
            lambda _: self.replace_find(focus_replace_text=True))
        self.replace_button = create_toolbutton(
            self,
            text=_('Replace/find next'),
            icon=ima.icon('DialogApplyButton'),
            triggered=self.replace_find,
            text_beside_icon=True)
        self.replace_sel_button = create_toolbutton(
            self,
            text=_('Replace in selection'),
            icon=ima.icon('DialogApplyButton'),
            triggered=self.replace_find_selection,
            text_beside_icon=True)
        self.replace_sel_button.clicked.connect(self.update_replace_combo)
        self.replace_sel_button.clicked.connect(self.update_search_combo)

        self.replace_all_button = create_toolbutton(
            self,
            text=_('Replace all'),
            icon=ima.icon('DialogApplyButton'),
            triggered=self.replace_find_all,
            text_beside_icon=True)
        self.replace_all_button.clicked.connect(self.update_replace_combo)
        self.replace_all_button.clicked.connect(self.update_search_combo)

        self.replace_layout = QHBoxLayout()
        widgets = [
            replace_with, self.replace_text, self.replace_button,
            self.replace_sel_button, self.replace_all_button
        ]
        for widget in widgets:
            self.replace_layout.addWidget(widget)
        glayout.addLayout(self.replace_layout, 1, 1)
        self.widgets.extend(widgets)
        self.replace_widgets = widgets
        self.hide_replace()

        self.search_text.setTabOrder(self.search_text, self.replace_text)

        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.shortcuts = self.create_shortcuts(parent)

        self.highlight_timer = QTimer(self)
        self.highlight_timer.setSingleShot(True)
        self.highlight_timer.setInterval(1000)
        self.highlight_timer.timeout.connect(self.highlight_matches)
        self.search_text.installEventFilter(self)
Example #19
0
class QtLayerProperties(QFrame):
    def __init__(self, layer):
        super().__init__()

        self.layer = layer
        layer.events.select.connect(lambda v: self.setSelected(True))
        layer.events.deselect.connect(lambda v: self.setSelected(False))
        layer.events.name.connect(self._on_layer_name_change)
        layer.events.blending.connect(self._on_blending_change)
        layer.events.opacity.connect(self._on_opacity_change)
        layer.events.visible.connect(self._on_visible_change)
        layer.events.thumbnail.connect(self._on_thumbnail_change)

        self.setObjectName('layer')

        self.vbox_layout = QVBoxLayout()
        self.top = QFrame()
        self.top_layout = QHBoxLayout()
        self.grid = QFrame()
        self.grid_layout = QGridLayout()
        self.vbox_layout.addWidget(self.top)
        self.vbox_layout.addWidget(self.grid)
        self.vbox_layout.setSpacing(0)
        self.top.setFixedHeight(38)
        self.top_layout.setContentsMargins(0, 0, 0, 0)
        self.grid_layout.setContentsMargins(0, 0, 0, 0)
        self.top_layout.setAlignment(Qt.AlignCenter)
        self.top.setLayout(self.top_layout)
        self.grid.setLayout(self.grid_layout)
        self.setLayout(self.vbox_layout)

        self.name_column = 0
        self.property_column = 1

        cb = QCheckBox(self)
        cb.setObjectName('visibility')
        cb.setToolTip('Layer visibility')
        cb.setChecked(self.layer.visible)
        cb.setProperty('mode', 'visibility')
        cb.stateChanged.connect(lambda state=cb: self.changeVisible(state))
        self.visibleCheckBox = cb
        self.top_layout.addWidget(cb)

        tb = QLabel(self)
        tb.setObjectName('thumbmnail')
        tb.setToolTip('Layer thumbmnail')
        self.thumbnail_label = tb
        self._on_thumbnail_change(None)
        self.top_layout.addWidget(tb)

        textbox = QLineEdit(self)
        textbox.setText(layer.name)
        textbox.home(False)
        textbox.setToolTip('Layer name')
        textbox.setAcceptDrops(False)
        textbox.setEnabled(True)
        textbox.editingFinished.connect(self.changeText)
        self.nameTextBox = textbox
        self.top_layout.addWidget(textbox)

        pb = QPushButton(self)
        pb.setToolTip('Expand properties')
        pb.clicked.connect(self.changeExpanded)
        pb.setObjectName('expand')
        self.expand_button = pb
        self.top_layout.addWidget(pb)

        row = self.grid_layout.rowCount()
        sld = QSlider(Qt.Horizontal, self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(self.layer.opacity * 100)
        sld.valueChanged[int].connect(
            lambda value=sld: self.changeOpacity(value))
        self.opacitySilder = sld
        row = self.grid_layout.rowCount()
        self.grid_layout.addWidget(QLabel('opacity:'), row, self.name_column)
        self.grid_layout.addWidget(sld, row, self.property_column)

        row = self.grid_layout.rowCount()
        blend_comboBox = QComboBox()
        for blend in Blending:
            blend_comboBox.addItem(str(blend))
        index = blend_comboBox.findText(self.layer.blending,
                                        Qt.MatchFixedString)
        blend_comboBox.setCurrentIndex(index)
        blend_comboBox.activated[str].connect(
            lambda text=blend_comboBox: self.changeBlending(text))
        self.blendComboBox = blend_comboBox
        self.grid_layout.addWidget(QLabel('blending:'), row, self.name_column)
        self.grid_layout.addWidget(blend_comboBox, row, self.property_column)

        msg = 'Click to select\nDrag to rearrange\nDouble click to expand'
        self.setToolTip(msg)
        self.setExpanded(False)
        self.setFixedWidth(250)
        self.grid_layout.setColumnMinimumWidth(0, 100)
        self.grid_layout.setColumnMinimumWidth(1, 100)

        self.setSelected(self.layer.selected)

    def setSelected(self, state):
        self.setProperty('selected', state)
        self.nameTextBox.setEnabled(state)
        self.style().unpolish(self)
        self.style().polish(self)

    def changeOpacity(self, value):
        with self.layer.events.blocker(self._on_opacity_change):
            self.layer.opacity = value / 100

    def changeVisible(self, state):
        if state == Qt.Checked:
            self.layer.visible = True
        else:
            self.layer.visible = False

    def changeText(self):
        self.layer.name = self.nameTextBox.text()

    def changeBlending(self, text):
        self.layer.blending = text

    def mouseReleaseEvent(self, event):
        event.ignore()

    def mousePressEvent(self, event):
        event.ignore()

    def mouseMoveEvent(self, event):
        event.ignore()

    def mouseDoubleClickEvent(self, event):
        self.setExpanded(not self.expanded)

    def changeExpanded(self):
        self.setExpanded(not self.expanded)

    def setExpanded(self, bool):
        if bool:
            self.expanded = True
            self.expand_button.setProperty('expanded', True)
            rows = self.grid_layout.rowCount()
            self.setFixedHeight(38 + 30 * rows)
            self.grid.show()
        else:
            self.expanded = False
            self.expand_button.setProperty('expanded', False)
            self.setFixedHeight(60)
            self.grid.hide()
        self.expand_button.style().unpolish(self.expand_button)
        self.expand_button.style().polish(self.expand_button)

    def _on_layer_name_change(self, event):
        with self.layer.events.name.blocker():
            self.nameTextBox.setText(self.layer.name)
            self.nameTextBox.home(False)

    def _on_opacity_change(self, event):
        with self.layer.events.opacity.blocker():
            self.opacitySilder.setValue(self.layer.opacity * 100)

    def _on_blending_change(self, event):
        with self.layer.events.blending.blocker():
            index = self.blendComboBox.findText(self.layer.blending,
                                                Qt.MatchFixedString)
            self.blendComboBox.setCurrentIndex(index)

    def _on_visible_change(self, event):
        with self.layer.events.visible.blocker():
            self.visibleCheckBox.setChecked(self.layer.visible)

    def _on_thumbnail_change(self, event):
        thumbnail = self.layer.thumbnail
        # Note that QImage expects the image width followed by height
        image = QImage(
            thumbnail,
            thumbnail.shape[1],
            thumbnail.shape[0],
            QImage.Format_RGBA8888,
        )
        self.thumbnail_label.setPixmap(QPixmap.fromImage(image))
Example #20
0
    def __init__(self, parent, adjacency):

        QSplitter.__init__(self, parent, orientation=Qt.Vertical)

        self.plugin = parent.plugin
        self.adjacency = adjacency

        treepane = QWidget(parent=self)

        # Create main widget
        self.model = MxAnalyzerModel(
            adjacency=adjacency,
            root=None,
            parent=self     # parent must be self,
                            # because self.model access self.tab
        )
        # from .modeltest import ModelTest
        # self.modeltest = ModelTest(self.model, self)
        self.tree = MxAnalyzerTree(treepane, self.model)
        self.shellwidget = None # Set by parent

        button_group = QButtonGroup(parent=self)
        self.object_radio = object_radio = QRadioButton("Object")
        self.expr_radio = expr_radio = QRadioButton("Expression")
        button_group.addButton(object_radio)
        button_group.addButton(expr_radio)
        object_radio.setChecked(True)

        object_radio.toggled.connect(self.toggleObject)

        # Layout of the top area in the plugin widget
        expr_layout = QHBoxLayout()
        expr_layout.setContentsMargins(0, 0, 0, 0)

        # Add Object textbox
        expr_layout.addSpacing(10)
        txt = _("Object")
        if sys.platform == 'darwin':
            obj_label = QLabel("  " + txt)
        else:
            obj_label = QLabel(txt)
        expr_layout.addWidget(obj_label)

        if spyder.version_info < (4,):
            font = parent.plugin.get_plugin_font()
        else:
            font = parent.plugin.get_font()

        self.objbox = QLabel(parent=self)
        self.argbox = MxPyExprLineEdit(self, font=font)
        self.attrdict = None

        objbox_layout = QHBoxLayout()
        objbox_layout.addWidget(self.objbox)
        objbox_layout.addWidget(self.argbox)
        objbox_layout.setStretch(0, 3)  # 3:1
        objbox_layout.setStretch(1, 1)

        self.exprobjbox = MxPyExprLineEdit(treepane, font=font)
        expr_layout.addWidget(self.exprobjbox)
        expr_layout.addSpacing(10)

        # Add Object textbox
        txt = _("Args")
        if sys.platform == 'darwin':
            arg_label = QLabel("  " + txt)
        else:
            arg_label = QLabel(txt)
        expr_layout.addWidget(arg_label)

        self.exprargbox = MxPyExprLineEdit(treepane, font=font)
        expr_layout.addWidget(self.exprargbox)
        # expr_layout.addSpacing(5)

        top_layout = QGridLayout()
        top_layout.addWidget(object_radio, 0, 0)
        top_layout.addWidget(expr_radio, 1, 0)
        top_layout.addLayout(objbox_layout, 0, 1)
        objbox_layout.setContentsMargins(0, 0, 0, 5)
        top_layout.addLayout(expr_layout, 1, 1)
        top_layout.setContentsMargins(5, 5, 5, 5)

        # Main layout of this widget
        layout = create_plugin_layout(top_layout, self.tree)
        treepane.setLayout(layout)

        self.status = QLabel()
        layout.addWidget(self.status)

        self.codepane = AnalyzerCodePane(parent=self)
class MxDataWidget(QWidget):
    """
    Dialog for displaying and editing DataFrame and related objects.

    Based on the gtabview project (ExtTableView).
    For more information please see:
    https://github.com/wavexx/gtabview/blob/master/gtabview/viewer.py

    Signals
    -------
    sig_option_changed(str, object): Raised if an option is changed.
       Arguments are name of option and its new value.
    """
    sig_option_changed = Signal(str, object)

    def __init__(self, parent=None, data=DataFrame()):
        QWidget.__init__(self, parent)
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.is_series = False
        self.layout = None
        self.setup_and_check(data)

    def setup_and_check(self, data, title=''):
        """
        Setup DataFrameEditor:
        return False if data is not supported, True otherwise.
        Supported types for data are DataFrame, Series and Index.
        """
        self._selection_rec = False
        self._model = None

        self.layout = QGridLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - %s" % data.__class__.__name__
        else:
            title = _("%s editor") % data.__class__.__name__
        if isinstance(data, Series):
            self.is_series = True
            data = data.to_frame()
        elif isinstance(data, Index):
            data = DataFrame(data)

        self.setWindowTitle(title)
        # self.resize(600, 500)

        self.hscroll = QScrollBar(Qt.Horizontal)
        self.vscroll = QScrollBar(Qt.Vertical)

        # Create the view for the level
        self.create_table_level()

        # Create the view for the horizontal header
        self.create_table_header()

        # Create the view for the vertical index
        self.create_table_index()

        # Create the model and view of the data
        self.dataModel = MxDataModel(data, parent=self)
        # self.dataModel.dataChanged.connect(self.save_and_close_enable)
        self.create_data_table()

        self.layout.addWidget(self.hscroll, 2, 0, 1, 2)
        self.layout.addWidget(self.vscroll, 0, 2, 2, 1)

        # autosize columns on-demand
        self._autosized_cols = set()
        self._max_autosize_ms = None
        self.dataTable.installEventFilter(self)

        avg_width = self.fontMetrics().averageCharWidth()
        self.min_trunc = avg_width * 8  # Minimum size for columns
        self.max_width = avg_width * 64  # Maximum size for columns

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

        self.setModel(self.dataModel)
        self.resizeColumnsToContents()

        return True

    def create_table_level(self):
        """Create the QTableView that will hold the level model."""
        self.table_level = QTableView()
        self.table_level.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_level.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_level.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_level.setFrameStyle(QFrame.Plain)
        self.table_level.horizontalHeader().sectionResized.connect(
            self._index_resized)
        self.table_level.verticalHeader().sectionResized.connect(
            self._header_resized)
        # self.table_level.setItemDelegate(QItemDelegate())
        self.layout.addWidget(self.table_level, 0, 0)
        self.table_level.setContentsMargins(0, 0, 0, 0)
        self.table_level.horizontalHeader().sectionClicked.connect(
            self.sortByIndex)

    def create_table_header(self):
        """Create the QTableView that will hold the header model."""
        self.table_header = QTableView()
        self.table_header.verticalHeader().hide()
        self.table_header.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_header.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_header.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_header.setHorizontalScrollMode(QTableView.ScrollPerPixel)
        self.table_header.setHorizontalScrollBar(self.hscroll)
        self.table_header.setFrameStyle(QFrame.Plain)
        self.table_header.horizontalHeader().sectionResized.connect(
            self._column_resized)
        # self.table_header.setItemDelegate(QItemDelegate())
        self.layout.addWidget(self.table_header, 0, 1)

    def create_table_index(self):
        """Create the QTableView that will hold the index model."""
        self.table_index = QTableView()
        self.table_index.horizontalHeader().hide()
        self.table_index.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_index.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_index.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.table_index.setVerticalScrollMode(QTableView.ScrollPerPixel)
        self.table_index.setVerticalScrollBar(self.vscroll)
        self.table_index.setFrameStyle(QFrame.Plain)
        self.table_index.verticalHeader().sectionResized.connect(
            self._row_resized)
        # self.table_index.setItemDelegate(QItemDelegate())
        self.layout.addWidget(self.table_index, 1, 0)
        self.table_index.setContentsMargins(0, 0, 0, 0)

    def create_data_table(self):
        """Create the QTableView that will hold the data model."""
        self.dataTable = MxDataTable(self, self.dataModel,
                                     self.table_header.horizontalHeader(),
                                     self.hscroll, self.vscroll)
        self.dataTable.verticalHeader().hide()
        self.dataTable.horizontalHeader().hide()
        self.dataTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.dataTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.dataTable.setHorizontalScrollMode(QTableView.ScrollPerPixel)
        self.dataTable.setVerticalScrollMode(QTableView.ScrollPerPixel)
        self.dataTable.setFrameStyle(QFrame.Plain)
        # self.dataTable.setItemDelegate(QItemDelegate())
        self.layout.addWidget(self.dataTable, 1, 1)
        self.setFocusProxy(self.dataTable)
        self.dataTable.sig_sort_by_column.connect(self._sort_update)
        self.dataTable.sig_fetch_more_columns.connect(self._fetch_more_columns)
        self.dataTable.sig_fetch_more_rows.connect(self._fetch_more_rows)

    def sortByIndex(self, index):
        """Implement a Index sort."""
        self.table_level.horizontalHeader().setSortIndicatorShown(True)
        sort_order = self.table_level.horizontalHeader().sortIndicatorOrder()
        self.table_index.model().sort(index, sort_order)
        self._sort_update()

    def model(self):
        """Get the model of the dataframe."""
        return self._model

    def _column_resized(self, col, old_width, new_width):
        """Update the column width."""
        self.dataTable.setColumnWidth(col, new_width)
        self._update_layout()

    def _row_resized(self, row, old_height, new_height):
        """Update the row height."""
        self.dataTable.setRowHeight(row, new_height)
        self._update_layout()

    def _index_resized(self, col, old_width, new_width):
        """Resize the corresponding column of the index section selected."""
        self.table_index.setColumnWidth(col, new_width)
        self._update_layout()

    def _header_resized(self, row, old_height, new_height):
        """Resize the corresponding row of the header section selected."""
        self.table_header.setRowHeight(row, new_height)
        self._update_layout()

    def _update_layout(self):
        """Set the width and height of the QTableViews and hide rows."""
        h_width = max(self.table_level.verticalHeader().sizeHint().width(),
                      self.table_index.verticalHeader().sizeHint().width())
        self.table_level.verticalHeader().setFixedWidth(h_width)
        self.table_index.verticalHeader().setFixedWidth(h_width)

        last_row = self._model.header_shape[0] - 1
        if last_row < 0:
            hdr_height = self.table_level.horizontalHeader().height()
        else:

            # Check if the header shape has only one row (which display the
            # same info than the horizontal header).
            if last_row == 0:
                self.table_level.setRowHidden(0, True)
                self.table_header.setRowHidden(0, True)
            else:
                self.table_level.setRowHidden(0, False)
                self.table_header.setRowHidden(0, False)

            hdr_height = self.table_level.rowViewportPosition(last_row) + \
                         self.table_level.rowHeight(last_row) + \
                         self.table_level.horizontalHeader().height()

        self.table_header.setFixedHeight(hdr_height)
        self.table_level.setFixedHeight(hdr_height)

        last_col = self._model.header_shape[1] - 1
        if last_col < 0:
            idx_width = self.table_level.verticalHeader().width()
        else:
            idx_width = self.table_level.columnViewportPosition(last_col) + \
                        self.table_level.columnWidth(last_col) + \
                        self.table_level.verticalHeader().width()
        self.table_index.setFixedWidth(idx_width)
        self.table_level.setFixedWidth(idx_width)
        self._resizeVisibleColumnsToContents()

    def _reset_model(self, table, model):
        """Set the model in the given table."""
        old_sel_model = table.selectionModel()
        table.setModel(model)
        if old_sel_model:
            del old_sel_model

    def setAutosizeLimit(self, limit_ms):
        """Set maximum size for columns."""
        self._max_autosize_ms = limit_ms

    def setModel(self, model, relayout=True):
        """Set the model for the data, header/index and level views."""
        self._model = model
        # sel_model = self.dataTable.selectionModel()
        # sel_model.currentColumnChanged.connect(
        #     self._resizeCurrentColumnToContents)

        self._reset_model(self.dataTable, model)

        # Asociate the models (level, vertical index and horizontal header)
        # with its corresponding view.
        self._reset_model(
            self.table_level,
            DataFrameLevelModel(model, self.palette(), self.font()))
        self._reset_model(self.table_header,
                          DataFrameHeaderModel(model, 0, self.palette()))
        self._reset_model(self.table_index,
                          DataFrameHeaderModel(model, 1, self.palette()))

        # Needs to be called after setting all table models
        if relayout:
            self._update_layout()

    def setCurrentIndex(self, y, x):
        """Set current selection."""
        self.dataTable.selectionModel().setCurrentIndex(
            self.dataTable.model().index(y, x),
            QItemSelectionModel.ClearAndSelect)

    def _sizeHintForColumn(self, table, col, limit_ms=None):
        """Get the size hint for a given column in a table."""
        max_row = table.model().rowCount()
        lm_start = time.perf_counter()
        lm_row = 64 if limit_ms else max_row
        max_width = 0
        for row in range(max_row):
            v = table.sizeHintForIndex(table.model().index(row, col))
            max_width = max(max_width, v.width())
            if row > lm_row:
                lm_now = time.perf_counter()
                lm_elapsed = (lm_now - lm_start) * 1000
                if lm_elapsed >= limit_ms:
                    break
                lm_row = int((row / lm_elapsed) * limit_ms)
        return max_width

    def _resizeColumnToContents(self, header, data, col, limit_ms):
        """Resize a column by its contents."""
        hdr_width = self._sizeHintForColumn(header, col, limit_ms)
        data_width = self._sizeHintForColumn(data, col, limit_ms)
        if data_width > hdr_width:
            width = min(self.max_width, data_width)
        elif hdr_width > data_width * 2:
            width = max(min(hdr_width, self.min_trunc),
                        min(self.max_width, data_width))
        else:
            width = min(self.max_width, hdr_width)
        header.setColumnWidth(col, width)

    def _resizeColumnsToContents(self, header, data, limit_ms):
        """Resize all the colummns to its contents."""
        max_col = data.model().columnCount()
        if limit_ms is None:
            max_col_ms = None
        else:
            max_col_ms = limit_ms / max(1, max_col)
        for col in range(max_col):
            self._resizeColumnToContents(header, data, col, max_col_ms)

    def eventFilter(self, obj, event):
        """Override eventFilter to catch resize event."""
        if obj == self.dataTable and event.type() == QEvent.Resize:
            self._resizeVisibleColumnsToContents()
        return False

    def _resizeVisibleColumnsToContents(self):
        """Resize the columns that are in the view."""
        index_column = self.dataTable.rect().topLeft().x()
        start = col = self.dataTable.columnAt(index_column)
        width = self._model.shape[1]
        end = self.dataTable.columnAt(self.dataTable.rect().bottomRight().x())
        end = width if end == -1 else end + 1
        if self._max_autosize_ms is None:
            max_col_ms = None
        else:
            max_col_ms = self._max_autosize_ms / max(1, end - start)
        while col < end:
            resized = False
            if col not in self._autosized_cols:
                self._autosized_cols.add(col)
                resized = True
                self._resizeColumnToContents(self.table_header, self.dataTable,
                                             col, max_col_ms)
            col += 1
            if resized:
                # As we resize columns, the boundary will change
                index_column = self.dataTable.rect().bottomRight().x()
                end = self.dataTable.columnAt(index_column)
                end = width if end == -1 else end + 1
                if max_col_ms is not None:
                    max_col_ms = self._max_autosize_ms / max(1, end - start)

    def _resizeCurrentColumnToContents(self, new_index, old_index):
        """Resize the current column to its contents."""
        if new_index.column() not in self._autosized_cols:
            # Ensure the requested column is fully into view after resizing
            self._resizeVisibleColumnsToContents()
            self.dataTable.scrollTo(new_index)

    def resizeColumnsToContents(self):
        """Resize the columns to its contents."""
        self._autosized_cols = set()
        self._resizeColumnsToContents(self.table_level, self.table_index,
                                      self._max_autosize_ms)
        self._update_layout()
        self.table_level.resizeColumnsToContents()

    def change_format(self):
        """
        Ask user for display format for floats and use it.

        This function also checks whether the format is valid and emits
        `sig_option_changed`.
        """
        format, valid = QInputDialog.getText(self, _('Format'),
                                             _("Float formatting"),
                                             QLineEdit.Normal,
                                             self.dataModel.get_format())
        if valid:
            format = str(format)
            try:
                format % 1.1
            except:
                msg = _("Format ({}) is incorrect").format(format)
                QMessageBox.critical(self, _("Error"), msg)
                return
            if not format.startswith('%'):
                msg = _("Format ({}) should start with '%'").format(format)
                QMessageBox.critical(self, _("Error"), msg)
                return
            self.dataModel.set_format(format)
            self.sig_option_changed.emit('dataframe_format', format)

    def get_value(self):
        """Return modified Dataframe -- this is *not* a copy"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        df = self.dataModel.get_data()
        if self.is_series:
            return df.iloc[:, 0]
        else:
            return df

    def _update_header_size(self):
        """Update the column width of the header."""
        column_count = self.table_header.model().columnCount()
        for index in range(0, column_count):
            if index < column_count:
                column_width = self.dataTable.columnWidth(index)
                self.table_header.setColumnWidth(index, column_width)
            else:
                break

    def _sort_update(self):
        """
        Update the model for all the QTableView objects.

        Uses the model of the dataTable as the base.
        """
        self.setModel(self.dataTable.model())

    def _fetch_more_columns(self):
        """Fetch more data for the header (columns)."""
        self.table_header.model().fetch_more()

    def _fetch_more_rows(self):
        """Fetch more data for the index (rows)."""
        self.table_index.model().fetch_more()

    def resize_to_contents(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        self.dataTable.resizeColumnsToContents()
        self.dataModel.fetch_more(columns=True)
        self.dataTable.resizeColumnsToContents()
        self._update_header_size()
        QApplication.restoreOverrideCursor()

    # --- mx specific ---
    def process_remote_view(self, data):

        if data is None:
            data = DataFrame()  # Empty DataFrame

        self.setModel(MxDataModel(data, parent=self))
Example #22
0
class FramesWidget(BaseFrame):
    rejected = Signal()
    accepted = Signal()
    back = Signal()

    def __init__(self, parent=None):
        super(FramesWidget, self).__init__(parent)
        self.config_frames = []
        self.frames_instances = []
        self.current_frame = 0

        self.widget_layout = QVBoxLayout(self)
        self.setLayout(self.widget_layout)

        self.frames_widget = QWidget(self)
        self.frames_widget.setContentsMargins(0, 0, 0, 0)
        self.frames_widget_layout = QGridLayout(self.frames_widget)
        self.frames_widget_layout.setContentsMargins(0, 0, 0, 0)
        self.widget_layout.addWidget(self.frames_widget)

        self.btn_box = QDialogButtonBox(self)
        self.btn_box.setObjectName("btn_box")
        self.btn_box.setStandardButtons(QDialogButtonBox.Reset
                                        | QDialogButtonBox.Cancel
                                        | QDialogButtonBox.Ok)
        self.btn_box.button(QDialogButtonBox.Reset).setText("Back")
        self.btn_box.button(QDialogButtonBox.Ok).setText("Next")
        self.widget_layout.addWidget(self.btn_box)

        self.btn_box.button(QDialogButtonBox.Reset).clicked.connect(
            self.on_btn_box_resetted)
        QMetaObject.connectSlotsByName(self)

    def add_frames(self, frames):
        for frame in frames:
            self.add_frame(frame)

    def add_frame(self, frame):
        f = frame(self.frames_widget)
        f.setVisible(False)
        f.set_next_enabled.connect(
            self.btn_box.button(QDialogButtonBox.Ok).setEnabled)
        self.frames_widget_layout.addWidget(f, 0, 0, 1, 1)
        self.frames_instances.append(f)

        if len(self.frames_instances) > 0:
            self.frames_instances[0].setVisible(True)
            if self.frames_instances[0].disable_next_on_enter:
                self.btn_box.button(QDialogButtonBox.Ok).setEnabled(False)

    @Slot(dict)
    def set_options(self, options):
        for frame in self.frames_instances:
            frame.set_options(options)

    @Slot()
    def collect_info(self) -> dict:
        """Get info from every page"""
        settings = {}
        for frame in self.frames_instances:
            settings = settings | frame.collect_info()
        return settings

    @Slot()
    def on_btn_box_rejected(self):
        """When user clicks cancel button"""
        self.rejected.emit()

    @Slot()
    def on_btn_box_accepted(self):
        """On Next button clicked"""
        if self.current_frame + 1 < len(self.frames_instances):
            # if not last
            self.frames_instances[self.current_frame].setVisible(False)
            self.current_frame = self.current_frame + 1
            self.frames_instances[self.current_frame].setVisible(True)

            # if disable on enter page
            self.btn_box.button(
                QDialogButtonBox.Ok).setEnabled(not self.frames_instances[
                    self.current_frame].disable_next_on_enter)

            # if next page is last
            self._change_next_finish()

        # if last page
        elif self.current_frame + 1 == len(self.frames_instances):
            self.accepted.emit()

    @Slot()
    def on_btn_box_resetted(self):
        """On Back button clicked."""
        if self.current_frame > 0:
            self.frames_instances[self.current_frame].setVisible(False)
            self.current_frame = self.current_frame - 1
            self.frames_instances[self.current_frame].setVisible(True)
            self._change_next_finish()

            self.btn_box.button(
                QDialogButtonBox.Ok).setEnabled(not self.frames_instances[
                    self.current_frame].disable_next_on_enter)
        else:
            self.back.emit()

    def _change_next_finish(self):
        if self.current_frame + 1 == len(self.frames_instances):
            self.btn_box.button(QDialogButtonBox.Ok).setText("Finish")
        else:
            self.btn_box.button(QDialogButtonBox.Ok).setText("Next")
Example #23
0
    def setup(self):
        """Setup the ShortcutEditor with the provided arguments."""
        # Widgets
        icon_info = HelperToolButton()
        icon_info.setIcon(get_std_icon('MessageBoxInformation'))
        layout_icon_info = QVBoxLayout()
        layout_icon_info.setContentsMargins(0, 0, 0, 0)
        layout_icon_info.setSpacing(0)
        layout_icon_info.addWidget(icon_info)
        layout_icon_info.addStretch(100)

        self.label_info = QLabel()
        self.label_info.setText(
            _("Press the new shortcut and select 'Ok' to confirm, "
              "click 'Cancel' to revert to the previous state, "
              "or use 'Clear' to unbind the command from a shortcut."))
        self.label_info.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.label_info.setWordWrap(True)
        layout_info = QHBoxLayout()
        layout_info.setContentsMargins(0, 0, 0, 0)
        layout_info.addLayout(layout_icon_info)
        layout_info.addWidget(self.label_info)
        layout_info.setStretch(1, 100)

        self.label_current_sequence = QLabel(_("Current shortcut:"))
        self.text_current_sequence = QLabel(self.current_sequence)

        self.label_new_sequence = QLabel(_("New shortcut:"))
        self.text_new_sequence = ShortcutLineEdit(self)
        self.text_new_sequence.setPlaceholderText(_("Press shortcut."))

        self.helper_button = HelperToolButton()
        self.helper_button.setIcon(QIcon())
        self.label_warning = QLabel()
        self.label_warning.setWordWrap(True)
        self.label_warning.setAlignment(Qt.AlignTop | Qt.AlignLeft)

        self.button_default = QPushButton(_('Default'))
        self.button_ok = QPushButton(_('Ok'))
        self.button_ok.setEnabled(False)
        self.button_clear = QPushButton(_('Clear'))
        self.button_cancel = QPushButton(_('Cancel'))
        button_box = QHBoxLayout()
        button_box.addWidget(self.button_default)
        button_box.addStretch(100)
        button_box.addWidget(self.button_ok)
        button_box.addWidget(self.button_clear)
        button_box.addWidget(self.button_cancel)

        # New Sequence button box
        self.btn_clear_sequence = create_toolbutton(
            self, icon=ima.icon('editclear'),
            tip=_("Clear all entered key sequences"),
            triggered=self.clear_new_sequence)
        self.button_back_sequence = create_toolbutton(
            self, icon=ima.icon('ArrowBack'),
            tip=_("Remove last key sequence entered"),
            triggered=self.back_new_sequence)

        newseq_btnbar = QHBoxLayout()
        newseq_btnbar.setSpacing(0)
        newseq_btnbar.setContentsMargins(0, 0, 0, 0)
        newseq_btnbar.addWidget(self.button_back_sequence)
        newseq_btnbar.addWidget(self.btn_clear_sequence)

        # Setup widgets
        self.setWindowTitle(_('Shortcut: {0}').format(self.name))
        self.helper_button.setToolTip('')
        style = """
            QToolButton {
              margin:1px;
              border: 0px solid grey;
              padding:0px;
              border-radius: 0px;
            }"""
        self.helper_button.setStyleSheet(style)
        icon_info.setToolTip('')
        icon_info.setStyleSheet(style)

        # Layout
        layout_sequence = QGridLayout()
        layout_sequence.setContentsMargins(0, 0, 0, 0)
        layout_sequence.addLayout(layout_info, 0, 0, 1, 4)
        layout_sequence.addItem(QSpacerItem(15, 15), 1, 0, 1, 4)
        layout_sequence.addWidget(self.label_current_sequence, 2, 0)
        layout_sequence.addWidget(self.text_current_sequence, 2, 2)
        layout_sequence.addWidget(self.label_new_sequence, 3, 0)
        layout_sequence.addWidget(self.helper_button, 3, 1)
        layout_sequence.addWidget(self.text_new_sequence, 3, 2)
        layout_sequence.addLayout(newseq_btnbar, 3, 3)
        layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2)
        layout_sequence.setColumnStretch(2, 100)
        layout_sequence.setRowStretch(4, 100)

        layout = QVBoxLayout(self)
        layout.addLayout(layout_sequence)
        layout.addSpacing(10)
        layout.addLayout(button_box)
        layout.setSizeConstraint(layout.SetFixedSize)

        # Signals
        self.button_ok.clicked.connect(self.accept_override)
        self.button_clear.clicked.connect(self.unbind_shortcut)
        self.button_cancel.clicked.connect(self.reject)
        self.button_default.clicked.connect(self.set_sequence_to_default)

        # Set all widget to no focus so that we can register <Tab> key
        # press event.
        widgets = (
            self.label_warning, self.helper_button, self.text_new_sequence,
            self.button_clear, self.button_default, self.button_cancel,
            self.button_ok, self.btn_clear_sequence, self.button_back_sequence)
        for w in widgets:
            w.setFocusPolicy(Qt.NoFocus)
            w.clearFocus()
Example #24
0
    def setup(self):
        """Setup the ShortcutEditor with the provided arguments."""
        # Widgets
        icon_info = HelperToolButton()
        icon_info.setIcon(get_std_icon('MessageBoxInformation'))
        layout_icon_info = QVBoxLayout()
        layout_icon_info.setContentsMargins(0, 0, 0, 0)
        layout_icon_info.setSpacing(0)
        layout_icon_info.addWidget(icon_info)
        layout_icon_info.addStretch(100)

        self.label_info = QLabel()
        self.label_info.setText(
            _("Press the new shortcut and select 'Ok' to confirm, "
              "click 'Cancel' to revert to the previous state, "
              "or use 'Clear' to unbind the command from a shortcut."))
        self.label_info.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.label_info.setWordWrap(True)
        layout_info = QHBoxLayout()
        layout_info.setContentsMargins(0, 0, 0, 0)
        layout_info.addLayout(layout_icon_info)
        layout_info.addWidget(self.label_info)
        layout_info.setStretch(1, 100)

        self.label_current_sequence = QLabel(_("Current shortcut:"))
        self.text_current_sequence = QLabel(self.current_sequence)

        self.label_new_sequence = QLabel(_("New shortcut:"))
        self.text_new_sequence = ShortcutLineEdit(self)
        self.text_new_sequence.setPlaceholderText(_("Press shortcut."))

        self.helper_button = HelperToolButton()
        self.helper_button.setIcon(QIcon())
        self.label_warning = QLabel()
        self.label_warning.setWordWrap(True)
        self.label_warning.setAlignment(Qt.AlignTop | Qt.AlignLeft)

        self.button_default = QPushButton(_('Default'))
        self.button_ok = QPushButton(_('Ok'))
        self.button_ok.setEnabled(False)
        self.button_clear = QPushButton(_('Clear'))
        self.button_cancel = QPushButton(_('Cancel'))
        button_box = QHBoxLayout()
        button_box.addWidget(self.button_default)
        button_box.addStretch(100)
        button_box.addWidget(self.button_ok)
        button_box.addWidget(self.button_clear)
        button_box.addWidget(self.button_cancel)

        # New Sequence button box
        self.btn_clear_sequence = create_toolbutton(
            self, icon=ima.icon('editclear'),
            tip=_("Clear all entered key sequences"),
            triggered=self.clear_new_sequence)
        self.button_back_sequence = create_toolbutton(
            self, icon=ima.icon('ArrowBack'),
            tip=_("Remove last key sequence entered"),
            triggered=self.back_new_sequence)

        newseq_btnbar = QHBoxLayout()
        newseq_btnbar.setSpacing(0)
        newseq_btnbar.setContentsMargins(0, 0, 0, 0)
        newseq_btnbar.addWidget(self.button_back_sequence)
        newseq_btnbar.addWidget(self.btn_clear_sequence)

        # Setup widgets
        self.setWindowTitle(_('Shortcut: {0}').format(self.name))
        self.helper_button.setToolTip('')
        style = """
            QToolButton {
              margin:1px;
              border: 0px solid grey;
              padding:0px;
              border-radius: 0px;
            }"""
        self.helper_button.setStyleSheet(style)
        icon_info.setToolTip('')
        icon_info.setStyleSheet(style)

        # Layout
        layout_sequence = QGridLayout()
        layout_sequence.setContentsMargins(0, 0, 0, 0)
        layout_sequence.addLayout(layout_info, 0, 0, 1, 4)
        layout_sequence.addItem(QSpacerItem(15, 15), 1, 0, 1, 4)
        layout_sequence.addWidget(self.label_current_sequence, 2, 0)
        layout_sequence.addWidget(self.text_current_sequence, 2, 2)
        layout_sequence.addWidget(self.label_new_sequence, 3, 0)
        layout_sequence.addWidget(self.helper_button, 3, 1)
        layout_sequence.addWidget(self.text_new_sequence, 3, 2)
        layout_sequence.addLayout(newseq_btnbar, 3, 3)
        layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2)
        layout_sequence.setColumnStretch(2, 100)
        layout_sequence.setRowStretch(4, 100)

        layout = QVBoxLayout()
        layout.addLayout(layout_sequence)
        layout.addSpacing(5)
        layout.addLayout(button_box)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept_override)
        self.button_clear.clicked.connect(self.unbind_shortcut)
        self.button_cancel.clicked.connect(self.reject)
        self.button_default.clicked.connect(self.set_sequence_to_default)

        # Set all widget to no focus so that we can register <Tab> key
        # press event.
        widgets = (
            self.label_warning, self.helper_button, self.text_new_sequence,
            self.button_clear, self.button_default, self.button_cancel,
            self.button_ok, self.btn_clear_sequence, self.button_back_sequence)
        for w in widgets:
            w.setFocusPolicy(Qt.NoFocus)
            w.clearFocus()
Example #25
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.mode.connect(self._on_mode_change)
        self.layer.events.edge_width.connect(self._on_edge_width_change)
        self.layer.events.current_edge_color.connect(
            self._on_current_edge_color_change)
        self.layer.events.current_face_color.connect(
            self._on_current_face_color_change)
        self.layer.events.editable.connect(self._on_editable_change)
        self.layer.text.events.visible.connect(self._on_text_visibility_change)

        sld = QSlider(Qt.Horizontal)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(40)
        sld.setSingleStep(1)
        value = self.layer.current_edge_width
        if isinstance(value, Iterable):
            if isinstance(value, list):
                value = np.asarray(value)
            value = value.mean()
        sld.setValue(int(value))
        sld.valueChanged.connect(self.changeWidth)
        self.widthSlider = sld

        self.select_button = QtModeRadioButton(layer,
                                               'select',
                                               Mode.SELECT,
                                               tooltip='Select shapes')
        self.direct_button = QtModeRadioButton(layer,
                                               'direct',
                                               Mode.DIRECT,
                                               tooltip='Select vertices')
        self.panzoom_button = QtModeRadioButton(layer,
                                                'zoom',
                                                Mode.PAN_ZOOM,
                                                tooltip='Pan/zoom',
                                                checked=True)
        self.rectangle_button = QtModeRadioButton(layer,
                                                  'rectangle',
                                                  Mode.ADD_RECTANGLE,
                                                  tooltip='Add rectangles')
        self.ellipse_button = QtModeRadioButton(layer,
                                                'ellipse',
                                                Mode.ADD_ELLIPSE,
                                                tooltip='Add ellipses')
        self.line_button = QtModeRadioButton(layer,
                                             'line',
                                             Mode.ADD_LINE,
                                             tooltip='Add lines')
        self.path_button = QtModeRadioButton(layer,
                                             'path',
                                             Mode.ADD_PATH,
                                             tooltip='Add paths')
        self.polygon_button = QtModeRadioButton(layer,
                                                'polygon',
                                                Mode.ADD_POLYGON,
                                                tooltip='Add polygons')
        self.vertex_insert_button = QtModeRadioButton(layer,
                                                      'vertex_insert',
                                                      Mode.VERTEX_INSERT,
                                                      tooltip='Insert vertex')
        self.vertex_remove_button = QtModeRadioButton(layer,
                                                      'vertex_remove',
                                                      Mode.VERTEX_REMOVE,
                                                      tooltip='Remove vertex')

        self.move_front_button = QtModePushButton(
            layer,
            'move_front',
            slot=self.layer.move_to_front,
            tooltip='Move to front',
        )
        self.move_back_button = QtModePushButton(
            layer,
            'move_back',
            slot=self.layer.move_to_back,
            tooltip='Move to back',
        )
        self.delete_button = QtModePushButton(
            layer,
            'delete_shape',
            slot=self.layer.remove_selected,
            tooltip='Delete selected shapes',
        )

        self.button_group = QButtonGroup(self)
        self.button_group.addButton(self.select_button)
        self.button_group.addButton(self.direct_button)
        self.button_group.addButton(self.panzoom_button)
        self.button_group.addButton(self.rectangle_button)
        self.button_group.addButton(self.ellipse_button)
        self.button_group.addButton(self.line_button)
        self.button_group.addButton(self.path_button)
        self.button_group.addButton(self.polygon_button)
        self.button_group.addButton(self.vertex_insert_button)
        self.button_group.addButton(self.vertex_remove_button)

        button_grid = QGridLayout()
        button_grid.addWidget(self.vertex_remove_button, 0, 2)
        button_grid.addWidget(self.vertex_insert_button, 0, 3)
        button_grid.addWidget(self.delete_button, 0, 4)
        button_grid.addWidget(self.direct_button, 0, 5)
        button_grid.addWidget(self.select_button, 0, 6)
        button_grid.addWidget(self.panzoom_button, 0, 7)
        button_grid.addWidget(self.move_back_button, 1, 1)
        button_grid.addWidget(self.move_front_button, 1, 2)
        button_grid.addWidget(self.ellipse_button, 1, 3)
        button_grid.addWidget(self.rectangle_button, 1, 4)
        button_grid.addWidget(self.polygon_button, 1, 5)
        button_grid.addWidget(self.line_button, 1, 6)
        button_grid.addWidget(self.path_button, 1, 7)
        button_grid.setContentsMargins(5, 0, 0, 5)
        button_grid.setColumnStretch(0, 1)
        button_grid.setSpacing(4)

        self.faceColorEdit = QColorSwatchEdit(
            initial_color=self.layer.current_face_color,
            tooltip='click to set current face color',
        )
        self._on_current_face_color_change()
        self.edgeColorEdit = QColorSwatchEdit(
            initial_color=self.layer.current_edge_color,
            tooltip='click to set current edge color',
        )
        self._on_current_edge_color_change()
        self.faceColorEdit.color_changed.connect(self.changeFaceColor)
        self.edgeColorEdit.color_changed.connect(self.changeEdgeColor)

        text_disp_cb = QCheckBox()
        text_disp_cb.setToolTip('toggle text visibility')
        text_disp_cb.setChecked(self.layer.text.visible)
        text_disp_cb.stateChanged.connect(self.change_text_visibility)
        self.textDispCheckBox = text_disp_cb

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addLayout(button_grid, 0, 0, 1, 2)
        self.grid_layout.addWidget(QLabel('opacity:'), 1, 0)
        self.grid_layout.addWidget(self.opacitySlider, 1, 1)
        self.grid_layout.addWidget(QLabel('edge width:'), 2, 0)
        self.grid_layout.addWidget(self.widthSlider, 2, 1)
        self.grid_layout.addWidget(QLabel('blending:'), 3, 0)
        self.grid_layout.addWidget(self.blendComboBox, 3, 1)
        self.grid_layout.addWidget(QLabel('face color:'), 4, 0)
        self.grid_layout.addWidget(self.faceColorEdit, 4, 1)
        self.grid_layout.addWidget(QLabel('edge color:'), 5, 0)
        self.grid_layout.addWidget(self.edgeColorEdit, 5, 1)
        self.grid_layout.addWidget(QLabel('display text:'), 6, 0)
        self.grid_layout.addWidget(self.textDispCheckBox, 6, 1)
        self.grid_layout.setRowStretch(7, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
Example #26
0
class PSContainer(QWidget):
    """PSContainer."""
    def __init__(self, widget, parent=None):
        super().__init__(parent)
        self._widget = widget
        self.name = widget.devname
        self.bbbname = widget.bbbname
        self.udcname = widget.udcname

        self.dclinks = list()
        self.dclinks_type = ''
        self.dclink_widgets = list()
        self.dclinksbbbname = set()
        self.dclinksudcname = set()
        dclinks = PSSearch.conv_psname_2_dclink(self.name)
        if dclinks:
            self.dclinks = dclinks
            self.dclinks_type = PSSearch.conv_psname_2_psmodel(dclinks[0])
            if self.dclinks_type != 'REGATRON_DCLink':
                for dc in dclinks:
                    self.dclinksbbbname.add(PSSearch.conv_psname_2_bbbname(dc))
                    self.dclinksudcname.add(PSSearch.conv_psname_2_udc(dc))
            self.all_props = get_prop2label(PVName(dclinks[0]))

        self.visible_props = sort_propties(
            ['detail', 'state', 'intlk', 'setpoint', 'monitor'])
        self._setup_ui()
        self._create_actions()
        self._enable_actions()
        self.setStyleSheet("""
            #HideButton {
                min-width: 10px;
                max-width: 10px;
            }
            #DCLinkContainer {
                background-color: lightgrey;
            }
        """)

    def _setup_ui(self):
        """Setup widget UI."""
        self._layout = QGridLayout()
        self._layout.setSpacing(10)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._layout)

        self._dclink_container = QWidget(self)
        self._dclink_container.setObjectName('DCLinkContainer')
        self._dclink_container.setLayout(QVBoxLayout())
        self._dclink_is_filled = False
        if self.dclinks:
            self._hide = QPushButton(qta.icon('mdi.plus'), '', self)
        else:
            self._hide = QPushButton('', self)
            self._hide.setEnabled(False)
        self._hide.setObjectName('HideButton')
        self._hide.setSizePolicy(QSzPlcy.Maximum, QSzPlcy.Maximum)
        self._hide.setFlat(True)

        self._layout.addWidget(self._hide, 0, 0, Qt.AlignCenter)
        self._layout.addWidget(self._widget, 0, 1)
        self._layout.addWidget(self._dclink_container, 1, 1)

        # Configure
        self._dclink_container.setHidden(True)
        self._hide.clicked.connect(self._toggle_dclink)

    def _toggle_dclink(self):
        if self._dclink_container.isHidden():
            if not self._dclink_is_filled:
                self._fill_dclink_container()
                self._enable_actions()
            self._hide.setIcon(qta.icon('mdi.minus'))
            self._dclink_container.setHidden(False)
        else:
            self._hide.setIcon(qta.icon('mdi.plus'))
            self._dclink_container.setHidden(True)

    def _fill_dclink_container(self):
        self._dclink_is_filled = True
        self._dclink_container.layout().addWidget(
            SummaryHeader(self.dclinks[0], self.visible_props, self))
        for dclink_name in self.dclinks:
            w = SummaryWidget(dclink_name, self.visible_props, self)
            if self.dclinks_type == 'REGATRON_DCLink':
                connect_newprocess(w.detail_bt, [
                    'sirius-hla-as-ps-regatron-individual', '-dev', dclink_name
                ],
                                   parent=self,
                                   is_pydm=True)
            else:
                connect_window(w.detail_bt,
                               PSDetailWindow,
                               self,
                               psname=dclink_name)
            self._dclink_container.layout().addWidget(w)
            self.dclink_widgets.append(w)

    def update_visible_props(self, new_value):
        self.visible_props = sort_propties(new_value)
        self._enable_actions()

    # Action methods
    def _create_actions(self):
        self._turn_on_action = QAction('Turn DCLinks On', self)
        self._turn_on_action.triggered.connect(
            lambda: self._set_dclink_pwrstate(True))
        self._turn_on_action.setEnabled(False)
        self._turn_off_action = QAction('Turn DCLinks Off', self)
        self._turn_off_action.triggered.connect(
            lambda: self._set_dclink_pwrstate(False))
        self._turn_off_action.setEnabled(False)
        self._open_loop_action = QAction('Open DCLinks Control Loop', self)
        self._open_loop_action.triggered.connect(
            lambda: self._set_dclink_control_loop(False))
        self._open_loop_action.setEnabled(False)
        self._close_loop_action = QAction('Close DCLinks Control Loop', self)
        self._close_loop_action.triggered.connect(
            lambda: self._set_dclink_control_loop(True))
        self._close_loop_action.setEnabled(False)
        self._set_setpoint_action = QAction('Set DCLinks Voltage', self)
        self._set_setpoint_action.triggered.connect(self._set_setpoint)
        self._set_setpoint_action.setEnabled(False)
        self._reset_intlk_action = QAction('Reset DCLinks Interlocks', self)
        self._reset_intlk_action.triggered.connect(self._reset_intlk)
        self._reset_intlk_action.setEnabled(False)

    def _enable_actions(self):
        if 'state' in self.visible_props and \
                not self._turn_on_action.isEnabled():
            self._turn_on_action.setEnabled(True)
            self._turn_off_action.setEnabled(True)
        if 'ctrlloop' in self.visible_props and \
                not self._open_loop_action.isEnabled():
            self._open_loop_action.setEnabled(True)
            self._close_loop_action.setEnabled(True)
        if 'setpoint' in self.visible_props and \
                not self._set_setpoint_action.isEnabled():
            self._set_setpoint_action.setEnabled(True)
        if 'reset' in self.visible_props and \
                not self._reset_intlk_action.isEnabled():
            self._reset_intlk_action.setEnabled(True)

    def _set_dclink_pwrstate(self, value):
        for dclink in self.dclink_widgets:
            if value:
                dclink.turn_on()
            else:
                dclink.turn_off()

    def _set_dclink_control_loop(self, value):
        for dclink in self.dclink_widgets:
            btn = dclink.ctrlloop_bt
            if value:
                if btn._bit_val:
                    btn.send_value()
            else:
                if not btn._bit_val:
                    btn.send_value()

    def _set_setpoint(self):
        """Set current setpoint for every visible widget."""
        dlg = QInputDialog(self)
        dlg.setLocale(QLocale(QLocale.English))
        new_value, ok = dlg.getDouble(self, "New setpoint", "Value")
        if ok:
            for dclink in self.dclink_widgets:
                sp = dclink.setpoint.sp_lineedit
                sp.setText(str(new_value))
                try:
                    sp.send_value()
                except TypeError:
                    pass

    def _reset_intlk(self):
        for dclink in self.dclink_widgets:
            dclink.reset()

    # Overloaded method
    def contextMenuEvent(self, event):
        """Overload to create a custom context menu."""
        widget = self.childAt(event.pos())
        parent = widget.parent()
        grand_parent = parent.parent()
        if widget.objectName() == 'DCLinkContainer' or \
                parent.objectName() == 'DCLinkContainer' or \
                grand_parent.objectName() == 'DCLinkContainer':
            menu = QMenu(self)
            menu.addAction(self._turn_on_action)
            menu.addAction(self._turn_off_action)
            menu.addSeparator()
            menu.addAction(self._close_loop_action)
            menu.addAction(self._open_loop_action)
            menu.addSeparator()
            menu.addAction(self._set_setpoint_action)
            menu.addSeparator()
            menu.addAction(self._reset_intlk_action)
            menu.addSeparator()
            action = menu.addAction('Show Connections...')
            action.triggered.connect(self.show_connections)
            menu.popup(event.globalPos())
        else:
            super().contextMenuEvent(event)

    def show_connections(self, checked):
        """."""
        _ = checked
        c = ConnectionInspector(self)
        c.show()
Example #27
0
    def setup_page(self):
        self.ICON = ima.icon('eyedropper')

        names = self.get_option("names")
        try:
            names.pop(names.index(u'Custom'))
        except ValueError:
            pass
        custom_names = self.get_option("custom_names", [])

        # Interface options
        theme_group = QGroupBox(_("Main interface"))

        # Interface Widgets
        ui_themes = ['Automatic', 'Light', 'Dark']
        ui_theme_choices = list(zip(ui_themes, [ui_theme.lower()
                                                for ui_theme in ui_themes]))
        ui_theme_combo = self.create_combobox(_('Interface theme'),
                                              ui_theme_choices,
                                              'ui_theme',
                                              restart=True)

        styles = [str(txt) for txt in list(QStyleFactory.keys())]
        # Don't offer users the possibility to change to a different
        # style in Gtk-based desktops
        # Fixes Issue 2036
        if is_gtk_desktop() and ('GTK+' in styles):
            styles = ['GTK+']
        choices = list(zip(styles, [style.lower() for style in styles]))
        style_combo = self.create_combobox(_('Qt windows style'), choices,
                                           'windows_style',
                                           default=self.main.default_style)
        self.style_combobox = style_combo.combobox

        themes = ['Spyder 2', 'Spyder 3']
        icon_choices = list(zip(themes, [theme.lower() for theme in themes]))
        icons_combo = self.create_combobox(_('Icon theme'), icon_choices,
                                           'icon_theme', restart=True)

        theme_comboboxes_layout = QGridLayout()
        theme_comboboxes_layout.addWidget(ui_theme_combo.label, 0, 0)
        theme_comboboxes_layout.addWidget(ui_theme_combo.combobox, 0, 1)
        theme_comboboxes_layout.addWidget(style_combo.label, 1, 0)
        theme_comboboxes_layout.addWidget(self.style_combobox, 1, 1)
        theme_comboboxes_layout.addWidget(icons_combo.label, 2, 0)
        theme_comboboxes_layout.addWidget(icons_combo.combobox, 2, 1)

        theme_layout = QVBoxLayout()
        theme_layout.addLayout(theme_comboboxes_layout)
        theme_group.setLayout(theme_layout)

        # Syntax coloring options
        syntax_group = QGroupBox(_("Syntax highlighting theme"))

        # Syntax Widgets
        edit_button = QPushButton(_("Edit selected scheme"))
        create_button = QPushButton(_("Create new scheme"))
        self.delete_button = QPushButton(_("Delete scheme"))
        self.reset_button = QPushButton(_("Reset to defaults"))

        self.preview_editor = CodeEditor(self)
        self.stacked_widget = QStackedWidget(self)
        self.scheme_editor_dialog = SchemeEditor(parent=self,
                                                 stack=self.stacked_widget)

        self.scheme_choices_dict = {}
        schemes_combobox_widget = self.create_combobox('', [('', '')],
                                                       'selected')
        self.schemes_combobox = schemes_combobox_widget.combobox

        # Syntax layout
        syntax_layout = QGridLayout(syntax_group)
        btns = [self.schemes_combobox, edit_button, self.reset_button,
                create_button, self.delete_button]
        for i, btn in enumerate(btns):
            syntax_layout.addWidget(btn, i, 1)
        syntax_layout.setColumnStretch(0, 1)
        syntax_layout.setColumnStretch(1, 2)
        syntax_layout.setColumnStretch(2, 1)
        syntax_layout.setContentsMargins(0, 12, 0, 12)

        # Fonts options
        fonts_group = QGroupBox(_("Fonts"))

        # Fonts widgets
        plain_text_font = self.create_fontgroup(
            option='font',
            title=_("Plain text"),
            fontfilters=QFontComboBox.MonospacedFonts,
            without_group=True)

        rich_text_font = self.create_fontgroup(
            option='rich_font',
            title=_("Rich text"),
            without_group=True)

        # Fonts layouts
        fonts_layout = QGridLayout()
        fonts_layout.addWidget(plain_text_font.fontlabel, 0, 0)
        fonts_layout.addWidget(plain_text_font.fontbox, 0, 1)
        fonts_layout.addWidget(plain_text_font.sizelabel, 0, 2)
        fonts_layout.addWidget(plain_text_font.sizebox, 0, 3)
        fonts_layout.addWidget(rich_text_font.fontlabel, 1, 0)
        fonts_layout.addWidget(rich_text_font.fontbox, 1, 1)
        fonts_layout.addWidget(rich_text_font.sizelabel, 1, 2)
        fonts_layout.addWidget(rich_text_font.sizebox, 1, 3)
        fonts_group.setLayout(fonts_layout)

        # Left options layout
        options_layout = QVBoxLayout()
        options_layout.addWidget(theme_group)
        options_layout.addWidget(syntax_group)
        options_layout.addWidget(fonts_group)

        # Right preview layout
        preview_group = QGroupBox(_("Preview"))
        preview_layout = QVBoxLayout()
        preview_layout.addWidget(self.preview_editor)
        preview_group.setLayout(preview_layout)

        # Combined layout
        combined_layout = QGridLayout()
        combined_layout.setRowStretch(0, 1)
        combined_layout.setColumnStretch(1, 100)
        combined_layout.addLayout(options_layout, 0, 0)
        combined_layout.addWidget(preview_group, 0, 1)
        self.setLayout(combined_layout)

        # Signals and slots
        create_button.clicked.connect(self.create_new_scheme)
        edit_button.clicked.connect(self.edit_scheme)
        self.reset_button.clicked.connect(self.reset_to_default)
        self.delete_button.clicked.connect(self.delete_scheme)
        self.schemes_combobox.currentIndexChanged.connect(self.update_preview)
        self.schemes_combobox.currentIndexChanged.connect(self.update_buttons)

        # Setup
        for name in names:
            self.scheme_editor_dialog.add_color_scheme_stack(name)

        for name in custom_names:
            self.scheme_editor_dialog.add_color_scheme_stack(name, custom=True)

        self.update_combobox()
        self.update_preview()
        self.update_qt_style_combobox()
Example #28
0
    def setup_page(self):
        self.ICON = ima.icon('eyedropper')

        names = self.get_option("names")
        try:
            names.pop(names.index(u'Custom'))
        except ValueError:
            pass
        custom_names = self.get_option("custom_names", [])

        # Interface options
        theme_group = QGroupBox(_("Main interface"))

        # Interface Widgets
        ui_themes = ['Automatic', 'Light', 'Dark']
        ui_theme_choices = list(
            zip(ui_themes, [ui_theme.lower() for ui_theme in ui_themes]))
        ui_theme_combo = self.create_combobox(_('Interface theme'),
                                              ui_theme_choices,
                                              'ui_theme',
                                              restart=True)

        styles = [str(txt) for txt in list(QStyleFactory.keys())]
        # Don't offer users the possibility to change to a different
        # style in Gtk-based desktops
        # See spyder-ide/spyder#2036.
        if is_gtk_desktop() and ('GTK+' in styles):
            styles = ['GTK+']
        choices = list(zip(styles, [style.lower() for style in styles]))
        style_combo = self.create_combobox(_('Qt windows style'),
                                           choices,
                                           'windows_style',
                                           default=self.main.default_style)
        self.style_combobox = style_combo.combobox

        themes = ['Spyder 2', 'Spyder 3']
        icon_choices = list(zip(themes, [theme.lower() for theme in themes]))
        icons_combo = self.create_combobox(_('Icon theme'),
                                           icon_choices,
                                           'icon_theme',
                                           restart=True)

        theme_comboboxes_layout = QGridLayout()
        theme_comboboxes_layout.addWidget(ui_theme_combo.label, 0, 0)
        theme_comboboxes_layout.addWidget(ui_theme_combo.combobox, 0, 1)
        theme_comboboxes_layout.addWidget(style_combo.label, 1, 0)
        theme_comboboxes_layout.addWidget(self.style_combobox, 1, 1)
        theme_comboboxes_layout.addWidget(icons_combo.label, 2, 0)
        theme_comboboxes_layout.addWidget(icons_combo.combobox, 2, 1)

        theme_layout = QVBoxLayout()
        theme_layout.addLayout(theme_comboboxes_layout)
        theme_group.setLayout(theme_layout)

        # Syntax coloring options
        syntax_group = QGroupBox(_("Syntax highlighting theme"))

        # Syntax Widgets
        edit_button = QPushButton(_("Edit selected scheme"))
        create_button = QPushButton(_("Create new scheme"))
        self.delete_button = QPushButton(_("Delete scheme"))
        self.reset_button = QPushButton(_("Reset to defaults"))

        self.preview_editor = CodeEditor(self)
        self.stacked_widget = QStackedWidget(self)
        self.scheme_editor_dialog = SchemeEditor(parent=self,
                                                 stack=self.stacked_widget)

        self.scheme_choices_dict = {}
        schemes_combobox_widget = self.create_combobox('', [('', '')],
                                                       'selected')
        self.schemes_combobox = schemes_combobox_widget.combobox

        # Syntax layout
        syntax_layout = QGridLayout(syntax_group)
        btns = [
            self.schemes_combobox, edit_button, self.reset_button,
            create_button, self.delete_button
        ]
        for i, btn in enumerate(btns):
            syntax_layout.addWidget(btn, i, 1)
        syntax_layout.setColumnStretch(0, 1)
        syntax_layout.setColumnStretch(1, 2)
        syntax_layout.setColumnStretch(2, 1)
        syntax_layout.setContentsMargins(0, 12, 0, 12)

        # Fonts options
        fonts_group = QGroupBox(_("Fonts"))

        # Fonts widgets
        plain_text_font = self.create_fontgroup(
            option='font',
            title=_("Plain text"),
            fontfilters=QFontComboBox.MonospacedFonts,
            without_group=True)

        rich_text_font = self.create_fontgroup(option='rich_font',
                                               title=_("Rich text"),
                                               without_group=True)

        # Fonts layouts
        fonts_layout = QGridLayout(fonts_group)
        fonts_layout.addWidget(plain_text_font.fontlabel, 0, 0)
        fonts_layout.addWidget(plain_text_font.fontbox, 0, 1)
        fonts_layout.addWidget(plain_text_font.sizelabel, 0, 2)
        fonts_layout.addWidget(plain_text_font.sizebox, 0, 3)
        fonts_layout.addWidget(rich_text_font.fontlabel, 1, 0)
        fonts_layout.addWidget(rich_text_font.fontbox, 1, 1)
        fonts_layout.addWidget(rich_text_font.sizelabel, 1, 2)
        fonts_layout.addWidget(rich_text_font.sizebox, 1, 3)
        fonts_layout.setRowStretch(fonts_layout.rowCount(), 1)

        # Left options layout
        options_layout = QVBoxLayout()
        options_layout.addWidget(theme_group)
        options_layout.addWidget(syntax_group)
        options_layout.addWidget(fonts_group)

        # Right preview layout
        preview_group = QGroupBox(_("Preview"))
        preview_layout = QVBoxLayout()
        preview_layout.addWidget(self.preview_editor)
        preview_group.setLayout(preview_layout)

        # Combined layout
        combined_layout = QGridLayout()
        combined_layout.setRowStretch(0, 1)
        combined_layout.setColumnStretch(1, 100)
        combined_layout.addLayout(options_layout, 0, 0)
        combined_layout.addWidget(preview_group, 0, 1)
        self.setLayout(combined_layout)

        # Signals and slots
        create_button.clicked.connect(self.create_new_scheme)
        edit_button.clicked.connect(self.edit_scheme)
        self.reset_button.clicked.connect(self.reset_to_default)
        self.delete_button.clicked.connect(self.delete_scheme)
        self.schemes_combobox.currentIndexChanged.connect(self.update_preview)
        self.schemes_combobox.currentIndexChanged.connect(self.update_buttons)

        # Setup
        for name in names:
            self.scheme_editor_dialog.add_color_scheme_stack(name)

        for name in custom_names:
            self.scheme_editor_dialog.add_color_scheme_stack(name, custom=True)

        self.update_combobox()
        self.update_preview()
        self.update_qt_style_combobox()
Example #29
0
    def __init__(self, parent, enable_replace=False):
        QWidget.__init__(self, parent)
        self.enable_replace = enable_replace
        self.editor = None
        self.is_code_editor = None
        
        glayout = QGridLayout()
        glayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(glayout)
        
        self.close_button = create_toolbutton(self, triggered=self.hide,
                                      icon=ima.icon('DialogCloseButton'))
        glayout.addWidget(self.close_button, 0, 0)
        
        # Find layout
        self.search_text = PatternComboBox(self, tip=_("Search string"),
                                           adjust_to_minimum=False)
        self.search_text.valid.connect(
                     lambda state:
                     self.find(changed=False, forward=True, rehighlight=False))
        self.search_text.lineEdit().textEdited.connect(
                                                     self.text_has_been_edited)
        
        self.previous_button = create_toolbutton(self,
                                             triggered=self.find_previous,
                                             icon=ima.icon('ArrowUp'))
        self.next_button = create_toolbutton(self,
                                             triggered=self.find_next,
                                             icon=ima.icon('ArrowDown'))
        self.next_button.clicked.connect(self.update_search_combo)
        self.previous_button.clicked.connect(self.update_search_combo)

        self.re_button = create_toolbutton(self, icon=ima.icon('advanced'),
                                           tip=_("Regular expression"))
        self.re_button.setCheckable(True)
        self.re_button.toggled.connect(lambda state: self.find())
        
        self.case_button = create_toolbutton(self,
                                             icon=get_icon("upper_lower.png"),
                                             tip=_("Case Sensitive"))
        self.case_button.setCheckable(True)
        self.case_button.toggled.connect(lambda state: self.find())
                     
        self.words_button = create_toolbutton(self,
                                              icon=get_icon("whole_words.png"),
                                              tip=_("Whole words"))
        self.words_button.setCheckable(True)
        self.words_button.toggled.connect(lambda state: self.find())
                     
        self.highlight_button = create_toolbutton(self,
                                              icon=get_icon("highlight.png"),
                                              tip=_("Highlight matches"))
        self.highlight_button.setCheckable(True)
        self.highlight_button.toggled.connect(self.toggle_highlighting)

        hlayout = QHBoxLayout()
        self.widgets = [self.close_button, self.search_text,
                        self.previous_button, self.next_button,
                        self.re_button, self.case_button, self.words_button,
                        self.highlight_button]
        for widget in self.widgets[1:]:
            hlayout.addWidget(widget)
        glayout.addLayout(hlayout, 0, 1)

        # Replace layout
        replace_with = QLabel(_("Replace with:"))
        self.replace_text = PatternComboBox(self, adjust_to_minimum=False,
                                            tip=_('Replace string'))
        
        self.replace_button = create_toolbutton(self,
                                     text=_('Replace/find'),
                                     icon=ima.icon('DialogApplyButton'),
                                     triggered=self.replace_find,
                                     text_beside_icon=True)
        self.replace_button.clicked.connect(self.update_replace_combo)
        self.replace_button.clicked.connect(self.update_search_combo)
        
        self.all_check = QCheckBox(_("Replace all"))
        
        self.replace_layout = QHBoxLayout()
        widgets = [replace_with, self.replace_text, self.replace_button,
                   self.all_check]
        for widget in widgets:
            self.replace_layout.addWidget(widget)
        glayout.addLayout(self.replace_layout, 1, 1)
        self.widgets.extend(widgets)
        self.replace_widgets = widgets
        self.hide_replace()
        
        self.search_text.setTabOrder(self.search_text, self.replace_text)
        
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        
        self.shortcuts = self.create_shortcuts(parent)
        
        self.highlight_timer = QTimer(self)
        self.highlight_timer.setSingleShot(True)
        self.highlight_timer.setInterval(1000)
        self.highlight_timer.timeout.connect(self.highlight_matches)
Example #30
0
class ThumbnailScrollBar(QFrame):
    """
    A widget that manages the display of the FigureThumbnails that are
    created when a figure is sent to the IPython console by the kernel and
    that controls what is displayed in the FigureViewer.
    """
    redirect_stdio = Signal(bool)
    _min_scrollbar_width = 100

    def __init__(self, figure_viewer, parent=None, background_color=None):
        super(ThumbnailScrollBar, self).__init__(parent)
        self._thumbnails = []
        self.background_color = background_color
        self.current_thumbnail = None
        self.set_figureviewer(figure_viewer)
        self.setup_gui()

    def setup_gui(self):
        """Setup the main layout of the widget."""
        scrollarea = self.setup_scrollarea()
        up_btn, down_btn = self.setup_arrow_buttons()

        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(up_btn)
        layout.addWidget(scrollarea)
        layout.addWidget(down_btn)

    def setup_scrollarea(self):
        """Setup the scrollarea that will contain the FigureThumbnails."""
        self.view = QWidget()

        self.scene = QGridLayout(self.view)
        self.scene.setContentsMargins(0, 0, 0, 0)

        self.scrollarea = QScrollArea()
        self.scrollarea.setWidget(self.view)
        self.scrollarea.setWidgetResizable(True)
        self.scrollarea.setFrameStyle(0)
        self.scrollarea.setViewportMargins(2, 2, 2, 2)
        self.scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scrollarea.setMinimumWidth(self._min_scrollbar_width)

        # Set the vertical scrollbar explicitely.
        # This is required to avoid a "RuntimeError: no access to protected
        # functions or signals for objects not created from Python" in Linux.
        self.scrollarea.setVerticalScrollBar(QScrollBar())

        # Install an event filter on the scrollbar.
        self.scrollarea.installEventFilter(self)

        return self.scrollarea

    def setup_arrow_buttons(self):
        """
        Setup the up and down arrow buttons that are placed at the top and
        bottom of the scrollarea.
        """
        # Get the size hint height of the horizontal scrollbar.
        height = self.scrollarea.horizontalScrollBar().sizeHint().height()

        # Setup the up and down arrow button.
        up_btn = up_btn = QPushButton(icon=ima.icon('last_edit_location'))
        up_btn.setFlat(True)
        up_btn.setFixedHeight(height)
        up_btn.clicked.connect(self.go_up)

        down_btn = QPushButton(icon=ima.icon('folding.arrow_down_on'))
        down_btn.setFlat(True)
        down_btn.setFixedHeight(height)
        down_btn.clicked.connect(self.go_down)

        return up_btn, down_btn

    def set_figureviewer(self, figure_viewer):
        """Set the bamespace for the FigureViewer."""
        self.figure_viewer = figure_viewer

    def eventFilter(self, widget, event):
        """
        An event filter to trigger an update of the thumbnails size so that
        their width fit that of the scrollarea.
        """
        if event.type() == QEvent.Resize:
            self._update_thumbnail_size()
        return super(ThumbnailScrollBar, self).eventFilter(widget, event)

    # ---- Save Figure
    def save_all_figures_as(self):
        """Save all the figures to a file."""
        self.redirect_stdio.emit(False)
        dirname = getexistingdirectory(self,
                                       caption='Save all figures',
                                       basedir=getcwd_or_home())
        self.redirect_stdio.emit(True)
        if dirname:
            return self.save_all_figures_todir(dirname)

    def save_all_figures_todir(self, dirname):
        """Save all figure in dirname."""
        fignames = []
        for thumbnail in self._thumbnails:
            fig = thumbnail.canvas.fig
            fmt = thumbnail.canvas.fmt
            fext = {
                'image/png': '.png',
                'image/jpeg': '.jpg',
                'image/svg+xml': '.svg'
            }[fmt]

            figname = get_unique_figname(dirname, 'Figure', fext)
            save_figure_tofile(fig, fmt, figname)
            fignames.append(figname)
        return fignames

    def save_current_figure_as(self):
        """Save the currently selected figure."""
        if self.current_thumbnail is not None:
            self.save_figure_as(self.current_thumbnail.canvas.fig,
                                self.current_thumbnail.canvas.fmt)

    def save_figure_as(self, fig, fmt):
        """Save the figure to a file."""
        fext, ffilt = {
            'image/png': ('.png', 'PNG (*.png)'),
            'image/jpeg': ('.jpg', 'JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)'),
            'image/svg+xml': ('.svg', 'SVG (*.svg);;PNG (*.png)')
        }[fmt]

        figname = get_unique_figname(getcwd_or_home(), 'Figure', fext)

        self.redirect_stdio.emit(False)
        fname, fext = getsavefilename(parent=self.parent(),
                                      caption='Save Figure',
                                      basedir=figname,
                                      filters=ffilt,
                                      selectedfilter='',
                                      options=None)
        self.redirect_stdio.emit(True)

        if fname:
            save_figure_tofile(fig, fmt, fname)

    # ---- Thumbails Handlers
    def _calculate_figure_canvas_width(self, thumbnail):
        """
        Calculate the witdh the thumbnail's figure canvas need to have for the
        thumbnail to fit the scrollarea.
        """
        extra_padding = 10 if sys.platform == 'darwin' else 0
        figure_canvas_width = (self.scrollarea.width() - 2 * self.lineWidth() -
                               self.scrollarea.viewportMargins().left() -
                               self.scrollarea.viewportMargins().right() -
                               thumbnail.savefig_btn.width() -
                               thumbnail.layout().spacing() - extra_padding)
        if is_dark_interface():
            # This is required to take into account some hard-coded padding
            # and margin in qdarkstyle.
            figure_canvas_width = figure_canvas_width - 6
        return figure_canvas_width

    def _setup_thumbnail_size(self, thumbnail):
        """
        Scale the thumbnail's canvas size so that it fits the thumbnail
        scrollbar's width.
        """
        max_canvas_size = self._calculate_figure_canvas_width(thumbnail)
        thumbnail.scale_canvas_size(max_canvas_size)

    def _update_thumbnail_size(self):
        """
        Update the thumbnails size so that their width fit that of
        the scrollarea.
        """
        # NOTE: We hide temporarily the thumbnails to prevent a repaint of
        # each thumbnail as soon as their size is updated in the loop, which
        # causes some flickering of the thumbnail scrollbar resizing animation.
        # Once the size of all the thumbnails has been updated, we show them
        # back so that they are repainted all at once instead of one after the
        # other. This is just a trick to make the resizing animation of the
        # thumbnail scrollbar look smoother.
        self.view.hide()
        for thumbnail in self._thumbnails:
            self._setup_thumbnail_size(thumbnail)
        self.view.show()

    def add_thumbnail(self, fig, fmt):
        """
        Add a new thumbnail to that thumbnail scrollbar.
        """
        thumbnail = FigureThumbnail(parent=self,
                                    background_color=self.background_color)
        thumbnail.canvas.load_figure(fig, fmt)
        thumbnail.sig_canvas_clicked.connect(self.set_current_thumbnail)
        thumbnail.sig_remove_figure.connect(self.remove_thumbnail)
        thumbnail.sig_save_figure.connect(self.save_figure_as)
        self._thumbnails.append(thumbnail)

        self.scene.setRowStretch(self.scene.rowCount() - 1, 0)
        self.scene.addWidget(thumbnail, self.scene.rowCount() - 1, 0)
        self.scene.setRowStretch(self.scene.rowCount(), 100)
        self.set_current_thumbnail(thumbnail)

        thumbnail.show()
        self._setup_thumbnail_size(thumbnail)

    def remove_current_thumbnail(self):
        """Remove the currently selected thumbnail."""
        if self.current_thumbnail is not None:
            self.remove_thumbnail(self.current_thumbnail)

    def remove_all_thumbnails(self):
        """Remove all thumbnails."""
        for thumbnail in self._thumbnails:
            self.layout().removeWidget(thumbnail)
            thumbnail.sig_canvas_clicked.disconnect()
            thumbnail.sig_remove_figure.disconnect()
            thumbnail.sig_save_figure.disconnect()
        self._thumbnails = []
        self.current_thumbnail = None
        self.figure_viewer.figcanvas.clear_canvas()

    def remove_thumbnail(self, thumbnail):
        """Remove thumbnail."""
        if thumbnail in self._thumbnails:
            index = self._thumbnails.index(thumbnail)
            self._thumbnails.remove(thumbnail)
        self.layout().removeWidget(thumbnail)
        thumbnail.sig_canvas_clicked.disconnect()
        thumbnail.sig_remove_figure.disconnect()
        thumbnail.sig_save_figure.disconnect()

        # Select a new thumbnail if any :
        if thumbnail == self.current_thumbnail:
            if len(self._thumbnails) > 0:
                self.set_current_index(min(index, len(self._thumbnails) - 1))
            else:
                self.current_thumbnail = None
                self.figure_viewer.figcanvas.clear_canvas()

    def set_current_index(self, index):
        """Set the currently selected thumbnail by its index."""
        self.set_current_thumbnail(self._thumbnails[index])

    def get_current_index(self):
        """Return the index of the currently selected thumbnail."""
        try:
            return self._thumbnails.index(self.current_thumbnail)
        except ValueError:
            return -1

    def set_current_thumbnail(self, thumbnail):
        """Set the currently selected thumbnail."""
        self.current_thumbnail = thumbnail
        self.figure_viewer.load_figure(thumbnail.canvas.fig,
                                       thumbnail.canvas.fmt)
        for thumbnail in self._thumbnails:
            thumbnail.highlight_canvas(thumbnail == self.current_thumbnail)

    def go_previous_thumbnail(self):
        """Select the thumbnail previous to the currently selected one."""
        if self.current_thumbnail is not None:
            index = self._thumbnails.index(self.current_thumbnail) - 1
            index = index if index >= 0 else len(self._thumbnails) - 1
            self.set_current_index(index)
            self.scroll_to_item(index)

    def go_next_thumbnail(self):
        """Select thumbnail next to the currently selected one."""
        if self.current_thumbnail is not None:
            index = self._thumbnails.index(self.current_thumbnail) + 1
            index = 0 if index >= len(self._thumbnails) else index
            self.set_current_index(index)
            self.scroll_to_item(index)

    def scroll_to_item(self, index):
        """Scroll to the selected item of ThumbnailScrollBar."""
        spacing_between_items = self.scene.verticalSpacing()
        height_view = self.scrollarea.viewport().height()
        height_item = self.scene.itemAt(index).sizeHint().height()
        height_view_excluding_item = max(0, height_view - height_item)

        height_of_top_items = spacing_between_items
        for i in range(index):
            item = self.scene.itemAt(i)
            height_of_top_items += item.sizeHint().height()
            height_of_top_items += spacing_between_items

        pos_scroll = height_of_top_items - height_view_excluding_item // 2

        vsb = self.scrollarea.verticalScrollBar()
        vsb.setValue(pos_scroll)

    # ---- ScrollBar Handlers
    def go_up(self):
        """Scroll the scrollbar of the scrollarea up by a single step."""
        vsb = self.scrollarea.verticalScrollBar()
        vsb.setValue(int(vsb.value() - vsb.singleStep()))

    def go_down(self):
        """Scroll the scrollbar of the scrollarea down by a single step."""
        vsb = self.scrollarea.verticalScrollBar()
        vsb.setValue(int(vsb.value() + vsb.singleStep()))
Example #31
0
    def _setupControlsWidget(self):
        ld_growenbl = QLabel('Grow/Damp Enable', self)
        cb_growenbl = PyDMEnumComboBox(self, self.dev_pref + ':GDEN')

        ld_down = QLabel('Rec. Downsample ', self)
        sb_down = PyDMSpinbox(self,
                              self.dev_pref + ':' + self.TYPE + '_REC_DS')
        sb_down.showStepExponent = False

        ld_rawdata = QLabel('Raw Data', self)
        cb_rawdata = PyDMStateButton(self,
                                     self.dev_pref + ':' + self.TYPE + '_DUMP')

        ld_acqtime = QLabel('Acquisition Time', self)
        sb_acqtime = PyDMSpinbox(self,
                                 self.dev_pref + ':' + self.TYPE + '_ACQTIME')
        sb_acqtime.showStepExponent = False
        sb_acqtime.showUnits = True

        ld_holdoff = QLabel('Hold-Off Time', self)
        sb_holdoff = PyDMSpinbox(self,
                                 self.dev_pref + ':' + self.TYPE + '_HOLDTIME')
        sb_holdoff.showStepExponent = False
        sb_holdoff.showUnits = True

        ld_posttrg = QLabel('Post Trigger', self)
        sb_posttrg = PyDMSpinbox(self,
                                 self.dev_pref + ':' + self.TYPE + '_POSTTIME')
        sb_posttrg.showStepExponent = False
        sb_posttrg.showUnits = True
        fr_posttrg = SiriusFrame(
            self, self.dev_pref + ':' + self.TYPE + '_POSTREG_SUBWR')
        fr_posttrg.add_widget(sb_posttrg)

        ld_growtime = QLabel('Growth Time', self)
        sb_growtime = PyDMSpinbox(self,
                                  self.dev_pref + ':' + self.TYPE + '_GDTIME')
        sb_growtime.showStepExponent = False
        sb_growtime.showUnits = True
        fr_growtime = SiriusFrame(
            self, self.dev_pref + ':' + self.TYPE + '_GDREG_SUBWR')
        fr_growtime.add_widget(sb_growtime)

        ld_acqlen = QLabel('Acquisition Length', self)
        lb_acqlen = PyDMLabel(self,
                              self.dev_pref + ':' + self.TYPE + '_ACQ_TURNS')
        lb_acqlen.showUnits = True

        ld_psttrglen = QLabel('Post Trigger Length', self)
        lb_psttrglen = PyDMLabel(
            self, self.dev_pref + ':' + self.TYPE + '_POST_TURNS')
        lb_psttrglen.showUnits = True

        bt_modal = QPushButton('Modal Analysis', self)

        window = create_window_from_widget(_BbBModalAnalysis,
                                           title='SRAM Modal Analysis',
                                           icon=get_bbb_icon(),
                                           is_main=True)
        connect_window(bt_modal,
                       window,
                       self,
                       prefix=self._prefix,
                       device=self._device,
                       acq_type=self.TYPE)

        gbox_dtacq = QGroupBox('Data Acquisition', self)
        lay_dtacq = QGridLayout(gbox_dtacq)
        lay_dtacq.addWidget(ld_growenbl, 0, 0)
        lay_dtacq.addWidget(cb_growenbl, 0, 1)
        lay_dtacq.addWidget(ld_down, 1, 0)
        lay_dtacq.addWidget(sb_down, 1, 1)
        lay_dtacq.addWidget(ld_rawdata, 2, 0)
        lay_dtacq.addWidget(cb_rawdata, 2, 1)
        lay_dtacq.addWidget(ld_acqtime, 3, 0)
        lay_dtacq.addWidget(sb_acqtime, 3, 1)
        lay_dtacq.addWidget(ld_holdoff, 4, 0)
        lay_dtacq.addWidget(sb_holdoff, 4, 1)
        lay_dtacq.addWidget(ld_posttrg, 5, 0)
        lay_dtacq.addWidget(fr_posttrg, 5, 1)
        lay_dtacq.addWidget(ld_growtime, 6, 0)
        lay_dtacq.addWidget(fr_growtime, 6, 1)
        lay_dtacq.addWidget(ld_acqlen, 7, 0)
        lay_dtacq.addWidget(lb_acqlen, 7, 1)
        lay_dtacq.addWidget(ld_psttrglen, 8, 0)
        lay_dtacq.addWidget(lb_psttrglen, 8, 1)
        lay_dtacq.addWidget(bt_modal, 9, 0, 1, 2)

        ld_acqtyp = QLabel('<h4>Acq Type</h4>', self, alignment=Qt.AlignCenter)
        cb_acqtyp = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_POSTSEL')

        gbox_acqtyp = QGroupBox(self)
        lay_acqtyp = QVBoxLayout(gbox_acqtyp)
        lay_acqtyp.addWidget(ld_acqtyp)
        lay_acqtyp.addWidget(cb_acqtyp)

        ld_trgexten = QLabel('Internal/External', self)
        cb_trgexten = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_HWTEN')

        ld_trginsel = QLabel('Selection', self)
        cb_trginsel = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_TRIG_IN_SEL')

        ld_trgarm = QLabel('Arm', self)
        cb_trgarm = PyDMStateButton(self,
                                    self.dev_pref + ':' + self.TYPE + '_ARM')
        lb_armmon = SiriusLedState(
            self, self.dev_pref + ':' + self.TYPE + '_ARM_MON')

        ld_trgbrarm = QLabel('Auto re-arm', self)
        cb_trgbrarm = PyDMStateButton(
            self, self.dev_pref + ':' + self.TYPE + '_BR_ARM')

        ld_rst = QLabel('Trigger 1/2 Cap.:', self)
        lb_rst1 = PyDMLabel(self,
                            self.dev_pref + ':' + self.TYPE + '_CAP_TRIG1')
        lb_rst2 = PyDMLabel(self,
                            self.dev_pref + ':' + self.TYPE + '_CAP_TRIG2')

        gbox_trig = QGroupBox('Trigger', self)
        lay_trig = QGridLayout(gbox_trig)
        lay_trig.setAlignment(Qt.AlignTop)
        lay_trig.addWidget(ld_trgexten, 0, 0)
        lay_trig.addWidget(cb_trgexten, 0, 1, 1, 2)
        lay_trig.addWidget(ld_trginsel, 1, 0)
        lay_trig.addWidget(cb_trginsel, 1, 1, 1, 2)
        lay_trig.addWidget(ld_trgarm, 2, 0)
        lay_trig.addWidget(cb_trgarm, 2, 1)
        lay_trig.addWidget(lb_armmon, 2, 2)
        lay_trig.addWidget(ld_trgbrarm, 3, 0)
        lay_trig.addWidget(cb_trgbrarm, 3, 1)
        lay_trig.addWidget(ld_rst, 4, 0)
        lay_trig.addWidget(lb_rst1, 4, 1)
        lay_trig.addWidget(lb_rst2, 4, 2)
        lay_trig.setRowStretch(5, 2)

        pixmap = QPixmap(
            _os.path.join(_os.path.abspath(_os.path.dirname(__file__)),
                          'grow_damp.png'))
        img_wid = QLabel(self)
        img_wid.setPixmap(pixmap)
        img_wid.setScaledContents(True)

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(gbox_acqtyp, 0, 0)
        lay.addWidget(gbox_dtacq, 1, 0)
        lay.addWidget(gbox_trig, 2, 0)
        lay.addWidget(img_wid, 4, 0)
        lay.setRowStretch(3, 5)
        lay.setRowStretch(5, 5)

        wid.setStyleSheet("SiriusFrame{max-height: 1.8em;}")

        return wid
Example #32
0
class UiLinelistsWindow(object):

    # this code was taken as-is from the Designer.
    # Cleaning it up sounds like a lower priority
    # task for now.
    def setupUi(self, MainWindow, title):
        MainWindow.setWindowTitle(title)
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(600, 850)
        MainWindow.setMinimumSize(QSize(300, 350))
        self.centralWidget = QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.gridLayout = QGridLayout(self.centralWidget)
        self.gridLayout.setContentsMargins(11, 11, 11, 11)
        self.gridLayout.setSpacing(6)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout_5 = QHBoxLayout()
        self.horizontalLayout_5.setContentsMargins(11, 11, 11, 11)
        self.horizontalLayout_5.setSpacing(6)
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.lines_selected_label = QLabel(self.centralWidget)
        self.lines_selected_label.setObjectName("lines_selected_label")
        self.horizontalLayout_5.addWidget(self.lines_selected_label)
        self.label = QLabel(self.centralWidget)
        self.label.setObjectName("label")
        self.horizontalLayout_5.addWidget(self.label)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.draw_button = QPushButton(self.centralWidget)
        self.draw_button.setObjectName("draw_button")
        self.horizontalLayout_5.addWidget(self.draw_button)
        self.erase_button = QPushButton(self.centralWidget)
        self.erase_button.setObjectName("erase_button")
        self.horizontalLayout_5.addWidget(self.erase_button)
        self.dismiss_button = QPushButton(self.centralWidget)
        self.dismiss_button.setObjectName("dismiss_button")
        self.horizontalLayout_5.addWidget(self.dismiss_button)
        self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1)
        self.verticalLayout_11 = QVBoxLayout()
        self.verticalLayout_11.setContentsMargins(11, 11, 11, 11)
        self.verticalLayout_11.setSpacing(6)
        self.verticalLayout_11.setObjectName("verticalLayout_11")
        self.tabWidget = QTabWidget(self.centralWidget)
        self.tabWidget.setObjectName("tabWidget")
        self.tabWidget.setTabsClosable(True)
        self.verticalLayout_11.addWidget(self.tabWidget)
        self.gridLayout.addLayout(self.verticalLayout_11, 0, 0, 1, 1)
        self.horizontalLayout_7 = QHBoxLayout()
        self.horizontalLayout_7.setContentsMargins(11, 11, 11, 11)
        self.horizontalLayout_7.setSpacing(6)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                 QSizePolicy.Minimum)
        self.horizontalLayout_7.addItem(spacerItem)
        self.horizontalLayout_7.setObjectName("horizontalLayout_7")
        self.gridLayout.addLayout(self.horizontalLayout_7, 2, 0, 2, 1)
        MainWindow.setCentralWidget(self.centralWidget)

        # self.menuBar = QMenuBar(MainWindow)
        # self.menuBar.setGeometry(QRect(0, 0, 767, 22))
        # self.menuBar.setObjectName("menuBar")
        #
        # self.menuFile = QMenu(self.menuBar)
        # self.menuFile.setObjectName("menuFile")
        #
        # MainWindow.setMenuBar(self.menuBar)

        self.mainToolBar = QToolBar(MainWindow)
        self.mainToolBar.setMovable(False)
        self.mainToolBar.setFloatable(False)
        self.mainToolBar.setObjectName("mainToolBar")
        MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar)

        # self.statusBar = QStatusBar(MainWindow)
        # self.statusBar.setObjectName("statusBar")
        # MainWindow.setStatusBar(self.statusBar)

        self.actionOpen = QAction(MainWindow)
        icon = QIcon(os.path.join(ICON_PATH, "Open Folder-48.png"))
        self.actionOpen.setIcon(icon)
        self.actionOpen.setObjectName("actionOpen")

        self.actionExport = QAction(MainWindow)
        icon = QIcon(os.path.join(ICON_PATH, "Export-48.png"))
        self.actionExport.setIcon(icon)
        self.actionExport.setObjectName("actionExport")

        self.line_list_selector = QComboBox()
        self.line_list_selector.setToolTip(
            "Select line list from internal library")

        self.actionExit = QAction(MainWindow)
        self.actionExit.setObjectName("actionExit")
        self.actionRemove = QAction(MainWindow)
        self.actionRemove.setObjectName("actionRemove")
        self.actionChange_Color = QAction(MainWindow)
        self.actionChange_Color.setObjectName("actionChange_Color")
        # self.menuFile.addAction(self.actionOpen)
        # self.menuFile.addSeparator()
        # self.menuFile.addAction(self.actionExit)
        # self.menuBar.addAction(self.menuFile.menuAction())
        self.mainToolBar.addAction(self.actionOpen)
        self.mainToolBar.addAction(self.actionExport)
        self.mainToolBar.addSeparator()
        self.mainToolBar.addWidget(self.line_list_selector)
        self.retranslateUi(MainWindow)
        QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QCoreApplication.translate
        self.lines_selected_label.setText(_translate("MainWindow", "0"))
        self.lines_selected_label.setToolTip(
            "Total number of lines selected in all sets.")
        self.label.setText(_translate("MainWindow", "lines selected"))
        self.label.setToolTip("Total number of lines selected in all sets.")
        self.draw_button.setText(_translate("MainWindow", "Draw"))
        self.draw_button.setToolTip(
            "Plot markers for all selected lines in all sets.")
        self.erase_button.setText(_translate("MainWindow", "Erase"))
        self.erase_button.setToolTip("Erase all markers")
        self.dismiss_button.setText(_translate("MainWindow", "Dismiss"))
        self.dismiss_button.setToolTip("Dismiss this window")
        # self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.actionOpen.setText(_translate("MainWindow", "Open"))
        self.actionExport.setText(
            _translate("MainWindow", "Export plotted lines"))
        self.actionExit.setText(_translate("MainWindow", "Exit"))
        self.actionRemove.setText(_translate("MainWindow", "Remove"))
        self.actionRemove.setToolTip(
            _translate("MainWindow", "Removes the selected layer"))
        self.actionChange_Color.setText(
            _translate("MainWindow", "Change Color"))
        self.actionChange_Color.setToolTip(
            _translate("MainWindow", "Change the line color selected layer"))
Example #33
0
    def _setupWaveformsWidget(self):
        gp_mean = WfmGraph(self)
        gp_mean.setPlotTitle('Mean')
        gp_mean.getAxis('bottom').setLabel('Bunch number')
        gp_mean.getAxis('left').setLabel('CNT')
        gp_mean.add_scatter_curve(
            ychannel=self.dev_pref + ':' + self.TYPE + '_MEAN',
            xchannel=self.dev_pref + ':' + self.TYPE + '_XSC',
            color=QColor('red'),
            lineStyle=Qt.SolidLine)

        gp_maxrms = WfmGraph(self)
        gp_maxrms.setPlotTitle('Max RMS Channel (filtered)')
        gp_maxrms.getAxis('bottom').setLabel('Time (ms)')
        gp_maxrms.getAxis('left').setLabel('CNT')
        gp_maxrms.add_scatter_curve(
            ychannel=self.dev_pref + ':' + self.TYPE + '_MAXRMS',
            xchannel=self.dev_pref + ':' + self.TYPE + '_TSC',
            color=QColor('blue'),
            lineStyle=Qt.SolidLine)

        gp_rms = WfmGraph(self)
        gp_rms.setPlotTitle('RMS')
        gp_rms.getAxis('bottom').setLabel('Bunch number')
        gp_rms.getAxis('left').setLabel('CNT')
        gp_rms.add_scatter_curve(
            ychannel=self.dev_pref + ':' + self.TYPE + '_RMS',
            xchannel=self.dev_pref + ':' + self.TYPE + '_XSC',
            color=QColor('green'),
            lineStyle=Qt.SolidLine)

        gp_avgspe = WfmGraph(self)
        gp_avgspe.setPlotTitle('Average spectrum')
        gp_avgspe.getAxis('bottom').setLabel('Frequency (kHz)')
        gp_avgspe.getAxis('left').setLabel('dB')
        gp_avgspe.add_scatter_curve(
            ychannel=self.dev_pref + ':' + self.TYPE + '_SPEC',
            xchannel=self.dev_pref + ':' + self.TYPE + '_FREQ',
            color=QColor('blue'),
            lineStyle=Qt.SolidLine)
        gp_avgspe.add_marker(
            name='Marker 1',
            xchannel=self.dev_pref + ':' + self.TYPE + '_PEAKFREQ1',
            ychannel=self.dev_pref + ':' + self.TYPE + '_PEAK1',
            color=QColor('red'),
            symbol='o')
        gp_avgspe.add_marker(
            name='Marker 2',
            xchannel=self.dev_pref + ':' + self.TYPE + '_PEAKFREQ2',
            ychannel=self.dev_pref + ':' + self.TYPE + '_PEAK2',
            color=QColor('magenta'),
            symbol='s')

        lay_graph = QGridLayout()
        lay_graph.setContentsMargins(9, 9, 9, 9)
        lay_graph.addWidget(gp_mean, 0, 0)
        lay_graph.addWidget(gp_maxrms, 0, 1)
        lay_graph.addWidget(gp_rms, 1, 0)
        lay_graph.addWidget(gp_avgspe, 1, 1)

        ld_acqenbl = QLabel('Acq. Enable', self)
        cb_acqenbl = PyDMStateButton(
            self, self.dev_pref + ':' + self.TYPE + '_ACQ_EN')

        ld_acqsing = QLabel('Acq. Mode', self)
        cb_acqsing = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_ACQ_SINGLE')

        ld_mean = QLabel('Mean', self, alignment=Qt.AlignCenter)
        lb_mean = PyDMLabel(self, self.dev_pref + ':' + self.TYPE + '_MEANVAL')

        ld_rms = QLabel('RMS', self, alignment=Qt.AlignCenter)
        lb_rms = PyDMLabel(self, self.dev_pref + ':' + self.TYPE + '_RMSVAL')

        ld_ampp2p = QLabel('Amp P-P', self, alignment=Qt.AlignCenter)
        lb_ampp2p = PyDMLabel(self,
                              self.dev_pref + ':' + self.TYPE + '_AMP_PP')

        ld_maxrms = QLabel('Max RMS', self, alignment=Qt.AlignCenter)
        lb_maxrms = PyDMLabel(self,
                              self.dev_pref + ':' + self.TYPE + '_MAXRMSVAL')

        ld_bunpatt = QLabel('Bunch\npattern', self)
        le_bunpatt = PyDMLineEdit(
            self, self.dev_pref + ':' + self.TYPE + '_ACQ_PATTERN')

        ld_avg = QLabel('Sample Avg', self)
        sb_avg = PyDMSpinbox(self, self.dev_pref + ':' + self.TYPE + '_SP_AVG')
        sb_avg.showStepExponent = False

        gbox_acqctrl = QGroupBox('Acquisition control', self)
        lay_acqctrl = QGridLayout(gbox_acqctrl)
        lay_acqctrl.addWidget(ld_acqenbl, 0, 0)
        lay_acqctrl.addWidget(cb_acqenbl, 0, 1)
        lay_acqctrl.addWidget(ld_acqsing, 1, 0)
        lay_acqctrl.addWidget(cb_acqsing, 1, 1)
        lay_acqctrl.addWidget(ld_avg, 2, 0)
        lay_acqctrl.addWidget(sb_avg, 2, 1)
        lay_acqctrl.addItem(QSpacerItem(15, 1, QSzPlcy.Fixed, QSzPlcy.Ignored),
                            0, 2, 3, 1)
        lay_acqctrl.addWidget(ld_mean, 0, 3)
        lay_acqctrl.addWidget(lb_mean, 0, 4)
        lay_acqctrl.addWidget(ld_ampp2p, 0, 5)
        lay_acqctrl.addWidget(lb_ampp2p, 0, 6)
        lay_acqctrl.addWidget(ld_rms, 1, 3)
        lay_acqctrl.addWidget(lb_rms, 1, 4)
        lay_acqctrl.addWidget(ld_maxrms, 1, 5)
        lay_acqctrl.addWidget(lb_maxrms, 1, 6)
        lay_acqctrl.addWidget(ld_bunpatt, 2, 3)
        lay_acqctrl.addWidget(le_bunpatt, 2, 4, 1, 3)

        # Markers
        ld_mk1 = QLabel('1', self, alignment=Qt.AlignCenter)
        ld_mk2 = QLabel('2', self, alignment=Qt.AlignCenter)
        ld_span = QLabel('Span (kHz)', self, alignment=Qt.AlignCenter)
        ld_mode = QLabel('Mode', self, alignment=Qt.AlignCenter)
        ld_val = QLabel('Value', self, alignment=Qt.AlignCenter)
        ld_pfrq = QLabel('Freq', self, alignment=Qt.AlignCenter)
        ld_tune = QLabel('Tune', self, alignment=Qt.AlignCenter)

        le_low1 = PyDMLineEdit(self,
                               self.dev_pref + ':' + self.TYPE + '_SP_LOW1')
        le_high1 = PyDMLineEdit(self,
                                self.dev_pref + ':' + self.TYPE + '_SP_HIGH1')
        cb_mode1 = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_SP_SEARCH1')
        lb_peak1 = PyDMLabel(self, self.dev_pref + ':' + self.TYPE + '_PEAK1')
        lb_peak1.showUnits = True
        lb_pfrq1 = PyDMLabel(self,
                             self.dev_pref + ':' + self.TYPE + '_PEAKFREQ1')
        lb_pfrq1.showUnits = True
        lb_tune1 = PyDMLabel(self,
                             self.dev_pref + ':' + self.TYPE + '_PEAKTUNE1')

        le_low2 = PyDMLineEdit(self,
                               self.dev_pref + ':' + self.TYPE + '_SP_LOW2')
        le_high2 = PyDMLineEdit(self,
                                self.dev_pref + ':' + self.TYPE + '_SP_HIGH2')
        cb_mode2 = PyDMEnumComboBox(
            self, self.dev_pref + ':' + self.TYPE + '_SP_SEARCH2')
        lb_peak2 = PyDMLabel(self, self.dev_pref + ':' + self.TYPE + '_PEAK2')
        lb_peak2.showUnits = True
        lb_pfrq2 = PyDMLabel(self,
                             self.dev_pref + ':' + self.TYPE + '_PEAKFREQ2')
        lb_pfrq2.showUnits = True
        lb_tune2 = PyDMLabel(self,
                             self.dev_pref + ':' + self.TYPE + '_PEAKTUNE2')

        gbox_mark = QGroupBox('Markers', self)
        lay_mark = QGridLayout(gbox_mark)
        lay_mark.addWidget(ld_span, 0, 1, 1, 2)
        lay_mark.addWidget(ld_mode, 0, 3)
        lay_mark.addWidget(ld_val, 0, 4)
        lay_mark.addWidget(ld_pfrq, 0, 5)
        lay_mark.addWidget(ld_tune, 0, 6)
        lay_mark.addWidget(ld_mk1, 1, 0)
        lay_mark.addWidget(le_low1, 1, 1)
        lay_mark.addWidget(le_high1, 1, 2)
        lay_mark.addWidget(cb_mode1, 1, 3)
        lay_mark.addWidget(lb_peak1, 1, 4)
        lay_mark.addWidget(lb_pfrq1, 1, 5)
        lay_mark.addWidget(lb_tune1, 1, 6)
        lay_mark.addWidget(ld_mk2, 2, 0)
        lay_mark.addWidget(le_low2, 2, 1)
        lay_mark.addWidget(le_high2, 2, 2)
        lay_mark.addWidget(cb_mode2, 2, 3)
        lay_mark.addWidget(lb_peak2, 2, 4)
        lay_mark.addWidget(lb_pfrq2, 2, 5)
        lay_mark.addWidget(lb_tune2, 2, 6)

        wid = QWidget()
        lay = QGridLayout(wid)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addLayout(lay_graph, 0, 0, 1, 2)
        lay.addWidget(gbox_acqctrl, 1, 0)
        lay.addWidget(gbox_mark, 1, 1)
        lay.setRowStretch(0, 5)
        lay.setRowStretch(1, 1)
        lay.setColumnStretch(0, 1)
        lay.setColumnStretch(1, 1)
        return wid
Example #34
0
class PyDMScaleIndicator(QFrame, TextFormatter, PyDMWidget):
    """
    A bar-shaped indicator for scalar value with support for Channels and
    more from PyDM.
    Configurable features include indicator type (bar/pointer), scale tick
    marks and orientation (horizontal/vertical).

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Scale
    init_channel : str, optional
        The channel to be used by the widget.
    """
    def __init__(self, parent=None, init_channel=None):
        QFrame.__init__(self, parent)
        PyDMWidget.__init__(self, init_channel=init_channel)
        self._show_value = True
        self._show_limits = True

        self.scale_indicator = QScale()
        self.value_label = QLabel()
        self.lower_label = QLabel()
        self.upper_label = QLabel()

        self.value_label.setText('<val>')
        self.lower_label.setText('<min>')
        self.upper_label.setText('<max>')

        self._value_position = Qt.TopEdge
        self._limits_from_channel = True
        self._user_lower_limit = 0
        self._user_upper_limit = 0

        self.value_label.setSizePolicy(QSizePolicy.Minimum,
                                       QSizePolicy.Minimum)
        self.setup_widgets_for_orientation(Qt.Horizontal, False, False,
                                           self._value_position)

    def update_labels(self):
        """
        Update the limits and value labels with the correct values.
        """
        self.lower_label.setText(str(self.scale_indicator._lower_limit))
        self.upper_label.setText(str(self.scale_indicator._upper_limit))
        self.value_label.setText(
            self.format_string.format(self.scale_indicator._value))

    def value_changed(self, new_value):
        """
        Callback invoked when the Channel value is changed.

        Parameters
        ----------
        new_val : int or float
            The new value from the channel.
        """
        super(PyDMScaleIndicator, self).value_changed(new_value)
        self.scale_indicator.set_value(new_value)
        self.update_labels()

    def upperCtrlLimitChanged(self, new_limit):
        """
        PyQT Slot for changes on the upper control limit value of the Channel
        This slot sends the new limit value to the
        ```ctrl_limit_changed``` callback.

        Parameters
        ----------
        new_limit : float
        """
        super(PyDMScaleIndicator, self).upperCtrlLimitChanged(new_limit)
        if self.limitsFromChannel:
            self.scale_indicator.set_upper_limit(new_limit)
            self.update_labels()

    def lowerCtrlLimitChanged(self, new_limit):
        """
        PyQT Slot for changes on the lower control limit value of the Channel
        This slot sends the new limit value to the
        ```ctrl_limit_changed``` callback.

        Parameters
        ----------
        new_limit : float
        """
        super(PyDMScaleIndicator, self).lowerCtrlLimitChanged(new_limit)
        if self.limitsFromChannel:
            self.scale_indicator.set_lower_limit(new_limit)
            self.update_labels()

    def setup_widgets_for_orientation(self, new_orientation, flipped, inverted,
                                      value_position):
        """
        Reconstruct the widget given the orientation.

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        flipped : bool
            Indicates if scale tick marks are flipped to the other side
        inverted : bool
            Indicates if scale appearance is inverted
        """
        self.limits_layout = None
        self.widget_layout = None
        if new_orientation == Qt.Horizontal:
            self.limits_layout = QHBoxLayout()
            if not inverted:
                self.limits_layout.addWidget(self.lower_label)
                self.limits_layout.addWidget(self.upper_label)
            else:
                self.limits_layout.addWidget(self.upper_label)
                self.limits_layout.addWidget(self.lower_label)

            self.widget_layout = QGridLayout()
            if not flipped:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addItem(self.limits_layout, 1, 1)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addItem(self.limits_layout, 2, 0)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 2, 0)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
                    self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignRight)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignRight)
                    self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
            else:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 1, 1)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 2, 0)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 2, 0)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignBottom
                                                  | Qt.AlignLeft)
                    self.upper_label.setAlignment(Qt.AlignBottom
                                                  | Qt.AlignRight)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignBottom
                                                  | Qt.AlignRight)
                    self.upper_label.setAlignment(Qt.AlignBottom
                                                  | Qt.AlignLeft)

        elif new_orientation == Qt.Vertical:
            self.limits_layout = QVBoxLayout()
            if (value_position == Qt.RightEdge and flipped == False) or \
                   (value_position == Qt.LeftEdge and flipped == True):
                add_value_between_limits = True
            else:
                add_value_between_limits = False
            if not inverted:
                self.limits_layout.addWidget(self.upper_label)
                if add_value_between_limits:
                    self.limits_layout.addWidget(self.value_label)
                self.limits_layout.addWidget(self.lower_label)
                self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
                self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
            else:
                self.limits_layout.addWidget(self.lower_label)
                if add_value_between_limits:
                    self.limits_layout.addWidget(self.value_label)
                self.limits_layout.addWidget(self.upper_label)
                self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
                self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)

            self.widget_layout = QGridLayout()
            if not flipped:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addItem(self.limits_layout, 0, 2)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 1)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignLeft
                                                  | Qt.AlignBottom)
                    self.upper_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
                    self.upper_label.setAlignment(Qt.AlignLeft
                                                  | Qt.AlignBottom)
            else:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 2)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 0, 2)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 1)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignRight
                                                  | Qt.AlignBottom)
                    self.upper_label.setAlignment(Qt.AlignRight | Qt.AlignTop)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignRight | Qt.AlignTop)
                    self.upper_label.setAlignment(Qt.AlignRight
                                                  | Qt.AlignBottom)

        self.value_label.setAlignment(Qt.AlignCenter)

        if self.layout() is not None:
            # Trick to remove the existing layout by re-parenting it in an empty widget.
            QWidget().setLayout(self.layout())
        self.widget_layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.widget_layout)

    @Property(bool)
    def showValue(self):
        """
        Whether or not the current value should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self._show_value

    @showValue.setter
    def showValue(self, checked):
        """
        Whether or not the current value should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        if self._show_value != bool(checked):
            self._show_value = checked
        if checked:
            self.value_label.show()
        else:
            self.value_label.hide()

    @Property(bool)
    def showLimits(self):
        """
        Whether or not the high and low limits should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self._show_limits

    @showLimits.setter
    def showLimits(self, checked):
        """
        Whether or not the high and low limits should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        if self._show_limits != bool(checked):
            self._show_limits = checked
        if checked:
            self.lower_label.show()
            self.upper_label.show()
        else:
            self.lower_label.hide()
            self.upper_label.hide()

    @Property(bool)
    def showTicks(self):
        """
        Whether or not the tick marks should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_show_ticks()

    @showTicks.setter
    def showTicks(self, checked):
        """
        Whether or not the tick marks should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_show_ticks(checked)

    @Property(Qt.Orientation)
    def orientation(self):
        """
        The scale orientation (Horizontal or Vertical)

        Returns
        -------
        int
            Qt.Horizontal or Qt.Vertical
        """
        return self.scale_indicator.get_orientation()

    @orientation.setter
    def orientation(self, orientation):
        """
        The scale orientation (Horizontal or Vertical)

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        """
        self.scale_indicator.set_orientation(orientation)
        self.setup_widgets_for_orientation(orientation, self.flipScale,
                                           self.invertedAppearance,
                                           self._value_position)

    @Property(bool)
    def flipScale(self):
        """
        Whether or not the scale should be flipped.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_flip_scale()

    @flipScale.setter
    def flipScale(self, checked):
        """
        Whether or not the scale should be flipped.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_flip_scale(checked)
        self.setup_widgets_for_orientation(self.orientation, checked,
                                           self.invertedAppearance,
                                           self._value_position)

    @Property(bool)
    def invertedAppearance(self):
        """
        Whether or not the scale appearence should be inverted.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_inverted_appearance()

    @invertedAppearance.setter
    def invertedAppearance(self, inverted):
        """
        Whether or not the scale appearence should be inverted.

        Parameters
        ----------
        inverted : bool
        """
        self.scale_indicator.set_inverted_appearance(inverted)
        self.setup_widgets_for_orientation(self.orientation, self.flipScale,
                                           inverted, self._value_position)

    @Property(bool)
    def barIndicator(self):
        """
        Whether or not the scale indicator should be a bar instead of a pointer.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_bar_indicator()

    @barIndicator.setter
    def barIndicator(self, checked):
        """
        Whether or not the scale indicator should be a bar instead of a pointer.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_bar_indicator(checked)

    @Property(QColor)
    def backgroundColor(self):
        """
        The color of the scale background.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_background_color()

    @backgroundColor.setter
    def backgroundColor(self, color):
        """
        The color of the scale background.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_background_color(color)

    @Property(QColor)
    def indicatorColor(self):
        """
        The color of the scale indicator.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_indicator_color()

    @indicatorColor.setter
    def indicatorColor(self, color):
        """
        The color of the scale indicator.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_indicator_color(color)

    @Property(QColor)
    def tickColor(self):
        """
        The color of the scale tick marks.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_tick_color()

    @tickColor.setter
    def tickColor(self, color):
        """
        The color of the scale tick marks.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_tick_color(color)

    @Property(float)
    def backgroundSizeRate(self):
        """
        The rate of background height size (from top to bottom).

        Returns
        -------
        float
        """
        return self.scale_indicator.get_background_size_rate()

    @backgroundSizeRate.setter
    def backgroundSizeRate(self, rate):
        """
        The rate of background height size (from top to bottom).

        Parameters
        -------
        rate : float
            Between 0 and 1.
        """
        self.scale_indicator.set_background_size_rate(rate)

    @Property(float)
    def tickSizeRate(self):
        """
        The rate of tick marks height size (from bottom to top).

        Returns
        -------
        float
        """
        return self.scale_indicator.get_tick_size_rate()

    @tickSizeRate.setter
    def tickSizeRate(self, rate):
        """
        The rate of tick marks height size (from bottom to top).

        Parameters
        -------
        rate : float
            Between 0 and 1.
        """
        self.scale_indicator.set_tick_size_rate(rate)

    @Property(int)
    def numDivisions(self):
        """
        The number in which the scale is divided.

        Returns
        -------
        int
        """
        return self.scale_indicator.get_num_divisions()

    @numDivisions.setter
    def numDivisions(self, divisions):
        """
        The number in which the scale is divided.

        Parameters
        -------
        divisions : int
            The number of scale divisions.
        """
        self.scale_indicator.set_num_divisions(divisions)

    @Property(int)
    def scaleHeight(self):
        """
        The scale height, fixed so it do not wiggle when value label resizes.

        Returns
        -------
        int
        """
        return self.scale_indicator.get_scale_height()

    @scaleHeight.setter
    def scaleHeight(self, value):
        """
        The scale height, fixed so it do not wiggle when value label resizes.

        Parameters
        -------
        divisions : int
            The scale height.
        """
        self.scale_indicator.set_scale_height(value)

    @Property(Qt.Edge)
    def valuePosition(self):
        """
        The position of the value label (Top, Bottom, Left or Right).

        Returns
        -------
        int
            Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge
        """
        return self._value_position

    @valuePosition.setter
    def valuePosition(self, position):
        """
       The position of the value label (Top, Bottom, Left or Right).

        Parameters
        ----------
        position : int
            Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge
        """
        self._value_position = position
        self.setup_widgets_for_orientation(self.orientation, self.flipScale,
                                           self.invertedAppearance, position)

    @Property(bool)
    def originAtZero(self):
        """
        Whether or not the scale indicator should start at zero value.
        Applies only for bar indicator.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_origin_at_zero()

    @originAtZero.setter
    def originAtZero(self, checked):
        """
        Whether or not the scale indicator should start at zero value.
        Applies only for bar indicator.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_origin_at_zero(checked)

    @Property(bool)
    def limitsFromChannel(self):
        """
        Whether or not the scale indicator should use the limits information
        from the channel.

        Returns
        -------
        bool
        """
        return self._limits_from_channel

    @limitsFromChannel.setter
    def limitsFromChannel(self, checked):
        """
        Whether or not the scale indicator should use the limits information
        from the channel.

        Parameters
        ----------
        checked : bool
            True to use the limits from the Channel, False to use the user-defined
            values.
        """
        if self._limits_from_channel != checked:
            self._limits_from_channel = checked
            if checked:
                if self._lower_ctrl_limit:
                    self.scale_indicator.set_lower_limit(
                        self._lower_ctrl_limit)
                if self._upper_ctrl_limit:
                    self.scale_indicator.set_upper_limit(
                        self._upper_ctrl_limit)
            else:
                self.scale_indicator.set_lower_limit(self._user_lower_limit)
                self.scale_indicator.set_upper_limit(self._user_upper_limit)
            self.update_labels()

    @Property(float)
    def userLowerLimit(self):
        """
        The user-defined lower limit for the scale.

        Returns
        -------
        float
        """
        return self._user_lower_limit

    @userLowerLimit.setter
    def userLowerLimit(self, value):
        """
        The user-defined lower limit for the scale.

        Parameters
        ----------
        value : float
            The new lower limit value.
        """
        if self._limits_from_channel:
            return
        self._user_lower_limit = value
        self.scale_indicator.set_lower_limit(self._user_lower_limit)
        self.update_labels()

    @Property(float)
    def userUpperLimit(self):
        """
        The user-defined upper limit for the scale.

        Returns
        -------
        float
        """
        return self._user_upper_limit

    @userUpperLimit.setter
    def userUpperLimit(self, value):
        """
        The user-defined upper limit for the scale.

        Parameters
        ----------
        value : float
            The new upper limit value.
        """
        if self._limits_from_channel:
            return
        self._user_upper_limit = value
        self.scale_indicator.set_upper_limit(self._user_upper_limit)
        self.update_labels()
Example #35
0
class SegmentationWidget(QWidget):
    def __init__(
        self,
        viewer,
        boundaries_string=BOUNDARIES_STRING,
    ):
        super(SegmentationWidget, self).__init__()

        # general variables
        self.viewer = viewer

        # Disable / overwrite napari viewer functions
        # that either do not make sense or should be avoided by the user
        disable_napari_btns(self.viewer)
        disable_napari_key_bindings()
        # overwrite_napari_roll(self.viewer)

        # Main layers
        self.base_layer = []  # Contains registered brain / reference brain
        self.atlas_layer = []  # Contains annotations / region information

        # Track variables
        self.track_layers = []

        # Region variables
        self.label_layers = []

        # Atlas variables
        self.current_atlas_name = ""
        self.atlas = None

        self.boundaries_string = boundaries_string
        self.directory = ""
        # Set up segmentation methods
        self.region_seg = RegionSeg(self)
        self.track_seg = TrackSeg(self)

        # Generate main layout
        self.setup_main_layout()

        if DISPLAY_REGION_INFO:

            @self.viewer.mouse_move_callbacks.append
            def display_region_info(v, event):
                """
                Show brain region info on mouse over in status bar on the right
                """
                assert self.viewer == v
                if len(v.layers) and self.atlas_layer and self.atlas:
                    _, _, _, region_info = structure_from_viewer(
                        self.viewer.status, self.atlas_layer, self.atlas)
                    self.viewer.help = region_info

    def setup_main_layout(self):
        """
        Construct main layout of widget
        """
        self.layout = QGridLayout()
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.layout.setAlignment(QtCore.Qt.AlignTop)
        self.layout.setSpacing(4)

        # 3 Steps:
        # - Loading panel
        # - Segmentation methods panel
        # -> Individual segmentation methods (which are invisible at first)
        # - Saving panel

        self.add_loading_panel(1)
        self.add_segmentation_methods_panel(1)
        self.track_seg.add_track_panel(2)  # Track segmentation subpanel
        self.region_seg.add_region_panel(3)  # Region segmentation subpanel
        self.add_saving_panel(4)

        # Take care of status label
        self.status_label = QLabel()
        self.status_label.setText("Ready")
        self.layout.addWidget(self.status_label, 5, 0)

        self.setLayout(self.layout)

    # PANELS ###############################################################

    def add_segmentation_methods_panel(self, row, column=1):
        """
        Segmentation methods chooser panel:
            Toggle visibility of segmentation
            methods
        """
        self.toggle_methods_panel = QGroupBox("Segmentation")
        self.toggle_methods_layout = QGridLayout()
        self.toggle_methods_layout.setContentsMargins(10, 10, 10, 10)
        self.toggle_methods_layout.setSpacing(5)
        self.toggle_methods_layout.setAlignment(QtCore.Qt.AlignBottom)

        self.show_trackseg_button = add_button(
            "Track tracing",
            self.toggle_methods_layout,
            self.track_seg.toggle_track_panel,
            0,
            1,
            minimum_width=COLUMN_WIDTH,
            alignment=SEGM_METHODS_PANEL_ALIGN,
        )
        self.show_trackseg_button.setEnabled(False)

        self.show_regionseg_button = add_button(
            "Region segmentation",
            self.toggle_methods_layout,
            self.region_seg.toggle_region_panel,
            1,
            1,
            minimum_width=COLUMN_WIDTH,
            alignment=SEGM_METHODS_PANEL_ALIGN,
        )
        self.show_regionseg_button.setEnabled(False)

        self.toggle_methods_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.toggle_methods_panel.setLayout(self.toggle_methods_layout)
        self.toggle_methods_panel.setVisible(True)

        self.layout.addWidget(self.toggle_methods_panel, row, column, 1, 1)

    def add_loading_panel(self, row, column=0):
        """
        Loading panel:
            - Load project (sample space)
            - Load project (atlas space)
            - Atlas chooser
        """
        self.load_data_panel = QGroupBox("Load data")
        self.load_data_layout = QGridLayout()
        self.load_data_layout.setSpacing(15)
        self.load_data_layout.setContentsMargins(10, 10, 10, 10)
        self.load_data_layout.setAlignment(QtCore.Qt.AlignBottom)

        self.load_button = add_button(
            "Load project (sample space)",
            self.load_data_layout,
            self.load_brainreg_directory_sample,
            0,
            0,
            minimum_width=COLUMN_WIDTH,
            alignment=LOADING_PANEL_ALIGN,
        )

        self.load_button_standard = add_button(
            "Load project (atlas space)",
            self.load_data_layout,
            self.load_brainreg_directory_standard,
            1,
            0,
            minimum_width=COLUMN_WIDTH,
            alignment=LOADING_PANEL_ALIGN,
        )

        self.add_atlas_menu(self.load_data_layout)

        self.load_data_layout.setColumnMinimumWidth(0, COLUMN_WIDTH)
        self.load_data_panel.setLayout(self.load_data_layout)
        self.load_data_panel.setVisible(True)

        self.layout.addWidget(self.load_data_panel, row, column, 1, 1)

    def add_saving_panel(self, row):
        """
        Saving/Export panel
        """
        self.save_data_panel = QGroupBox()
        self.save_data_layout = QGridLayout()

        self.export_button = add_button(
            "To brainrender",
            self.save_data_layout,
            self.export_to_brainrender,
            0,
            0,
            visibility=False,
        )
        self.save_button = add_button("Save",
                                      self.save_data_layout,
                                      self.save,
                                      0,
                                      1,
                                      visibility=False)

        self.save_data_layout.setColumnMinimumWidth(1, COLUMN_WIDTH)
        self.save_data_panel.setLayout(self.save_data_layout)
        self.layout.addWidget(self.save_data_panel, row, 0, 1, 2)

        self.save_data_panel.setVisible(False)

    # ATLAS INTERACTION ####################################################

    def add_atlas_menu(self, layout):
        list_of_atlasses = ["Load atlas"]
        available_atlases = get_available_atlases()
        for atlas in available_atlases.keys():
            atlas_desc = f"{atlas} v{available_atlases[atlas]}"
            list_of_atlasses.append(atlas_desc)
            atlas_menu, _ = add_combobox(
                layout,
                None,
                list_of_atlasses,
                2,
                0,
                label_stack=True,
                callback=self.initialise_atlas,
                width=COLUMN_WIDTH,
            )

        self.atlas_menu = atlas_menu

    def initialise_atlas(self):
        atlas_string = self.atlas_menu.currentText()
        atlas_name = atlas_string.split(" ")[0].strip()
        if atlas_name != self.current_atlas_name:
            status = self.remove_layers()
            if not status:  # Something prevented deletion
                self.reset_atlas_menu()
                return
        else:
            print(f"{atlas_string} already selected for segmentation.")
            self.reset_atlas_menu()
            return

        # Get / set output directory
        self.set_output_directory()
        if not self.directory:
            self.reset_atlas_menu()
            return

        self.current_atlas_name = atlas_name
        # Instantiate atlas layers
        self.load_atlas()

        self.directory = self.directory / atlas_name
        self.paths = Paths(self.directory, atlas_space=True)

        self.status_label.setText("Ready")
        # Set window title
        self.viewer.title = f"Atlas: {self.current_atlas_name}"
        self.initialise_segmentation_interface()
        # Check / load previous regions and tracks
        self.region_seg.check_saved_region()
        self.track_seg.check_saved_track()
        self.reset_atlas_menu()

    def set_output_directory(self):
        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.directory = QFileDialog.getExistingDirectory(
            self,
            "Select output directory",
            options=options,
        )
        if self.directory != "":
            self.directory = Path(self.directory)

    def load_atlas(self):
        atlas = BrainGlobeAtlas(self.current_atlas_name)
        self.atlas = atlas
        self.base_layer = self.viewer.add_image(
            self.atlas.reference,
            name="Reference",
        )
        self.atlas_layer = self.viewer.add_labels(
            self.atlas.annotation,
            name=self.atlas.atlas_name,
            blending="additive",
            opacity=0.3,
            visible=False,
        )
        self.standard_space = True

    def reset_atlas_menu(self):
        # Reset menu for atlas - show initial description
        self.atlas_menu.blockSignals(True)
        self.atlas_menu.setCurrentIndex(0)
        self.atlas_menu.blockSignals(False)

    # BRAINREG INTERACTION #################################################

    def load_brainreg_directory_sample(self):
        self.get_brainreg_directory(standard_space=False)

    def load_brainreg_directory_standard(self):
        self.get_brainreg_directory(standard_space=True)

    def get_brainreg_directory(self, standard_space):
        """
        Shows file dialog to choose output directory
        and sets global directory info
        """
        if standard_space:
            self.plugin = "brainreg_standard"
            self.standard_space = True
        else:
            self.plugin = "brainreg"
            self.standard_space = False

        self.status_label.setText("Loading...")
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        brainreg_directory = QFileDialog.getExistingDirectory(
            self,
            "Select brainreg directory",
            options=options,
        )

        if not brainreg_directory:
            return

        if self.directory != brainreg_directory:
            status = self.remove_layers()
            if not status:
                return  # Something prevented deletion
            self.directory = Path(brainreg_directory)
        else:
            print(f"{str(brainreg_directory)} already loaded.")
            return

        # Otherwise, proceed loading brainreg dir
        self.load_brainreg_directory()

    def load_brainreg_directory(self):
        """
        Opens brainreg folder in napari.
        Calls initialise_loaded_data to set up layers / info.
        Then checks for previously loaded data.

        """
        try:
            self.viewer.open(str(self.directory), plugin=self.plugin)
            self.paths = Paths(
                self.directory,
                standard_space=self.standard_space,
            )
            self.initialise_loaded_data()
        except ValueError:
            print(f"The directory ({self.directory}) does not appear to be "
                  f"a brainreg directory, please try again.")
            return

        # Check / load previous regions and tracks
        self.region_seg.check_saved_region()
        self.track_seg.check_saved_track()

    def initialise_loaded_data(self):
        """
        Set up brainreg layers in napari / fill with new data and info

        """
        try:
            self.viewer.layers.remove(self.boundaries_string)
        except KeyError:
            pass

        self.base_layer = self.viewer.layers["Registered image"]
        self.metadata = self.base_layer.metadata
        self.atlas = self.metadata["atlas_class"]
        self.atlas_layer = self.viewer.layers[self.metadata["atlas"]]
        self.initialise_segmentation_interface()

        # Set window title
        self.viewer.title = (
            f"Brainreg: {self.metadata['atlas']} ({self.plugin})")
        self.status_label.setText("Ready")

    # MORE LAYOUT COMPONENTS ###########################################

    def initialise_segmentation_interface(self):
        self.reset_variables()
        self.initialise_image_view()
        self.save_data_panel.setVisible(True)
        self.save_button.setVisible(True)
        self.export_button.setVisible(self.standard_space)
        self.show_regionseg_button.setEnabled(True)
        self.show_trackseg_button.setEnabled(True)
        self.status_label.setText("Ready")

    def initialise_image_view(self):
        self.set_z_position()

    def set_z_position(self):
        midpoint = int(round(len(self.base_layer.data) / 2))
        self.viewer.dims.set_point(0, midpoint)

    def reset_variables(self):
        """
        Reset atlas scale dependent variables
        - point_size (Track segmentation)
        - spline_size (Track segmentation)
        - brush_size (Region segmentation)
        """
        self.mean_voxel_size = int(
            np.sum(self.atlas.resolution) / len(self.atlas.resolution))
        self.track_seg.point_size = (self.track_seg.point_size_default /
                                     self.mean_voxel_size)
        self.track_seg.spline_size = (self.track_seg.spline_size_default /
                                      self.mean_voxel_size)
        self.region_seg.brush_size = (self.region_seg.brush_size_default /
                                      self.mean_voxel_size)
        return

    def display_delete_warning(self):
        """
        Display a warning in a pop up that informs
        about deletion of all annotation layers
        """
        message_reply = QMessageBox.question(
            self,
            "About to remove layers",
            "All layers are about to be deleted. Proceed?",
            QMessageBox.Yes | QMessageBox.Cancel,
        )
        if message_reply == QMessageBox.Yes:
            return True
        else:
            return False

    def remove_layers(self):
        """
        TODO: This needs work. Runs into an error currently
        when switching from a annotated project to another one
        """
        if len(self.viewer.layers) != 0:
            # Check with user if that is really what is wanted
            if self.track_layers or self.label_layers:
                choice = self.display_delete_warning()
                if not choice:
                    print('Preventing deletion because user chose "Cancel"')
                    return False

            # Remove old layers
            for layer in list(self.viewer.layers):
                try:
                    self.viewer.layers.remove(layer)
                except IndexError:  # no idea why this happens
                    pass

        # There seems to be a napari bug trying to access previously used slider
        # values. Trying to circument for now
        self.viewer.window.qt_viewer.dims._last_used = None

        self.track_layers = []
        self.label_layers = []
        return True

    def save(self):
        if self.label_layers or self.track_layers:
            print("Saving")
            worker = save_all(
                self.paths.regions_directory,
                self.paths.tracks_directory,
                self.label_layers,
                self.track_layers,
                track_file_extension=TRACK_FILE_EXT,
            )
            worker.start()

    def export_to_brainrender(self):
        print("Exporting")
        max_axis_2 = self.base_layer.shape[2]
        worker = export_all(
            self.paths.regions_directory,
            self.paths.tracks_directory,
            self.label_layers,
            self.track_seg.splines,
            self.track_seg.spline_names,
            self.atlas.resolution[0],
            max_axis_2,
        )
        worker.start()
Example #36
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.mode.connect(self._on_mode_change)
        self.layer.events.edge_width.connect(self._on_edge_width_change)
        self.layer.events.current_edge_color.connect(
            self._on_current_edge_color_change
        )
        self.layer.events.current_face_color.connect(
            self._on_current_face_color_change
        )
        self.layer.events.editable.connect(self._on_editable_change)
        self.layer.text.events.visible.connect(self._on_text_visibility_change)

        sld = QSlider(Qt.Horizontal)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(40)
        sld.setSingleStep(1)
        value = self.layer.current_edge_width
        if isinstance(value, Iterable):
            if isinstance(value, list):
                value = np.asarray(value)
            value = value.mean()
        sld.setValue(int(value))
        sld.valueChanged.connect(self.changeWidth)
        self.widthSlider = sld

        def _radio_button(
            parent,
            btn_name,
            mode,
            action_name,
            extra_tooltip_text='',
            **kwargs,
        ):
            """
            Convenience local function to create a RadioButton and bind it to
            an action at the same time.

            Parameters
            ----------
            parent : Any
                Parent of the generated QtModeRadioButton
            btn_name : str
                name fo the button
            mode : Enum
                Value Associated to current button
            action_name : str
                Action triggered when button pressed
            extra_tooltip_text : str
                Text you want added after the automatic tooltip set by the
                action manager
            **kwargs:
                Passed to QtModeRadioButton

            Returns
            -------
            button: QtModeRadioButton
                button bound (or that will be bound to) to action `action_name`

            Notes
            -----
            When shortcuts are modifed/added/removed via the action manager, the
            tooltip will be updated to reflect the new shortcut.
            """
            action_name = 'napari:' + action_name
            btn = QtModeRadioButton(parent, btn_name, mode, **kwargs)
            action_manager.bind_button(
                action_name,
                btn,
                extra_tooltip_text='',
            )
            return btn

        self.select_button = _radio_button(
            layer, 'select', Mode.SELECT, "activate_select_mode"
        )

        self.direct_button = _radio_button(
            layer, 'direct', Mode.DIRECT, "activate_direct_mode"
        )

        self.panzoom_button = _radio_button(
            layer,
            'zoom',
            Mode.PAN_ZOOM,
            "napari:activate_shape_pan_zoom_mode",
            extra_tooltip_text=trans._('(or hold Space)'),
            checked=True,
        )

        self.rectangle_button = _radio_button(
            layer,
            'rectangle',
            Mode.ADD_RECTANGLE,
            "activate_add_rectangle_mode",
        )
        self.ellipse_button = _radio_button(
            layer,
            'ellipse',
            Mode.ADD_ELLIPSE,
            "activate_add_ellipse_mode",
        )

        self.line_button = _radio_button(
            layer, 'line', Mode.ADD_LINE, "activate_add_line_mode"
        )
        self.path_button = _radio_button(
            layer, 'path', Mode.ADD_PATH, "activate_add_path_mode"
        )
        self.polygon_button = _radio_button(
            layer,
            'polygon',
            Mode.ADD_POLYGON,
            "activate_add_polygon_mode",
        )
        self.vertex_insert_button = _radio_button(
            layer,
            'vertex_insert',
            Mode.VERTEX_INSERT,
            "activate_vertex_insert_mode",
        )
        self.vertex_remove_button = _radio_button(
            layer,
            'vertex_remove',
            Mode.VERTEX_REMOVE,
            "activate_vertex_remove_mode",
        )

        self.move_front_button = QtModePushButton(
            layer,
            'move_front',
            slot=self.layer.move_to_front,
            tooltip=trans._('Move to front'),
        )

        action_manager.bind_button(
            'napari:move_shapes_selection_to_front', self.move_front_button
        )

        self.move_back_button = QtModePushButton(
            layer,
            'move_back',
            slot=self.layer.move_to_back,
            tooltip=trans._('Move to back'),
        )
        action_manager.bind_button(
            'napari:move_shapes_selection_to_back', self.move_back_button
        )

        self.delete_button = QtModePushButton(
            layer,
            'delete_shape',
            slot=self.layer.remove_selected,
            tooltip=trans._(
                "Delete selected shapes ({shortcut})",
                shortcut=Shortcut('Backspace').platform,
            ),
        )

        self.button_group = QButtonGroup(self)
        self.button_group.addButton(self.select_button)
        self.button_group.addButton(self.direct_button)
        self.button_group.addButton(self.panzoom_button)
        self.button_group.addButton(self.rectangle_button)
        self.button_group.addButton(self.ellipse_button)
        self.button_group.addButton(self.line_button)
        self.button_group.addButton(self.path_button)
        self.button_group.addButton(self.polygon_button)
        self.button_group.addButton(self.vertex_insert_button)
        self.button_group.addButton(self.vertex_remove_button)

        button_grid = QGridLayout()
        button_grid.addWidget(self.vertex_remove_button, 0, 2)
        button_grid.addWidget(self.vertex_insert_button, 0, 3)
        button_grid.addWidget(self.delete_button, 0, 4)
        button_grid.addWidget(self.direct_button, 0, 5)
        button_grid.addWidget(self.select_button, 0, 6)
        button_grid.addWidget(self.panzoom_button, 0, 7)
        button_grid.addWidget(self.move_back_button, 1, 1)
        button_grid.addWidget(self.move_front_button, 1, 2)
        button_grid.addWidget(self.ellipse_button, 1, 3)
        button_grid.addWidget(self.rectangle_button, 1, 4)
        button_grid.addWidget(self.polygon_button, 1, 5)
        button_grid.addWidget(self.line_button, 1, 6)
        button_grid.addWidget(self.path_button, 1, 7)
        button_grid.setContentsMargins(5, 0, 0, 5)
        button_grid.setColumnStretch(0, 1)
        button_grid.setSpacing(4)

        self.faceColorEdit = QColorSwatchEdit(
            initial_color=self.layer.current_face_color,
            tooltip=trans._('click to set current face color'),
        )
        self._on_current_face_color_change()
        self.edgeColorEdit = QColorSwatchEdit(
            initial_color=self.layer.current_edge_color,
            tooltip=trans._('click to set current edge color'),
        )
        self._on_current_edge_color_change()
        self.faceColorEdit.color_changed.connect(self.changeFaceColor)
        self.edgeColorEdit.color_changed.connect(self.changeEdgeColor)

        text_disp_cb = QCheckBox()
        text_disp_cb.setToolTip(trans._('toggle text visibility'))
        text_disp_cb.setChecked(self.layer.text.visible)
        text_disp_cb.stateChanged.connect(self.change_text_visibility)
        self.textDispCheckBox = text_disp_cb

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addLayout(button_grid, 0, 0, 1, 2)
        self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0)
        self.grid_layout.addWidget(self.opacitySlider, 1, 1)
        self.grid_layout.addWidget(QLabel(trans._('edge width:')), 2, 0)
        self.grid_layout.addWidget(self.widthSlider, 2, 1)
        self.grid_layout.addWidget(QLabel(trans._('blending:')), 3, 0)
        self.grid_layout.addWidget(self.blendComboBox, 3, 1)
        self.grid_layout.addWidget(QLabel(trans._('face color:')), 4, 0)
        self.grid_layout.addWidget(self.faceColorEdit, 4, 1)
        self.grid_layout.addWidget(QLabel(trans._('edge color:')), 5, 0)
        self.grid_layout.addWidget(self.edgeColorEdit, 5, 1)
        self.grid_layout.addWidget(QLabel(trans._('display text:')), 6, 0)
        self.grid_layout.addWidget(self.textDispCheckBox, 6, 1)
        self.grid_layout.setRowStretch(7, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
Example #37
0
class PyDMScaleIndicator(QFrame, TextFormatter, PyDMWidget):
    """
    A bar-shaped indicator for scalar value with support for Channels and
    more from PyDM.
    Configurable features include indicator type (bar/pointer), scale tick
    marks and orientation (horizontal/vertical).

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Scale
    init_channel : str, optional
        The channel to be used by the widget.
    """

    def __init__(self, parent=None, init_channel=None):
        QFrame.__init__(self, parent)
        PyDMWidget.__init__(self, init_channel=init_channel)
        self._show_value = True
        self._show_limits = True

        self.scale_indicator = QScale()
        self.value_label = QLabel()
        self.lower_label = QLabel()
        self.upper_label = QLabel()

        self.value_label.setText('<val>')
        self.lower_label.setText('<min>')
        self.upper_label.setText('<max>')

        self._value_position = Qt.TopEdge
        self._limits_from_channel = True
        self._user_lower_limit = 0
        self._user_upper_limit = 0

        self.value_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.setup_widgets_for_orientation(Qt.Horizontal, False, False, self._value_position)

    def update_labels(self):
        """
        Update the limits and value labels with the correct values.
        """
        self.lower_label.setText(str(self.scale_indicator._lower_limit))
        self.upper_label.setText(str(self.scale_indicator._upper_limit))
        self.value_label.setText(self.format_string.format(self.scale_indicator._value))

    def value_changed(self, new_value):
        """
        Callback invoked when the Channel value is changed.

        Parameters
        ----------
        new_val : int or float
            The new value from the channel.
        """
        super(PyDMScaleIndicator, self).value_changed(new_value)
        self.scale_indicator.set_value(new_value)
        self.update_labels()

    def upperCtrlLimitChanged(self, new_limit):
        """
        PyQT Slot for changes on the upper control limit value of the Channel
        This slot sends the new limit value to the
        ```ctrl_limit_changed``` callback.

        Parameters
        ----------
        new_limit : float
        """
        super(PyDMScaleIndicator, self).upperCtrlLimitChanged(new_limit)
        if self.limitsFromChannel:
            self.scale_indicator.set_upper_limit(new_limit)
            self.update_labels()

    def lowerCtrlLimitChanged(self, new_limit):
        """
        PyQT Slot for changes on the lower control limit value of the Channel
        This slot sends the new limit value to the
        ```ctrl_limit_changed``` callback.

        Parameters
        ----------
        new_limit : float
        """
        super(PyDMScaleIndicator, self).lowerCtrlLimitChanged(new_limit)
        if self.limitsFromChannel:
            self.scale_indicator.set_lower_limit(new_limit)
            self.update_labels()

    def setup_widgets_for_orientation(self, new_orientation, flipped, inverted,
                                      value_position):
        """
        Reconstruct the widget given the orientation.

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        flipped : bool
            Indicates if scale tick marks are flipped to the other side
        inverted : bool
            Indicates if scale appearance is inverted
        """
        self.limits_layout = None
        self.widget_layout = None
        if new_orientation == Qt.Horizontal:
            self.limits_layout = QHBoxLayout()
            if not inverted:
                self.limits_layout.addWidget(self.lower_label)
                self.limits_layout.addWidget(self.upper_label)
            else:
                self.limits_layout.addWidget(self.upper_label)
                self.limits_layout.addWidget(self.lower_label)

            self.widget_layout = QGridLayout()
            if not flipped:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addItem(self.limits_layout, 1, 1)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addItem(self.limits_layout, 2, 0)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 2, 0)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
                    self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignRight)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignRight)
                    self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
            else:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 1, 1)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 2, 0)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addWidget(self.value_label, 2, 0)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignBottom | Qt.AlignLeft)
                    self.upper_label.setAlignment(Qt.AlignBottom | Qt.AlignRight)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignBottom | Qt.AlignRight)
                    self.upper_label.setAlignment(Qt.AlignBottom | Qt.AlignLeft)

        elif new_orientation == Qt.Vertical:
            self.limits_layout = QVBoxLayout()
            if (value_position == Qt.RightEdge and flipped == False) or \
                   (value_position == Qt.LeftEdge and flipped == True):
                add_value_between_limits = True
            else:
                add_value_between_limits = False
            if not inverted:
                self.limits_layout.addWidget(self.upper_label)
                if add_value_between_limits:
                    self.limits_layout.addWidget(self.value_label)
                self.limits_layout.addWidget(self.lower_label)
                self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
                self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
            else:
                self.limits_layout.addWidget(self.lower_label)
                if add_value_between_limits:
                    self.limits_layout.addWidget(self.value_label)
                self.limits_layout.addWidget(self.upper_label)
                self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
                self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)

            self.widget_layout = QGridLayout()
            if not flipped:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addItem(self.limits_layout, 0, 2)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 0)
                    self.widget_layout.addItem(self.limits_layout, 1, 1)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addWidget(self.scale_indicator, 0, 0)
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignLeft | Qt.AlignBottom)
                    self.upper_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
                    self.upper_label.setAlignment(Qt.AlignLeft | Qt.AlignBottom)
            else:
                if value_position == Qt.LeftEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 1)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 2)
                elif value_position == Qt.RightEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 0, 2)
                elif value_position == Qt.TopEdge:
                    self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2)
                    self.widget_layout.addItem(self.limits_layout, 1, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 1, 1)
                elif value_position == Qt.BottomEdge:
                    self.widget_layout.addItem(self.limits_layout, 0, 0)
                    self.widget_layout.addWidget(self.scale_indicator, 0, 1)
                    self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2)

                if not inverted:
                    self.lower_label.setAlignment(Qt.AlignRight | Qt.AlignBottom)
                    self.upper_label.setAlignment(Qt.AlignRight | Qt.AlignTop)
                elif inverted:
                    self.lower_label.setAlignment(Qt.AlignRight | Qt.AlignTop)
                    self.upper_label.setAlignment(Qt.AlignRight | Qt.AlignBottom)

        self.value_label.setAlignment(Qt.AlignCenter)

        if self.layout() is not None:
            # Trick to remove the existing layout by re-parenting it in an empty widget.
            QWidget().setLayout(self.layout())
        self.widget_layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.widget_layout)

    @Property(bool)
    def showValue(self):
        """
        Whether or not the current value should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self._show_value

    @showValue.setter
    def showValue(self, checked):
        """
        Whether or not the current value should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        if self._show_value != bool(checked):
            self._show_value = checked
        if checked:
            self.value_label.show()
        else:
            self.value_label.hide()

    @Property(bool)
    def showLimits(self):
        """
        Whether or not the high and low limits should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self._show_limits

    @showLimits.setter
    def showLimits(self, checked):
        """
        Whether or not the high and low limits should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        if self._show_limits != bool(checked):
            self._show_limits = checked
        if checked:
            self.lower_label.show()
            self.upper_label.show()
        else:
            self.lower_label.hide()
            self.upper_label.hide()

    @Property(bool)
    def showTicks(self):
        """
        Whether or not the tick marks should be displayed on the scale.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_show_ticks()

    @showTicks.setter
    def showTicks(self, checked):
        """
        Whether or not the tick marks should be displayed on the scale.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_show_ticks(checked)

    @Property(Qt.Orientation)
    def orientation(self):
        """
        The scale orientation (Horizontal or Vertical)

        Returns
        -------
        int
            Qt.Horizontal or Qt.Vertical
        """
        return self.scale_indicator.get_orientation()

    @orientation.setter
    def orientation(self, orientation):
        """
        The scale orientation (Horizontal or Vertical)

        Parameters
        ----------
        new_orientation : int
            Qt.Horizontal or Qt.Vertical
        """
        self.scale_indicator.set_orientation(orientation)
        self.setup_widgets_for_orientation(orientation, self.flipScale, self.invertedAppearance, self._value_position)

    @Property(bool)
    def flipScale(self):
        """
        Whether or not the scale should be flipped.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_flip_scale()

    @flipScale.setter
    def flipScale(self, checked):
        """
        Whether or not the scale should be flipped.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_flip_scale(checked)
        self.setup_widgets_for_orientation(self.orientation, checked, self.invertedAppearance, self._value_position)

    @Property(bool)
    def invertedAppearance(self):
        """
        Whether or not the scale appearence should be inverted.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_inverted_appearance()

    @invertedAppearance.setter
    def invertedAppearance(self, inverted):
        """
        Whether or not the scale appearence should be inverted.

        Parameters
        ----------
        inverted : bool
        """
        self.scale_indicator.set_inverted_appearance(inverted)
        self.setup_widgets_for_orientation(self.orientation, self.flipScale, inverted, self._value_position)

    @Property(bool)
    def barIndicator(self):
        """
        Whether or not the scale indicator should be a bar instead of a pointer.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_bar_indicator()

    @barIndicator.setter
    def barIndicator(self, checked):
        """
        Whether or not the scale indicator should be a bar instead of a pointer.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_bar_indicator(checked)

    @Property(QColor)
    def backgroundColor(self):
        """
        The color of the scale background.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_background_color()

    @backgroundColor.setter
    def backgroundColor(self, color):
        """
        The color of the scale background.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_background_color(color)

    @Property(QColor)
    def indicatorColor(self):
        """
        The color of the scale indicator.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_indicator_color()

    @indicatorColor.setter
    def indicatorColor(self, color):
        """
        The color of the scale indicator.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_indicator_color(color)

    @Property(QColor)
    def tickColor(self):
        """
        The color of the scale tick marks.

        Returns
        -------
        QColor
        """
        return self.scale_indicator.get_tick_color()

    @tickColor.setter
    def tickColor(self, color):
        """
        The color of the scale tick marks.

        Parameters
        -------
        color : QColor
        """
        self.scale_indicator.set_tick_color(color)

    @Property(float)
    def backgroundSizeRate(self):
        """
        The rate of background height size (from top to bottom).

        Returns
        -------
        float
        """
        return self.scale_indicator.get_background_size_rate()

    @backgroundSizeRate.setter
    def backgroundSizeRate(self, rate):
        """
        The rate of background height size (from top to bottom).

        Parameters
        -------
        rate : float
            Between 0 and 1.
        """
        self.scale_indicator.set_background_size_rate(rate)

    @Property(float)
    def tickSizeRate(self):
        """
        The rate of tick marks height size (from bottom to top).

        Returns
        -------
        float
        """
        return self.scale_indicator.get_tick_size_rate()

    @tickSizeRate.setter
    def tickSizeRate(self, rate):
        """
        The rate of tick marks height size (from bottom to top).

        Parameters
        -------
        rate : float
            Between 0 and 1.
        """
        self.scale_indicator.set_tick_size_rate(rate)

    @Property(int)
    def numDivisions(self):
        """
        The number in which the scale is divided.

        Returns
        -------
        int
        """
        return self.scale_indicator.get_num_divisions()

    @numDivisions.setter
    def numDivisions(self, divisions):
        """
        The number in which the scale is divided.

        Parameters
        -------
        divisions : int
            The number of scale divisions.
        """
        self.scale_indicator.set_num_divisions(divisions)

    @Property(int)
    def scaleHeight(self):
        """
        The scale height, fixed so it do not wiggle when value label resizes.

        Returns
        -------
        int
        """
        return self.scale_indicator.get_scale_height()

    @scaleHeight.setter
    def scaleHeight(self, value):
        """
        The scale height, fixed so it do not wiggle when value label resizes.

        Parameters
        -------
        divisions : int
            The scale height.
        """
        self.scale_indicator.set_scale_height(value)

    @Property(Qt.Edge)
    def valuePosition(self):
        """
        The position of the value label (Top, Bottom, Left or Right).

        Returns
        -------
        int
            Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge
        """
        return self._value_position

    @valuePosition.setter
    def valuePosition(self, position):
        """
       The position of the value label (Top, Bottom, Left or Right).

        Parameters
        ----------
        position : int
            Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge
        """
        self._value_position = position
        self.setup_widgets_for_orientation(self.orientation, self.flipScale, self.invertedAppearance, position)

    @Property(bool)
    def originAtZero(self):
        """
        Whether or not the scale indicator should start at zero value.
        Applies only for bar indicator.

        Returns
        -------
        bool
        """
        return self.scale_indicator.get_origin_at_zero()

    @originAtZero.setter
    def originAtZero(self, checked):
        """
        Whether or not the scale indicator should start at zero value.
        Applies only for bar indicator.

        Parameters
        ----------
        checked : bool
        """
        self.scale_indicator.set_origin_at_zero(checked)

    @Property(bool)
    def limitsFromChannel(self):
        """
        Whether or not the scale indicator should use the limits information
        from the channel.

        Returns
        -------
        bool
        """
        return self._limits_from_channel

    @limitsFromChannel.setter
    def limitsFromChannel(self, checked):
        """
        Whether or not the scale indicator should use the limits information
        from the channel.

        Parameters
        ----------
        checked : bool
            True to use the limits from the Channel, False to use the user-defined
            values.
        """
        if self._limits_from_channel != checked:
            self._limits_from_channel = checked
            if checked:
                if self._lower_ctrl_limit:
                    self.scale_indicator.set_lower_limit(self._lower_ctrl_limit)
                if self._upper_ctrl_limit:
                    self.scale_indicator.set_upper_limit(self._upper_ctrl_limit)
            else:
                self.scale_indicator.set_lower_limit(self._user_lower_limit)
                self.scale_indicator.set_upper_limit(self._user_upper_limit)
            self.update_labels()

    @Property(float)
    def userLowerLimit(self):
        """
        The user-defined lower limit for the scale.

        Returns
        -------
        float
        """
        return self._user_lower_limit

    @userLowerLimit.setter
    def userLowerLimit(self, value):
        """
        The user-defined lower limit for the scale.

        Parameters
        ----------
        value : float
            The new lower limit value.
        """
        if self._limits_from_channel:
            return
        self._user_lower_limit = value
        self.scale_indicator.set_lower_limit(self._user_lower_limit)
        self.update_labels()

    @Property(float)
    def userUpperLimit(self):
        """
        The user-defined upper limit for the scale.

        Returns
        -------
        float
        """
        return self._user_upper_limit

    @userUpperLimit.setter
    def userUpperLimit(self, value):
        """
        The user-defined upper limit for the scale.

        Parameters
        ----------
        value : float
            The new upper limit value.
        """
        if self._limits_from_channel:
            return
        self._user_upper_limit = value
        self.scale_indicator.set_upper_limit(self._user_upper_limit)
        self.update_labels()