예제 #1
0
    def __init__(self, editor):
        Panel.__init__(self, editor)
        self._editor = editor
        self._editor.sig_cursor_position_changed.connect(
            self._handle_cursor_position_change_event
        )

        # The layout
        hbox = QHBoxLayout()
        self.class_cb = QComboBox()
        self.method_cb = QComboBox()
        hbox.addWidget(self.class_cb)
        hbox.addWidget(self.method_cb)
        hbox.setSpacing(0)
        hbox.setContentsMargins(0, 0, 0, 0)

        self.setLayout(hbox)

        # Internal data
        self.folds = None
        self.parents = None
        self.classes = None
        self.funcs = None

        # Initial data for the dropdowns.
        self.class_cb.addItem("<None>", 0)
        self.method_cb.addItem("<None>", 0)

        # Attach some events.
        self.class_cb.activated.connect(self.combobox_activated)
        self.method_cb.activated.connect(self.combobox_activated)
예제 #2
0
    def populate_unit_layout(self, unit_layout, gui=None):
        """
        Populate horizontal layout (living on conversion gui)
        with appropriate widgets.

        Layouts:
            [QComboBox]

        :param unit_layout: (QHBoxLayout) Horizontal layout
        :param gui: conversion gui
        :return: updated unit_layout
        """

        unit_str = self._unit.to_string()
        options = self._unit.find_equivalent_units(include_prefix_units=True)
        options = [i.to_string() for i in options]
        options.sort(key=lambda x: x.upper())
        if unit_str not in options:
            options.append(unit_str)
        index = options.index(unit_str)
        combo = QComboBox()
        # combo.setFixedWidth(200)
        combo.addItems(options)
        combo.setCurrentIndex(index)
        combo.currentIndexChanged.connect(self._update_message)
        self.options_combo = combo
        unit_layout.addWidget(combo)
        self._update_message()
        return unit_layout
예제 #3
0
class FormComboWidget(QWidget):
    update_buttons = Signal()
    
    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)
        
        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.combobox.currentIndexChanged.connect(
                                              self.stackwidget.setCurrentIndex)
        
        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)
            
    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [ widget.get() for widget in self.widgetlist]
예제 #4
0
class buttonwidget(QWidget):

    def __init__(self):
        # how connect
        super(buttonwidget, self).__init__()
        self.grid = QGridLayout()
        self.btnConnect = QPushButton("Connect")
        self.grid.addWidget(self.btnConnect, 0, 0, 1, 5)

        self.btnLock = QPushButton("Lock Cassette")
        self.grid.addWidget(self.btnLock, 1, 0, 1, 5)

        self.btnPurge = QPushButton("Purge")
        self.grid.addWidget(self.btnPurge, 2, 0, 1, 2)

        self.cbxPurgeType = QComboBox()
        self.cbxPurgeType.addItems(["Normal", "Extended", "Add Conditioner", "Custom"])
        self.grid.addWidget(self.cbxPurgeType, 2, 2, 1, 2)

        self.txtNumPurge = QLineEdit()
        self.grid.addWidget(self.txtNumPurge, 2, 4, 1, 1)

        self.btnRecover = QPushButton("Recover")
        self.grid.addWidget(self.btnRecover, 3, 0, 1, 5)

        self.btnHelp = QPushButton("Help")
        self.grid.addWidget(self.btnHelp, 4, 0, 1, 5)

        self.setLayout(self.grid)

    def updateButtonText(self):
        print('updating text')

    def EnableDisableButtons(self):
        print('enabeling,disabeling buttons')
예제 #5
0
    def _init_ui(self):
        self.slit_type_label = QLabel('Slit Type')
        self.slit_type_combo = QComboBox()
        self.slit_type_combo.currentIndexChanged.connect(self.update_info)

        hbl1 = QHBoxLayout()
        hbl1.addWidget(self.slit_type_label)
        hbl1.addWidget(self.slit_type_combo)

        self.slit_width_label = QLabel('Slit Width')
        self.slit_width_input = QLineEdit()
        self.slit_width_combo = QComboBox()
        self.slit_width_units = QLabel('arcsec')

        hbl2 = QHBoxLayout()
        hbl2.addWidget(self.slit_width_label)
        hbl2.addWidget(self.slit_width_input)
        hbl2.addWidget(self.slit_width_combo)
        hbl2.addWidget(self.slit_width_units)

        self.slit_length_label = QLabel('Slit Length')
        self.slit_length_input = QLineEdit()
        self.slit_length_combo = QComboBox()
        self.slit_length_units = QLabel('arcsec')

        hbl3 = QHBoxLayout()
        hbl3.addWidget(self.slit_length_label)
        hbl3.addWidget(self.slit_length_input)
        hbl3.addWidget(self.slit_length_combo)
        hbl3.addWidget(self.slit_length_units)

        self.okButton = QPushButton('Apply')
        self.okButton.clicked.connect(self.apply)
        self.okButton.setDefault(True)

        self.cancelButton = QPushButton('Cancel')
        self.cancelButton.clicked.connect(self.cancel)

        hbl4 = QHBoxLayout()
        hbl4.addWidget(self.cancelButton)
        hbl4.addWidget(self.okButton)

        vbl = QVBoxLayout()
        vbl.addLayout(hbl1)
        vbl.addLayout(hbl2)
        vbl.addLayout(hbl3)
        vbl.addLayout(hbl4)
        self.setLayout(vbl)
        self.vbl = vbl

        self._load_selections()
        self._populate_combo()
        self.update_info(0)

        self.show()
예제 #6
0
class _CompositionWidget(_ConditionWidget):

    def _init_ui(self):
        # Widgets
        self._cb_unit = QComboBox()
        self._cb_unit.addItems(list(_COMPOSITION_UNITS))

        # Layouts
        layout = _ConditionWidget._init_ui(self)
        layout.addRow('<i>Unit</i>', self._cb_unit)

        # Signals
        self._cb_unit.currentIndexChanged.connect(self.edited)

        return layout

    def parameter(self, parameter=None):
        parameter = _ConditionWidget.parameter(self, parameter)
        parameter.unit = self._cb_unit.currentText()
        return parameter

    def setParameter(self, condition):
        _ConditionWidget.setParameter(self, condition)
        self._cb_unit.setCurrentIndex(self._cb_unit.findText(condition.unit))

    def setReadOnly(self, state):
        _ConditionWidget.setReadOnly(self, state)
        self._cb_unit.setEnabled(not state)

    def isReadOnly(self):
        return _ConditionWidget.isReadOnly(self) and \
            not self._cb_unit.isEnabled()
예제 #7
0
class _AcquisitionRasterWidget(_AcquisitionWidget):

    def _init_ui(self):
        # Widgets
        self._cb_raster_mode = QComboBox()
        self._cb_raster_mode.addItems([None] + list(_RASTER_MODES))

        # Layouts
        layout = _AcquisitionWidget._init_ui(self)
        layout.addRow('Raster mode', self._cb_raster_mode)

        # Singals
        self._cb_raster_mode.currentIndexChanged.connect(self.edited)

        return layout

    def parameter(self, parameter=None):
        parameter = _AcquisitionWidget.parameter(self, parameter)
        parameter.raster_mode = self._cb_raster_mode.currentText()
        return parameter

    def setParameter(self, condition):
        _AcquisitionWidget.setParameter(self, condition)
        self._cb_raster_mode.setCurrentIndex(self._cb_raster_mode.findText(condition.raster_mode))

    def setReadOnly(self, state):
        _AcquisitionWidget.setReadOnly(self, state)
        self._cb_raster_mode.setEnabled(not state)

    def isReadOnly(self):
        return _AcquisitionWidget.isReadOnly(self) and \
            not self._cb_raster_mode.isEnabled()
예제 #8
0
 def create_combobox(self, text, choices, option, default=NoDefault,
                     tip=None, restart=False):
     """choices: couples (name, key)"""
     label = QLabel(text)
     combobox = QComboBox()
     if tip is not None:
         combobox.setToolTip(tip)
     for name, key in choices:
         if not (name is None and key is None):
             combobox.addItem(name, to_qvariant(key))
     # Insert separators
     count = 0
     for index, item in enumerate(choices):
         name, key = item
         if name is None and key is None:
             combobox.insertSeparator(index + count)
             count += 1
     self.comboboxes[combobox] = (option, default)
     layout = QHBoxLayout()
     layout.addWidget(label)
     layout.addWidget(combobox)
     layout.addStretch(1)
     layout.setContentsMargins(0, 0, 0, 0)
     widget = QWidget(self)
     widget.label = label
     widget.combobox = combobox
     widget.setLayout(layout)
     combobox.restart_required = restart
     combobox.label_text = text
     return widget
예제 #9
0
    def focusOutEvent(self, event):
        """Handle focus out event restoring the last valid selected path."""
        # Calling asynchronously the 'add_current_text' to avoid crash
        # https://groups.google.com/group/spyderlib/browse_thread/thread/2257abf530e210bd
        if not self.is_valid():
            lineedit = self.lineEdit()
            QTimer.singleShot(50, lambda: lineedit.setText(self.selected_text))

        hide_status = getattr(self.lineEdit(), 'hide_status_icon', None)
        if hide_status:
            hide_status()
        QComboBox.focusOutEvent(self, event)
예제 #10
0
class DetectorSpectrometerWidget(_DetectorWidget):

    def __init__(self, parent=None):
        _DetectorWidget.__init__(self, DetectorSpectrometer, parent)

    def _init_ui(self):
        # Widgets
        self._txt_channel_count = NumericalAttributeLineEdit(self.CLASS.channel_count)
        self._txt_channel_count.setFormat('{0:d}')
        self._wdg_calibration = CalibrationWidget()
        self._cb_collection_mode = QComboBox()
        self._cb_collection_mode.addItems([None] + list(_COLLECTION_MODES))

        # Layout
        layout = _DetectorWidget._init_ui(self)
        layout.insertRow(0, '<i>Channel count</i>', self._txt_channel_count)
        layout.insertRow(1, '<i>Calibration</i>', self._wdg_calibration)
        layout.addRow('Collection mode', self._cb_collection_mode)

        # Signals
        self._txt_channel_count.textEdited.connect(self.edited)
        self._wdg_calibration.edited.connect(self.edited)
        self._cb_collection_mode.currentIndexChanged.connect(self.edited)

        return layout

    def _create_parameter(self):
        return self.CLASS(1, CalibrationConstant('Quantity', 'm', 0.0))

    def parameter(self, parameter=None):
        parameter = _DetectorWidget.parameter(self, parameter)
        parameter.channel_count = self._txt_channel_count.text()
        parameter.calibration = self._wdg_calibration.calibration()
        parameter.collection_mode = self._cb_collection_mode.currentText()
        return parameter

    def setParameter(self, condition):
        _DetectorWidget.setParameter(self, condition)
        self._txt_channel_count.setText(condition.channel_count)
        self._wdg_calibration.setCalibration(condition.calibration)
        self._cb_collection_mode.setCurrentIndex(self._cb_collection_mode.findText(condition.collection_mode))

    def setReadOnly(self, state):
        _DetectorWidget.setReadOnly(self, state)
        self._txt_channel_count.setReadOnly(state)
        self._wdg_calibration.setReadOnly(state)
        self._cb_collection_mode.setEnabled(not state)

    def isReadOnly(self):
        return _DetectorWidget.isReadOnly(self) and \
            self._txt_channel_count.isReadOnly() and \
            self._wdg_calibration.isReadOnly() and \
            not self._cb_collection_mode.isEnabled()

    def hasAcceptableInput(self):
        return _DetectorWidget.hasAcceptableInput(self) and \
            self._txt_channel_count.hasAcceptableInput() and \
            self._wdg_calibration.hasAcceptableInput()
예제 #11
0
    def keyPressEvent(self, event):
        """Qt Override.

        Handle key press events.
        """
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            if self.add_current_text_if_valid():
                self.selected()
                self.hide_completer()
        elif event.key() == Qt.Key_Escape:
            self.set_current_text(self.selected_text)
            self.hide_completer()
        else:
            QComboBox.keyPressEvent(self, event)
예제 #12
0
class NewGrainDialog(QDialog):
    def __init__(self):
        super(NewGrainDialog, self).__init__()
        self.setup_ui()

    def setup_ui(self):
        self.setWindowTitle("Add New Grain")
        self.setWindowIcon(QIcon(RESOURCE_PATH + "icons/nakka-finocyl.gif"))

        self.resize(400, 400)

        controls = QGridLayout()
        gb_frame = QGroupBox(self.tr("Grain Design"))
        gb_frame.setLayout(controls)
        
        self.cb_grain_type = QComboBox()

        # TODO: make grain types auto propagate combobox
        self.cb_grain_type.addItems([self.tr("Cylindrical (BATES)")])
        controls.addWidget(QLabel(self.tr("Core Shape")), 0, 0)
        controls.addWidget(self.cb_grain_type, 0, 1)

        self.cb_propellant_type = QComboBox()
        self.cb_propellant_type.addItems(app_context.propellant_db.propellant_names())
        controls.addWidget(QLabel(self.tr("Propellant Type")), 1, 0)
        controls.addWidget(self.cb_propellant_type, 1, 1)

        # ok and cancel buttons
        btn_ok = QPushButton(self.tr("Apply"))
        btn_ok.clicked.connect(self.confirm_grain)
        btn_cancel = QPushButton(self.tr("Close"))
        btn_cancel.clicked.connect(self.close)

        lay_btns = QHBoxLayout()
        lay_btns.addWidget(btn_ok)
        lay_btns.addWidget(btn_cancel)
        frame_btns = QFrame()
        frame_btns.setLayout(lay_btns)

        # master layout
        master = QVBoxLayout()
        master.addWidget(gb_frame)
        master.addSpacing(10)
        master.addWidget(frame_btns)
        self.setLayout(master)

    def confirm_grain(self):
        # grain = OpenBurnGrain.from_widget_factory(...)
        pass
예제 #13
0
파일: widget.py 프로젝트: DanNixon/mantid
 def _make_search_box(self):
     """
     Make a algorithm search box.
     :return: A QComboBox
     """
     search_box = QComboBox(self)
     search_box.setEditable(True)
     search_box.completer().setCompletionMode(QCompleter.PopupCompletion)
     search_box.setInsertPolicy(QComboBox.NoInsert)
     search_box.editTextChanged.connect(self._on_search_box_selection_changed)
     search_box.lineEdit().returnPressed.connect(self.execute_algorithm)
     return search_box
예제 #14
0
파일: logdisplay.py 프로젝트: slaclab/pydm
 def __init__(self, parent=None, logname=None, level=logging.NOTSET):
     QWidget.__init__(self, parent=parent)
     # Create Widgets
     self.label = QLabel('Minimum displayed log level: ', parent=self)
     self.combo = QComboBox(parent=self)
     self.text = QPlainTextEdit(parent=self)
     self.text.setReadOnly(True)
     self.clear_btn = QPushButton("Clear", parent=self)
     # Create layout
     layout = QVBoxLayout()
     level_control = QHBoxLayout()
     level_control.addWidget(self.label)
     level_control.addWidget(self.combo)
     layout.addLayout(level_control)
     layout.addWidget(self.text)
     layout.addWidget(self.clear_btn)
     self.setLayout(layout)
     # Allow QCombobox to control log level
     for log_level, value in LogLevels.as_dict().items():
         self.combo.addItem(log_level, value)
     self.combo.currentIndexChanged[str].connect(self.setLevel)
     # Allow QPushButton to clear log text
     self.clear_btn.clicked.connect(self.clear)
     # Create a handler with the default format
     self.handler = GuiHandler(level=level, parent=self)
     self.logFormat = self.default_format
     self.handler.message.connect(self.write)
     # Create logger. Either as a root or given logname
     self.log = None
     self.level = None
     self.logName = logname or ''
     self.logLevel = level
     self.destroyed.connect(functools.partial(logger_destroyed, self.log))
예제 #15
0
    def _init_ui(self):
        # Widgets
        self._cb_technology = QComboBox()
        self._cb_technology.addItems([None] + list(_XEDS_TECHNOLOGIES))
        self._txt_nominal_throughput = NumericalAttributeLineEdit(self.CLASS.nominal_throughput)
        self._txt_time_constant = NumericalAttributeLineEdit(self.CLASS.time_constant)
        self._txt_strobe_rate = NumericalAttributeLineEdit(self.CLASS.strobe_rate)
        self._wdg_window = WindowWidget()

        # Layout
        form = DetectorSpectrometerWidget._init_ui(self)
        form.addRow('Technology', self._cb_technology)
        form.addRow('Nominal throughput', self._txt_nominal_throughput)
        form.addRow('Time constant', self._txt_time_constant)
        form.addRow('Strobe rate', self._txt_strobe_rate)
        form.addRow('Window', self._wdg_window)

        # Signals
        self._cb_technology.currentIndexChanged.connect(self.edited)
        self._txt_nominal_throughput.textEdited.connect(self.edited)
        self._txt_time_constant.textEdited.connect(self.edited)
        self._txt_strobe_rate.textEdited.connect(self.edited)
        self._wdg_window.edited.connect(self.edited)

        return form
예제 #16
0
    def _init_ui(self):
        # Widgets
        self._txt_bias = NumericalAttributeLineEdit(self.CLASS.bias)
        self._txt_gain = NumericalAttributeLineEdit(self.CLASS.gain)
        self._txt_base_level = NumericalAttributeLineEdit(self.CLASS.base_level)
        self._txt_window = NumericalAttributeLineEdit(self.CLASS.window)
        self._cb_mode = QComboBox()
        self._cb_mode.addItems([None] + list(_PHA_MODES))

        # Layouts
        layout = ParameterWidget._init_ui(self)
        layout.addRow('Bias', self._txt_bias)
        layout.addRow('Gain', self._txt_gain)
        layout.addRow('Base level', self._txt_base_level)
        layout.addRow('Window', self._txt_window)
        layout.addRow('Mode', self._cb_mode)

        # Signals
        self._txt_bias.textEdited.connect(self.edited)
        self._txt_gain.textEdited.connect(self.edited)
        self._txt_base_level.textEdited.connect(self.edited)
        self._txt_window.textEdited.connect(self.edited)
        self._cb_mode.currentIndexChanged.connect(self.edited)

        return layout
예제 #17
0
    def __init__(self, parent, order):
        super(LayoutSaveDialog, self).__init__(parent)

        # variables
        self._parent = parent

        # widgets
        self.combo_box = QComboBox(self)
        self.combo_box.addItems(order)
        self.combo_box.setEditable(True)
        self.combo_box.clearEditText()
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok |
                                           QDialogButtonBox.Cancel,
                                           Qt.Horizontal, self)
        self.button_ok = self.button_box.button(QDialogButtonBox.Ok)
        self.button_cancel = self.button_box.button(QDialogButtonBox.Cancel)

        # widget setup
        self.button_ok.setEnabled(False)
        self.dialog_size = QSize(300, 100)
        self.setWindowTitle('Save layout as')
        self.setModal(True)
        self.setMinimumSize(self.dialog_size)
        self.setFixedSize(self.dialog_size)

        # layouts
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.combo_box)
        self.layout.addWidget(self.button_box)
        self.setLayout(self.layout)

        # signals and slots
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.close)
        self.combo_box.editTextChanged.connect(self.check_text)
예제 #18
0
    def __init__(self):
        # how connect
        super(buttonwidget, self).__init__()
        self.grid = QGridLayout()
        self.btnConnect = QPushButton("Connect")
        self.grid.addWidget(self.btnConnect, 0, 0, 1, 5)

        self.btnLock = QPushButton("Lock Cassette")
        self.grid.addWidget(self.btnLock, 1, 0, 1, 5)

        self.btnPurge = QPushButton("Purge")
        self.grid.addWidget(self.btnPurge, 2, 0, 1, 2)

        self.cbxPurgeType = QComboBox()
        self.cbxPurgeType.addItems(["Normal", "Extended", "Add Conditioner", "Custom"])
        self.grid.addWidget(self.cbxPurgeType, 2, 2, 1, 2)

        self.txtNumPurge = QLineEdit()
        self.grid.addWidget(self.txtNumPurge, 2, 4, 1, 1)

        self.btnRecover = QPushButton("Recover")
        self.grid.addWidget(self.btnRecover, 3, 0, 1, 5)

        self.btnHelp = QPushButton("Help")
        self.grid.addWidget(self.btnHelp, 4, 0, 1, 5)

        self.setLayout(self.grid)
예제 #19
0
    def _init_ui(self):
        # Widgets
        self._combobox = QComboBox()
        self._stack = QStackedWidget()

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

        # Register classes
        self._widget_indexes = {}

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

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

        return layout
예제 #20
0
    def _init_ui(self):
        # Widgets
        self._txt_step_count_x = NumericalAttributeLineEdit(self.CLASS.step_count_x)
        self._txt_step_count_x.setFormat('{0:d}')
        self._txt_step_count_y = NumericalAttributeLineEdit(self.CLASS.step_count_y)
        self._txt_step_count_y.setFormat('{0:d}')
        self._txt_step_size_x = NumericalAttributeLineEdit(self.CLASS.step_size_x)
        self._txt_step_size_y = NumericalAttributeLineEdit(self.CLASS.step_size_y)
        self._txt_frame_count = NumericalAttributeLineEdit(self.CLASS.frame_count)
        self._txt_frame_count.setFormat('{0:d}')
        self._cb_location = QComboBox()
        self._cb_location.addItems(list(_POSITION_LOCATIONS))
        self._wdg_position = SpecimenPositionWidget(inline=True)

        # Layouts
        layout = _AcquisitionRasterWidget._init_ui(self)
        layout.insertRow(0, '<i>Step count (x)</i>', self._txt_step_count_x)
        layout.insertRow(1, '<i>Step count (y)</i>', self._txt_step_count_y)
        layout.insertRow(2, 'Step size (x)', self._txt_step_size_x)
        layout.insertRow(3, 'Step size (y)', self._txt_step_size_y)
        layout.addRow('Frame count', self._txt_frame_count)
        layout.addRow('Position', self._cb_location)
        layout.addWidget(self._wdg_position)

        # Signals
        self._txt_step_count_x.textEdited.connect(self.edited)
        self._txt_step_count_y.textEdited.connect(self.edited)
        self._txt_step_size_x.textEdited.connect(self.edited)
        self._txt_step_size_y.textEdited.connect(self.edited)
        self._txt_frame_count.textEdited.connect(self.edited)
        self._cb_location.currentIndexChanged.connect(self.edited)
        self._wdg_position.edited.connect(self.edited)

        return layout
예제 #21
0
 def __init__(self, value, parent=None):
     QGridLayout.__init__(self)
     font = tuple_to_qfont(value)
     assert font is not None
     
     # Font family
     self.family = QFontComboBox(parent)
     self.family.setCurrentFont(font)
     self.addWidget(self.family, 0, 0, 1, -1)
     
     # Font size
     self.size = QComboBox(parent)
     self.size.setEditable(True)
     sizelist = list(range(6, 12)) + list(range(12, 30, 2)) + [36, 48, 72]
     size = font.pointSize()
     if size not in sizelist:
         sizelist.append(size)
         sizelist.sort()
     self.size.addItems([str(s) for s in sizelist])
     self.size.setCurrentIndex(sizelist.index(size))
     self.addWidget(self.size, 1, 0)
     
     # Italic or not
     self.italic = QCheckBox(_("Italic"), parent)
     self.italic.setChecked(font.italic())
     self.addWidget(self.italic, 1, 1)
     
     # Bold or not
     self.bold = QCheckBox(_("Bold"), parent)
     self.bold.setChecked(font.bold())
     self.addWidget(self.bold, 1, 2)
예제 #22
0
    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)
        self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
        self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        
        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        self.add_widgets(combo_label, self.combo, 10, self.stack)
        self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run configuration per file"))
예제 #23
0
    def _init_ui(self):
        # LINE 1: Data component drop down
        self.component_prompt = QLabel("Data Component:")
        self.component_prompt.setWordWrap(True)
        # Add the data component labels to the drop down, with the ComponentID
        # set as the userData:
        if self.parent is not None and hasattr(self.parent, 'data_components'):
            self.label_data = [(str(cid), cid) for cid in self.parent.data_components]
        else:
            self.label_data = [(str(cid), cid) for cid in self.data.visible_components]

        default_index = 0
        self.component_combo = QComboBox()
        self.component_combo.setFixedWidth(200)
        update_combobox(self.component_combo, self.label_data, default_index=default_index)
        self.component_combo.currentIndexChanged.connect(self.update_unit_layout)

        # hbl is short for Horizontal Box Layout
        hbl1 = QHBoxLayout()
        hbl1.addWidget(self.component_prompt)
        hbl1.addWidget(self.component_combo)
        hbl1.addStretch(1)

        # LINE 2: Unit conversion layout
        # This layout is filled by CubeVizUnit
        self.unit_layout = QHBoxLayout()  # this is hbl2

        # LINE 3: Message box
        self.message_box = QLabel("")
        hbl3 = QHBoxLayout()
        hbl3.addWidget(self.message_box)
        hbl3.addStretch(1)

        # Line 4: Buttons
        ok_text = "Convert Data" if self.convert_data else "Convert Displayed Units"
        ok_function = self.convert_data_units if self.convert_data else self.convert_displayed_units
        self.okButton = QPushButton(ok_text)
        self.okButton.clicked.connect(ok_function)
        self.okButton.setDefault(True)

        self.cancelButton = QPushButton("Cancel")
        self.cancelButton.clicked.connect(self.cancel)

        hbl4 = QHBoxLayout()
        hbl4.addStretch(1)
        hbl4.addWidget(self.cancelButton)
        hbl4.addWidget(self.okButton)

        vbl = QVBoxLayout()
        vbl.addLayout(hbl1)
        vbl.addLayout(self.unit_layout)
        vbl.addLayout(hbl3)
        vbl.addLayout(hbl4)
        self.setLayout(vbl)
        self.vbl = vbl

        self.update_unit_layout(default_index)

        self.show()
예제 #24
0
    def __init__(self, parent=None):
        super().__init__(parent)
        self.settings = QSettings()

        # decimal
        self.decimalLabel = QLabel(self.tr("decimal:"))
        self.decimalComboBox = QComboBox()
        self.decimalLabel.setBuddy(self.decimalComboBox)
        self.decimalComboBox.addItems([",", "."])

        # separator
        self.separatorLabel = QLabel(self.tr("separator:"))
        self.separatorComboBox = QComboBox()
        self.separatorLabel.setBuddy(self.separatorComboBox)
        self.separatorComboBox.addItem("Semicolon ';'", ';')
        self.separatorComboBox.addItem("Comma ','", ',')
        self.separatorComboBox.addItem("Tabulator '\\t'", '\t')
        self.separatorComboBox.addItem("Whitespace ' '", ' ')

        # buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

        # layout
        layout = QGridLayout()
        layout.addWidget(self.decimalLabel, 0, 0)
        layout.addWidget(self.decimalComboBox, 0, 1)
        layout.addWidget(self.separatorLabel, 1, 0)
        layout.addWidget(self.separatorComboBox, 1, 1)
        layout.addWidget(self.buttons, 2, 0, 1, 2)
        self.setLayout(layout)

        # settings
        self.decimalComboBox.setCurrentIndex(
            self.decimalComboBox.findText(
                self.settings.value(DECIMAL_SETTING, ","))
        )
        self.separatorComboBox.setCurrentIndex(
            self.separatorComboBox.findData(
                self.settings.value(SEPARATOR_SETTING, ";"))
        )

        self.setWindowTitle(self.tr("record settings"))
예제 #25
0
    def __init__(self, settings, parent=None):
        super().__init__(parent)
        self.settings = settings
        self.serialports = []

        # port
        self.portLabel = QLabel(self.tr("COM Port:"))
        self.portComboBox = QComboBox()
        self.portLabel.setBuddy(self.portComboBox)
        self.refresh_comports(self.portComboBox)

        # baudrate
        self.baudrateLabel = QLabel(self.tr("Baudrate:"))
        self.baudrateComboBox = QComboBox()
        self.baudrateLabel.setBuddy(self.baudrateComboBox)
        for br in BAUDRATES:
            self.baudrateComboBox.addItem(str(br), br)

        # buttons
        self.dlgbuttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal)
        self.dlgbuttons.rejected.connect(self.reject)
        self.dlgbuttons.accepted.connect(self.accept)

        # layout
        layout = QGridLayout()
        layout.addWidget(self.portLabel, 0, 0)
        layout.addWidget(self.portComboBox, 0, 1)
        layout.addWidget(self.baudrateLabel, 1, 0)
        layout.addWidget(self.baudrateComboBox, 1, 1)
        layout.addWidget(self.dlgbuttons, 2, 0, 1, 2)
        self.setLayout(layout)
        self.setWindowTitle(self.tr("Serial Settings"))

        # settings
        defaults = {
            PORT_SETTING: "",
            BAUDRATE_SETTING: "115200"
        }
        self.tmp_settings = ConfigManager()
        self.tmp_settings.set_defaults(defaults)
        self.tmp_settings.set_many(
            {key: self.settings.get(key) for key in defaults.keys()}
        )
        self.tmp_settings.add_handler(PORT_SETTING, self.portComboBox)
        self.tmp_settings.add_handler(BAUDRATE_SETTING, self.baudrateComboBox)
예제 #26
0
    def _init_ui(self):
        print("TEST HUHU")
        # Controls
        self._cb_type = QComboBox()
        self._cb_type.addItems([None] + list(_INTENSITY_TYPES))
        self._cb_measure = QComboBox()
        self._cb_measure.addItems([None] + list(_INTENSITY_MEASURES))

        # Layouts
        layout = _ConditionWidget._init_ui(self)
        layout.addRow("<i>Type</i>", self._cb_type)
        layout.addRow("<i>Measure</i>", self._cb_measure)

        # Signals
        self._cb_type.currentIndexChanged.connect(self.edited)
        self._cb_measure.currentIndexChanged.connect(self.edited)

        return layout
예제 #27
0
    def event(self, event):
        """Qt Override.

        Filter tab keys and process double tab keys.
        """
        if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab):
            self.sig_tab_pressed.emit(True)
            self.numpress += 1
            if self.numpress == 1:
                self.presstimer = QTimer.singleShot(400, self.handle_keypress)
            return True
        return QComboBox.event(self, event)
예제 #28
0
    def _init_ui(self):
        # Widgets
        self._cb_unit = QComboBox()
        self._cb_unit.addItems(list(_COMPOSITION_UNITS))

        # Layouts
        layout = _ConditionWidget._init_ui(self)
        layout.addRow('<i>Unit</i>', self._cb_unit)

        # Signals
        self._cb_unit.currentIndexChanged.connect(self.edited)

        return layout
예제 #29
0
    def _init_ui(self):
        # Controls
        self._cb_interpolation = QComboBox()
        self._cb_interpolation.addItems([None] + list(_BACKGROUND_INTERPOLATIONS))

        # Layouts
        layout = _ConditionWidget._init_ui(self)
        layout.addRow("<i>Interpolation</i>", self._cb_interpolation)

        # Signals
        self._cb_interpolation.currentIndexChanged.connect(self.edited)

        return layout
예제 #30
0
    def _init_ui(self):
        # Widgets
        self._cb_raster_mode = QComboBox()
        self._cb_raster_mode.addItems([None] + list(_RASTER_MODES))

        # Layouts
        layout = _AcquisitionWidget._init_ui(self)
        layout.addRow('Raster mode', self._cb_raster_mode)

        # Singals
        self._cb_raster_mode.currentIndexChanged.connect(self.edited)

        return layout
예제 #31
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.mode.connect(self.set_mode)
        self.layer.events.n_dimensional.connect(self._on_n_dim_change)
        self.layer.events.symbol.connect(self._on_symbol_change)
        self.layer.events.size.connect(self._on_size_change)
        self.layer.events.current_edge_color.connect(
            self._on_edge_color_change)
        self.layer.events.current_face_color.connect(
            self._on_face_color_change)
        self.layer.events.editable.connect(self._on_editable_change)

        sld = QSlider(Qt.Horizontal)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(1)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        value = self.layer.current_size
        sld.setValue(int(value))
        sld.valueChanged.connect(self.changeSize)
        self.sizeSlider = sld

        face_comboBox = QComboBox()
        face_comboBox.addItems(self.layer._colors)
        face_comboBox.activated[str].connect(self.changeFaceColor)
        self.faceComboBox = face_comboBox
        self.faceColorSwatch = QFrame()
        self.faceColorSwatch.setObjectName('swatch')
        self.faceColorSwatch.setToolTip('Face color swatch')
        self._on_face_color_change()

        edge_comboBox = QComboBox()
        edge_comboBox.addItems(self.layer._colors)
        edge_comboBox.activated[str].connect(self.changeEdgeColor)
        self.edgeComboBox = edge_comboBox
        self.edgeColorSwatch = QFrame()
        self.edgeColorSwatch.setObjectName('swatch')
        self.edgeColorSwatch.setToolTip('Edge color swatch')
        self._on_edge_color_change()

        symbol_comboBox = QComboBox()
        symbol_comboBox.addItems([str(s) for s in Symbol])
        index = symbol_comboBox.findText(self.layer.symbol,
                                         Qt.MatchFixedString)
        symbol_comboBox.setCurrentIndex(index)
        symbol_comboBox.activated[str].connect(self.changeSymbol)
        self.symbolComboBox = symbol_comboBox

        ndim_cb = QCheckBox()
        ndim_cb.setToolTip('N-dimensional points')
        ndim_cb.setChecked(self.layer.n_dimensional)
        ndim_cb.stateChanged.connect(self.change_ndim)
        self.ndimCheckBox = ndim_cb

        self.select_button = QtModeRadioButton(layer,
                                               'select_points',
                                               Mode.SELECT,
                                               tooltip='Select points')
        self.addition_button = QtModeRadioButton(layer,
                                                 'add_points',
                                                 Mode.ADD,
                                                 tooltip='Add points')
        self.panzoom_button = QtModeRadioButton(layer,
                                                'pan_zoom',
                                                Mode.PAN_ZOOM,
                                                tooltip='Pan/zoom',
                                                checked=True)
        self.delete_button = QtModePushButton(
            layer,
            'delete_shape',
            slot=self.layer.remove_selected,
            tooltip='Delete selected points',
        )

        self.button_group = QButtonGroup(self)
        self.button_group.addButton(self.select_button)
        self.button_group.addButton(self.addition_button)
        self.button_group.addButton(self.panzoom_button)

        button_row = QHBoxLayout()
        button_row.addWidget(self.delete_button)
        button_row.addWidget(self.addition_button)
        button_row.addWidget(self.select_button)
        button_row.addWidget(self.panzoom_button)
        button_row.addStretch(1)
        button_row.setSpacing(4)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addLayout(button_row, 0, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('opacity:'), 1, 0)
        self.grid_layout.addWidget(self.opacitySlider, 1, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('point size:'), 2, 0)
        self.grid_layout.addWidget(self.sizeSlider, 2, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('blending:'), 3, 0)
        self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('symbol:'), 4, 0)
        self.grid_layout.addWidget(self.symbolComboBox, 4, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('face color:'), 5, 0)
        self.grid_layout.addWidget(self.faceComboBox, 5, 2)
        self.grid_layout.addWidget(self.faceColorSwatch, 5, 1)
        self.grid_layout.addWidget(QLabel('edge color:'), 6, 0)
        self.grid_layout.addWidget(self.edgeComboBox, 6, 2)
        self.grid_layout.addWidget(self.edgeColorSwatch, 6, 1)
        self.grid_layout.addWidget(QLabel('n-dim:'), 7, 0)
        self.grid_layout.addWidget(self.ndimCheckBox, 7, 1)
        self.grid_layout.setRowStretch(8, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
예제 #32
0
파일: elements.py 프로젝트: ablot/neuro
def add_combobox(layout, label, items, row, column=0):
    combobox = QComboBox()
    combobox.addItems(items)
    layout.addWidget(QLabel(label), row, column)
    layout.addWidget(combobox, row, column + 1)
    return combobox
예제 #33
0
파일: widgets.py 프로젝트: trollodel/spyder
class RunConfigDialog(BaseRunConfigDialog):
    """Run configuration dialog box: multiple file version"""
    def __init__(self, parent=None):
        BaseRunConfigDialog.__init__(self, parent)
        self.file_to_run = None
        self.combo = None
        self.stack = None

    def run_btn_clicked(self):
        """Run button was just clicked"""
        self.file_to_run = str(self.combo.currentText())

    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)

        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            widget.layout().setContentsMargins(0, 0, 0, 0)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        layout = self.add_widgets(combo_label, self.combo, 10, self.stack)
        widget_dialog = QWidget()
        widget_dialog.setLayout(layout)
        scrollarea = QScrollArea(self)
        scrollarea.setWidget(widget_dialog)
        scrollarea.setMinimumWidth(600)
        scrollarea.setWidgetResizable(True)
        scroll_layout = QVBoxLayout(self)
        scroll_layout.addWidget(scrollarea)
        self.add_button_box(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run configuration per file"))

    def accept(self):
        """Reimplement Qt method"""
        configurations = []
        for index in range(self.stack.count()):
            filename = str(self.combo.itemText(index))
            runconfigoptions = self.stack.widget(index)
            if index == self.stack.currentIndex() and\
               not runconfigoptions.is_valid():
                return
            options = runconfigoptions.get()
            configurations.append((filename, options))
        _set_run_configurations(configurations)
        QDialog.accept(self)
예제 #34
0
    def __init__(self, parent):
        if PYQT5:
            SpyderPluginWidget.__init__(self, parent, main = parent)
        else:
            SpyderPluginWidget.__init__(self, parent)

        self.internal_shell = None

        # Initialize plugin
        self.initialize_plugin()

        self.no_doc_string = _("No documentation available")

        self._last_console_cb = None
        self._last_editor_cb = None

        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        color_scheme = self.get_color_scheme()
        self.set_plain_text_font(self.get_plugin_font(), color_scheme)
        self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap'))

        # Add entries to read-only editor context-menu
        self.wrap_action = create_action(self, _("Wrap lines"),
                                         toggled=self.toggle_wrap_mode)
        self.wrap_action.setChecked(self.get_option('wrap'))
        self.plain_text.editor.readonly_menu.addSeparator()
        add_actions(self.plain_text.editor.readonly_menu, (self.wrap_action,))

        self.set_rich_text_font(self.get_plugin_font('rich_text'))

        self.shell = None

        # locked = disable link with Console
        self.locked = False
        self._last_texts = [None, None]
        self._last_editor_doc = None

        # Object name
        layout_edit = QHBoxLayout()
        layout_edit.setContentsMargins(0, 0, 0, 0)
        txt = _("Source")
        if sys.platform == 'darwin':
            source_label = QLabel("  " + txt)
        else:
            source_label = QLabel(txt)
        layout_edit.addWidget(source_label)
        self.source_combo = QComboBox(self)
        self.source_combo.addItems([_("Console"), _("Editor")])
        self.source_combo.currentIndexChanged.connect(self.source_changed)
        if (not programs.is_module_installed('rope') and
                not programs.is_module_installed('jedi', '>=0.8.1')):
            self.source_combo.hide()
            source_label.hide()
        layout_edit.addWidget(self.source_combo)
        layout_edit.addSpacing(10)
        layout_edit.addWidget(QLabel(_("Object")))
        self.combo = ObjectComboBox(self)
        layout_edit.addWidget(self.combo)
        self.object_edit = QLineEdit(self)
        self.object_edit.setReadOnly(True)
        layout_edit.addWidget(self.object_edit)
        self.combo.setMaxCount(self.get_option('max_history_entries'))
        self.combo.addItems( self.load_history() )
        self.combo.setItemText(0, '')
        self.combo.valid.connect(lambda valid: self.force_refresh())

        # Plain text docstring option
        self.docstring = True
        self.rich_help = self.get_option('rich_mode', True)
        self.plain_text_action = create_action(self, _("Plain Text"),
                                               toggled=self.toggle_plain_text)

        # Source code option
        self.show_source_action = create_action(self, _("Show Source"),
                                                toggled=self.toggle_show_source)

        # Rich text option
        self.rich_text_action = create_action(self, _("Rich Text"),
                                         toggled=self.toggle_rich_text)

        # Add the help actions to an exclusive QActionGroup
        help_actions = QActionGroup(self)
        help_actions.setExclusive(True)
        help_actions.addAction(self.plain_text_action)
        help_actions.addAction(self.rich_text_action)

        # Automatic import option
        self.auto_import_action = create_action(self, _("Automatic import"),
                                                toggled=self.toggle_auto_import)
        auto_import_state = self.get_option('automatic_import')
        self.auto_import_action.setChecked(auto_import_state)

        # Lock checkbox
        self.locked_button = create_toolbutton(self,
                                               triggered=self.toggle_locked)
        layout_edit.addWidget(self.locked_button)
        self._update_lock_icon()

        # Option menu
        options_button = create_toolbutton(self, text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, [self.rich_text_action, self.plain_text_action,
                           self.show_source_action, None,
                           self.auto_import_action])
        options_button.setMenu(menu)
        layout_edit.addWidget(options_button)

        if self.rich_help:
            self.switch_to_rich_text()
        else:
            self.switch_to_plain_text()
        self.plain_text_action.setChecked(not self.rich_help)
        self.rich_text_action.setChecked(self.rich_help)
        self.source_changed()

        # Main layout
        layout = create_plugin_layout(layout_edit)
        # we have two main widgets, but only one of them is shown at a time
        layout.addWidget(self.plain_text)
        layout.addWidget(self.rich_text)
        self.setLayout(layout)

        # Add worker thread for handling rich text rendering
        self._sphinx_thread = SphinxThread(
                                  html_text_no_doc=warning(self.no_doc_string))
        self._sphinx_thread.html_ready.connect(
                                             self._on_sphinx_thread_html_ready)
        self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg)

        # Handle internal and external links
        view = self.rich_text.webview
        if not WEBENGINE:
            view.page().setLinkDelegationPolicy(QWebEnginePage.DelegateAllLinks)
        view.linkClicked.connect(self.handle_link_clicks)

        self._starting_up = True
예제 #35
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.edge_width.connect(self._on_edge_width_change)
        self.layer.events.length.connect(self._on_length_change)
        self.layer.events.edge_color_mode.connect(
            self._on_edge_color_mode_change)
        self.layer.events.edge_color.connect(self._on_edge_color_change)

        # dropdown to select the property for mapping edge_color
        color_properties = self._get_property_values()
        color_prop_box = QComboBox(self)
        color_prop_box.activated[str].connect(self.change_edge_color_property)
        color_prop_box.addItems(color_properties)
        self.color_prop_box = color_prop_box
        self.edge_prop_label = QLabel('edge property:')

        # vector direct color mode adjustment and widget
        self.edgeColorEdit = QColorSwatchEdit(
            initial_color=self.layer.edge_color,
            tooltip='click to set current edge color',
        )
        self.edgeColorEdit.color_changed.connect(self.change_edge_color_direct)
        self.edge_color_label = QLabel('edge color:')
        self._on_edge_color_change()

        # dropdown to select the edge color mode
        colorModeComboBox = QComboBox(self)
        colorModeComboBox.addItems(ColorMode.keys())
        colorModeComboBox.activated[str].connect(self.change_edge_color_mode)
        self.color_mode_comboBox = colorModeComboBox
        self._on_edge_color_mode_change()

        # line width in pixels
        self.widthSpinBox = QDoubleSpinBox()
        self.widthSpinBox.setKeyboardTracking(False)
        self.widthSpinBox.setSingleStep(0.1)
        self.widthSpinBox.setMinimum(0.1)
        self.widthSpinBox.setValue(self.layer.edge_width)
        self.widthSpinBox.valueChanged.connect(self.change_width)

        # line length
        self.lengthSpinBox = QDoubleSpinBox()
        self.lengthSpinBox.setKeyboardTracking(False)
        self.lengthSpinBox.setSingleStep(0.1)
        self.lengthSpinBox.setValue(self.layer.length)
        self.lengthSpinBox.setMinimum(0.1)
        self.lengthSpinBox.valueChanged.connect(self.change_length)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addWidget(QLabel('opacity:'), 0, 0)
        self.grid_layout.addWidget(self.opacitySlider, 0, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('width:'), 1, 0)
        self.grid_layout.addWidget(self.widthSpinBox, 1, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('length:'), 2, 0)
        self.grid_layout.addWidget(self.lengthSpinBox, 2, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('blending:'), 3, 0)
        self.grid_layout.addWidget(self.blendComboBox, 3, 1, 1, 2)
        self.grid_layout.addWidget(QLabel('edge color mode:'), 4, 0)
        self.grid_layout.addWidget(self.color_mode_comboBox, 4, 1, 1, 2)
        self.grid_layout.addWidget(self.edge_color_label, 5, 0)
        self.grid_layout.addWidget(self.edgeColorEdit, 5, 1, 1, 2)
        self.grid_layout.addWidget(self.edge_prop_label, 6, 0)
        self.grid_layout.addWidget(self.color_prop_box, 6, 1, 1, 2)
        self.grid_layout.setRowStretch(7, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
예제 #36
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.mode.connect(self._on_mode_change)
        self.layer.events.selected_label.connect(self._on_selection_change)
        self.layer.events.brush_size.connect(self._on_brush_size_change)
        self.layer.events.contiguous.connect(self._on_contig_change)
        self.layer.events.n_dimensional.connect(self._on_n_dim_change)
        self.layer.events.editable.connect(self._on_editable_change)
        self.layer.events.preserve_labels.connect(
            self._on_preserve_labels_change
        )
        self.layer.events.color_mode.connect(self._on_color_mode_change)

        # selection spinbox
        self.selectionSpinBox = QSpinBox()
        self.selectionSpinBox.setKeyboardTracking(False)
        self.selectionSpinBox.setSingleStep(1)
        self.selectionSpinBox.setMinimum(0)
        self.selectionSpinBox.setMaximum(2147483647)
        self.selectionSpinBox.valueChanged.connect(self.changeSelection)
        self.selectionSpinBox.setAlignment(Qt.AlignCenter)
        self._on_selection_change()

        sld = QSlider(Qt.Horizontal)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(1)
        sld.setMaximum(40)
        sld.setSingleStep(1)
        sld.valueChanged.connect(self.changeSize)
        self.brushSizeSlider = sld
        self._on_brush_size_change()

        contig_cb = QCheckBox()
        contig_cb.setToolTip('contiguous editing')
        contig_cb.stateChanged.connect(self.change_contig)
        self.contigCheckBox = contig_cb
        self._on_contig_change()

        ndim_cb = QCheckBox()
        ndim_cb.setToolTip('n-dimensional editing')
        ndim_cb.stateChanged.connect(self.change_ndim)
        self.ndimCheckBox = ndim_cb
        self._on_n_dim_change()

        preserve_labels_cb = QCheckBox()
        preserve_labels_cb.setToolTip(
            'preserve existing labels while painting'
        )
        preserve_labels_cb.stateChanged.connect(self.change_preserve_labels)
        self.preserveLabelsCheckBox = preserve_labels_cb
        self._on_preserve_labels_change()

        # shuffle colormap button
        self.colormapUpdate = QtModePushButton(
            None, 'shuffle', slot=self.changeColor, tooltip='shuffle colors',
        )

        self.panzoom_button = QtModeRadioButton(
            layer,
            'zoom',
            Mode.PAN_ZOOM,
            tooltip='Pan/zoom mode (Space)',
            checked=True,
        )
        self.pick_button = QtModeRadioButton(
            layer, 'picker', Mode.PICK, tooltip='Pick mode'
        )
        self.paint_button = QtModeRadioButton(
            layer, 'paint', Mode.PAINT, tooltip='Paint mode'
        )
        btn = 'Cmd' if sys.platform == 'darwin' else 'Ctrl'
        self.fill_button = QtModeRadioButton(
            layer, 'fill', Mode.FILL, tooltip=f'Fill mode ({btn})'
        )
        self.erase_button = QtModeRadioButton(
            layer, 'erase', Mode.ERASE, tooltip='Erase mode (Alt)'
        )

        self.button_group = QButtonGroup(self)
        self.button_group.addButton(self.panzoom_button)
        self.button_group.addButton(self.paint_button)
        self.button_group.addButton(self.pick_button)
        self.button_group.addButton(self.fill_button)
        self.button_group.addButton(self.erase_button)
        self._on_editable_change()

        button_row = QHBoxLayout()
        button_row.addStretch(1)
        button_row.addWidget(self.colormapUpdate)
        button_row.addWidget(self.erase_button)
        button_row.addWidget(self.fill_button)
        button_row.addWidget(self.paint_button)
        button_row.addWidget(self.pick_button)
        button_row.addWidget(self.panzoom_button)
        button_row.setSpacing(4)
        button_row.setContentsMargins(0, 0, 0, 5)

        color_mode_comboBox = QComboBox(self)
        color_mode_comboBox.addItems(LabelColorMode.keys())
        index = color_mode_comboBox.findText(
            self.layer.color_mode, Qt.MatchFixedString
        )
        color_mode_comboBox.setCurrentIndex(index)
        color_mode_comboBox.activated[str].connect(self.change_color_mode)
        self.colorModeComboBox = color_mode_comboBox
        self._on_color_mode_change()

        color_layout = QHBoxLayout()
        color_layout.addWidget(QtColorBox(layer))
        color_layout.addWidget(self.selectionSpinBox)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addLayout(button_row, 0, 0, 1, 4)
        self.grid_layout.addWidget(QLabel('label:'), 1, 0, 1, 1)
        self.grid_layout.addLayout(color_layout, 1, 1, 1, 3)
        self.grid_layout.addWidget(QLabel('opacity:'), 2, 0, 1, 1)
        self.grid_layout.addWidget(self.opacitySlider, 2, 1, 1, 3)
        self.grid_layout.addWidget(QLabel('brush size:'), 3, 0, 1, 1)
        self.grid_layout.addWidget(self.brushSizeSlider, 3, 1, 1, 3)
        self.grid_layout.addWidget(QLabel('blending:'), 4, 0, 1, 1)
        self.grid_layout.addWidget(self.blendComboBox, 4, 1, 1, 3)
        self.grid_layout.addWidget(QLabel('color mode:'), 5, 0, 1, 1)
        self.grid_layout.addWidget(self.colorModeComboBox, 5, 1, 1, 3)
        self.grid_layout.addWidget(QLabel('contiguous:'), 6, 0, 1, 1)
        self.grid_layout.addWidget(self.contigCheckBox, 6, 1, 1, 1)
        self.grid_layout.addWidget(QLabel('n-dim:'), 6, 2, 1, 1)
        self.grid_layout.addWidget(self.ndimCheckBox, 6, 3, 1, 1)
        self.grid_layout.addWidget(QLabel('preserve labels:'), 7, 0, 1, 2)
        self.grid_layout.addWidget(self.preserveLabelsCheckBox, 7, 1, 1, 1)
        self.grid_layout.setRowStretch(8, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
예제 #37
0
class QtTracksControls(QtLayerControls):
    """Qt view and controls for the Tracks layer.

    Parameters
    ----------
    layer : napari.layers.Tracks
        An instance of a Tracks layer.

    Attributes
    ----------
    grid_layout : qtpy.QtWidgets.QGridLayout
        Layout of Qt widget controls for the layer.
    layer : layers.Tracks
        An instance of a Tracks layer.

    """
    def __init__(self, layer):
        super().__init__(layer)

        # NOTE(arl): there are no events fired for changing checkboxes
        self.layer.events.tail_width.connect(self._on_tail_width_change)
        self.layer.events.tail_length.connect(self._on_tail_length_change)
        self.layer.events.properties.connect(self._on_properties_change)
        self.layer.events.colormap.connect(self._on_colormap_change)
        self.layer.events.color_by.connect(self._on_color_by_change)

        # combo box for track coloring, we can get these from the properties
        # keys
        self.color_by_combobox = QComboBox()
        self.color_by_combobox.addItems(self.layer.properties_to_color_by)

        self.colormap_combobox = QComboBox()
        self.colormap_combobox.addItems(list(AVAILABLE_COLORMAPS.keys()))

        # slider for track tail length
        self.tail_length_slider = QSlider(Qt.Horizontal)
        self.tail_length_slider.setFocusPolicy(Qt.NoFocus)
        self.tail_length_slider.setMinimum(1)
        self.tail_length_slider.setMaximum(MAX_TAIL_LENGTH)
        self.tail_length_slider.setSingleStep(1)

        # slider for track edge width
        self.tail_width_slider = QSlider(Qt.Horizontal)
        self.tail_width_slider.setFocusPolicy(Qt.NoFocus)
        self.tail_width_slider.setMinimum(1)
        self.tail_width_slider.setMaximum(MAX_TAIL_WIDTH)
        self.tail_width_slider.setSingleStep(1)

        # checkboxes for display
        self.id_checkbox = QCheckBox()
        self.tail_checkbox = QCheckBox()
        self.tail_checkbox.setChecked(True)
        self.graph_checkbox = QCheckBox()
        self.graph_checkbox.setChecked(True)

        self.tail_width_slider.valueChanged.connect(self.change_tail_width)
        self.tail_length_slider.valueChanged.connect(self.change_tail_length)
        self.tail_checkbox.stateChanged.connect(self.change_display_tail)
        self.id_checkbox.stateChanged.connect(self.change_display_id)
        self.graph_checkbox.stateChanged.connect(self.change_display_graph)
        self.color_by_combobox.currentTextChanged.connect(self.change_color_by)
        self.colormap_combobox.currentTextChanged.connect(self.change_colormap)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])

        self.grid_layout.addWidget(QLabel('color by:'), 0, 0)
        self.grid_layout.addWidget(self.color_by_combobox, 0, 1)
        self.grid_layout.addWidget(QLabel('colormap:'), 1, 0)
        self.grid_layout.addWidget(self.colormap_combobox, 1, 1)
        self.grid_layout.addWidget(QLabel('blending:'), 2, 0)
        self.grid_layout.addWidget(self.blendComboBox, 2, 1)
        self.grid_layout.addWidget(QLabel('opacity:'), 3, 0)
        self.grid_layout.addWidget(self.opacitySlider, 3, 1)
        self.grid_layout.addWidget(QLabel('tail width:'), 4, 0)
        self.grid_layout.addWidget(self.tail_width_slider, 4, 1)
        self.grid_layout.addWidget(QLabel('tail length:'), 5, 0)
        self.grid_layout.addWidget(self.tail_length_slider, 5, 1)
        self.grid_layout.addWidget(QLabel('tail:'), 6, 0)
        self.grid_layout.addWidget(self.tail_checkbox, 6, 1)
        self.grid_layout.addWidget(QLabel('show ID:'), 7, 0)
        self.grid_layout.addWidget(self.id_checkbox, 7, 1)
        self.grid_layout.addWidget(QLabel('graph:'), 8, 0)
        self.grid_layout.addWidget(self.graph_checkbox, 8, 1)
        self.grid_layout.setRowStretch(9, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)

        self._on_tail_length_change()
        self._on_tail_width_change()
        self._on_colormap_change()
        self._on_color_by_change()

    def _on_tail_width_change(self, event=None):
        """Receive layer model track line width change event and update slider.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent, optional.
            Event from the Qt context, by default None.
        """
        with self.layer.events.tail_width.blocker():
            value = self.layer.tail_width
            value = np.clip(int(2 * value), 1, MAX_TAIL_WIDTH)
            self.tail_width_slider.setValue(value)

    def _on_tail_length_change(self, event=None):
        """Receive layer model track line width change event and update slider.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent, optional.
            Event from the Qt context, by default None.
        """
        with self.layer.events.tail_length.blocker():
            value = self.layer.tail_length
            value = np.clip(value, 1, MAX_TAIL_LENGTH)
            self.tail_length_slider.setValue(value)

    def _on_properties_change(self, event=None):
        """Change the properties that can be used to color the tracks."""
        with self.layer.events.properties.blocker():
            self.color_by_combobox.clear()
            self.color_by_combobox.addItems(self.layer.properties_to_color_by)

    def _on_colormap_change(self, event=None):
        """Receive layer model colormap change event and update combobox.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent, optional.
            Event from the Qt context, by default None.
        """
        with self.layer.events.colormap.blocker():
            colormap = self.layer.colormap

            idx = self.colormap_combobox.findText(colormap,
                                                  Qt.MatchFixedString)
            self.colormap_combobox.setCurrentIndex(idx)

    def _on_color_by_change(self, event=None):
        """Receive layer model color_by change event and update combobox.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent, optional.
            Event from the Qt context, by default None.
        """
        with self.layer.events.color_by.blocker():
            color_by = self.layer.color_by

            idx = self.color_by_combobox.findText(color_by,
                                                  Qt.MatchFixedString)
            self.color_by_combobox.setCurrentIndex(idx)

    def change_tail_width(self, value):
        """Change track line width of shapes on the layer model.

        Parameters
        ----------
        value : float
            Line width of track tails.
        """
        self.layer.tail_width = float(value) / 2.0

    def change_tail_length(self, value):
        """Change edge line width of shapes on the layer model.

        Parameters
        ----------
        value : int
            Line length of track tails.
        """
        self.layer.tail_length = value

    def change_display_tail(self, state):
        self.layer.display_tail = self.tail_checkbox.isChecked()

    def change_display_id(self, state):
        self.layer.display_id = self.id_checkbox.isChecked()

    def change_display_graph(self, state):
        self.layer.display_graph = self.graph_checkbox.isChecked()

    def change_color_by(self, value: str):
        self.layer.color_by = value

    def change_colormap(self, colormap: str):
        self.layer.colormap = colormap
예제 #38
0
    def __init__(self, name=None, plugin=None, parent=None):
        super().__init__(name, plugin, parent)

        # Attributes
        self._starting_up = True
        self._current_color_scheme = None
        self._last_texts = [None, None]
        self._last_editor_doc = None
        self._last_console_cb = None
        self._last_editor_cb = None
        self.css_path = self.get_conf('css_path', CSS_PATH, 'appearance')
        self.no_docs = _("No documentation available")
        self.docstring = True  # TODO: What is this used for?

        # Widgets
        self._sphinx_thread = SphinxThread(
            html_text_no_doc=warning(self.no_docs, css_path=self.css_path),
            css_path=self.css_path,
        )
        self.shell = None
        self.internal_console = None
        self.internal_shell = None
        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        self.source_label = QLabel(_("Source"))
        self.source_label.ID = HelpWidgetToolbarItems.SourceLabel

        self.source_combo = QComboBox(self)
        self.source_combo.ID = HelpWidgetToolbarItems.SourceCombo

        self.object_label = QLabel(_("Object"))
        self.object_label.ID = HelpWidgetToolbarItems.ObjectLabel

        self.object_combo = ObjectComboBox(self,
                                           HelpWidgetToolbarItems.ObjectCombo)

        self.object_edit = QLineEdit(self)
        self.object_edit.ID = HelpWidgetToolbarItems.ObjectEdit

        # Setup
        self.object_edit.setReadOnly(True)
        self.object_combo.setMaxCount(self.get_conf('max_history_entries'))
        self.object_combo.setItemText(0, '')
        self.plain_text.set_wrap_mode(self.get_conf('wrap'))
        self.source_combo.addItems([_("Console"), _("Editor")])
        if (not programs.is_module_installed('rope')
                and not programs.is_module_installed('jedi', '>=0.11.0')):
            self.source_combo.hide()
            self.source_label.hide()

        # Layout
        self.stack_layout = layout = QStackedLayout()
        layout.addWidget(self.rich_text)
        layout.addWidget(self.plain_text)
        self.setLayout(layout)

        # Signals
        self._sphinx_thread.html_ready.connect(
            self._on_sphinx_thread_html_ready)
        self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg)
        self.object_combo.valid.connect(self.force_refresh)
        self.rich_text.sig_link_clicked.connect(self.handle_link_clicks)
        self.source_combo.currentIndexChanged.connect(
            lambda x: self.source_changed())
        self.sig_render_started.connect(self.start_spinner)
        self.sig_render_finished.connect(self.stop_spinner)
예제 #39
0
class ElementSelection(QWidget):
    """Width of the widgets can be set using `setMaximumWidth`. The size policy is set
    so that the widget may shrink if there is not enough space."""

    signal_current_item_changed = Signal(int, str)

    def __init__(self):
        super().__init__()

        self._item_list = []
        # The 'first item' is appended to the beginning of the list. The index returned
        #   by the functions of the class is the index of 'self._item_list' array, that
        #   does not include the 'first item'.
        self._first_item = "Select Line:"

        self.cb_element_list = QComboBox()
        self.cb_element_list.currentIndexChanged.connect(
            self.cb_element_list_current_index_changed)

        self.setMaximumWidth(300)
        self.pb_prev = PushButtonMinimumWidth("<")
        self.pb_prev.pressed.connect(self.pb_prev_pressed)
        self.pb_next = PushButtonMinimumWidth(">")
        self.pb_next.pressed.connect(self.pb_next_pressed)

        self.cb_element_list.addItems([self._first_item])
        self.cb_element_list.setCurrentIndex(0)

        hbox = QHBoxLayout()
        hbox.setSpacing(0)
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(self.pb_prev)
        hbox.addWidget(self.cb_element_list)
        hbox.addWidget(self.pb_next)

        self.setLayout(hbox)

        sp = QSizePolicy()
        sp.setControlType(QSizePolicy.PushButton)
        sp.setHorizontalPolicy(QSizePolicy.Maximum)
        self.setSizePolicy(sp)

    def set_item_list(self, item_list):
        _, current_item = self.get_current_item()

        self._item_list = item_list.copy()

        current_index = -1
        if current_item:
            try:
                current_index = self._item_list.index(current_item)
            except ValueError:
                pass

        self.cb_element_list.clear()
        self.cb_element_list.addItems([self._first_item] + self._item_list)

        self.cb_element_list.setCurrentIndex(current_index + 1)

        self._adjust_button_state()

    def set_current_index(self, index):
        current_index = -1
        if 0 <= index < len(self._item_list):
            current_index = index
        self.cb_element_list.setCurrentIndex(current_index + 1)

    def set_current_item(self, item):
        current_index = -1
        try:
            current_index = self._item_list.index(item)
        except ValueError:
            pass
        self.cb_element_list.setCurrentIndex(current_index + 1)

    def get_current_item(self):
        current_index = self.cb_element_list.currentIndex() - 1
        if 0 <= current_index < len(self._item_list):
            current_item = self._item_list[current_index]
        else:
            current_item = ""

        return current_index, current_item

    def _adjust_button_state(self):

        current_index = self.cb_element_list.currentIndex() - 1
        n_items = len(self._item_list)

        enable_prev, enable_next = True, True
        if n_items == 0:
            enable_prev, enable_next = False, False
        else:
            if current_index < 0:
                enable_prev = False
            if current_index >= n_items - 1:
                enable_next = False

        self.pb_prev.setEnabled(enable_prev)
        self.pb_next.setEnabled(enable_next)

    def cb_element_list_current_index_changed(self, index):
        self._adjust_button_state()
        current_index, current_item = self.get_current_item()
        self.signal_current_item_changed.emit(current_index, current_item)

    def pb_prev_pressed(self):
        current_index = self.cb_element_list.currentIndex() - 1
        if current_index >= 0:
            self.cb_element_list.setCurrentIndex(current_index)

    def pb_next_pressed(self):
        current_index = self.cb_element_list.currentIndex() - 1
        n_items = len(self._item_list)
        if current_index < n_items - 1:
            self.cb_element_list.setCurrentIndex(current_index + 2)
예제 #40
0
class LSPServerEditor(QDialog):
    DEFAULT_HOST = '127.0.0.1'
    DEFAULT_PORT = 2084
    DEFAULT_CMD = ''
    DEFAULT_ARGS = ''
    DEFAULT_CONFIGURATION = '{}'
    DEFAULT_EXTERNAL = False
    HOST_REGEX = re.compile(r'^\w+([.]\w+)*$')
    NON_EMPTY_REGEX = re.compile(r'^\S+$')
    JSON_VALID = _('JSON valid')
    JSON_INVALID = _('JSON invalid')

    def __init__(self, parent, language=None, cmd='', host='127.0.0.1',
                 port=2084, args='', external=False, configurations={},
                 **kwargs):
        super(LSPServerEditor, self).__init__(parent)
        self.parent = parent
        self.external = external
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_ok = bbox.button(QDialogButtonBox.Ok)
        self.button_cancel = bbox.button(QDialogButtonBox.Cancel)
        self.button_ok.setEnabled(False)

        description = _('To create a new configuration, '
                        'you need to select a programming '
                        'language, along with a executable '
                        'name for the server to execute '
                        '(If the instance is local), '
                        'and the host and port. Finally, '
                        'you need to provide the '
                        'arguments that the server accepts. '
                        'The placeholders <tt>{host}</tt> and '
                        '<tt>{port}</tt> refer to the host '
                        'and the port, respectively.')
        server_settings_description = QLabel(description)
        server_settings_description.setWordWrap(True)

        lang_label = QLabel(_('Language:'))
        self.lang_cb = QComboBox(self)
        self.lang_cb.setToolTip(_('Programming language provided '
                                  'by the LSP server'))
        self.lang_cb.addItem(_('Select a language'))
        self.lang_cb.addItems(LSP_LANGUAGES)

        if language is not None:
            idx = LSP_LANGUAGES.index(language)
            self.lang_cb.setCurrentIndex(idx + 1)
            self.button_ok.setEnabled(True)

        host_label = QLabel(_('Host:'))
        self.host_input = QLineEdit(self)
        self.host_input.setToolTip(_('Name of the host that will provide '
                                     'access to the server'))
        self.host_input.setText(host)
        self.host_input.textChanged.connect(lambda x: self.validate())

        port_label = QLabel(_('Port:'))
        self.port_spinner = QSpinBox(self)
        self.port_spinner.setToolTip(_('TCP port number of the server'))
        self.port_spinner.setMinimum(1)
        self.port_spinner.setMaximum(60000)
        self.port_spinner.setValue(port)

        cmd_label = QLabel(_('Command to execute:'))
        self.cmd_input = QLineEdit(self)
        self.cmd_input.setToolTip(_('Command used to start the '
                                    'LSP server locally'))
        self.cmd_input.setText(cmd)

        if not external:
            self.cmd_input.textChanged.connect(lambda x: self.validate())

        args_label = QLabel(_('Server arguments:'))
        self.args_input = QLineEdit(self)
        self.args_input.setToolTip(_('Additional arguments required to '
                                     'start the server'))
        self.args_input.setText(args)

        conf_label = QLabel(_('LSP Server Configurations:'))
        self.conf_input = CodeEditor(None)
        self.conf_input.textChanged.connect(self.validate)
        color_scheme = CONF.get('appearance', 'selected')
        self.conf_input.setup_editor(
            language='JSON',
            color_scheme=color_scheme,
            wrap=False,
            edge_line=True,
            highlight_current_line=True,
            highlight_current_cell=True,
            occurrence_highlighting=True,
            auto_unindent=True,
            font=get_font(),
            filename='config.json')
        self.conf_input.setToolTip(_('Additional LSP server configurations '
                                     'set at runtime. JSON required'))
        conf_text = '{}'
        try:
            conf_text = json.dumps(configurations, indent=4, sort_keys=True)
        except Exception:
            pass
        self.conf_input.set_text(conf_text)
        self.json_label = QLabel(self.JSON_VALID, self)

        self.external_cb = QCheckBox(_('External server'), self)
        self.external_cb.setToolTip(_('Check if the server runs '
                                      'on a remote location'))
        self.external_cb.setChecked(external)
        self.external_cb.stateChanged.connect(self.set_local_options)

        hlayout = QHBoxLayout()
        general_vlayout = QVBoxLayout()
        general_vlayout.addWidget(server_settings_description)

        vlayout = QVBoxLayout()
        lang_layout = QVBoxLayout()
        lang_layout.addWidget(lang_label)
        lang_layout.addWidget(self.lang_cb)

        # layout2 = QHBoxLayout()
        # layout2.addLayout(lang_layout)
        lang_layout.addWidget(self.external_cb)
        vlayout.addLayout(lang_layout)

        host_layout = QVBoxLayout()
        host_layout.addWidget(host_label)
        host_layout.addWidget(self.host_input)

        port_layout = QVBoxLayout()
        port_layout.addWidget(port_label)
        port_layout.addWidget(self.port_spinner)

        conn_info_layout = QHBoxLayout()
        conn_info_layout.addLayout(host_layout)
        conn_info_layout.addLayout(port_layout)
        vlayout.addLayout(conn_info_layout)

        cmd_layout = QVBoxLayout()
        cmd_layout.addWidget(cmd_label)
        cmd_layout.addWidget(self.cmd_input)
        vlayout.addLayout(cmd_layout)

        args_layout = QVBoxLayout()
        args_layout.addWidget(args_label)
        args_layout.addWidget(self.args_input)
        vlayout.addLayout(args_layout)

        conf_layout = QVBoxLayout()
        conf_layout.addWidget(conf_label)
        conf_layout.addWidget(self.conf_input)
        conf_layout.addWidget(self.json_label)

        hlayout.addLayout(vlayout)
        hlayout.addLayout(conf_layout)
        general_vlayout.addLayout(hlayout)

        general_vlayout.addWidget(bbox)
        self.setLayout(general_vlayout)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        self.lang_cb.currentIndexChanged.connect(
            self.lang_selection_changed)
        self.form_status(False)
        if language is not None:
            self.form_status(True)
            self.validate()

    @Slot()
    def validate(self):
        host_text = self.host_input.text()
        cmd_text = self.cmd_input.text()
        if not self.HOST_REGEX.match(host_text):
            self.button_ok.setEnabled(False)
            self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}")
            self.host_input.setToolTip('Hostname must be valid')
            return
        else:
            self.host_input.setStyleSheet(
                "QLineEdit{border: 1px solid green;}")
            self.host_input.setToolTip('Hostname is valid')
            self.button_ok.setEnabled(True)

        if not self.external:
            if not self.NON_EMPTY_REGEX.match(cmd_text):
                self.button_ok.setEnabled(False)
                self.cmd_input.setStyleSheet(
                    "QLineEdit{border: 1px solid red;}")
                self.cmd_input.setToolTip('Command must be non empty')
                return

            if find_program(cmd_text) is None:
                self.button_ok.setEnabled(False)
                self.cmd_input.setStyleSheet(
                    "QLineEdit{border: 1px solid red;}")
                self.cmd_input.setToolTip('Program was not found '
                                          'on your system')
                return
            else:
                self.cmd_input.setStyleSheet(
                    "QLineEdit{border: 1px solid green;}")
                self.cmd_input.setToolTip('Program was found on your system')
                self.button_ok.setEnabled(True)
        try:
            json.loads(self.conf_input.toPlainText())
            try:
                self.json_label.setText(self.JSON_VALID)
            except Exception:
                pass
        except (ValueError, json.decoder.JSONDecodeError):
            try:
                self.json_label.setText(self.JSON_INVALID)
                self.button_ok.setEnabled(False)
            except Exception:
                pass

    def form_status(self, status):
        self.host_input.setEnabled(status)
        self.port_spinner.setEnabled(status)
        self.external_cb.setEnabled(status)
        self.cmd_input.setEnabled(status)
        self.args_input.setEnabled(status)
        self.conf_input.setEnabled(status)
        self.json_label.setVisible(status)

    @Slot()
    def lang_selection_changed(self):
        idx = self.lang_cb.currentIndex()
        if idx == 0:
            self.set_defaults()
            self.form_status(False)
            self.button_ok.setEnabled(False)
        else:
            server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1])
            self.form_status(True)
            if server is not None:
                self.host_input.setText(server.host)
                self.port_spinner.setValue(server.port)
                self.external_cb.setChecked(server.external)
                self.cmd_input.setText(server.cmd)
                self.args_input.setText(server.args)
                self.conf_input.set_text(json.dumps(server.configurations))
                self.json_label.setText(self.JSON_VALID)
                self.button_ok.setEnabled(True)
            else:
                self.set_defaults()

    def set_defaults(self):
        self.cmd_input.setStyleSheet('')
        self.host_input.setStyleSheet('')
        self.host_input.setText(self.DEFAULT_HOST)
        self.port_spinner.setValue(self.DEFAULT_PORT)
        self.external_cb.setChecked(self.DEFAULT_EXTERNAL)
        self.cmd_input.setText(self.DEFAULT_CMD)
        self.args_input.setText(self.DEFAULT_ARGS)
        self.conf_input.set_text(self.DEFAULT_CONFIGURATION)
        self.json_label.setText(self.JSON_VALID)

    @Slot(bool)
    @Slot(int)
    def set_local_options(self, enabled):
        self.external = enabled
        self.cmd_input.setEnabled(True)
        self.args_input.setEnabled(True)
        if enabled:
            self.cmd_input.setEnabled(False)
            self.cmd_input.setStyleSheet('')
            self.args_input.setEnabled(False)
        try:
            self.validate()
        except Exception:
            pass

    def get_options(self):
        language_idx = self.lang_cb.currentIndex()
        language = LSP_LANGUAGES[language_idx - 1]
        host = self.host_input.text()
        port = int(self.port_spinner.value())
        external = self.external_cb.isChecked()
        args = self.args_input.text()
        cmd = self.cmd_input.text()
        configurations = json.loads(self.conf_input.toPlainText())
        server = LSPServer(language=language.lower(), cmd=cmd, args=args,
                           host=host, port=port, external=external,
                           configurations=configurations)
        return server
예제 #41
0
class ColorbarWidget(QWidget):
    colorbarChanged = Signal()  # The parent should simply redraw their canvas

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

        self.setWindowTitle("Colorbar")
        self.setMaximumWidth(200)

        self.cmap = QComboBox()
        self.cmap.addItems(sorted(cm.cmap_d.keys()))
        self.cmap.currentIndexChanged.connect(self.cmap_index_changed)

        self.cmin = QLineEdit()
        self.cmin_value = 0
        self.cmin.setMaximumWidth(100)
        self.cmin.editingFinished.connect(self.clim_changed)
        self.cmin_layout = QHBoxLayout()
        self.cmin_layout.addStretch()
        self.cmin_layout.addWidget(self.cmin)
        self.cmin_layout.addStretch()

        self.cmax = QLineEdit()
        self.cmax_value = 1
        self.cmax.setMaximumWidth(100)
        self.cmax.editingFinished.connect(self.clim_changed)
        self.cmin.setValidator(QDoubleValidator())
        self.cmax.setValidator(QDoubleValidator())
        self.cmax_layout = QHBoxLayout()
        self.cmax_layout.addStretch()
        self.cmax_layout.addWidget(self.cmax)
        self.cmax_layout.addStretch()

        self.norm_layout = QHBoxLayout()
        self.norm = QComboBox()
        self.norm.addItems(NORM_OPTS)
        self.norm.currentIndexChanged.connect(self.norm_changed)

        self.powerscale = QLineEdit()
        self.powerscale_value = 2
        self.powerscale.setText("2")
        self.powerscale.setValidator(QDoubleValidator(0.001, 100, 3))
        self.powerscale.setMaximumWidth(50)
        self.powerscale.editingFinished.connect(self.norm_changed)
        self.powerscale.hide()
        self.powerscale_label = QLabel("n=")
        self.powerscale_label.hide()

        self.norm_layout.addStretch()
        self.norm_layout.addWidget(self.norm)
        self.norm_layout.addStretch()
        self.norm_layout.addWidget(self.powerscale_label)
        self.norm_layout.addWidget(self.powerscale)

        self.autoscale = QCheckBox("Autoscaling")
        self.autoscale.setChecked(True)
        self.autoscale.stateChanged.connect(self.update_clim)

        self.canvas = FigureCanvas(Figure())
        if parent:
            # Set facecolor to match parent
            self.canvas.figure.set_facecolor(
                parent.palette().window().color().getRgbF())
        self.ax = self.canvas.figure.add_axes([0.4, 0.05, 0.2, 0.9])

        # layout
        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.cmap)
        self.layout.addLayout(self.cmax_layout)
        self.layout.addWidget(self.canvas, stretch=1)
        self.layout.addLayout(self.cmin_layout)
        self.layout.addLayout(self.norm_layout)
        self.layout.addWidget(self.autoscale)

    def set_mappable(self, mappable):
        """
        When a new plot is created this method should be called with the new mappable
        """
        self.ax.clear()
        try:  # Use current cmap
            cmap = get_current_cmap(self.colorbar)
        except AttributeError:
            try:  # else use viridis
                cmap = cm.viridis
            except AttributeError:  # else default
                cmap = None
        self.colorbar = Colorbar(ax=self.ax, mappable=mappable)
        self.cmin_value, self.cmax_value = mappable.get_clim()
        self.update_clim_text()
        self.cmap_changed(cmap)

        mappable_cmap = get_current_cmap(mappable)
        self.cmap.setCurrentIndex(
            sorted(cm.cmap_d.keys()).index(mappable_cmap.name))
        self.redraw()

    def cmap_index_changed(self):
        self.cmap_changed(self.cmap.currentText())

    def cmap_changed(self, name):
        self.colorbar.mappable.set_cmap(name)
        if mpl_version_info() >= (3, 1):
            self.colorbar.update_normal(self.colorbar.mappable)
        else:
            self.colorbar.set_cmap(name)
        self.redraw()

    def mappable_changed(self):
        """
        Updates the colormap and min/max values of the colorbar
        when the plot changes via settings.
        """
        mappable_cmap = get_current_cmap(self.colorbar.mappable)
        low, high = self.colorbar.mappable.get_clim()
        self.cmin_value = low
        self.cmax_value = high
        self.update_clim_text()
        self.cmap.setCurrentIndex(
            sorted(cm.cmap_d.keys()).index(mappable_cmap.name))
        self.redraw()

    def norm_changed(self):
        """
        Called when a different normalization is selected
        """
        idx = self.norm.currentIndex()
        if NORM_OPTS[idx] == 'Power':
            self.powerscale.show()
            self.powerscale_label.show()
        else:
            self.powerscale.hide()
            self.powerscale_label.hide()
        self.colorbar.mappable.set_norm(self.get_norm())
        self.set_mappable(self.colorbar.mappable)

    def get_norm(self):
        """
        This will create a matplotlib.colors.Normalize from selected idx, limits and powerscale
        """
        idx = self.norm.currentIndex()
        if self.autoscale.isChecked():
            cmin = cmax = None
        else:
            cmin = self.cmin_value
            cmax = self.cmax_value
        if NORM_OPTS[idx] == 'Power':
            if self.powerscale.hasAcceptableInput():
                self.powerscale_value = float(self.powerscale.text())
            return PowerNorm(gamma=self.powerscale_value, vmin=cmin, vmax=cmax)
        elif NORM_OPTS[idx] == "SymmetricLog10":
            return SymLogNorm(
                1e-8 if cmin is None else max(1e-8,
                                              abs(cmin) * 1e-3),
                vmin=cmin,
                vmax=cmax)
        else:
            return Normalize(vmin=cmin, vmax=cmax)

    def clim_changed(self):
        """
        Called when either the min or max is changed. Will unset the autoscale.
        """
        self.autoscale.blockSignals(True)
        self.autoscale.setChecked(False)
        self.autoscale.blockSignals(False)
        self.update_clim()

    def update_clim(self):
        """
        This will update the clim of the plot based on min, max, and autoscale
        """
        if self.autoscale.isChecked():
            data = self.colorbar.mappable.get_array()
            try:
                try:
                    self.cmin_value = data[~data.mask].min()
                    self.cmax_value = data[~data.mask].max()
                except (AttributeError, IndexError):
                    self.cmin_value = np.nanmin(data)
                    self.cmax_value = np.nanmax(data)
            except (ValueError, RuntimeWarning):
                # all values mask
                pass
            self.update_clim_text()
        else:
            if self.cmin.hasAcceptableInput():
                cmin = float(self.cmin.text())
                if cmin < self.cmax_value:
                    self.cmin_value = cmin
                else:  # reset values back
                    self.update_clim_text()
            if self.cmax.hasAcceptableInput():
                cmax = float(self.cmax.text())
                if cmax > self.cmin_value:
                    self.cmax_value = cmax
                else:  # reset values back
                    self.update_clim_text()
        self.colorbar.mappable.set_clim(self.cmin_value, self.cmax_value)
        self.redraw()

    def update_clim_text(self):
        """
        Update displayed limit values based on stored ones
        """
        self.cmin.setText("{:.4}".format(self.cmin_value))
        self.cmax.setText("{:.4}".format(self.cmax_value))

    def redraw(self):
        """
        Redraws the colobar and emits signal to cause the parent to redraw
        """
        self.colorbar.update_ticks()
        self.colorbar.draw_all()
        self.canvas.draw_idle()
        self.colorbarChanged.emit()
예제 #42
0
class CollapseCube(QDialog):
    def __init__(self,
                 data,
                 data_collection=[],
                 allow_preview=False,
                 parent=None):
        super(CollapseCube, self).__init__(parent)

        self.setWindowTitle("Collapse Cube Along Spectral Axis")

        # Get the data_components (e.g., FLUX, DQ, ERROR etc)
        # Using list comprehension to keep the order of the component_ids
        self.data_components = [
            str(x).strip() for x in data.component_ids()
            if not x in data.coordinate_components
        ]

        self.setWindowFlags(self.windowFlags() | Qt.Tool)
        self.title = "Cube Collapse"
        self.data = data
        self.data_collection = data_collection
        self.parent = parent

        self._general_description = "Collapse the data cube over the spectral range based on the mathematical operation.  The nearest index or wavelength will be chosen if a specified number is out of bounds"
        self._custom_description = "To use the spectral viewer to define a region to collapse over, cancel this, create an ROI and then select this Collapse Cube again."

        self.currentAxes = None
        self.currentKernel = None

        self.createUI()

    def createUI(self):
        """
        Create the popup box with the calculation input area and buttons.

        :return:
        """
        boldFont = QtGui.QFont()
        boldFont.setBold(True)

        # Create data component label and input box
        self.widget_desc = QLabel(self._general_description)
        self.widget_desc.setWordWrap(True)
        self.widget_desc.setFixedWidth(350)
        self.widget_desc.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_desc = QHBoxLayout()
        hb_desc.addWidget(self.widget_desc)

        # Create data component label and input box
        self.data_label = QLabel("Data:")
        self.data_label.setFixedWidth(100)
        self.data_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.data_label.setFont(boldFont)

        self.data_combobox = QComboBox()
        self.data_combobox.addItems([
            str(x).strip() for x in self.data.component_ids()
            if not x in self.data.coordinate_components
        ])
        self.data_combobox.setMinimumWidth(200)

        hb_data = QHBoxLayout()
        hb_data.addWidget(self.data_label)
        hb_data.addWidget(self.data_combobox)

        # Create operation label and input box
        self.operation_label = QLabel("Operation:")
        self.operation_label.setFixedWidth(100)
        self.operation_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.operation_label.setFont(boldFont)

        self.operation_combobox = QComboBox()
        self.operation_combobox.addItems(operations.keys())
        self.operation_combobox.setMinimumWidth(200)

        hb_operation = QHBoxLayout()
        hb_operation.addWidget(self.operation_label)
        hb_operation.addWidget(self.operation_combobox)

        # Create region label and input box
        self.region_label = QLabel("region:")
        self.region_label.setFixedWidth(100)
        self.region_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.region_label.setFont(boldFont)

        self.region_combobox = QComboBox()

        # Get the Specviz regions and add them in to the Combo box
        for roi in self.parent.specviz._widget.roi_bounds:
            self.region_combobox.addItem("Specviz ROI ({:.3}, {:.3})".format(
                roi[0], roi[1]))

        self.region_combobox.addItems(
            ["Custom (Wavelengths)", "Custom (Indices)"])
        self.region_combobox.setMinimumWidth(200)
        self.region_combobox.currentIndexChanged.connect(
            self._region_selection_change)

        hb_region = QHBoxLayout()
        hb_region.addWidget(self.region_label)
        hb_region.addWidget(self.region_combobox)

        # Create error label
        self.error_label = QLabel("")
        self.error_label.setFixedWidth(100)

        self.error_label_text = QLabel("")
        self.error_label_text.setMinimumWidth(200)
        self.error_label_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hbl_error = QHBoxLayout()
        hbl_error.addWidget(self.error_label)
        hbl_error.addWidget(self.error_label_text)

        # Create start label and input box
        self.start_label = QLabel("Start:")
        self.start_label.setFixedWidth(100)
        self.start_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.start_label.setFont(boldFont)

        self.start_text = QLineEdit()
        self.start_text.setMinimumWidth(200)
        self.start_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_start = QHBoxLayout()
        hb_start.addWidget(self.start_label)
        hb_start.addWidget(self.start_text)

        # Create end label and input box
        self.end_label = QLabel("End:")
        self.end_label.setFixedWidth(100)
        self.end_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.end_label.setFont(boldFont)

        self.end_text = QLineEdit()
        self.end_text.setMinimumWidth(200)
        self.end_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_end = QHBoxLayout()
        hb_end.addWidget(self.end_label)
        hb_end.addWidget(self.end_text)

        # Create Calculate and Cancel buttons
        self.calculateButton = QPushButton("Calculate")
        self.calculateButton.clicked.connect(self.calculate_callback)
        self.calculateButton.setDefault(True)

        self.cancelButton = QPushButton("Cancel")
        self.cancelButton.clicked.connect(self.cancel_callback)

        hb_buttons = QHBoxLayout()
        hb_buttons.addStretch(1)
        hb_buttons.addWidget(self.cancelButton)
        hb_buttons.addWidget(self.calculateButton)

        #
        #  Sigma clipping
        #
        vbox_sigma_clipping = QVBoxLayout()

        self.sigma_description = QLabel(
            "Sigma clipping is implemented using <a href='http://docs.astropy.org/en/stable/api/astropy.stats.sigma_clip.html'>astropy.stats.sigma_clip</a>. Empty values will use defaults listed on the webpage, <b>but</b> if the first sigma is empty, then no clipping will be done."
        )
        self.sigma_description.setWordWrap(True)
        hb_sigma = QHBoxLayout()
        hb_sigma.addWidget(self.sigma_description)
        vbox_sigma_clipping.addLayout(hb_sigma)

        # Create sigma
        self.sigma_label = QLabel("Sigma:")
        self.sigma_label.setFixedWidth(100)
        self.sigma_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_label.setFont(boldFont)
        self.sigma_text = QLineEdit()
        self.sigma_text.setMinimumWidth(200)
        self.sigma_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma = QHBoxLayout()
        hb_sigma.addWidget(self.sigma_label)
        hb_sigma.addWidget(self.sigma_text)
        vbox_sigma_clipping.addLayout(hb_sigma)

        # Create sigma_lower
        self.sigma_lower_label = QLabel("Sigma Lower:")
        self.sigma_lower_label.setFixedWidth(100)
        self.sigma_lower_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_lower_label.setFont(boldFont)
        self.sigma_lower_text = QLineEdit()
        self.sigma_lower_text.setMinimumWidth(200)
        self.sigma_lower_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_lower = QHBoxLayout()
        hb_sigma_lower.addWidget(self.sigma_lower_label)
        hb_sigma_lower.addWidget(self.sigma_lower_text)
        vbox_sigma_clipping.addLayout(hb_sigma_lower)

        # Create sigma_upper
        self.sigma_upper_label = QLabel("Sigma Upper:")
        self.sigma_upper_label.setFixedWidth(100)
        self.sigma_upper_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_upper_label.setFont(boldFont)
        self.sigma_upper_text = QLineEdit()
        self.sigma_upper_text.setMinimumWidth(200)
        self.sigma_upper_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_upper = QHBoxLayout()
        hb_sigma_upper.addWidget(self.sigma_upper_label)
        hb_sigma_upper.addWidget(self.sigma_upper_text)
        vbox_sigma_clipping.addLayout(hb_sigma_upper)

        # Create sigma_iters
        self.sigma_iters_label = QLabel("Sigma Iterations:")
        self.sigma_iters_label.setFixedWidth(100)
        self.sigma_iters_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_iters_label.setFont(boldFont)
        self.sigma_iters_text = QLineEdit()
        self.sigma_iters_text.setMinimumWidth(200)
        self.sigma_iters_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_iters = QHBoxLayout()
        hb_sigma_iters.addWidget(self.sigma_iters_label)
        hb_sigma_iters.addWidget(self.sigma_iters_text)
        vbox_sigma_clipping.addLayout(hb_sigma_iters)

        # Add calculation and buttons to popup box
        vbl = QVBoxLayout()
        vbl.addLayout(hb_desc)
        vbl.addLayout(hb_data)
        vbl.addLayout(hb_operation)
        vbl.addLayout(hb_region)
        vbl.addLayout(hb_start)
        vbl.addLayout(hb_end)
        vbl.addLayout(vbox_sigma_clipping)
        vbl.addLayout(hbl_error)
        vbl.addLayout(hb_buttons)

        self.setLayout(vbl)
        self.setMaximumWidth(700)
        self.show()

        # Fire the callback to set the default values for everything
        self._region_selection_change(0)

    def _region_selection_change(self, index):
        """
        Callback for a change on the region selection combo box.

        :param newvalue:
        :return:
        """

        newvalue = self.region_combobox.currentText()

        # First, let's see if this is one of the custom options
        if 'Custom' in newvalue and 'Wavelength' in newvalue:
            # Custom Wavelengths
            self.hide_start_end(False)
            self.start_label.setText("Start Wavelength:")
            self.end_label.setText("End Wavelength:")

        elif 'Custom' in newvalue and 'Indices' in newvalue:
            # Custom indices
            self.hide_start_end(False)
            self.start_label.setText("Start Index:")
            self.end_label.setText("End Index:")

        else:
            # Region defined in specviz
            self.hide_start_end(True)

            # We are going to store the start and end wavelengths in the text boxes even though
            # they are hidden. This way we can use the text boxes later as a hidden storage container.
            # TODO: Should probably save the ROIs so the start and end values are more accurate.
            regex = r"-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?"
            floating = re.findall(regex, newvalue)
            self.start_text.setText(floating[0])
            self.end_text.setText(floating[1])

        # Let's update the text on the widget
        if 'Custom' in newvalue:
            self.widget_desc.setText(self._general_description + "\n\n" +
                                     self._custom_description)
        else:
            self.widget_desc.setText(self._general_description)

    def hide_start_end(self, dohide):
        """
        Show or hide the start and end indices depending if the region
        is defined from the specviz plot OR if we are using custom limits.

        :param dohide:
        :return:
        """
        if dohide:
            self.start_label.hide()
            self.start_text.hide()
            self.end_label.hide()
            self.end_text.hide()
        else:
            self.start_label.show()
            self.start_text.show()
            self.end_label.show()
            self.end_text.show()

    def calculate_callback(self):
        """
        Callback for when they hit calculate
        :return:
        """

        # Grab the values of interest
        data_name = self.data_combobox.currentText()
        start_value = self.start_text.text().strip()
        end_value = self.end_text.text().strip()

        self.error_label_text.setText(' ')
        self.error_label_text.setStyleSheet("color: rgba(255, 0, 0, 128)")

        # Sanity checks first
        if not start_value and not end_value:
            self.start_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.error_label_text.setText(
                'Must set at least one of start or end value')
            return

        wavelengths = np.array(self.parent._wavelengths)

        # If indicies, get them and check to see if the inputs are good.
        if 'Indices' in self.region_combobox.currentText():
            if len(start_value) == 0:
                start_index = 0
            else:
                try:
                    start_index = int(start_value)
                except ValueError as e:
                    self.start_label.setStyleSheet(
                        "color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'Start value must be an integer')
                    return

                if start_index < 0:
                    start_index = 0

            if len(end_value) == 0:
                end_index = len(wavelengths) - 1
            else:
                try:
                    end_index = int(end_value)
                except ValueError as e:
                    self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'End value must be an integer')
                    return

                if end_index > len(wavelengths) - 1:
                    end_index = len(wavelengths) - 1
        else:
            # Wavelength inputs
            if len(start_value) == 0:
                start_index = 0
            else:
                # convert wavelength to float value
                try:
                    start_value = float(start_value)
                except ValueError as e:
                    self.start_label.setStyleSheet(
                        "color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'Start value must be a floating point number')
                    return

                # Look up index
                start_index = np.argsort(np.abs(wavelengths - start_value))[0]

            if len(end_value) == 0:
                end_index = len(wavelengths) - 1

            else:
                # convert wavelength to float value
                try:
                    end_value = float(end_value)
                except ValueError as e:
                    self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'End value must be a floating point number')
                    return

                # Look up index
                end_index = np.argsort(np.abs(wavelengths - end_value))[0]

        # Check to make sure at least one of start or end is within the range of the wavelengths.
        if (start_index < 0
                and end_index < 0) or (start_index > len(wavelengths)
                                       and end_index > len(wavelengths)):
            self.start_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.error_label_text.setText(
                'Can not have both start and end outside of the wavelength range.'
            )
            return

        if start_index > end_index:
            self.start_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.error_label_text.setText(
                'Start value must be less than end value')
            return

        # Check to see if the wavelength (indices) are the same.
        if start_index == end_index:
            self.start_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.end_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
            self.error_label_text.setText(
                'Can not have both start and end wavelengths be the same.')
            return

        # Set the start and end values in the text boxes -- in case they enter one way out of range then
        # we'll fix it.
        ts = start_index if 'Indices' in self.region_combobox.currentText(
        ) else wavelengths[start_index]
        self.start_text.setText('{}'.format(ts))

        te = end_index if 'Indices' in self.region_combobox.currentText(
        ) else wavelengths[end_index]
        self.end_text.setText('{}'.format(te))

        data_name = self.data_combobox.currentText()
        operation = self.operation_combobox.currentText()

        # Do calculation if we got this far
        wavelengths, new_component = collapse_cube(self.data[data_name],
                                                   data_name,
                                                   self.data.coords.wcs,
                                                   operation, start_index,
                                                   end_index)

        # Get the start and end wavelengths from the newly created spectral cube and use for labeling the cube.
        # Convert to the current units.
        start_wavelength = wavelengths[0].to(
            self.parent._units_controller._new_units)
        end_wavelength = wavelengths[-1].to(
            self.parent._units_controller._new_units)

        label = '{}-collapse-{} ({:0.3}, {:0.3})'.format(
            data_name, operation, start_wavelength, end_wavelength)

        # Apply sigma clipping
        sigma = self.sigma_text.text().strip()

        if len(sigma) > 0:
            try:
                sigma = float(sigma)
            except ValueError as e:
                self.sigma_label.setStyleSheet("color: rgba(255, 0, 0, 128)")
                self.error_label_text.setText(
                    'If sigma set, it must be a floating point number')
                return

            sigma_lower = self.sigma_lower_text.text().strip()
            if len(sigma_lower) > 0:
                try:
                    sigma_lower = float(sigma_lower)
                except ValueError as e:
                    self.sigma_lower_label.setStyleSheet(
                        "color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'If sigma lower set, it must be a floating point number'
                    )
                    return
            else:
                sigma_lower = None

            sigma_upper = self.sigma_upper_text.text().strip()
            if len(sigma_upper) > 0:
                try:
                    sigma_upper = float(sigma_upper)
                except ValueError as e:
                    self.sigma_upper_label.setStyleSheet(
                        "color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'If sigma upper set, it must be a floating point number'
                    )
                    return
            else:
                sigma_upper = None

            sigma_iters = self.sigma_iters_text.text().strip()
            if len(sigma_iters) > 0:
                try:
                    sigma_iters = float(sigma_iters)
                except ValueError as e:
                    self.sigma_iters_label.setStyleSheet(
                        "color: rgba(255, 0, 0, 128)")
                    self.error_label_text.setText(
                        'If sigma iters set, it must be a floating point number'
                    )
                    return
            else:
                sigma_iters = None

            new_component = sigma_clip(new_component,
                                       sigma=sigma,
                                       sigma_lower=sigma_lower,
                                       sigma_upper=sigma_upper,
                                       iters=sigma_iters)

            # Add to label so it is clear which overlay/component is which
            if sigma:
                label += ' sigma={}'.format(sigma)

            if sigma_lower:
                label += ' sigma_lower={}'.format(sigma_lower)

            if sigma_upper:
                label += ' sigma_upper={}'.format(sigma_upper)

            if sigma_iters:
                label += ' sigma_iters={}'.format(sigma_iters)

        # Add new overlay/component to cubeviz
        self.parent.add_overlay(new_component, label)

        self.close()

        # Show new dialog
        self.final_dialog(label)

    def final_dialog(self, label):
        """
        Final dialog that to show where the calculated collapsed cube was put.

        :param label:
        :return:
        """

        final_dialog = QDialog()

        # Create data component label and input box
        widget_desc = QLabel(
            'The collapsed cube was added as an overlay with label "{}"'.
            format(label))
        widget_desc.setWordWrap(True)
        widget_desc.setFixedWidth(350)
        widget_desc.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_desc = QHBoxLayout()
        hb_desc.addWidget(widget_desc)

        # Create Ok button
        okButton = QPushButton("Ok")
        okButton.clicked.connect(lambda: final_dialog.close())
        okButton.setDefault(True)

        hb_buttons = QHBoxLayout()
        hb_buttons.addStretch(1)
        hb_buttons.addWidget(okButton)

        # Add description and buttons to popup box
        vbl = QVBoxLayout()
        vbl.addLayout(hb_desc)
        vbl.addLayout(hb_buttons)

        final_dialog.setLayout(vbl)
        final_dialog.setMaximumWidth(400)
        final_dialog.show()

    def cancel_callback(self, caller=0):
        """
        Cancel callback when the person hits the cancel button

        :param caller:
        :return:
        """
        self.close()

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Escape:
            self.cancel_callback()
예제 #43
0
class LSPServerEditor(QDialog):
    DEFAULT_HOST = '127.0.0.1'
    DEFAULT_PORT = 2084
    DEFAULT_CMD = ''
    DEFAULT_ARGS = ''
    DEFAULT_CONFIGURATION = '{}'
    DEFAULT_EXTERNAL = False
    DEFAULT_STDIO = False
    HOST_REGEX = re.compile(r'^\w+([.]\w+)*$')
    NON_EMPTY_REGEX = re.compile(r'^\S+$')
    JSON_VALID = _('Valid JSON')
    JSON_INVALID = _('Invalid JSON')
    MIN_SIZE = QSize(850, 600)
    INVALID_CSS = "QLineEdit {border: 1px solid red;}"
    VALID_CSS = "QLineEdit {border: 1px solid green;}"

    def __init__(self,
                 parent,
                 language=None,
                 cmd='',
                 host='127.0.0.1',
                 port=2084,
                 args='',
                 external=False,
                 stdio=False,
                 configurations={},
                 get_option=None,
                 set_option=None,
                 remove_option=None,
                 **kwargs):
        super(LSPServerEditor, self).__init__(parent)

        description = _(
            "To create a new server configuration, you need to select a "
            "programming language, set the command to start its associated "
            "server and enter any arguments that should be passed to it on "
            "startup. Additionally, you can set the server's hostname and "
            "port if connecting to an external server, "
            "or to a local one using TCP instead of stdio pipes."
            "<br><br>"
            "<i>Note</i>: You can use the placeholders <tt>{host}</tt> and "
            "<tt>{port}</tt> in the server arguments field to automatically "
            "fill in the respective values.<br>")
        self.parent = parent
        self.external = external
        self.set_option = set_option
        self.get_option = get_option
        self.remove_option = remove_option

        # Widgets
        self.server_settings_description = QLabel(description)
        self.lang_cb = QComboBox(self)
        self.external_cb = QCheckBox(_('External server'), self)
        self.host_label = QLabel(_('Host:'))
        self.host_input = QLineEdit(self)
        self.port_label = QLabel(_('Port:'))
        self.port_spinner = QSpinBox(self)
        self.cmd_label = QLabel(_('Command:'))
        self.cmd_input = QLineEdit(self)
        self.args_label = QLabel(_('Arguments:'))
        self.args_input = QLineEdit(self)
        self.json_label = QLabel(self.JSON_VALID, self)
        self.conf_label = QLabel(_('<b>Server Configuration:</b>'))
        self.conf_input = SimpleCodeEditor(None)

        self.bbox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Cancel)
        self.button_ok = self.bbox.button(QDialogButtonBox.Ok)
        self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel)

        # Widget setup
        self.setMinimumSize(self.MIN_SIZE)
        self.setWindowTitle(_('LSP server editor'))

        self.server_settings_description.setWordWrap(True)

        self.lang_cb.setToolTip(
            _('Programming language provided by the LSP server'))
        self.lang_cb.addItem(_('Select a language'))
        self.lang_cb.addItems(SUPPORTED_LANGUAGES)

        self.button_ok.setEnabled(False)
        if language is not None:
            idx = SUPPORTED_LANGUAGES.index(language)
            self.lang_cb.setCurrentIndex(idx + 1)
            self.button_ok.setEnabled(True)

        self.host_input.setPlaceholderText('127.0.0.1')
        self.host_input.setText(host)
        self.host_input.textChanged.connect(lambda _: self.validate())

        self.port_spinner.setToolTip(_('TCP port number of the server'))
        self.port_spinner.setMinimum(1)
        self.port_spinner.setMaximum(60000)
        self.port_spinner.setValue(port)
        self.port_spinner.valueChanged.connect(lambda _: self.validate())

        self.cmd_input.setText(cmd)
        self.cmd_input.setPlaceholderText('/absolute/path/to/command')

        self.args_input.setToolTip(
            _('Additional arguments required to start the server'))
        self.args_input.setText(args)
        self.args_input.setPlaceholderText(r'--host {host} --port {port}')

        self.conf_input.setup_editor(language='json',
                                     color_scheme=get_option(
                                         'selected', section='appearance'),
                                     wrap=False,
                                     highlight_current_line=True,
                                     font=get_font())
        self.conf_input.set_language('json')
        self.conf_input.setToolTip(
            _('Additional LSP server configuration '
              'set at runtime. JSON required'))
        try:
            conf_text = json.dumps(configurations, indent=4, sort_keys=True)
        except Exception:
            conf_text = '{}'
        self.conf_input.set_text(conf_text)

        self.external_cb.setToolTip(
            _('Check if the server runs on a remote location'))
        self.external_cb.setChecked(external)

        self.stdio_cb = QCheckBox(_('Use stdio pipes for communication'), self)
        self.stdio_cb.setToolTip(
            _('Check if the server communicates '
              'using stdin/out pipes'))
        self.stdio_cb.setChecked(stdio)

        # Layout setup
        hlayout = QHBoxLayout()
        general_vlayout = QVBoxLayout()
        general_vlayout.addWidget(self.server_settings_description)

        vlayout = QVBoxLayout()

        lang_group = QGroupBox(_('Language'))
        lang_layout = QVBoxLayout()
        lang_layout.addWidget(self.lang_cb)
        lang_group.setLayout(lang_layout)
        vlayout.addWidget(lang_group)

        server_group = QGroupBox(_('Language server'))
        server_layout = QGridLayout()
        server_layout.addWidget(self.cmd_label, 0, 0)
        server_layout.addWidget(self.cmd_input, 0, 1)
        server_layout.addWidget(self.args_label, 1, 0)
        server_layout.addWidget(self.args_input, 1, 1)
        server_group.setLayout(server_layout)
        vlayout.addWidget(server_group)

        address_group = QGroupBox(_('Server address'))
        host_layout = QVBoxLayout()
        host_layout.addWidget(self.host_label)
        host_layout.addWidget(self.host_input)

        port_layout = QVBoxLayout()
        port_layout.addWidget(self.port_label)
        port_layout.addWidget(self.port_spinner)

        conn_info_layout = QHBoxLayout()
        conn_info_layout.addLayout(host_layout)
        conn_info_layout.addLayout(port_layout)
        address_group.setLayout(conn_info_layout)
        vlayout.addWidget(address_group)

        advanced_group = QGroupBox(_('Advanced'))
        advanced_layout = QVBoxLayout()
        advanced_layout.addWidget(self.external_cb)
        advanced_layout.addWidget(self.stdio_cb)
        advanced_group.setLayout(advanced_layout)
        vlayout.addWidget(advanced_group)

        conf_layout = QVBoxLayout()
        conf_layout.addWidget(self.conf_label)
        conf_layout.addWidget(self.conf_input)
        conf_layout.addWidget(self.json_label)

        vlayout.addStretch()
        hlayout.addLayout(vlayout, 2)
        hlayout.addLayout(conf_layout, 3)
        general_vlayout.addLayout(hlayout)

        general_vlayout.addWidget(self.bbox)
        self.setLayout(general_vlayout)
        self.form_status(False)

        # Signals
        if not external:
            self.cmd_input.textChanged.connect(lambda x: self.validate())
        self.external_cb.stateChanged.connect(self.set_local_options)
        self.stdio_cb.stateChanged.connect(self.set_stdio_options)
        self.lang_cb.currentIndexChanged.connect(self.lang_selection_changed)
        self.conf_input.textChanged.connect(self.validate)
        self.bbox.accepted.connect(self.accept)
        self.bbox.rejected.connect(self.reject)

        # Final setup
        if language is not None:
            self.form_status(True)
            self.validate()
            if stdio:
                self.set_stdio_options(True)
            if external:
                self.set_local_options(True)

    @Slot()
    def validate(self):
        host_text = self.host_input.text()
        cmd_text = self.cmd_input.text()

        if host_text not in ['127.0.0.1', 'localhost']:
            self.external = True
            self.external_cb.setChecked(True)

        if not self.HOST_REGEX.match(host_text):
            self.button_ok.setEnabled(False)
            self.host_input.setStyleSheet(self.INVALID_CSS)
            if bool(host_text):
                self.host_input.setToolTip(_('Hostname must be valid'))
            else:
                self.host_input.setToolTip(
                    _('Hostname or IP address of the host on which the server '
                      'is running. Must be non empty.'))
        else:
            self.host_input.setStyleSheet(self.VALID_CSS)
            self.host_input.setToolTip(_('Hostname is valid'))
            self.button_ok.setEnabled(True)

        if not self.external:
            if not self.NON_EMPTY_REGEX.match(cmd_text):
                self.button_ok.setEnabled(False)
                self.cmd_input.setStyleSheet(self.INVALID_CSS)
                self.cmd_input.setToolTip(
                    _('Command used to start the LSP server locally. Must be '
                      'non empty'))
                return

            if find_program(cmd_text) is None:
                self.button_ok.setEnabled(False)
                self.cmd_input.setStyleSheet(self.INVALID_CSS)
                self.cmd_input.setToolTip(
                    _('Program was not found '
                      'on your system'))
            else:
                self.cmd_input.setStyleSheet(self.VALID_CSS)
                self.cmd_input.setToolTip(
                    _('Program was found on your '
                      'system'))
                self.button_ok.setEnabled(True)
        else:
            port = int(self.port_spinner.text())
            response = check_connection_port(host_text, port)
            if not response:
                self.button_ok.setEnabled(False)

        try:
            json.loads(self.conf_input.toPlainText())
            try:
                self.json_label.setText(self.JSON_VALID)
            except Exception:
                pass
        except ValueError:
            try:
                self.json_label.setText(self.JSON_INVALID)
                self.button_ok.setEnabled(False)
            except Exception:
                pass

    def form_status(self, status):
        self.host_input.setEnabled(status)
        self.port_spinner.setEnabled(status)
        self.external_cb.setEnabled(status)
        self.stdio_cb.setEnabled(status)
        self.cmd_input.setEnabled(status)
        self.args_input.setEnabled(status)
        self.conf_input.setEnabled(status)
        self.json_label.setVisible(status)

    @Slot()
    def lang_selection_changed(self):
        idx = self.lang_cb.currentIndex()
        if idx == 0:
            self.set_defaults()
            self.form_status(False)
            self.button_ok.setEnabled(False)
        else:
            server = self.parent.get_server_by_lang(SUPPORTED_LANGUAGES[idx -
                                                                        1])
            self.form_status(True)
            if server is not None:
                self.host_input.setText(server.host)
                self.port_spinner.setValue(server.port)
                self.external_cb.setChecked(server.external)
                self.stdio_cb.setChecked(server.stdio)
                self.cmd_input.setText(server.cmd)
                self.args_input.setText(server.args)
                self.conf_input.set_text(json.dumps(server.configurations))
                self.json_label.setText(self.JSON_VALID)
                self.button_ok.setEnabled(True)
            else:
                self.set_defaults()

    def set_defaults(self):
        self.cmd_input.setStyleSheet('')
        self.host_input.setStyleSheet('')
        self.host_input.setText(self.DEFAULT_HOST)
        self.port_spinner.setValue(self.DEFAULT_PORT)
        self.external_cb.setChecked(self.DEFAULT_EXTERNAL)
        self.stdio_cb.setChecked(self.DEFAULT_STDIO)
        self.cmd_input.setText(self.DEFAULT_CMD)
        self.args_input.setText(self.DEFAULT_ARGS)
        self.conf_input.set_text(self.DEFAULT_CONFIGURATION)
        self.json_label.setText(self.JSON_VALID)

    @Slot(bool)
    @Slot(int)
    def set_local_options(self, enabled):
        self.external = enabled
        self.cmd_input.setEnabled(True)
        self.args_input.setEnabled(True)
        if enabled:
            self.cmd_input.setEnabled(False)
            self.cmd_input.setStyleSheet('')
            self.args_input.setEnabled(False)
            self.stdio_cb.stateChanged.disconnect()
            self.stdio_cb.setChecked(False)
            self.stdio_cb.setEnabled(False)
        else:
            self.cmd_input.setEnabled(True)
            self.args_input.setEnabled(True)
            self.stdio_cb.setEnabled(True)
            self.stdio_cb.setChecked(False)
            self.stdio_cb.stateChanged.connect(self.set_stdio_options)
        try:
            self.validate()
        except Exception:
            pass

    @Slot(bool)
    @Slot(int)
    def set_stdio_options(self, enabled):
        self.stdio = enabled
        if enabled:
            self.cmd_input.setEnabled(True)
            self.args_input.setEnabled(True)
            self.external_cb.stateChanged.disconnect()
            self.external_cb.setChecked(False)
            self.external_cb.setEnabled(False)
            self.host_input.setStyleSheet('')
            self.host_input.setEnabled(False)
            self.port_spinner.setEnabled(False)
        else:
            self.cmd_input.setEnabled(True)
            self.args_input.setEnabled(True)
            self.external_cb.setChecked(False)
            self.external_cb.setEnabled(True)
            self.external_cb.stateChanged.connect(self.set_local_options)
            self.host_input.setEnabled(True)
            self.port_spinner.setEnabled(True)
        try:
            self.validate()
        except Exception:
            pass

    def get_options(self):
        language_idx = self.lang_cb.currentIndex()
        language = SUPPORTED_LANGUAGES[language_idx - 1]
        host = self.host_input.text()
        port = int(self.port_spinner.value())
        external = self.external_cb.isChecked()
        stdio = self.stdio_cb.isChecked()
        args = self.args_input.text()
        cmd = self.cmd_input.text()
        configurations = json.loads(self.conf_input.toPlainText())
        server = LSPServer(language=language.lower(),
                           cmd=cmd,
                           args=args,
                           host=host,
                           port=port,
                           external=external,
                           stdio=stdio,
                           configurations=configurations,
                           get_option=self.get_option,
                           set_option=self.set_option,
                           remove_option=self.remove_option)
        return server
예제 #44
0
    def setup_and_check(self,
                        data,
                        title='',
                        readonly=False,
                        xlabels=None,
                        ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        self.data.flags.writeable = True
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)

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

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

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

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

        self.setMinimumSize(400, 300)

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

        return True
예제 #45
0
class Sidebar(QWidget):
    """
    +----------------------+
    |        Results       |
    +======================+
    |                      |
    |  Name = Main         |
    |                      |
    |  +----------------+  |
    |  | ResultsWindow  |  |
    |  +----------------+  |
    |  |                |  |
    |  |  +----------+  |  |
    |  |  | - a      |  |  |
    |  |  |  - b1    |  |  |
    |  |  |  - b2    |  |  |
    |  |  |  - b3    |  |  |
    |  |  +----------+  |  |
    |  |                |  |
    |  +----------------+  |
    |                      |
    |                      |
    |         Apply        |
    +----------------------+

    For Nastran:
      - a: Subcase 1
       - b1. Displacement
       - b2. Stress
       - b3. Strain

    For Cart3d:
      - a1. Geometry
       - b1. ElementID
       - b2. Region
      - a2. Results Case 1
       - b1. U
       - b2. V
       - b3. W

    +--------------+
    |  Sub-Result  | (pulldown)
    +==============+
    | - a1         |
    | - a2         |
    | - a3         |
    | - a4         |
    +--------------+

    For Nastran:
      - a1: Displacement X
      - a2. Displacement Y
      - a3. Displacmenet Z
      - a4. Displacmenet Mag

    For Cart3d:
      - NA (Greyed Out)

    +----------------+
    |     Plot       | (pulldown)
    +================+
    | - Fringe       |
    | - Marker       |
    | - Displacement |
    +----------------+
    - Cart3d -> Fringe (disabled)


    +---------------+
    | Scale Display | (text box)
    +===============+
    | 0 < x < 1000  | (not for fringe)
    +---------------+

    +--------------+
    |   Location   | (pulldown)
    +==============+
    | - nodal      |
    | - centroidal |
    +--------------+
    (disabled)

    +------------------+
    |  Complex Method  | (pulldown)
    +==================+
    | - real           | (usually set to real and disabled)
    | - imag           |
    | - mag            |
    | - phase          |
    | - max over phase |
    +------------------+

    +--------------+
    |    Derive    | (pulldown; only for nodal results)
    +==============+
    | - derive/avg |  (default?)
    | - avg/derive |
    +--------------+
    """
    def __init__(self, parent, debug=False, data=None, clear_data=True, name='main',
                 setup_dict=None):
        """
        Creates the buttons in the Sidebar, not the actual layout

        Parameters
        ----------
        parent : MainWindow()
            the gui
        debug : bool; default=False
            flag for debug info
        data : List[tree]
            the tree
        clear_data : bool; default=True
            ???
        name : str; default='main'
            the active name
        setup_dict : Dict[irow] = List[QWidgets]
            a way to add additional widgets to the sidebar
        """
        QWidget.__init__(self)
        self.parent = parent
        self.debug = debug
        self.setup_dict = setup_dict

        choices = ['keys2', 'purse2', 'cellphone2', 'credit_card2', 'money2']
        if data is None:
            data = []

        self.result_case_windows = [ResultsWindow(self, 'Case/Results', data, choices)]
        data = [
            ('A', 1, []),
            #('B', 2, []),
            #('C', 3, []),
        ]
        self.result_method_window = ResultsWindow(self, 'Method', data, choices)
        self.result_method_window.setVisible(False)
        #else:
            #self.result_method_window = None

        self.show_pulldown = False
        if self.show_pulldown:
            combo_options = ['a1', 'a2', 'a3']
            self.pulldown = QComboBox()
            self.pulldown.addItems(choices)
            self.pulldown.activated[str].connect(self.on_pulldown)

        self.apply_button = QPushButton('Apply', self)
        self.apply_button.clicked.connect(self.on_apply)

        if name is None:
            self.name = None
            self.names = ['N/A']
            name = 'N/A'
        else:
            self.name = str(name)
            self.names = [name]

        self.name_label = QLabel("Name:")
        self.name_pulldown = QComboBox()
        self.name_pulldown.addItem(name)
        self.name_pulldown.setDisabled(True)

        self.setup_layout(data, choices, clear_data=clear_data)
        self.name_pulldown.currentIndexChanged.connect(self.on_update_name)

    @property
    def result_case_window(self):
        if self.name is None:
            i = 0
        else:
            i = self.names.index(self.name)
        i = 0
        return self.result_case_windows[i]

    def setup_layout(self, data, choices, clear_data=True, init=True):
        """creates the sidebar visual layout"""
        if not init:
            #self.frameGeometry().
            width = self.frameGeometry().width()
            height = self.frameGeometry().height()
            #print('width=%s height=%s' % (width, height))

        #print('init...')
        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        irow = 0
        self._add_from_setup_dict(vbox, irow)

        hbox.addWidget(self.name_label)
        hbox.addWidget(self.name_pulldown)
        vbox.addLayout(hbox)

        irow += 1
        self._add_from_setup_dict(vbox, irow)

        nwindows = len(self.result_case_windows)
        #print('nwindows=%s self.names=%s' % (nwindows, self.names))
        for i in range(nwindows):
            #print('*using existing window')
            result_case_window = self.result_case_windows[i]
            vbox.addWidget(result_case_window)
            #result_case_window.setVisible(False)  # be very careful of this...

        nwindows = len(self.result_case_windows)
        for name in self.names[nwindows:]:
            #print('*creating a window')
            result_case_window = ResultsWindow(self, 'Case/Results', data, choices)
            result_case_window.setVisible(False)
            vbox.addWidget(result_case_window)
            self.result_case_windows.append(result_case_window)

        iname = 0
        #if self.name is None:
            #iname = 0
        #else:
            #iname = self.names.index(self.name)
        #for i in range(nwindows):
            #if i != iname:
                #self.result_case_windows[iname].setVisible(False)
        #self.result_case_windows[iname].setVisible(True)

        irow += 1
        self._add_from_setup_dict(vbox, irow)

        if self.result_method_window:
            vbox.addWidget(self.result_method_window)
        if self.show_pulldown:
            vbox.addWidget(self.pulldown)

        irow += 1
        self._add_from_setup_dict(vbox, irow)

        vbox.addWidget(self.apply_button)

        irow += 1
        self._add_from_setup_dict(vbox, irow)

        self.setLayout(vbox)

        if clear_data:
            self.clear_data()

        #if not init:
            #self.frameGeometry().width()
            #self.frameGeometry().height()
            #self.resize(width, height)

    def _add_from_setup_dict(self, vbox, irow):
        if self.setup_dict is None:
            return

        if irow in self.setup_dict:
            widgets = self.setup_dict[irow]
            assert widgets is not None, widgets
            if isinstance(widgets, list):
                for widget_layout in widgets:
                    if isinstance(widget_layout, QBoxLayout):
                        vbox.addLayout(widget_layout)
                    else:
                        vbox.addWidget(widget_layout)
            else:
                widget_layout = widgets
                if isinstance(widget_layout, QBoxLayout):
                    vbox.addLayout(widget_layout)
                else:
                    vbox.addWidget(widget_layout)

    def update_method(self, method):
        if isinstance(method, string_types):
            datai = self.result_method_window.data[0]
            self.result_method_window.data[0] = (method, datai[1], datai[2])
            #print('method=%s datai=%s' % (method, datai))
            self.result_method_window.update_data(self.result_method_window.data)
        else:
            return
             # pragma: no cover
            #datai = self.result_method_window.data[0]

    def get_form(self):
        """
        TODO: At this point, we should clear out the data block and refresh it
        """
        return self.result_case_window.data

    def update_results(self, data, name):
        """
        Updates the sidebar

        Parameters
        ----------
        data : List[tuple]
            the form data
        name : str
            the name that goes at the side
        """
        name = str(name)
        update_name = False
        setup_layout = False
        if self.name is None:
            self.names = [name]
            self.name_pulldown.clear()
            self.name_pulldown.addItems(self.names)
            #self.name_pulldown.setItemText(0, name)
            #self.name_pulldown.setCurrentIndex(1)
            update_name = True
            setup_layout = True

        else:
            if name in self.names:
                i = self.names.index(name)
                self.name_pulldown.setCurrentIndex(i)
            else:
                self.name_pulldown.addItem(name)
                self.names.append(name)
                setup_layout = True

            if len(self.names) >= 2:
                self.name_pulldown.setEnabled(True)
        self.name = name

        self.result_case_window.update_data(data)
        self.apply_button.setEnabled(True)
        if update_name:
            self.on_update_name(None)

        if setup_layout and 0:
            #print('setup_layout******')
            ## TODO: screws up the width of the window
            choices = ['keys2', 'purse2', 'cellphone2', 'credit_card2', 'money2']
            data = [
                ('A', 1, []),
                #('B', 2, []),
                #('C', 3, []),
            ]
            self.setup_layout(data, choices, init=False, clear_data=True)

    def update_methods(self, data):
        """the methods is a hidden box"""
        if self.result_method_window is not None:
            self.result_method_window.update_data(data)
        self.apply_button.setEnabled(True)

    def clear_data(self):
        self.result_case_window.clear_data()
        if self.result_method_window is not None:
            self.result_method_window.clear_data()
        self.apply_button.setEnabled(False)

    def on_pulldown(self):  # pragma: no cover
        print('pulldown...')

    def on_update_name(self):  # pragma: no cover
        """user clicked the pulldown"""
        name = str(self.name_pulldown.currentText())
        data = self.parent._get_sidebar_data(name)
        #self.result_case_window.update_data(data)

    def on_apply(self):
        data = self.result_case_window.data
        valid_a, keys_a = self.result_case_window.treeView.get_row()

        data = self.result_method_window.data
        valid_b, keys_b = self.result_method_window.treeView.get_row()
        if valid_a and valid_b:
            if self.debug:  # pragma: no cover
                print('  rows1 = %s' % self.result_case_window.treeView.old_rows)
                print('        = %s' % str(keys_a))
                print('  rows2 = %s' % self.result_method_window.treeView.old_rows)
                print('        = %s' % str(keys_b))
            else:
                self.update_vtk_window(keys_a, keys_b)

    def update_vtk_window(self, keys_a, keys_b):
        if 0:  # pragma: no cover
            print('keys_a = %s' % str(keys_a))
            for i, key in enumerate(self.parent.case_keys):
                if key[1] == keys_a[0]:
                    break
            print('*i=%s key=%s' % (i, str(key)))
            #self.parent.update_vtk_window_by_key(i)
            result_name = key[1]
            #self.parent.cycle_results_explicit(result_name=result_name, explicit=True)
            #j = self.parent._get_icase(result_name)
            #j = i
        i = keys_a
        result_name = None
        self.parent._set_case(result_name, i, explicit=True)

    def on_clear_results(self):
        if hasattr(self.parent, 'on_clear_results'):
            self.parent.on_clear_results()
    def on_fringe(self, icase):
        if hasattr(self.parent, 'on_fringe'):
            self.parent.on_fringe(icase)
    def on_disp(self, icase):
        if hasattr(self.parent, 'on_disp'):
            self.parent.on_disp(icase)
    def on_vector(self, icase):
        if hasattr(self.parent, 'on_vector'):
            self.parent.on_vector(icase)

    def get_clicked(self):
        self.result_method_window.treeView.get_clicked()
예제 #46
0
    def createUI(self):
        """
        Create the popup box with the calculation input area and buttons.

        :return:
        """
        boldFont = QtGui.QFont()
        boldFont.setBold(True)

        # Create data component label and input box
        self.widget_desc = QLabel(self._general_description)
        self.widget_desc.setWordWrap(True)
        self.widget_desc.setFixedWidth(350)
        self.widget_desc.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_desc = QHBoxLayout()
        hb_desc.addWidget(self.widget_desc)

        # Create data component label and input box
        self.data_label = QLabel("Data:")
        self.data_label.setFixedWidth(100)
        self.data_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.data_label.setFont(boldFont)

        self.data_combobox = QComboBox()
        self.data_combobox.addItems([
            str(x).strip() for x in self.data.component_ids()
            if not x in self.data.coordinate_components
        ])
        self.data_combobox.setMinimumWidth(200)

        hb_data = QHBoxLayout()
        hb_data.addWidget(self.data_label)
        hb_data.addWidget(self.data_combobox)

        # Create operation label and input box
        self.operation_label = QLabel("Operation:")
        self.operation_label.setFixedWidth(100)
        self.operation_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.operation_label.setFont(boldFont)

        self.operation_combobox = QComboBox()
        self.operation_combobox.addItems(operations.keys())
        self.operation_combobox.setMinimumWidth(200)

        hb_operation = QHBoxLayout()
        hb_operation.addWidget(self.operation_label)
        hb_operation.addWidget(self.operation_combobox)

        # Create region label and input box
        self.region_label = QLabel("region:")
        self.region_label.setFixedWidth(100)
        self.region_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.region_label.setFont(boldFont)

        self.region_combobox = QComboBox()

        # Get the Specviz regions and add them in to the Combo box
        for roi in self.parent.specviz._widget.roi_bounds:
            self.region_combobox.addItem("Specviz ROI ({:.3}, {:.3})".format(
                roi[0], roi[1]))

        self.region_combobox.addItems(
            ["Custom (Wavelengths)", "Custom (Indices)"])
        self.region_combobox.setMinimumWidth(200)
        self.region_combobox.currentIndexChanged.connect(
            self._region_selection_change)

        hb_region = QHBoxLayout()
        hb_region.addWidget(self.region_label)
        hb_region.addWidget(self.region_combobox)

        # Create error label
        self.error_label = QLabel("")
        self.error_label.setFixedWidth(100)

        self.error_label_text = QLabel("")
        self.error_label_text.setMinimumWidth(200)
        self.error_label_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hbl_error = QHBoxLayout()
        hbl_error.addWidget(self.error_label)
        hbl_error.addWidget(self.error_label_text)

        # Create start label and input box
        self.start_label = QLabel("Start:")
        self.start_label.setFixedWidth(100)
        self.start_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.start_label.setFont(boldFont)

        self.start_text = QLineEdit()
        self.start_text.setMinimumWidth(200)
        self.start_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_start = QHBoxLayout()
        hb_start.addWidget(self.start_label)
        hb_start.addWidget(self.start_text)

        # Create end label and input box
        self.end_label = QLabel("End:")
        self.end_label.setFixedWidth(100)
        self.end_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.end_label.setFont(boldFont)

        self.end_text = QLineEdit()
        self.end_text.setMinimumWidth(200)
        self.end_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))

        hb_end = QHBoxLayout()
        hb_end.addWidget(self.end_label)
        hb_end.addWidget(self.end_text)

        # Create Calculate and Cancel buttons
        self.calculateButton = QPushButton("Calculate")
        self.calculateButton.clicked.connect(self.calculate_callback)
        self.calculateButton.setDefault(True)

        self.cancelButton = QPushButton("Cancel")
        self.cancelButton.clicked.connect(self.cancel_callback)

        hb_buttons = QHBoxLayout()
        hb_buttons.addStretch(1)
        hb_buttons.addWidget(self.cancelButton)
        hb_buttons.addWidget(self.calculateButton)

        #
        #  Sigma clipping
        #
        vbox_sigma_clipping = QVBoxLayout()

        self.sigma_description = QLabel(
            "Sigma clipping is implemented using <a href='http://docs.astropy.org/en/stable/api/astropy.stats.sigma_clip.html'>astropy.stats.sigma_clip</a>. Empty values will use defaults listed on the webpage, <b>but</b> if the first sigma is empty, then no clipping will be done."
        )
        self.sigma_description.setWordWrap(True)
        hb_sigma = QHBoxLayout()
        hb_sigma.addWidget(self.sigma_description)
        vbox_sigma_clipping.addLayout(hb_sigma)

        # Create sigma
        self.sigma_label = QLabel("Sigma:")
        self.sigma_label.setFixedWidth(100)
        self.sigma_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_label.setFont(boldFont)
        self.sigma_text = QLineEdit()
        self.sigma_text.setMinimumWidth(200)
        self.sigma_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma = QHBoxLayout()
        hb_sigma.addWidget(self.sigma_label)
        hb_sigma.addWidget(self.sigma_text)
        vbox_sigma_clipping.addLayout(hb_sigma)

        # Create sigma_lower
        self.sigma_lower_label = QLabel("Sigma Lower:")
        self.sigma_lower_label.setFixedWidth(100)
        self.sigma_lower_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_lower_label.setFont(boldFont)
        self.sigma_lower_text = QLineEdit()
        self.sigma_lower_text.setMinimumWidth(200)
        self.sigma_lower_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_lower = QHBoxLayout()
        hb_sigma_lower.addWidget(self.sigma_lower_label)
        hb_sigma_lower.addWidget(self.sigma_lower_text)
        vbox_sigma_clipping.addLayout(hb_sigma_lower)

        # Create sigma_upper
        self.sigma_upper_label = QLabel("Sigma Upper:")
        self.sigma_upper_label.setFixedWidth(100)
        self.sigma_upper_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_upper_label.setFont(boldFont)
        self.sigma_upper_text = QLineEdit()
        self.sigma_upper_text.setMinimumWidth(200)
        self.sigma_upper_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_upper = QHBoxLayout()
        hb_sigma_upper.addWidget(self.sigma_upper_label)
        hb_sigma_upper.addWidget(self.sigma_upper_text)
        vbox_sigma_clipping.addLayout(hb_sigma_upper)

        # Create sigma_iters
        self.sigma_iters_label = QLabel("Sigma Iterations:")
        self.sigma_iters_label.setFixedWidth(100)
        self.sigma_iters_label.setAlignment((Qt.AlignRight | Qt.AlignTop))
        self.sigma_iters_label.setFont(boldFont)
        self.sigma_iters_text = QLineEdit()
        self.sigma_iters_text.setMinimumWidth(200)
        self.sigma_iters_text.setAlignment((Qt.AlignLeft | Qt.AlignTop))
        hb_sigma_iters = QHBoxLayout()
        hb_sigma_iters.addWidget(self.sigma_iters_label)
        hb_sigma_iters.addWidget(self.sigma_iters_text)
        vbox_sigma_clipping.addLayout(hb_sigma_iters)

        # Add calculation and buttons to popup box
        vbl = QVBoxLayout()
        vbl.addLayout(hb_desc)
        vbl.addLayout(hb_data)
        vbl.addLayout(hb_operation)
        vbl.addLayout(hb_region)
        vbl.addLayout(hb_start)
        vbl.addLayout(hb_end)
        vbl.addLayout(vbox_sigma_clipping)
        vbl.addLayout(hbl_error)
        vbl.addLayout(hb_buttons)

        self.setLayout(vbl)
        self.setMaximumWidth(700)
        self.show()

        # Fire the callback to set the default values for everything
        self._region_selection_change(0)
예제 #47
0
class Help(SpyderPluginWidget):
    """
    Docstrings viewer widget
    """
    CONF_SECTION = 'help'
    CONFIGWIDGET_CLASS = HelpConfigPage
    LOG_PATH = get_conf_path(CONF_SECTION)
    FONT_SIZE_DELTA = DEFAULT_SMALL_DELTA

    # Signals
    focus_changed = Signal()

    def __init__(self, parent):
        if PYQT5:
            SpyderPluginWidget.__init__(self, parent, main = parent)
        else:
            SpyderPluginWidget.__init__(self, parent)

        self.internal_shell = None

        # Initialize plugin
        self.initialize_plugin()

        self.no_doc_string = _("No documentation available")

        self._last_console_cb = None
        self._last_editor_cb = None

        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        color_scheme = self.get_color_scheme()
        self.set_plain_text_font(self.get_plugin_font(), color_scheme)
        self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap'))

        # Add entries to read-only editor context-menu
        self.wrap_action = create_action(self, _("Wrap lines"),
                                         toggled=self.toggle_wrap_mode)
        self.wrap_action.setChecked(self.get_option('wrap'))
        self.plain_text.editor.readonly_menu.addSeparator()
        add_actions(self.plain_text.editor.readonly_menu, (self.wrap_action,))

        self.set_rich_text_font(self.get_plugin_font('rich_text'))

        self.shell = None

        # locked = disable link with Console
        self.locked = False
        self._last_texts = [None, None]
        self._last_editor_doc = None

        # Object name
        layout_edit = QHBoxLayout()
        layout_edit.setContentsMargins(0, 0, 0, 0)
        txt = _("Source")
        if sys.platform == 'darwin':
            source_label = QLabel("  " + txt)
        else:
            source_label = QLabel(txt)
        layout_edit.addWidget(source_label)
        self.source_combo = QComboBox(self)
        self.source_combo.addItems([_("Console"), _("Editor")])
        self.source_combo.currentIndexChanged.connect(self.source_changed)
        if (not programs.is_module_installed('rope') and
                not programs.is_module_installed('jedi', '>=0.8.1')):
            self.source_combo.hide()
            source_label.hide()
        layout_edit.addWidget(self.source_combo)
        layout_edit.addSpacing(10)
        layout_edit.addWidget(QLabel(_("Object")))
        self.combo = ObjectComboBox(self)
        layout_edit.addWidget(self.combo)
        self.object_edit = QLineEdit(self)
        self.object_edit.setReadOnly(True)
        layout_edit.addWidget(self.object_edit)
        self.combo.setMaxCount(self.get_option('max_history_entries'))
        self.combo.addItems( self.load_history() )
        self.combo.setItemText(0, '')
        self.combo.valid.connect(lambda valid: self.force_refresh())

        # Plain text docstring option
        self.docstring = True
        self.rich_help = self.get_option('rich_mode', True)
        self.plain_text_action = create_action(self, _("Plain Text"),
                                               toggled=self.toggle_plain_text)

        # Source code option
        self.show_source_action = create_action(self, _("Show Source"),
                                                toggled=self.toggle_show_source)

        # Rich text option
        self.rich_text_action = create_action(self, _("Rich Text"),
                                         toggled=self.toggle_rich_text)

        # Add the help actions to an exclusive QActionGroup
        help_actions = QActionGroup(self)
        help_actions.setExclusive(True)
        help_actions.addAction(self.plain_text_action)
        help_actions.addAction(self.rich_text_action)

        # Automatic import option
        self.auto_import_action = create_action(self, _("Automatic import"),
                                                toggled=self.toggle_auto_import)
        auto_import_state = self.get_option('automatic_import')
        self.auto_import_action.setChecked(auto_import_state)

        # Lock checkbox
        self.locked_button = create_toolbutton(self,
                                               triggered=self.toggle_locked)
        layout_edit.addWidget(self.locked_button)
        self._update_lock_icon()

        # Option menu
        options_button = create_toolbutton(self, text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, [self.rich_text_action, self.plain_text_action,
                           self.show_source_action, None,
                           self.auto_import_action])
        options_button.setMenu(menu)
        layout_edit.addWidget(options_button)

        if self.rich_help:
            self.switch_to_rich_text()
        else:
            self.switch_to_plain_text()
        self.plain_text_action.setChecked(not self.rich_help)
        self.rich_text_action.setChecked(self.rich_help)
        self.source_changed()

        # Main layout
        layout = create_plugin_layout(layout_edit)
        # we have two main widgets, but only one of them is shown at a time
        layout.addWidget(self.plain_text)
        layout.addWidget(self.rich_text)
        self.setLayout(layout)

        # Add worker thread for handling rich text rendering
        self._sphinx_thread = SphinxThread(
                                  html_text_no_doc=warning(self.no_doc_string))
        self._sphinx_thread.html_ready.connect(
                                             self._on_sphinx_thread_html_ready)
        self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg)

        # Handle internal and external links
        view = self.rich_text.webview
        if not WEBENGINE:
            view.page().setLinkDelegationPolicy(QWebEnginePage.DelegateAllLinks)
        view.linkClicked.connect(self.handle_link_clicks)

        self._starting_up = True

    #------ SpyderPluginWidget API ---------------------------------------------
    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.variableexplorer, self)

    def get_plugin_title(self):
        """Return widget title"""
        return _('Help')

    def get_plugin_icon(self):
        """Return widget icon"""
        return ima.icon('help')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        self.combo.lineEdit().selectAll()
        return self.combo

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.focus_changed.connect(self.main.plugin_focus_changed)
        self.main.add_dockwidget(self)
        self.main.console.set_help(self)
        self.internal_shell = self.main.console.shell

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        if self._starting_up:
            self._starting_up = False
            self.switch_to_rich_text()
            self.show_intro_message()

    def update_font(self):
        """Update font from Preferences"""
        color_scheme = self.get_color_scheme()
        font = self.get_plugin_font()
        rich_font = self.get_plugin_font(rich_text=True)

        self.set_plain_text_font(font, color_scheme=color_scheme)
        self.set_rich_text_font(rich_font)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        color_scheme_n = 'color_scheme_name'
        color_scheme_o = self.get_color_scheme()
        connect_n = 'connect_to_oi'
        wrap_n = 'wrap'
        wrap_o = self.get_option(wrap_n)
        self.wrap_action.setChecked(wrap_o)
        math_n = 'math'
        math_o = self.get_option(math_n)

        if color_scheme_n in options:
            self.set_plain_text_color_scheme(color_scheme_o)
        if wrap_n in options:
            self.toggle_wrap_mode(wrap_o)
        if math_n in options:
            self.toggle_math_mode(math_o)

        # To make auto-connection changes take place instantly
        self.main.editor.apply_plugin_settings(options=[connect_n])
        self.main.ipyconsole.apply_plugin_settings(options=[connect_n])

    #------ Public API (related to Help's source) -------------------------
    def source_is_console(self):
        """Return True if source is Console"""
        return self.source_combo.currentIndex() == 0

    def switch_to_editor_source(self):
        self.source_combo.setCurrentIndex(1)

    def switch_to_console_source(self):
        self.source_combo.setCurrentIndex(0)

    def source_changed(self, index=None):
        if self.source_is_console():
            # Console
            self.combo.show()
            self.object_edit.hide()
            self.show_source_action.setEnabled(True)
            self.auto_import_action.setEnabled(True)
        else:
            # Editor
            self.combo.hide()
            self.object_edit.show()
            self.show_source_action.setDisabled(True)
            self.auto_import_action.setDisabled(True)
        self.restore_text()

    def save_text(self, callback):
        if self.source_is_console():
            self._last_console_cb = callback
        else:
            self._last_editor_cb = callback

    def restore_text(self):
        if self.source_is_console():
            cb = self._last_console_cb
        else:
            cb = self._last_editor_cb
        if cb is None:
            if self.is_plain_text_mode():
                self.plain_text.clear()
            else:
                self.rich_text.clear()
        else:
            func = cb[0]
            args = cb[1:]
            func(*args)
            if get_meth_class_inst(func) is self.rich_text:
                self.switch_to_rich_text()
            else:
                self.switch_to_plain_text()

    #------ Public API (related to rich/plain text widgets) --------------------
    @property
    def find_widget(self):
        if self.plain_text.isVisible():
            return self.plain_text.find_widget
        else:
            return self.rich_text.find_widget

    def set_rich_text_font(self, font):
        """Set rich text mode font"""
        self.rich_text.set_font(font, fixed_font=self.get_plugin_font())

    def set_plain_text_font(self, font, color_scheme=None):
        """Set plain text mode font"""
        self.plain_text.set_font(font, color_scheme=color_scheme)

    def set_plain_text_color_scheme(self, color_scheme):
        """Set plain text mode color scheme"""
        self.plain_text.set_color_scheme(color_scheme)

    @Slot(bool)
    def toggle_wrap_mode(self, checked):
        """Toggle wrap mode"""
        self.plain_text.editor.toggle_wrap_mode(checked)
        self.set_option('wrap', checked)

    def toggle_math_mode(self, checked):
        """Toggle math mode"""
        self.set_option('math', checked)

    def is_plain_text_mode(self):
        """Return True if plain text mode is active"""
        return self.plain_text.isVisible()

    def is_rich_text_mode(self):
        """Return True if rich text mode is active"""
        return self.rich_text.isVisible()

    def switch_to_plain_text(self):
        """Switch to plain text mode"""
        self.rich_help = False
        self.plain_text.show()
        self.rich_text.hide()
        self.plain_text_action.setChecked(True)

    def switch_to_rich_text(self):
        """Switch to rich text mode"""
        self.rich_help = True
        self.plain_text.hide()
        self.rich_text.show()
        self.rich_text_action.setChecked(True)
        self.show_source_action.setChecked(False)

    def set_plain_text(self, text, is_code):
        """Set plain text docs"""

        # text is coming from utils.dochelpers.getdoc
        if type(text) is dict:
            name = text['name']
            if name:
                rst_title = ''.join(['='*len(name), '\n', name, '\n',
                                    '='*len(name), '\n\n'])
            else:
                rst_title = ''

            if text['argspec']:
                definition = ''.join(['Definition: ', name, text['argspec'],
                                      '\n'])
            else:
                definition = ''

            if text['note']:
                note = ''.join(['Type: ', text['note'], '\n\n----\n\n'])
            else:
                note = ''

            full_text = ''.join([rst_title, definition, note,
                                 text['docstring']])
        else:
            full_text = text

        self.plain_text.set_text(full_text, is_code)
        self.save_text([self.plain_text.set_text, full_text, is_code])

    def set_rich_text_html(self, html_text, base_url):
        """Set rich text"""
        self.rich_text.set_html(html_text, base_url)
        self.save_text([self.rich_text.set_html, html_text, base_url])

    def show_intro_message(self):
        intro_message = _("Here you can get help of any object by pressing "
                          "%s in front of it, either on the Editor or the "
                          "Console.%s"
                          "Help can also be shown automatically after writing "
                          "a left parenthesis next to an object. You can "
                          "activate this behavior in %s.")
        prefs = _("Preferences > Help")
        if sys.platform == 'darwin':
            shortcut = "Cmd+I"
        else:
            shortcut = "Ctrl+I"

        if self.is_rich_text_mode():
            title = _("Usage")
            tutorial_message = _("New to Spyder? Read our")
            tutorial = _("tutorial")
            intro_message = intro_message % ("<b>"+shortcut+"</b>", "<br><br>",
                                             "<i>"+prefs+"</i>")
            self.set_rich_text_html(usage(title, intro_message,
                                          tutorial_message, tutorial),
                                    QUrl.fromLocalFile(CSS_PATH))
        else:
            install_sphinx = "\n\n%s" % _("Please consider installing Sphinx "
                                          "to get documentation rendered in "
                                          "rich text.")
            intro_message = intro_message % (shortcut, "\n\n", prefs)
            intro_message += install_sphinx
            self.set_plain_text(intro_message, is_code=False)

    def show_rich_text(self, text, collapse=False, img_path=''):
        """Show text in rich mode"""
        self.visibility_changed(True)
        self.raise_()
        self.switch_to_rich_text()
        context = generate_context(collapse=collapse, img_path=img_path)
        self.render_sphinx_doc(text, context)

    def show_plain_text(self, text):
        """Show text in plain mode"""
        self.visibility_changed(True)
        self.raise_()
        self.switch_to_plain_text()
        self.set_plain_text(text, is_code=False)

    @Slot()
    def show_tutorial(self):
        tutorial_path = get_module_source_path('spyder.utils.help')
        tutorial = osp.join(tutorial_path, 'tutorial.rst')
        text = open(tutorial).read()
        self.show_rich_text(text, collapse=True)

    def handle_link_clicks(self, url):
        url = to_text_string(url.toString())
        if url == "spy://tutorial":
            self.show_tutorial()
        elif url.startswith('http'):
            programs.start_file(url)
        else:
            self.rich_text.webview.load(QUrl(url))

    #------ Public API ---------------------------------------------------------
    def force_refresh(self):
        if self.source_is_console():
            self.set_object_text(None, force_refresh=True)
        elif self._last_editor_doc is not None:
            self.set_editor_doc(self._last_editor_doc, force_refresh=True)

    def set_object_text(self, text, force_refresh=False, ignore_unknown=False):
        """Set object analyzed by Help"""
        if (self.locked and not force_refresh):
            return
        self.switch_to_console_source()

        add_to_combo = True
        if text is None:
            text = to_text_string(self.combo.currentText())
            add_to_combo = False

        found = self.show_help(text, ignore_unknown=ignore_unknown)
        if ignore_unknown and not found:
            return

        if add_to_combo:
            self.combo.add_text(text)
        if found:
            self.save_history()

        if self.dockwidget is not None:
            self.dockwidget.blockSignals(True)
        self.__eventually_raise_help(text, force=force_refresh)
        if self.dockwidget is not None:
            self.dockwidget.blockSignals(False)

    def set_editor_doc(self, doc, force_refresh=False):
        """
        Use the help plugin to show docstring dictionary computed
        with introspection plugin from the Editor plugin
        """
        if (self.locked and not force_refresh):
            return
        self.switch_to_editor_source()
        self._last_editor_doc = doc
        self.object_edit.setText(doc['obj_text'])

        if self.rich_help:
            self.render_sphinx_doc(doc)
        else:
            self.set_plain_text(doc, is_code=False)

        if self.dockwidget is not None:
            self.dockwidget.blockSignals(True)
        self.__eventually_raise_help(doc['docstring'], force=force_refresh)
        if self.dockwidget is not None:
            self.dockwidget.blockSignals(False)

    def __eventually_raise_help(self, text, force=False):
        index = self.source_combo.currentIndex()
        if hasattr(self.main, 'tabifiedDockWidgets'):
            # 'QMainWindow.tabifiedDockWidgets' was introduced in PyQt 4.5
            if self.dockwidget and (force or self.dockwidget.isVisible()) \
               and not self.ismaximized \
               and (force or text != self._last_texts[index]):
                dockwidgets = self.main.tabifiedDockWidgets(self.dockwidget)
                if self.main.console.dockwidget not in dockwidgets and \
                   (hasattr(self.main, 'ipyconsole') and \
                    self.main.ipyconsole.dockwidget not in dockwidgets):
                    self.dockwidget.show()
                    self.dockwidget.raise_()
        self._last_texts[index] = text

    def load_history(self, obj=None):
        """Load history from a text file in user home directory"""
        if osp.isfile(self.LOG_PATH):
            history = [line.replace('\n', '')
                       for line in open(self.LOG_PATH, 'r').readlines()]
        else:
            history = []
        return history

    def save_history(self):
        """Save history to a text file in user home directory"""
        open(self.LOG_PATH, 'w').write("\n".join( \
                [to_text_string(self.combo.itemText(index))
                 for index in range(self.combo.count())] ))

    @Slot(bool)
    def toggle_plain_text(self, checked):
        """Toggle plain text docstring"""
        if checked:
            self.docstring = checked
            self.switch_to_plain_text()
            self.force_refresh()
        self.set_option('rich_mode', not checked)

    @Slot(bool)
    def toggle_show_source(self, checked):
        """Toggle show source code"""
        if checked:
            self.switch_to_plain_text()
        self.docstring = not checked
        self.force_refresh()
        self.set_option('rich_mode', not checked)

    @Slot(bool)
    def toggle_rich_text(self, checked):
        """Toggle between sphinxified docstrings or plain ones"""
        if checked:
            self.docstring = not checked
            self.switch_to_rich_text()
        self.set_option('rich_mode', checked)

    @Slot(bool)
    def toggle_auto_import(self, checked):
        """Toggle automatic import feature"""
        self.combo.validate_current_text()
        self.set_option('automatic_import', checked)
        self.force_refresh()

    @Slot()
    def toggle_locked(self):
        """
        Toggle locked state
        locked = disable link with Console
        """
        self.locked = not self.locked
        self._update_lock_icon()

    def _update_lock_icon(self):
        """Update locked state icon"""
        icon = ima.icon('lock') if self.locked else ima.icon('lock_open')
        self.locked_button.setIcon(icon)
        tip = _("Unlock") if self.locked else _("Lock")
        self.locked_button.setToolTip(tip)

    def set_shell(self, shell):
        """Bind to shell"""
        self.shell = shell

    def get_shell(self):
        """
        Return shell which is currently bound to Help,
        or another running shell if it has been terminated
        """
        if not hasattr(self.shell, 'get_doc') or not self.shell.is_running():
            self.shell = None
            if self.main.ipyconsole is not None:
                shell = self.main.ipyconsole.get_current_shellwidget()
                if shell is not None and shell.kernel_client is not None:
                    self.shell = shell
            if self.shell is None:
                self.shell = self.internal_shell
        return self.shell

    def render_sphinx_doc(self, doc, context=None):
        """Transform doc string dictionary to HTML and show it"""
        # Math rendering option could have changed
        fname = self.parent().parent().editor.get_current_filename()
        dname = osp.dirname(fname)
        self._sphinx_thread.render(doc, context, self.get_option('math'),
                                   dname)

    def _on_sphinx_thread_html_ready(self, html_text):
        """Set our sphinx documentation based on thread result"""
        self._sphinx_thread.wait()
        self.set_rich_text_html(html_text, QUrl.fromLocalFile(CSS_PATH))

    def _on_sphinx_thread_error_msg(self, error_msg):
        """ Display error message on Sphinx rich text failure"""
        self._sphinx_thread.wait()
        self.plain_text_action.setChecked(True)
        sphinx_ver = programs.get_module_version('sphinx')
        QMessageBox.critical(self,
                    _('Help'),
                    _("The following error occured when calling "
                      "<b>Sphinx %s</b>. <br>Incompatible Sphinx "
                      "version or doc string decoding failed."
                      "<br><br>Error message:<br>%s"
                      ) % (sphinx_ver, error_msg))

    def show_help(self, obj_text, ignore_unknown=False):
        """Show help"""
        shell = self.get_shell()
        if shell is None:
            return
        obj_text = to_text_string(obj_text)

        if not shell.is_defined(obj_text):
            if self.get_option('automatic_import') and \
               self.internal_shell.is_defined(obj_text, force_import=True):
                shell = self.internal_shell
            else:
                shell = None
                doc = None
                source_text = None

        if shell is not None:
            doc = shell.get_doc(obj_text)
            source_text = shell.get_source(obj_text)

        is_code = False

        if self.rich_help:
            self.render_sphinx_doc(doc)
            return doc is not None
        elif self.docstring:
            hlp_text = doc
            if hlp_text is None:
                hlp_text = source_text
                if hlp_text is None:
                    hlp_text = self.no_doc_string
                    if ignore_unknown:
                        return False
        else:
            hlp_text = source_text
            if hlp_text is None:
                hlp_text = doc
                if hlp_text is None:
                    hlp_text = _("No source code available.")
                    if ignore_unknown:
                        return False
            else:
                is_code = True
        self.set_plain_text(hlp_text, is_code=is_code)
        return True
예제 #48
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.interpolation.connect(self._on_interpolation_change)
        self.layer.events.rendering.connect(self._on_rendering_change)
        self.layer.events.iso_threshold.connect(self._on_iso_threshold_change)
        self.layer.events.attenuation.connect(self._on_attenuation_change)
        self.layer.events._ndisplay.connect(self._on_ndisplay_change)
        self.layer.events.depiction.connect(self._on_depiction_change)
        self.layer.plane.events.thickness.connect(
            self._on_plane_thickness_change)

        self.interpComboBox = QComboBox(self)
        self.interpComboBox.activated[str].connect(self.changeInterpolation)
        self.interpLabel = QLabel(trans._('interpolation:'))

        renderComboBox = QComboBox(self)
        rendering_options = [i.value for i in ImageRendering]
        renderComboBox.addItems(rendering_options)
        index = renderComboBox.findText(self.layer.rendering,
                                        Qt.MatchFixedString)
        renderComboBox.setCurrentIndex(index)
        renderComboBox.activated[str].connect(self.changeRendering)
        self.renderComboBox = renderComboBox
        self.renderLabel = QLabel(trans._('rendering:'))

        self.depictionComboBox = QComboBox(self)
        depiction_options = [d.value for d in VolumeDepiction]
        self.depictionComboBox.addItems(depiction_options)
        index = self.depictionComboBox.findText(self.layer.depiction,
                                                Qt.MatchFixedString)
        self.depictionComboBox.setCurrentIndex(index)
        self.depictionComboBox.activated[str].connect(self.changeDepiction)
        self.depictionLabel = QLabel(trans._('depiction:'))

        self.planeControls = QtPlaneControls()
        self.planeControls.planeThicknessSlider.setValue(
            self.layer.plane.thickness)
        self.planeControls.planeThicknessSlider.valueChanged.connect(
            self.changePlaneThickness)
        action_manager.bind_button(
            'napari:orient_plane_normal_along_z',
            self.planeControls.planeNormalButtons.zButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_y',
            self.planeControls.planeNormalButtons.yButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_x',
            self.planeControls.planeNormalButtons.xButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_view_direction',
            self.planeControls.planeNormalButtons.obliqueButton,
        )

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.iso_threshold * 100))
        sld.valueChanged.connect(self.changeIsoThreshold)
        self.isoThresholdSlider = sld
        self.isoThresholdLabel = QLabel(trans._('iso threshold:'))

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.attenuation * 200))
        sld.valueChanged.connect(self.changeAttenuation)
        self.attenuationSlider = sld
        self.attenuationLabel = QLabel(trans._('attenuation:'))
        self._on_ndisplay_change()

        colormap_layout = QHBoxLayout()
        if hasattr(self.layer, 'rgb') and self.layer.rgb:
            colormap_layout.addWidget(QLabel("RGB"))
            self.colormapComboBox.setVisible(False)
            self.colorbarLabel.setVisible(False)
        else:
            colormap_layout.addWidget(self.colorbarLabel)
            colormap_layout.addWidget(self.colormapComboBox)
        colormap_layout.addStretch(1)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addWidget(QLabel(trans._('opacity:')), 0, 0)
        self.grid_layout.addWidget(self.opacitySlider, 0, 1)
        self.grid_layout.addWidget(QLabel(trans._('contrast limits:')), 1, 0)
        self.grid_layout.addWidget(self.contrastLimitsSlider, 1, 1)
        self.grid_layout.addWidget(QLabel(trans._('auto-contrast:')), 2, 0)
        self.grid_layout.addWidget(self.autoScaleBar, 2, 1)
        self.grid_layout.addWidget(QLabel(trans._('gamma:')), 3, 0)
        self.grid_layout.addWidget(self.gammaSlider, 3, 1)
        self.grid_layout.addWidget(QLabel(trans._('colormap:')), 4, 0)
        self.grid_layout.addLayout(colormap_layout, 4, 1)
        self.grid_layout.addWidget(QLabel(trans._('blending:')), 5, 0)
        self.grid_layout.addWidget(self.blendComboBox, 5, 1)
        self.grid_layout.addWidget(self.interpLabel, 6, 0)
        self.grid_layout.addWidget(self.interpComboBox, 6, 1)
        self.grid_layout.addWidget(self.renderLabel, 7, 0)
        self.grid_layout.addWidget(self.renderComboBox, 7, 1)
        self.grid_layout.addWidget(self.depictionLabel, 8, 0)
        self.grid_layout.addWidget(self.depictionComboBox, 8, 1)
        self.grid_layout.addWidget(self.planeControls, 9, 0, 2, 2)
        self.grid_layout.addWidget(self.isoThresholdLabel, 11, 0)
        self.grid_layout.addWidget(self.isoThresholdSlider, 11, 1)
        self.grid_layout.addWidget(self.attenuationLabel, 12, 0)
        self.grid_layout.addWidget(self.attenuationSlider, 12, 1)
        self.grid_layout.setRowStretch(13, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
예제 #49
0
class HelpWidget(PluginMainWidget):

    ENABLE_SPINNER = True

    # Signals
    sig_item_found = Signal()
    """This signal is emitted when an item is found."""

    sig_render_started = Signal()
    """This signal is emitted to inform a help text rendering has started."""

    sig_render_finished = Signal()
    """This signal is emitted to inform a help text rendering has finished."""
    def __init__(self, name=None, plugin=None, parent=None):
        super().__init__(name, plugin, parent)

        # Attributes
        self._starting_up = True
        self._current_color_scheme = None
        self._last_texts = [None, None]
        self._last_editor_doc = None
        self._last_console_cb = None
        self._last_editor_cb = None
        self.css_path = self.get_conf('css_path', CSS_PATH, 'appearance')
        self.no_docs = _("No documentation available")
        self.docstring = True  # TODO: What is this used for?

        # Widgets
        self._sphinx_thread = SphinxThread(
            html_text_no_doc=warning(self.no_docs, css_path=self.css_path),
            css_path=self.css_path,
        )
        self.shell = None
        self.internal_console = None
        self.internal_shell = None
        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        self.source_label = QLabel(_("Source"))
        self.source_label.ID = HelpWidgetToolbarItems.SourceLabel

        self.source_combo = QComboBox(self)
        self.source_combo.ID = HelpWidgetToolbarItems.SourceCombo

        self.object_label = QLabel(_("Object"))
        self.object_label.ID = HelpWidgetToolbarItems.ObjectLabel

        self.object_combo = ObjectComboBox(self,
                                           HelpWidgetToolbarItems.ObjectCombo)

        self.object_edit = QLineEdit(self)
        self.object_edit.ID = HelpWidgetToolbarItems.ObjectEdit

        # Setup
        self.object_edit.setReadOnly(True)
        self.object_combo.setMaxCount(self.get_conf('max_history_entries'))
        self.object_combo.setItemText(0, '')
        self.plain_text.set_wrap_mode(self.get_conf('wrap'))
        self.source_combo.addItems([_("Console"), _("Editor")])
        if (not programs.is_module_installed('rope')
                and not programs.is_module_installed('jedi', '>=0.11.0')):
            self.source_combo.hide()
            self.source_label.hide()

        # Layout
        self.stack_layout = layout = QStackedLayout()
        layout.addWidget(self.rich_text)
        layout.addWidget(self.plain_text)
        self.setLayout(layout)

        # Signals
        self._sphinx_thread.html_ready.connect(
            self._on_sphinx_thread_html_ready)
        self._sphinx_thread.error_msg.connect(self._on_sphinx_thread_error_msg)
        self.object_combo.valid.connect(self.force_refresh)
        self.rich_text.sig_link_clicked.connect(self.handle_link_clicks)
        self.source_combo.currentIndexChanged.connect(
            lambda x: self.source_changed())
        self.sig_render_started.connect(self.start_spinner)
        self.sig_render_finished.connect(self.stop_spinner)

    # --- PluginMainWidget API
    # ------------------------------------------------------------------------
    def get_title(self):
        return _('Help')

    def setup(self):
        self.wrap_action = self.create_action(
            name=HelpWidgetActions.ToggleWrap,
            text=_("Wrap lines"),
            toggled=True,
            initial=self.get_conf('wrap'),
            option='wrap')
        self.copy_action = self.create_action(
            name=HelpWidgetActions.CopyAction,
            text=_("Copy"),
            triggered=lambda value: self.plain_text.copy(),
            register_shortcut=False,
        )
        self.select_all_action = self.create_action(
            name=HelpWidgetActions.SelectAll,
            text=_("Select All"),
            triggered=lambda value: self.plain_text.select_all(),
            register_shortcut=False,
        )
        self.auto_import_action = self.create_action(
            name=HelpWidgetActions.ToggleAutomaticImport,
            text=_("Automatic import"),
            toggled=True,
            initial=self.get_conf('automatic_import'),
            option='automatic_import')
        self.show_source_action = self.create_action(
            name=HelpWidgetActions.ToggleShowSource,
            text=_("Show Source"),
            toggled=True,
            option='show_source')
        self.rich_text_action = self.create_action(
            name=HelpWidgetActions.ToggleRichMode,
            text=_("Rich Text"),
            toggled=True,
            initial=self.get_conf('rich_mode'),
            option='rich_mode')
        self.plain_text_action = self.create_action(
            name=HelpWidgetActions.TogglePlainMode,
            text=_("Plain Text"),
            toggled=True,
            initial=self.get_conf('plain_mode'),
            option='plain_mode')
        self.locked_action = self.create_action(
            name=HelpWidgetActions.ToggleLocked,
            text=_("Lock/Unlock"),
            toggled=True,
            icon=self.create_icon('lock_open'),
            initial=self.get_conf('locked'),
            option='locked')
        self.home_action = self.create_action(
            name=HelpWidgetActions.Home,
            text=_("Home"),
            triggered=self.show_intro_message,
            icon=self.create_icon('home'),
        )

        # Add the help actions to an exclusive QActionGroup
        help_actions = QActionGroup(self)
        help_actions.setExclusive(True)
        help_actions.addAction(self.plain_text_action)
        help_actions.addAction(self.rich_text_action)

        # Menu
        menu = self.get_options_menu()
        for item in [
                self.rich_text_action, self.plain_text_action,
                self.show_source_action
        ]:
            self.add_item_to_menu(
                item,
                menu=menu,
                section=HelpWidgetOptionsMenuSections.Display,
            )

        self.add_item_to_menu(
            self.auto_import_action,
            menu=menu,
            section=HelpWidgetOptionsMenuSections.Other,
        )

        # Plain text menu
        self._plain_text_context_menu = self.create_menu(
            "plain_text_context_menu")
        self.add_item_to_menu(
            self.copy_action,
            self._plain_text_context_menu,
            section="copy_section",
        )
        self.add_item_to_menu(
            self.select_all_action,
            self._plain_text_context_menu,
            section="select_section",
        )
        self.add_item_to_menu(
            self.wrap_action,
            self._plain_text_context_menu,
            section="wrap_section",
        )

        # Toolbar
        toolbar = self.get_main_toolbar()
        for item in [
                self.source_label, self.source_combo, self.object_label,
                self.object_combo, self.object_edit, self.home_action,
                self.locked_action
        ]:
            self.add_item_to_toolbar(
                item,
                toolbar=toolbar,
                section=HelpWidgetMainToolbarSections.Main,
            )

        self.source_changed()
        self.switch_to_rich_text()
        self.show_intro_message()

        # Signals
        self.plain_text.sig_custom_context_menu_requested.connect(
            self._show_plain_text_context_menu)

    def _should_display_welcome_page(self):
        """Determine if the help welcome page should be displayed."""
        return (self._last_editor_doc is None or self._last_console_cb is None
                or self._last_editor_cb is None)

    @on_conf_change(option='wrap')
    def on_wrap_option_update(self, value):
        self.plain_text.set_wrap_mode(value)

    @on_conf_change(option='locked')
    def on_lock_update(self, value):
        if value:
            icon = self.create_icon('lock')
            tip = _("Unlock")
        else:
            icon = self.create_icon('lock_open')
            tip = _("Lock")

        action = self.get_action(HelpWidgetActions.ToggleLocked)
        action.setIcon(icon)
        action.setToolTip(tip)

    @on_conf_change(option='automatic_import')
    def on_automatic_import_update(self, value):
        self.object_combo.validate_current_text()
        if self._should_display_welcome_page():
            self.show_intro_message()
        else:
            self.force_refresh()

    @on_conf_change(option='rich_mode')
    def on_rich_mode_update(self, value):
        if value:
            # Plain Text OFF / Rich text ON
            self.docstring = not value
            self.stack_layout.setCurrentWidget(self.rich_text)
            self.get_action(
                HelpWidgetActions.ToggleShowSource).setChecked(False)
        else:
            # Plain Text ON / Rich text OFF
            self.docstring = value
            self.stack_layout.setCurrentWidget(self.plain_text)

        if self._should_display_welcome_page():
            self.show_intro_message()
        else:
            self.force_refresh()

    @on_conf_change(option='show_source')
    def on_show_source_update(self, value):
        if value:
            self.switch_to_plain_text()
            self.get_action(HelpWidgetActions.ToggleRichMode).setChecked(False)

        self.docstring = not value
        if self._should_display_welcome_page():
            self.show_intro_message()
        else:
            self.force_refresh()

    def update_actions(self):
        for __, action in self.get_actions().items():
            # IMPORTANT: Since we are defining the main actions in here
            # and the context is WidgetWithChildrenShortcut we need to
            # assign the same actions to the children widgets in order
            # for shortcuts to work
            for widget in [
                    self.plain_text, self.rich_text, self.source_combo,
                    self.object_combo, self.object_edit
            ]:
                if action not in widget.actions():
                    widget.addAction(action)

    def get_focus_widget(self):
        self.object_combo.lineEdit().selectAll()
        return self.object_combo

    # --- Private API
    # ------------------------------------------------------------------------
    @Slot(QPoint)
    def _show_plain_text_context_menu(self, point):
        point = self.plain_text.mapToGlobal(point)
        self._plain_text_context_menu.popup(point)

    def _on_sphinx_thread_html_ready(self, html_text):
        """
        Set our sphinx documentation based on thread result.

        Parameters
        ----------
        html_text: str
            Html results text.
        """
        self._sphinx_thread.wait()
        self.set_rich_text_html(html_text, QUrl.fromLocalFile(self.css_path))
        self.sig_render_finished.emit()
        self.stop_spinner()

    def _on_sphinx_thread_error_msg(self, error_msg):
        """
        Display error message on Sphinx rich text failure.

        Parameters
        ----------
        error_msg: str
            Error message text.
        """
        self._sphinx_thread.wait()
        self.plain_text_action.setChecked(True)
        sphinx_ver = programs.get_module_version('sphinx')
        QMessageBox.critical(
            self,
            _('Help'),
            _("The following error occurred when calling "
              "<b>Sphinx %s</b>. <br>Incompatible Sphinx "
              "version or doc string decoding failed."
              "<br><br>Error message:<br>%s") % (sphinx_ver, error_msg),
        )
        self.sig_render_finished.emit()

    # --- Public API
    # ------------------------------------------------------------------------
    def source_is_console(self):
        """Return True if source is Console."""
        return self.source_combo.currentIndex() == 0

    def switch_to_editor_source(self):
        """Switch to editor view of the help viewer."""
        self.source_combo.setCurrentIndex(1)

    def switch_to_console_source(self):
        """Switch to console view of the help viewer."""
        self.source_combo.setCurrentIndex(0)

    def source_changed(self):
        """Handle a source (plain/rich) change."""
        is_console = self.source_is_console()
        if is_console:
            self.object_combo.show()
            self.object_edit.hide()
        else:
            # Editor
            self.object_combo.hide()
            self.object_edit.show()

        self.get_action(
            HelpWidgetActions.ToggleShowSource).setEnabled(is_console)
        self.get_action(
            HelpWidgetActions.ToggleAutomaticImport).setEnabled(is_console)
        self.restore_text()

    def save_text(self, callback):
        """
        Save help text.

        Parameters
        ----------
        callback: callable
            Method to call on save.
        """
        if self.source_is_console():
            self._last_console_cb = callback
        else:
            self._last_editor_cb = callback

    def restore_text(self):
        """Restore last text using callback."""
        if self.source_is_console():
            cb = self._last_console_cb
        else:
            cb = self._last_editor_cb

        if cb is None:
            if self.get_conf('plain_mode'):
                self.switch_to_plain_text()
            else:
                self.switch_to_rich_text()
        else:
            func = cb[0]
            args = cb[1:]
            func(*args)
            if get_meth_class_inst(func) is self.rich_text:
                self.switch_to_rich_text()
            else:
                self.switch_to_plain_text()

    @property
    def find_widget(self):
        """Show find widget."""
        if self.get_conf('plain_mode'):
            return self.plain_text.find_widget
        else:
            return self.rich_text.find_widget

    def switch_to_plain_text(self):
        """Switch to plain text mode."""
        self.get_action(HelpWidgetActions.TogglePlainMode).setChecked(True)

    def switch_to_rich_text(self):
        """Switch to rich text mode."""
        self.get_action(HelpWidgetActions.ToggleRichMode).setChecked(True)

    def set_plain_text(self, text, is_code):
        """
        Set plain text docs.

        Parameters
        ----------
        text: str
            Text content.
        is_code: bool
            True if it is code text.

        Notes
        -----
        Text is coming from utils.dochelpers.getdoc
        """
        if type(text) is dict:
            name = text['name']
            if name:
                rst_title = ''.join([
                    '=' * len(name), '\n', name, '\n', '=' * len(name), '\n\n'
                ])
            else:
                rst_title = ''
            try:
                if text['argspec']:
                    definition = ''.join(
                        ['Definition: ', name, text['argspec'], '\n\n'])
                else:
                    definition = ''

                if text['note']:
                    note = ''.join(['Type: ', text['note'], '\n\n----\n\n'])
                else:
                    note = ''
            except TypeError:
                definition = self.no_docs
                note = ''

            full_text = ''.join(
                [rst_title, definition, note, text['docstring']])
        else:
            full_text = text

        self.plain_text.set_text(full_text, is_code)
        self.save_text([self.plain_text.set_text, full_text, is_code])

    def set_rich_text_html(self, html_text, base_url):
        """
        Set rich text.

        Parameters
        ----------
        html_text: str
            Html string.
        base_url: str
            Location of stylesheets and images to load in the page.
        """
        self.rich_text.set_html(html_text, base_url)
        self.save_text([self.rich_text.set_html, html_text, base_url])

    def show_loading_message(self):
        """Create html page to show while the documentation is generated."""
        self.sig_render_started.emit()
        loading_message = _("Retrieving documentation")
        loading_img = get_image_path('loading_sprites')
        if os.name == 'nt':
            loading_img = loading_img.replace('\\', '/')

        self.set_rich_text_html(
            loading(loading_message, loading_img, css_path=self.css_path),
            QUrl.fromLocalFile(self.css_path),
        )

    def show_intro_message(self):
        """Show message on Help with the right shortcuts."""
        intro_message_eq = _("Here you can get help of any object by pressing "
                             "%s in front of it, either on the Editor or the "
                             "Console.%s")
        intro_message_dif = _(
            "Here you can get help of any object by pressing "
            "%s in front of it on the Editor, or %s in front "
            "of it on the Console.%s")
        intro_message_common = _(
            "Help can also be shown automatically after writing "
            "a left parenthesis next to an object. You can "
            "activate this behavior in %s.")
        prefs = _("Preferences > Help")

        shortcut_editor = self.get_conf('editor/inspect current object',
                                        section='shortcuts')
        shortcut_console = self.get_conf('console/inspect current object',
                                         section='shortcuts')

        if sys.platform == 'darwin':
            shortcut_editor = shortcut_editor.replace('Ctrl', 'Cmd')
            shortcut_console = shortcut_console.replace('Ctrl', 'Cmd')

        if self.get_conf('rich_mode'):
            title = _("Usage")
            tutorial_message = _("New to Spyder? Read our")
            tutorial = _("tutorial")
            if shortcut_editor == shortcut_console:
                intro_message = (intro_message_eq + intro_message_common) % (
                    "<b>" + shortcut_editor + "</b>", "<br><br>",
                    "<i>" + prefs + "</i>")
            else:
                intro_message = (intro_message_dif + intro_message_common) % (
                    "<b>" + shortcut_editor + "</b>", "<b>" + shortcut_console
                    + "</b>", "<br><br>", "<i>" + prefs + "</i>")

            self.set_rich_text_html(
                usage(title,
                      intro_message,
                      tutorial_message,
                      tutorial,
                      css_path=self.css_path),
                QUrl.fromLocalFile(self.css_path))
        else:
            install_sphinx = "\n\n%s" % _("Please consider installing Sphinx "
                                          "to get documentation rendered in "
                                          "rich text.")
            if shortcut_editor == shortcut_console:
                intro_message = (intro_message_eq + intro_message_common) % (
                    shortcut_editor, "\n\n", prefs)
            else:
                intro_message = (intro_message_dif + intro_message_common) % (
                    shortcut_editor, shortcut_console, "\n\n", prefs)

            intro_message += install_sphinx
            self.set_plain_text(intro_message, is_code=False)

    def show_rich_text(self, text, collapse=False, img_path=''):
        """
        Show text in rich mode.

        Parameters
        ----------
        text: str
            Plain text to display.
        collapse: bool, optional
            Show collapsable sections as collapsed/expanded. Default is False.
        img_path: str, optional
            Path to folder with additional images needed to correctly
            display the rich text help. Default is ''.
        """
        self.switch_to_rich_text()
        context = generate_context(collapse=collapse,
                                   img_path=img_path,
                                   css_path=self.css_path)
        self.render_sphinx_doc(text, context)

    def show_plain_text(self, text):
        """
        Show text in plain mode.

        Parameters
        ----------
        text: str
            Plain text to display.
        """
        self.switch_to_plain_text()
        self.set_plain_text(text, is_code=False)

    @Slot()
    def show_tutorial(self):
        """Show the Spyder tutorial."""
        tutorial_path = get_module_source_path('spyder.plugins.help.utils')
        tutorial = os.path.join(tutorial_path, 'tutorial.rst')

        with open(tutorial, 'r') as fh:
            text = fh.read()

        self.show_rich_text(text, collapse=True)

    def handle_link_clicks(self, url):
        """
        Handle how url links should be opened.

        Parameters
        ----------
        url: QUrl
            QUrl object containing the link to open.
        """
        url = to_text_string(url.toString())
        if url == "spy://tutorial":
            self.show_tutorial()
        elif url.startswith('http'):
            start_file(url)
        else:
            self.rich_text.load_url(url)

    @Slot()
    @Slot(bool)
    @Slot(bool, bool)
    def force_refresh(self, valid=True, editing=True):
        """
        Force a refresh/rerender of the help viewer content.

        Parameters
        ----------
        valid: bool, optional
            Default is True.
        editing: bool, optional
            Default is True.
        """
        if valid:
            if self.source_is_console():
                self.set_object_text(None, force_refresh=True)
            elif self._last_editor_doc is not None:
                self.set_editor_doc(self._last_editor_doc, force_refresh=True)

    def set_object_text(self, text, force_refresh=False, ignore_unknown=False):
        """
        Set object's name in Help's combobox.

        Parameters
        ----------
        text: str
            Object name.
        force_refresh: bool, optional
            Force a refresh with the rendering.
        ignore_unknown: bool, optional
            Ignore not found object names.

        See Also
        --------
        :py:meth:spyder.widgets.mixins.GetHelpMixin.show_object_info
        """
        if self.get_conf('locked') and not force_refresh:
            return

        self.switch_to_console_source()
        add_to_combo = True
        if text is None:
            text = to_text_string(self.object_combo.currentText())
            add_to_combo = False

        found = self.show_help(text, ignore_unknown=ignore_unknown)
        if ignore_unknown and not found:
            return

        if add_to_combo:
            self.object_combo.add_text(text)

        if found:
            self.sig_item_found.emit()

        index = self.source_combo.currentIndex()
        self._last_texts[index] = text

    def set_editor_doc(self, help_data, force_refresh=False):
        """
        Set content for help data sent from the editor.

        Parameters
        ----------
        help_data: dict
            Dictionary with editor introspection information.
        force_refresh: bool, optional
            Force a refresh with the rendering.

        Examples
        --------
        >>> help_data = {
            'obj_text': str,
            'name': str,
            'argspec': str,
            'note': str,
            'docstring': str,
            'path': str,
        }
        """
        if self.get_conf('locked') and not force_refresh:
            return

        self.switch_to_editor_source()
        self._last_editor_doc = help_data
        self.object_edit.setText(help_data['obj_text'])

        if self.get_conf('rich_mode'):
            self.render_sphinx_doc(help_data)
        else:
            self.set_plain_text(help_data, is_code=False)

        index = self.source_combo.currentIndex()
        self._last_texts[index] = help_data['docstring']

    def set_shell(self, shell):
        """
        Bind to shell.

        Parameters
        ----------
        shell: object
            internal shell or ipython console shell
        """
        self.shell = shell

    def get_shell(self):
        """
        Return shell which is currently bound to Help.
        """
        if self.shell is None:
            self.shell = self.internal_shell

        return self.shell

    def render_sphinx_doc(self, help_data, context=None, css_path=CSS_PATH):
        """
        Transform help_data dictionary to HTML and show it.

        Parameters
        ----------
        help_data: str or dict
            Dictionary with editor introspection information.
        context: dict
            Sphinx context.
        css_path: str
            Path to CSS file for styling.
        """
        if isinstance(help_data, dict):
            path = help_data.pop('path', '')
            dname = os.path.dirname(path)
        else:
            dname = ''

        # Math rendering option could have changed
        self._sphinx_thread.render(help_data,
                                   context,
                                   self.get_conf('math'),
                                   dname,
                                   css_path=self.css_path)
        self.show_loading_message()

    def show_help(self, obj_text, ignore_unknown=False):
        """
        Show help for an object's name.

        Parameters
        ----------
        obj_text: str
            Object's name.
        ignore_unknown: bool, optional
            Ignore unknown object's name.
        """
        # TODO: This method makes active use of the shells. It would be better
        # to use signals and pass information this way for better decoupling.
        shell = self.get_shell()
        if shell is None:
            return

        obj_text = to_text_string(obj_text)

        if not shell.is_defined(obj_text):
            if (self.get_conf('automatic_import')
                    and self.internal_shell.is_defined(obj_text,
                                                       force_import=True)):
                shell = self.internal_shell
            else:
                shell = None
                doc = None
                source_text = None

        if shell is not None:
            doc = shell.get_doc(obj_text)
            source_text = shell.get_source(obj_text)

        is_code = False

        if self.get_conf('rich_mode'):
            self.render_sphinx_doc(doc, css_path=self.css_path)
            return doc is not None
        elif self.docstring:
            hlp_text = doc
            if hlp_text is None:
                hlp_text = source_text
                if hlp_text is None:
                    return False
        else:
            hlp_text = source_text
            if hlp_text is None:
                hlp_text = doc
                if hlp_text is None:
                    hlp_text = _("No source code available.")
                    if ignore_unknown:
                        return False
            else:
                is_code = True

        self.set_plain_text(hlp_text, is_code=is_code)
        return True

    def set_rich_text_font(self, font, fixed_font):
        """
        Set rich text mode font.

        Parameters
        ----------
        fixed_font: QFont
            The current rich text font to use.
        """

        self.rich_text.set_font(font, fixed_font=fixed_font)

    def set_plain_text_font(self, font, color_scheme=None):
        """
        Set plain text mode font.

        Parameters
        ----------
        font: QFont
            The current plain text font to use.
        color_scheme: str
            The selected color scheme.
        """
        if color_scheme is None:
            color_scheme = self._current_color_scheme

        self.plain_text.set_font(font, color_scheme=color_scheme)

    def set_plain_text_color_scheme(self, color_scheme):
        """
        Set plain text mode color scheme.

        Parameters
        ----------
        color_scheme: str
            The selected color scheme.
        """
        self._current_color_scheme = color_scheme
        self.plain_text.set_color_scheme(color_scheme)

    def set_history(self, history):
        """
        Set list of strings on object combo box.

        Parameters
        ----------
        history: list
            List of strings of objects.
        """
        self.object_combo.addItems(history)

    def get_history(self):
        """
        Return list of strings on object combo box.
        """
        history = []
        for index in range(self.object_combo.count()):
            history.append(to_text_string(self.object_combo.itemText(index)))

        return history

    def set_internal_console(self, console):
        """
        Set the internal console shell.

        Parameters
        ----------
        console: :py:class:spyder.plugins.console.plugin.Console
            Console plugin.
        """
        self.internal_console = console
        if self.internal_console is not None:
            self.internal_shell = console.get_widget().shell
예제 #50
0
 def createEditor(self, parent, option, index):
     editor = QComboBox(parent)
     editor.addItems(self.choices.keys())
     return editor
예제 #51
0
    def __init__(self, layer):
        super().__init__(layer)

        # NOTE(arl): there are no events fired for changing checkboxes
        self.layer.events.tail_width.connect(self._on_tail_width_change)
        self.layer.events.tail_length.connect(self._on_tail_length_change)
        self.layer.events.properties.connect(self._on_properties_change)
        self.layer.events.colormap.connect(self._on_colormap_change)
        self.layer.events.color_by.connect(self._on_color_by_change)

        # combo box for track coloring, we can get these from the properties
        # keys
        self.color_by_combobox = QComboBox()
        self.color_by_combobox.addItems(self.layer.properties_to_color_by)

        self.colormap_combobox = QComboBox()
        self.colormap_combobox.addItems(list(AVAILABLE_COLORMAPS.keys()))

        # slider for track tail length
        self.tail_length_slider = QSlider(Qt.Horizontal)
        self.tail_length_slider.setFocusPolicy(Qt.NoFocus)
        self.tail_length_slider.setMinimum(1)
        self.tail_length_slider.setMaximum(MAX_TAIL_LENGTH)
        self.tail_length_slider.setSingleStep(1)

        # slider for track edge width
        self.tail_width_slider = QSlider(Qt.Horizontal)
        self.tail_width_slider.setFocusPolicy(Qt.NoFocus)
        self.tail_width_slider.setMinimum(1)
        self.tail_width_slider.setMaximum(MAX_TAIL_WIDTH)
        self.tail_width_slider.setSingleStep(1)

        # checkboxes for display
        self.id_checkbox = QCheckBox()
        self.tail_checkbox = QCheckBox()
        self.tail_checkbox.setChecked(True)
        self.graph_checkbox = QCheckBox()
        self.graph_checkbox.setChecked(True)

        self.tail_width_slider.valueChanged.connect(self.change_tail_width)
        self.tail_length_slider.valueChanged.connect(self.change_tail_length)
        self.tail_checkbox.stateChanged.connect(self.change_display_tail)
        self.id_checkbox.stateChanged.connect(self.change_display_id)
        self.graph_checkbox.stateChanged.connect(self.change_display_graph)
        self.color_by_combobox.currentTextChanged.connect(self.change_color_by)
        self.colormap_combobox.currentTextChanged.connect(self.change_colormap)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])

        self.grid_layout.addWidget(QLabel('color by:'), 0, 0)
        self.grid_layout.addWidget(self.color_by_combobox, 0, 1)
        self.grid_layout.addWidget(QLabel('colormap:'), 1, 0)
        self.grid_layout.addWidget(self.colormap_combobox, 1, 1)
        self.grid_layout.addWidget(QLabel('blending:'), 2, 0)
        self.grid_layout.addWidget(self.blendComboBox, 2, 1)
        self.grid_layout.addWidget(QLabel('opacity:'), 3, 0)
        self.grid_layout.addWidget(self.opacitySlider, 3, 1)
        self.grid_layout.addWidget(QLabel('tail width:'), 4, 0)
        self.grid_layout.addWidget(self.tail_width_slider, 4, 1)
        self.grid_layout.addWidget(QLabel('tail length:'), 5, 0)
        self.grid_layout.addWidget(self.tail_length_slider, 5, 1)
        self.grid_layout.addWidget(QLabel('tail:'), 6, 0)
        self.grid_layout.addWidget(self.tail_checkbox, 6, 1)
        self.grid_layout.addWidget(QLabel('show ID:'), 7, 0)
        self.grid_layout.addWidget(self.id_checkbox, 7, 1)
        self.grid_layout.addWidget(QLabel('graph:'), 8, 0)
        self.grid_layout.addWidget(self.graph_checkbox, 8, 1)
        self.grid_layout.setRowStretch(9, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)

        self._on_tail_length_change()
        self._on_tail_width_change()
        self._on_colormap_change()
        self._on_color_by_change()
예제 #52
0
class QtImageControls(QtBaseImageControls):
    """Qt view and controls for the napari Image layer.

    Parameters
    ----------
    layer : napari.layers.Image
        An instance of a napari Image layer.

    Attributes
    ----------
    attenuationSlider : qtpy.QtWidgets.QSlider
        Slider controlling attenuation rate for `attenuated_mip` mode.
    attenuationLabel : qtpy.QtWidgets.QLabel
        Label for the attenuation slider widget.
    grid_layout : qtpy.QtWidgets.QGridLayout
        Layout of Qt widget controls for the layer.
    interpComboBox : qtpy.QtWidgets.QComboBox
        Dropdown menu to select the interpolation mode for image display.
    interpLabel : qtpy.QtWidgets.QLabel
        Label for the interpolation dropdown menu.
    isoThresholdSlider : qtpy.QtWidgets.QSlider
        Slider controlling the isosurface threshold value for rendering.
    isoThresholdLabel : qtpy.QtWidgets.QLabel
        Label for the isosurface threshold slider widget.
    layer : napari.layers.Image
        An instance of a napari Image layer.
    renderComboBox : qtpy.QtWidgets.QComboBox
        Dropdown menu to select the rendering mode for image display.
    renderLabel : qtpy.QtWidgets.QLabel
        Label for the rendering mode dropdown menu.
    """
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.interpolation.connect(self._on_interpolation_change)
        self.layer.events.rendering.connect(self._on_rendering_change)
        self.layer.events.iso_threshold.connect(self._on_iso_threshold_change)
        self.layer.events.attenuation.connect(self._on_attenuation_change)
        self.layer.events._ndisplay.connect(self._on_ndisplay_change)
        self.layer.events.depiction.connect(self._on_depiction_change)
        self.layer.plane.events.thickness.connect(
            self._on_plane_thickness_change)

        self.interpComboBox = QComboBox(self)
        self.interpComboBox.activated[str].connect(self.changeInterpolation)
        self.interpLabel = QLabel(trans._('interpolation:'))

        renderComboBox = QComboBox(self)
        rendering_options = [i.value for i in ImageRendering]
        renderComboBox.addItems(rendering_options)
        index = renderComboBox.findText(self.layer.rendering,
                                        Qt.MatchFixedString)
        renderComboBox.setCurrentIndex(index)
        renderComboBox.activated[str].connect(self.changeRendering)
        self.renderComboBox = renderComboBox
        self.renderLabel = QLabel(trans._('rendering:'))

        self.depictionComboBox = QComboBox(self)
        depiction_options = [d.value for d in VolumeDepiction]
        self.depictionComboBox.addItems(depiction_options)
        index = self.depictionComboBox.findText(self.layer.depiction,
                                                Qt.MatchFixedString)
        self.depictionComboBox.setCurrentIndex(index)
        self.depictionComboBox.activated[str].connect(self.changeDepiction)
        self.depictionLabel = QLabel(trans._('depiction:'))

        self.planeControls = QtPlaneControls()
        self.planeControls.planeThicknessSlider.setValue(
            self.layer.plane.thickness)
        self.planeControls.planeThicknessSlider.valueChanged.connect(
            self.changePlaneThickness)
        action_manager.bind_button(
            'napari:orient_plane_normal_along_z',
            self.planeControls.planeNormalButtons.zButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_y',
            self.planeControls.planeNormalButtons.yButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_x',
            self.planeControls.planeNormalButtons.xButton,
        )
        action_manager.bind_button(
            'napari:orient_plane_normal_along_view_direction',
            self.planeControls.planeNormalButtons.obliqueButton,
        )

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.iso_threshold * 100))
        sld.valueChanged.connect(self.changeIsoThreshold)
        self.isoThresholdSlider = sld
        self.isoThresholdLabel = QLabel(trans._('iso threshold:'))

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.attenuation * 200))
        sld.valueChanged.connect(self.changeAttenuation)
        self.attenuationSlider = sld
        self.attenuationLabel = QLabel(trans._('attenuation:'))
        self._on_ndisplay_change()

        colormap_layout = QHBoxLayout()
        if hasattr(self.layer, 'rgb') and self.layer.rgb:
            colormap_layout.addWidget(QLabel("RGB"))
            self.colormapComboBox.setVisible(False)
            self.colorbarLabel.setVisible(False)
        else:
            colormap_layout.addWidget(self.colorbarLabel)
            colormap_layout.addWidget(self.colormapComboBox)
        colormap_layout.addStretch(1)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addWidget(QLabel(trans._('opacity:')), 0, 0)
        self.grid_layout.addWidget(self.opacitySlider, 0, 1)
        self.grid_layout.addWidget(QLabel(trans._('contrast limits:')), 1, 0)
        self.grid_layout.addWidget(self.contrastLimitsSlider, 1, 1)
        self.grid_layout.addWidget(QLabel(trans._('auto-contrast:')), 2, 0)
        self.grid_layout.addWidget(self.autoScaleBar, 2, 1)
        self.grid_layout.addWidget(QLabel(trans._('gamma:')), 3, 0)
        self.grid_layout.addWidget(self.gammaSlider, 3, 1)
        self.grid_layout.addWidget(QLabel(trans._('colormap:')), 4, 0)
        self.grid_layout.addLayout(colormap_layout, 4, 1)
        self.grid_layout.addWidget(QLabel(trans._('blending:')), 5, 0)
        self.grid_layout.addWidget(self.blendComboBox, 5, 1)
        self.grid_layout.addWidget(self.interpLabel, 6, 0)
        self.grid_layout.addWidget(self.interpComboBox, 6, 1)
        self.grid_layout.addWidget(self.renderLabel, 7, 0)
        self.grid_layout.addWidget(self.renderComboBox, 7, 1)
        self.grid_layout.addWidget(self.depictionLabel, 8, 0)
        self.grid_layout.addWidget(self.depictionComboBox, 8, 1)
        self.grid_layout.addWidget(self.planeControls, 9, 0, 2, 2)
        self.grid_layout.addWidget(self.isoThresholdLabel, 11, 0)
        self.grid_layout.addWidget(self.isoThresholdSlider, 11, 1)
        self.grid_layout.addWidget(self.attenuationLabel, 12, 0)
        self.grid_layout.addWidget(self.attenuationSlider, 12, 1)
        self.grid_layout.setRowStretch(13, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)

    def changeInterpolation(self, text):
        """Change interpolation mode for image display.

        Parameters
        ----------
        text : str
            Interpolation mode used by vispy. Must be one of our supported
            modes:
            'bessel', 'bicubic', 'bilinear', 'blackman', 'catrom', 'gaussian',
            'hamming', 'hanning', 'hermite', 'kaiser', 'lanczos', 'mitchell',
            'nearest', 'spline16', 'spline36'
        """
        self.layer.interpolation = text

    def changeRendering(self, text):
        """Change rendering mode for image display.

        Parameters
        ----------
        text : str
            Rendering mode used by vispy.
            Selects a preset rendering mode in vispy that determines how
            volume is displayed:
            * translucent: voxel colors are blended along the view ray until
              the result is opaque.
            * mip: maximum intensity projection. Cast a ray and display the
              maximum value that was encountered.
            * additive: voxel colors are added along the view ray until
              the result is saturated.
            * iso: isosurface. Cast a ray until a certain threshold is
              encountered. At that location, lighning calculations are
              performed to give the visual appearance of a surface.
            * attenuated_mip: attenuated maximum intensity projection. Cast a
              ray and attenuate values based on integral of encountered values,
              display the maximum value that was encountered after attenuation.
              This will make nearer objects appear more prominent.
        """
        self.layer.rendering = text
        self._toggle_rendering_parameter_visbility()

    def changeDepiction(self, text):
        self.layer.depiction = text
        self._toggle_plane_parameter_visibility()

    def changePlaneThickness(self, value: float):
        self.layer.plane.thickness = value

    def changeIsoThreshold(self, value):
        """Change isosurface threshold on the layer model.

        Parameters
        ----------
        value : float
            Threshold for isosurface.
        """
        with self.layer.events.blocker(self._on_iso_threshold_change):
            self.layer.iso_threshold = value / 100

    def _on_iso_threshold_change(self):
        """Receive layer model isosurface change event and update the slider."""
        with self.layer.events.iso_threshold.blocker():
            self.isoThresholdSlider.setValue(
                int(self.layer.iso_threshold * 100))

    def changeAttenuation(self, value):
        """Change attenuation rate for attenuated maximum intensity projection.

        Parameters
        ----------
        value : Float
            Attenuation rate for attenuated maximum intensity projection.
        """
        with self.layer.events.blocker(self._on_attenuation_change):
            self.layer.attenuation = value / 200

    def _on_attenuation_change(self):
        """Receive layer model attenuation change event and update the slider."""
        with self.layer.events.attenuation.blocker():
            self.attenuationSlider.setValue(int(self.layer.attenuation * 200))

    def _on_interpolation_change(self, event):
        """Receive layer interpolation change event and update dropdown menu.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        interp_string = event.value.value

        with self.layer.events.interpolation.blocker():
            if self.interpComboBox.findText(interp_string) == -1:
                self.interpComboBox.addItem(interp_string)
            self.interpComboBox.setCurrentText(interp_string)

    def _on_rendering_change(self):
        """Receive layer model rendering change event and update dropdown menu."""
        with self.layer.events.rendering.blocker():
            index = self.renderComboBox.findText(self.layer.rendering,
                                                 Qt.MatchFixedString)
            self.renderComboBox.setCurrentIndex(index)
            self._toggle_rendering_parameter_visbility()

    def _on_depiction_change(self):
        """Receive layer model depiction change event and update combobox."""
        with self.layer.events.depiction.blocker():
            index = self.depictionComboBox.findText(self.layer.depiction,
                                                    Qt.MatchFixedString)
            self.depictionComboBox.setCurrentIndex(index)
            self._toggle_plane_parameter_visibility()

    def _on_plane_thickness_change(self):
        with self.layer.plane.events.blocker():
            self.planeControls.planeThicknessSlider.setValue(
                self.layer.plane.thickness)

    def _toggle_rendering_parameter_visbility(self):
        """Hide isosurface rendering parameters if they aren't needed."""
        rendering = ImageRendering(self.layer.rendering)
        if rendering == ImageRendering.ISO:
            self.isoThresholdSlider.show()
            self.isoThresholdLabel.show()
        else:
            self.isoThresholdSlider.hide()
            self.isoThresholdLabel.hide()
        if rendering == ImageRendering.ATTENUATED_MIP:
            self.attenuationSlider.show()
            self.attenuationLabel.show()
        else:
            self.attenuationSlider.hide()
            self.attenuationLabel.hide()

    def _toggle_plane_parameter_visibility(self):
        """Hide plane rendering controls if they aren't needed."""
        depiction = VolumeDepiction(self.layer.depiction)
        if depiction == VolumeDepiction.VOLUME or self.layer._ndisplay == 2:
            self.planeControls.hide()
        if depiction == VolumeDepiction.PLANE and self.layer._ndisplay == 3:
            self.planeControls.show()

    def _update_interpolation_combo(self):
        self.interpComboBox.clear()
        interp_names = (Interpolation3D.keys() if self.layer._ndisplay == 3
                        else [i.value for i in Interpolation.view_subset()])
        self.interpComboBox.addItems(interp_names)
        index = self.interpComboBox.findText(self.layer.interpolation,
                                             Qt.MatchFixedString)
        self.interpComboBox.setCurrentIndex(index)

    def _on_ndisplay_change(self):
        """Toggle between 2D and 3D visualization modes."""
        self._update_interpolation_combo()
        self._toggle_plane_parameter_visibility()
        if self.layer._ndisplay == 2:
            self.isoThresholdSlider.hide()
            self.isoThresholdLabel.hide()
            self.attenuationSlider.hide()
            self.attenuationLabel.hide()
            self.renderComboBox.hide()
            self.renderLabel.hide()
            self.depictionComboBox.hide()
            self.depictionLabel.hide()
        else:
            self.renderComboBox.show()
            self.renderLabel.show()
            self._toggle_rendering_parameter_visbility()
            self.depictionComboBox.show()
            self.depictionLabel.show()
예제 #53
0
class ClassFunctionDropdown(Panel):
    """
    Class and Function/Method Dropdowns Widget.

    Parameters
    ----------
    editor : :class:`spyder.plugins.editor.widgets.codeeditor.CodeEditor`
        The editor to act on.
    """
    def __init__(self, editor):
        super(ClassFunctionDropdown, self).__init__(editor)

        # Internal data
        self._tree = IntervalTree()
        self._data = None
        self.classes = []
        self.funcs = []

        # Widgets
        self._editor = editor
        self.class_cb = QComboBox()
        self.method_cb = QComboBox()

        # Widget setup
        self.class_cb.addItem(_('<None>'), 0)
        self.method_cb.addItem(_('<None>'), 0)

        # The layout
        hbox = QHBoxLayout()
        hbox.addWidget(self.class_cb)
        hbox.addWidget(self.method_cb)
        hbox.setSpacing(0)
        hbox.setContentsMargins(0, 0, 0, 0)
        self.setLayout(hbox)

        # Signals
        self._editor.sig_cursor_position_changed.connect(
            self._handle_cursor_position_change_event)
        self.class_cb.activated.connect(self.combobox_activated)
        self.method_cb.activated.connect(self.combobox_activated)

    def _getVerticalSize(self):
        """Get the default height of a QComboBox."""
        return self.class_cb.height()

    @Slot(int, int)
    def _handle_cursor_position_change_event(self, linenum, column):
        self.update_selected(linenum)

    def sizeHint(self):
        """Override Qt method."""
        return QSize(0, self._getVerticalSize())

    def combobox_activated(self):
        """Move the cursor to the selected definition."""
        sender = self.sender()
        item = sender.itemData(sender.currentIndex())

        if item:
            line = item['location']['range']['start']['line'] + 1
            self.editor.go_to_line(line)

        if sender == self.class_cb:
            self.method_cb.setCurrentIndex(0)

    def update_selected(self, linenum):
        """Updates the dropdowns to reflect the current class and function."""
        possible_parents = list(sorted(self._tree[linenum]))
        for iv in possible_parents:
            item = iv.data
            kind = item.get('kind')

            if kind in [SymbolKind.CLASS]:
                # Update class combobox
                for idx in range(self.class_cb.count()):
                    if self.class_cb.itemData(idx) == item:
                        self.class_cb.setCurrentIndex(idx)
                        break
                else:
                    self.class_cb.setCurrentIndex(0)
            elif kind in [SymbolKind.FUNCTION, SymbolKind.METHOD]:
                # Update func combobox
                for idx in range(self.method_cb.count()):
                    if self.method_cb.itemData(idx) == item:
                        self.method_cb.setCurrentIndex(idx)
                        break
                else:
                    self.method_cb.setCurrentIndex(0)
            else:
                continue

        if len(possible_parents) == 0:
            self.class_cb.setCurrentIndex(0)
            self.method_cb.setCurrentIndex(0)

    def populate(self, combobox, data, add_parents=False):
        """
        Populate the given ``combobox`` with the class or function names.

        Parameters
        ----------
        combobox : :class:`qtpy.QtWidgets.QComboBox`
            The combobox to populate
        data : list of :class:`dict`
            The data to populate with. There should be one list element per
            class or function definition in the file.
        add_parents : bool
            Add parents to name to create a fully qualified name.

        Returns
        -------
        None
        """
        combobox.clear()
        combobox.addItem(_('<None>'), 0)
        model = combobox.model()
        item = model.item(0)
        item.setFlags(Qt.NoItemFlags)

        cb_data = []
        for item in data:
            fqn = item['name']

            # Create a list of fully-qualified names if requested
            if add_parents:
                begin = item['location']['range']['start']['line']
                end = item['location']['range']['end']['line']
                possible_parents = sorted(self._tree.overlap(begin, end),
                                          reverse=True)
                for iv in possible_parents:
                    if iv.begin == begin and iv.end == end:
                        continue

                    # Check if it is a real parent
                    p_item = iv.data
                    p_begin = p_item['location']['range']['start']['line']
                    p_end = p_item['location']['range']['end']['line']
                    if p_begin <= begin and p_end >= end:
                        fqn = p_item['name'] + "." + fqn

            cb_data.append((fqn, item))

        for fqn, item in cb_data:
            # Set the icon (See: editortools.py)
            icon = None
            name = item['name']
            if item['kind'] in [SymbolKind.CLASS]:
                icon = ima.icon('class')
            else:
                if name.startswith('__'):
                    icon = ima.icon('private2')
                elif name.startswith('_'):
                    icon = ima.icon('private1')
                else:
                    icon = ima.icon('method')

            # Add the combobox item
            if icon is not None:
                combobox.addItem(icon, fqn, item)
            else:
                combobox.addItem(fqn, item)

        line, column = self._editor.get_cursor_line_column()
        self.update_selected(line)

    def update_data(self, data):
        """Update and process symbol data."""
        if data == self._data:
            return

        self._data = data
        self._tree.clear()
        self.classes = []
        self.funcs = []

        for item in data:
            line_start = item['location']['range']['start']['line']
            line_end = item['location']['range']['end']['line']
            kind = item.get('kind')

            block = self._editor.document().findBlockByLineNumber(line_start)
            line_text = line_text = block.text() if block else ''

            # The symbol finder returns classes in import statements as well
            # so we filter them out
            if line_start != line_end and ' import ' not in line_text:
                self._tree[line_start:line_end] = item

                if kind in [SymbolKind.CLASS]:
                    self.classes.append(item)
                elif kind in [SymbolKind.FUNCTION, SymbolKind.METHOD]:
                    self.funcs.append(item)

        self.class_cb.clear()
        self.method_cb.clear()
        self.populate(self.class_cb, self.classes, add_parents=False)
        self.populate(self.method_cb, self.funcs, add_parents=True)
예제 #54
0
 def createEditor(self, parent, option, index):
     editor = QComboBox(parent)
     editor.addItems(BasePlotCurveItem.symbols.keys())
     return editor
예제 #55
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.interpolation.connect(self._on_interpolation_change)
        self.layer.events.rendering.connect(self._on_rendering_change)
        self.layer.events.iso_threshold.connect(self._on_iso_threshold_change)
        self.layer.events.attenuation.connect(self._on_attenuation_change)
        self.layer.events._ndisplay.connect(self._on_ndisplay_change)

        self.interpComboBox = QComboBox(self)
        self.interpComboBox.activated[str].connect(self.changeInterpolation)
        self.interpLabel = QLabel(trans._('interpolation:'))

        renderComboBox = QComboBox(self)
        renderComboBox.addItems(Rendering.keys())
        index = renderComboBox.findText(self.layer.rendering,
                                        Qt.MatchFixedString)
        renderComboBox.setCurrentIndex(index)
        renderComboBox.activated[str].connect(self.changeRendering)
        self.renderComboBox = renderComboBox
        self.renderLabel = QLabel(trans._('rendering:'))

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.iso_threshold * 100))
        sld.valueChanged.connect(self.changeIsoThreshold)
        self.isoThresholdSlider = sld
        self.isoThresholdLabel = QLabel(trans._('iso threshold:'))

        sld = QSlider(Qt.Horizontal, parent=self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setMinimum(0)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        sld.setValue(int(self.layer.attenuation * 200))
        sld.valueChanged.connect(self.changeAttenuation)
        self.attenuationSlider = sld
        self.attenuationLabel = QLabel(trans._('attenuation:'))
        self._on_ndisplay_change()

        colormap_layout = QHBoxLayout()
        if hasattr(self.layer, 'rgb') and self.layer.rgb:
            colormap_layout.addWidget(QLabel("RGB"))
            self.colormapComboBox.setVisible(False)
            self.colorbarLabel.setVisible(False)
        else:
            colormap_layout.addWidget(self.colorbarLabel)
            colormap_layout.addWidget(self.colormapComboBox)
        colormap_layout.addStretch(1)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addWidget(QLabel(trans._('opacity:')), 0, 0)
        self.grid_layout.addWidget(self.opacitySlider, 0, 1)
        self.grid_layout.addWidget(QLabel(trans._('contrast limits:')), 1, 0)
        self.grid_layout.addWidget(self.contrastLimitsSlider, 1, 1)
        self.grid_layout.addWidget(QLabel(trans._('gamma:')), 2, 0)
        self.grid_layout.addWidget(self.gammaSlider, 2, 1)
        self.grid_layout.addWidget(QLabel(trans._('colormap:')), 3, 0)
        self.grid_layout.addLayout(colormap_layout, 3, 1)
        self.grid_layout.addWidget(QLabel(trans._('blending:')), 4, 0)
        self.grid_layout.addWidget(self.blendComboBox, 4, 1)
        self.grid_layout.addWidget(self.interpLabel, 5, 0)
        self.grid_layout.addWidget(self.interpComboBox, 5, 1)
        self.grid_layout.addWidget(self.renderLabel, 6, 0)
        self.grid_layout.addWidget(self.renderComboBox, 6, 1)
        self.grid_layout.addWidget(self.isoThresholdLabel, 7, 0)
        self.grid_layout.addWidget(self.isoThresholdSlider, 7, 1)
        self.grid_layout.addWidget(self.attenuationLabel, 8, 0)
        self.grid_layout.addWidget(self.attenuationSlider, 8, 1)
        self.grid_layout.setRowStretch(9, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)
예제 #56
0
    def __init__(self, parent=None):
        super(ColorbarWidget, self).__init__(parent)

        self.setWindowTitle("Colorbar")
        self.setMaximumWidth(200)

        self.cmap = QComboBox()
        self.cmap.addItems(sorted(cm.cmap_d.keys()))
        self.cmap.currentIndexChanged.connect(self.cmap_index_changed)

        self.cmin = QLineEdit()
        self.cmin_value = 0
        self.cmin.setMaximumWidth(100)
        self.cmin.editingFinished.connect(self.clim_changed)
        self.cmin_layout = QHBoxLayout()
        self.cmin_layout.addStretch()
        self.cmin_layout.addWidget(self.cmin)
        self.cmin_layout.addStretch()

        self.cmax = QLineEdit()
        self.cmax_value = 1
        self.cmax.setMaximumWidth(100)
        self.cmax.editingFinished.connect(self.clim_changed)
        self.cmin.setValidator(QDoubleValidator())
        self.cmax.setValidator(QDoubleValidator())
        self.cmax_layout = QHBoxLayout()
        self.cmax_layout.addStretch()
        self.cmax_layout.addWidget(self.cmax)
        self.cmax_layout.addStretch()

        self.norm_layout = QHBoxLayout()
        self.norm = QComboBox()
        self.norm.addItems(NORM_OPTS)
        self.norm.currentIndexChanged.connect(self.norm_changed)

        self.powerscale = QLineEdit()
        self.powerscale_value = 2
        self.powerscale.setText("2")
        self.powerscale.setValidator(QDoubleValidator(0.001, 100, 3))
        self.powerscale.setMaximumWidth(50)
        self.powerscale.editingFinished.connect(self.norm_changed)
        self.powerscale.hide()
        self.powerscale_label = QLabel("n=")
        self.powerscale_label.hide()

        self.norm_layout.addStretch()
        self.norm_layout.addWidget(self.norm)
        self.norm_layout.addStretch()
        self.norm_layout.addWidget(self.powerscale_label)
        self.norm_layout.addWidget(self.powerscale)

        self.autoscale = QCheckBox("Autoscaling")
        self.autoscale.setChecked(True)
        self.autoscale.stateChanged.connect(self.update_clim)

        self.canvas = FigureCanvas(Figure())
        if parent:
            # Set facecolor to match parent
            self.canvas.figure.set_facecolor(
                parent.palette().window().color().getRgbF())
        self.ax = self.canvas.figure.add_axes([0.4, 0.05, 0.2, 0.9])

        # layout
        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.cmap)
        self.layout.addLayout(self.cmax_layout)
        self.layout.addWidget(self.canvas, stretch=1)
        self.layout.addLayout(self.cmin_layout)
        self.layout.addLayout(self.norm_layout)
        self.layout.addWidget(self.autoscale)
예제 #57
0
class QtPerformance(QWidget):
    """Dockable widget to show performance info.

    Notes
    -----

    1) The progress bar doesn't show "progress", we use it as a bar graph to
       show the average duration of recent "UpdateRequest" events. This
       is actually not the total draw time, but it's generally the biggest
       part of each frame.

    2) We log any event whose duration is longer than the threshold.

    3) We show uptime so you can tell if this window is being updated at all.

    Attributes
    ----------
    start_time : float
        Time is seconds when widget was created.
    bar : QProgressBar
        The progress bar we use as your draw time indicator.
    thresh_ms : float
        Log events whose duration is longer then this.
    timer_label : QLabel
        We write the current "uptime" into this label.
    timer : QTimer
        To update our window every UPDATE_MS.
    """

    # We log events slower than some threshold (in milliseconds).
    THRESH_DEFAULT = 100
    THRESH_OPTIONS = [
        "1",
        "5",
        "10",
        "15",
        "20",
        "30",
        "40",
        "50",
        "100",
        "200",
    ]

    # Update at 250ms / 4Hz for now. The more we update more alive our
    # display will look, but the more we will slow things down.
    UPDATE_MS = 250

    def __init__(self):
        """Create our windgets."""
        super().__init__()
        layout = QVBoxLayout()

        # For our "uptime" timer.
        self.start_time = time.time()

        # Label for our progress bar.
        bar_label = QLabel(trans._("Draw Time:"))
        layout.addWidget(bar_label)

        # Progress bar is not used for "progress", it's just a bar graph to show
        # the "draw time", the duration of the "UpdateRequest" event.
        bar = QProgressBar()
        bar.setRange(0, 100)
        bar.setValue(50)
        bar.setFormat("%vms")
        layout.addWidget(bar)
        self.bar = bar

        # We let the user set the "slow event" threshold.
        self.thresh_ms = self.THRESH_DEFAULT
        self.thresh_combo = QComboBox()
        self.thresh_combo.addItems(self.THRESH_OPTIONS)
        self.thresh_combo.activated[str].connect(self._change_thresh)
        self.thresh_combo.setCurrentText(str(self.thresh_ms))

        combo_layout = QHBoxLayout()
        combo_layout.addWidget(QLabel(trans._("Show Events Slower Than:")))
        combo_layout.addWidget(self.thresh_combo)
        combo_layout.addWidget(QLabel(trans._("milliseconds")))
        combo_layout.addItem(
            QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        )
        layout.addLayout(combo_layout)

        # We log slow events to this window.
        self.log = TextLog()
        layout.addWidget(self.log)

        # Uptime label. To indicate if the widget is getting updated.
        label = QLabel('')
        layout.addWidget(label)
        self.timer_label = label

        self.setLayout(layout)

        # Update us with a timer.
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update)
        self.timer.setInterval(self.UPDATE_MS)
        self.timer.start()

    def _change_thresh(self, text):
        """Threshold combo box change."""
        self.thresh_ms = float(text)
        self.log.clear()  # start fresh with this new threshold

    def _get_timer_info(self):
        """Get the information from the timers that we want to display."""
        average = None
        long_events = []

        # We don't update any GUI/widgets while iterating over the timers.
        # Updating widgets can create immediate Qt Events which would modify the
        # timers out from under us!
        for name, timer in perf.timers.timers.items():

            # The Qt Event "UpdateRequest" is the main "draw" event, so
            # that's what we use for our progress bar.
            if name == "UpdateRequest":
                average = timer.average

            # Log any "long" events to the text window.
            if timer.max >= self.thresh_ms:
                long_events.append((name, timer.max))

        return average, long_events

    def update(self):
        """Update our label and progress bar and log any new slow events."""
        # Update our timer label.
        elapsed = time.time() - self.start_time
        self.timer_label.setText(
            trans._("Uptime: {elapsed:.2f}", elapsed=elapsed)
        )

        average, long_events = self._get_timer_info()

        # Now safe to update the GUI: progress bar first.
        if average is not None:
            self.bar.setValue(int(average))

        # And log any new slow events.
        for name, time_ms in long_events:
            self.log.append(name, time_ms)

        # Clear all the timers since we've displayed them. They will immediately
        # start accumulating numbers for the next update.
        perf.timers.clear()
예제 #58
0
    def __init__(self, parent, language=None, cmd='', host='127.0.0.1',
                 port=2084, args='', external=False, configurations={},
                 **kwargs):
        super(LSPServerEditor, self).__init__(parent)
        self.parent = parent
        self.external = external
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_ok = bbox.button(QDialogButtonBox.Ok)
        self.button_cancel = bbox.button(QDialogButtonBox.Cancel)
        self.button_ok.setEnabled(False)

        description = _('To create a new configuration, '
                        'you need to select a programming '
                        'language, along with a executable '
                        'name for the server to execute '
                        '(If the instance is local), '
                        'and the host and port. Finally, '
                        'you need to provide the '
                        'arguments that the server accepts. '
                        'The placeholders <tt>{host}</tt> and '
                        '<tt>{port}</tt> refer to the host '
                        'and the port, respectively.')
        server_settings_description = QLabel(description)
        server_settings_description.setWordWrap(True)

        lang_label = QLabel(_('Language:'))
        self.lang_cb = QComboBox(self)
        self.lang_cb.setToolTip(_('Programming language provided '
                                  'by the LSP server'))
        self.lang_cb.addItem(_('Select a language'))
        self.lang_cb.addItems(LSP_LANGUAGES)

        if language is not None:
            idx = LSP_LANGUAGES.index(language)
            self.lang_cb.setCurrentIndex(idx + 1)
            self.button_ok.setEnabled(True)

        host_label = QLabel(_('Host:'))
        self.host_input = QLineEdit(self)
        self.host_input.setToolTip(_('Name of the host that will provide '
                                     'access to the server'))
        self.host_input.setText(host)
        self.host_input.textChanged.connect(lambda x: self.validate())

        port_label = QLabel(_('Port:'))
        self.port_spinner = QSpinBox(self)
        self.port_spinner.setToolTip(_('TCP port number of the server'))
        self.port_spinner.setMinimum(1)
        self.port_spinner.setMaximum(60000)
        self.port_spinner.setValue(port)

        cmd_label = QLabel(_('Command to execute:'))
        self.cmd_input = QLineEdit(self)
        self.cmd_input.setToolTip(_('Command used to start the '
                                    'LSP server locally'))
        self.cmd_input.setText(cmd)

        if not external:
            self.cmd_input.textChanged.connect(lambda x: self.validate())

        args_label = QLabel(_('Server arguments:'))
        self.args_input = QLineEdit(self)
        self.args_input.setToolTip(_('Additional arguments required to '
                                     'start the server'))
        self.args_input.setText(args)

        conf_label = QLabel(_('LSP Server Configurations:'))
        self.conf_input = CodeEditor(None)
        self.conf_input.textChanged.connect(self.validate)
        color_scheme = CONF.get('appearance', 'selected')
        self.conf_input.setup_editor(
            language='JSON',
            color_scheme=color_scheme,
            wrap=False,
            edge_line=True,
            highlight_current_line=True,
            highlight_current_cell=True,
            occurrence_highlighting=True,
            auto_unindent=True,
            font=get_font(),
            filename='config.json')
        self.conf_input.setToolTip(_('Additional LSP server configurations '
                                     'set at runtime. JSON required'))
        conf_text = '{}'
        try:
            conf_text = json.dumps(configurations, indent=4, sort_keys=True)
        except Exception:
            pass
        self.conf_input.set_text(conf_text)
        self.json_label = QLabel(self.JSON_VALID, self)

        self.external_cb = QCheckBox(_('External server'), self)
        self.external_cb.setToolTip(_('Check if the server runs '
                                      'on a remote location'))
        self.external_cb.setChecked(external)
        self.external_cb.stateChanged.connect(self.set_local_options)

        hlayout = QHBoxLayout()
        general_vlayout = QVBoxLayout()
        general_vlayout.addWidget(server_settings_description)

        vlayout = QVBoxLayout()
        lang_layout = QVBoxLayout()
        lang_layout.addWidget(lang_label)
        lang_layout.addWidget(self.lang_cb)

        # layout2 = QHBoxLayout()
        # layout2.addLayout(lang_layout)
        lang_layout.addWidget(self.external_cb)
        vlayout.addLayout(lang_layout)

        host_layout = QVBoxLayout()
        host_layout.addWidget(host_label)
        host_layout.addWidget(self.host_input)

        port_layout = QVBoxLayout()
        port_layout.addWidget(port_label)
        port_layout.addWidget(self.port_spinner)

        conn_info_layout = QHBoxLayout()
        conn_info_layout.addLayout(host_layout)
        conn_info_layout.addLayout(port_layout)
        vlayout.addLayout(conn_info_layout)

        cmd_layout = QVBoxLayout()
        cmd_layout.addWidget(cmd_label)
        cmd_layout.addWidget(self.cmd_input)
        vlayout.addLayout(cmd_layout)

        args_layout = QVBoxLayout()
        args_layout.addWidget(args_label)
        args_layout.addWidget(self.args_input)
        vlayout.addLayout(args_layout)

        conf_layout = QVBoxLayout()
        conf_layout.addWidget(conf_label)
        conf_layout.addWidget(self.conf_input)
        conf_layout.addWidget(self.json_label)

        hlayout.addLayout(vlayout)
        hlayout.addLayout(conf_layout)
        general_vlayout.addLayout(hlayout)

        general_vlayout.addWidget(bbox)
        self.setLayout(general_vlayout)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        self.lang_cb.currentIndexChanged.connect(
            self.lang_selection_changed)
        self.form_status(False)
        if language is not None:
            self.form_status(True)
            self.validate()
예제 #59
0
    def __init__(self, parent, debug=False, data=None, clear_data=True, name='main',
                 setup_dict=None):
        """
        Creates the buttons in the Sidebar, not the actual layout

        Parameters
        ----------
        parent : MainWindow()
            the gui
        debug : bool; default=False
            flag for debug info
        data : List[tree]
            the tree
        clear_data : bool; default=True
            ???
        name : str; default='main'
            the active name
        setup_dict : Dict[irow] = List[QWidgets]
            a way to add additional widgets to the sidebar
        """
        QWidget.__init__(self)
        self.parent = parent
        self.debug = debug
        self.setup_dict = setup_dict

        choices = ['keys2', 'purse2', 'cellphone2', 'credit_card2', 'money2']
        if data is None:
            data = []

        self.result_case_windows = [ResultsWindow(self, 'Case/Results', data, choices)]
        data = [
            ('A', 1, []),
            #('B', 2, []),
            #('C', 3, []),
        ]
        self.result_method_window = ResultsWindow(self, 'Method', data, choices)
        self.result_method_window.setVisible(False)
        #else:
            #self.result_method_window = None

        self.show_pulldown = False
        if self.show_pulldown:
            combo_options = ['a1', 'a2', 'a3']
            self.pulldown = QComboBox()
            self.pulldown.addItems(choices)
            self.pulldown.activated[str].connect(self.on_pulldown)

        self.apply_button = QPushButton('Apply', self)
        self.apply_button.clicked.connect(self.on_apply)

        if name is None:
            self.name = None
            self.names = ['N/A']
            name = 'N/A'
        else:
            self.name = str(name)
            self.names = [name]

        self.name_label = QLabel("Name:")
        self.name_pulldown = QComboBox()
        self.name_pulldown.addItem(name)
        self.name_pulldown.setDisabled(True)

        self.setup_layout(data, choices, clear_data=clear_data)
        self.name_pulldown.currentIndexChanged.connect(self.on_update_name)
예제 #60
0
    def __init__(self, layer):
        super().__init__(layer)

        self.layer.events.mode.connect(self._on_mode_change)
        self.layer.events.n_dimensional.connect(self._on_n_dimensional_change)
        self.layer.events.symbol.connect(self._on_symbol_change)
        self.layer.events.size.connect(self._on_size_change)
        self.layer.events.current_edge_color.connect(
            self._on_current_edge_color_change)
        self.layer._edge.events.current_color.connect(
            self._on_current_edge_color_change)
        self.layer.events.current_face_color.connect(
            self._on_current_face_color_change)
        self.layer._face.events.current_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(1)
        sld.setMaximum(100)
        sld.setSingleStep(1)
        value = self.layer.current_size
        sld.setValue(int(value))
        sld.valueChanged.connect(self.changeSize)
        self.sizeSlider = sld

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

        symbol_comboBox = QComboBox()
        current_index = 0
        for index, (data, text) in enumerate(SYMBOL_TRANSLATION.items()):
            data = data.value
            symbol_comboBox.addItem(text, data)

            if data == self.layer.symbol:
                current_index = index

        symbol_comboBox.setCurrentIndex(current_index)
        symbol_comboBox.activated[str].connect(self.changeSymbol)
        self.symbolComboBox = symbol_comboBox

        ndim_cb = QCheckBox()
        ndim_cb.setToolTip(trans._('N-dimensional points'))
        ndim_cb.setChecked(self.layer.n_dimensional)
        ndim_cb.stateChanged.connect(self.change_ndim)
        self.ndimCheckBox = ndim_cb

        self.select_button = QtModeRadioButton(
            layer,
            'select_points',
            Mode.SELECT,
            tooltip=trans._('Select points (S)'),
        )
        self.addition_button = QtModeRadioButton(
            layer, 'add_points', Mode.ADD, tooltip=trans._('Add points (P)'))
        self.panzoom_button = QtModeRadioButton(
            layer,
            'pan_zoom',
            Mode.PAN_ZOOM,
            tooltip=trans._('Pan/zoom (Z)'),
            checked=True,
        )
        self.delete_button = QtModePushButton(
            layer,
            'delete_shape',
            slot=self.layer.remove_selected,
            tooltip=trans._("Delete selected points ({key})").format(
                key=KEY_SYMBOLS['Backspace']),
        )

        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

        self.button_group = QButtonGroup(self)
        self.button_group.addButton(self.select_button)
        self.button_group.addButton(self.addition_button)
        self.button_group.addButton(self.panzoom_button)

        button_row = QHBoxLayout()
        button_row.addStretch(1)
        button_row.addWidget(self.delete_button)
        button_row.addWidget(self.addition_button)
        button_row.addWidget(self.select_button)
        button_row.addWidget(self.panzoom_button)
        button_row.setContentsMargins(0, 0, 0, 5)
        button_row.setSpacing(4)

        # grid_layout created in QtLayerControls
        # addWidget(widget, row, column, [row_span, column_span])
        self.grid_layout.addLayout(button_row, 0, 1)
        self.grid_layout.addWidget(QLabel(trans._('opacity:')), 1, 0)
        self.grid_layout.addWidget(self.opacitySlider, 1, 1)
        self.grid_layout.addWidget(QLabel(trans._('point size:')), 2, 0)
        self.grid_layout.addWidget(self.sizeSlider, 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._('symbol:')), 4, 0)
        self.grid_layout.addWidget(self.symbolComboBox, 4, 1)
        self.grid_layout.addWidget(QLabel(trans._('face color:')), 5, 0)
        self.grid_layout.addWidget(self.faceColorEdit, 5, 1)
        self.grid_layout.addWidget(QLabel(trans._('edge color:')), 6, 0)
        self.grid_layout.addWidget(self.edgeColorEdit, 6, 1)
        self.grid_layout.addWidget(QLabel(trans._('display text:')), 7, 0)
        self.grid_layout.addWidget(self.textDispCheckBox, 7, 1)
        self.grid_layout.addWidget(QLabel(trans._('n-dim:')), 8, 0)
        self.grid_layout.addWidget(self.ndimCheckBox, 8, 1)
        self.grid_layout.setRowStretch(9, 1)
        self.grid_layout.setColumnStretch(1, 1)
        self.grid_layout.setSpacing(4)