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)
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
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]
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')
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()
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()
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()
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
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)
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()
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)
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
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
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))
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
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
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)
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 _init_ui(self): # Widgets self._combobox = QComboBox() self._stack = QStackedWidget() # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._combobox) layout.addRow(self._stack) # Register classes self._widget_indexes = {} for entry_point in iter_entry_points('pyhmsa_gui.spec.condition.calibration'): widget_class = entry_point.load(require=False) widget = widget_class() self._combobox.addItem(widget.accessibleName().title()) self._widget_indexes[widget.CLASS] = self._stack.addWidget(widget) widget.edited.connect(self.edited) # Signals self._combobox.currentIndexChanged.connect(self._on_combo_box) self._combobox.currentIndexChanged.connect(self.edited) return layout
def _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
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)
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"))
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()
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"))
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)
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
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)
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 _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
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 __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)
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
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)
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
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)
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)
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
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)
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)
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
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()
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()
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
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
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()
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)
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
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)
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
def createEditor(self, parent, option, index): editor = QComboBox(parent) editor.addItems(self.choices.keys()) return editor
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()
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()
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)
def createEditor(self, parent, option, index): editor = QComboBox(parent) editor.addItems(BasePlotCurveItem.symbols.keys()) return editor
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)
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)
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()
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()
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)
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)