def create_file_combobox(self, text, choices, option, default=NoDefault, tip=None, restart=False, filters=None, adjust_to_contents=False, default_line_edit=False): """choices: couples (name, key)""" combobox = FileComboBox(self, adjust_to_contents=adjust_to_contents, default_line_edit=default_line_edit) combobox.restart_required = restart combobox.label_text = text edit = combobox.lineEdit() edit.label_text = text edit.restart_required = restart self.lineedits[edit] = (option, default) if tip is not None: combobox.setToolTip(tip) combobox.addItems(choices) msg = _('Invalid file path') self.validate_data[edit] = (osp.isfile, msg) browse_btn = QPushButton(ima.icon('FileIcon'), '', self) browse_btn.setToolTip(_("Select file")) browse_btn.clicked.connect(lambda: self.select_file(edit, filters)) layout = QGridLayout() layout.addWidget(combobox, 0, 0, 0, 9) layout.addWidget(browse_btn, 0, 10) layout.setContentsMargins(0, 0, 0, 0) widget = QWidget(self) widget.combobox = combobox widget.browse_btn = browse_btn widget.setLayout(layout) return widget
def setup_gui(self): """Setup the main layout of the widget.""" layout = QGridLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.canvas, 0, 1) layout.addLayout(self.setup_toolbar(), 0, 3, 2, 1) layout.setColumnStretch(0, 100) layout.setColumnStretch(2, 100) layout.setRowStretch(1, 100)
def __init__(self, parent): SpyderPluginWidget.__init__(self, parent) # Widgets self.stack = QStackedWidget(self) self.shellwidgets = {} # Layout layout = QGridLayout(self) layout.addWidget(self.stack) # Initialize plugin self.initialize_plugin()
def __init__(self, parent=None): QWidget.__init__(self, parent) # Widgets, layouts and signals self._group = QButtonGroup() layout = QGridLayout() layout.setSpacing(0) ## Element for z, position in _ELEMENT_POSITIONS.items(): widget = ElementPushButton(z) widget.setCheckable(True) layout.addWidget(widget, *position) self._group.addButton(widget, z) ## Labels layout.addWidget(QLabel(''), 7, 0) # Dummy layout.addWidget(QLabel('*'), 5, 2, Qt.AlignRight) layout.addWidget(QLabel('*'), 8, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 6, 2, Qt.AlignRight) layout.addWidget(QLabel('**'), 9, 2, Qt.AlignRight) for row in [0, 1, 2, 3, 4, 5, 6, 8, 9]: layout.setRowStretch(row, 1) self.setLayout(layout) # Signals self._group.buttonClicked.connect(self.selectionChanged) # Default self.setColorFunction(_category_color_function)
def setupUI(self, widget): self.ui.setupUi(widget) root_widgets = [w for w in widget.children() if w.__class__.__name__ == "QWidget"] if len(root_widgets) == 0 or widget.layout() is not None: self.ui_widget = self else: self.ui_widget = root_widgets[-1] g = QGridLayout() if qtpy.API=="pyqt5": pass else: if isinstance(self, QtWidgetLoader): g.setMargin(0) g.setSpacing(0) widget.setLayout(g) g.addWidget(self.ui_widget)
def __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, viewer_state=None, session=None): super(BaseCustomOptionsWidget, self).__init__() layout = QGridLayout() for row, (name, (prefix, viewer_cls)) in enumerate(self._widgets.items()): widget = viewer_cls() setattr(self, prefix + name, widget) layout.addWidget(QLabel(name.capitalize()), row, 0) layout.addWidget(widget, row, 1) if len(self._widgets) > 0: layout.setRowStretch(row + 1, 10) self.setLayout(layout) self.viewer_state = viewer_state self.session = session autoconnect_callbacks_to_qt(self.viewer_state, self)
class ImageWindow(QMainWindow): def __init__(self, arr, mgr): QMainWindow.__init__(self) self.setWindowTitle('skimage') self.mgr = mgr self.main_widget = QWidget() self.layout = QGridLayout(self.main_widget) self.setCentralWidget(self.main_widget) self.label = ImageLabel(self, arr) self.layout.addWidget(self.label, 0, 0) self.layout.addLayout self.mgr.add_window(self) self.main_widget.show() def closeEvent(self, event): # Allow window to be destroyed by removing any # references to it self.mgr.remove_window(self)
def _ask_overwrite(self): msg = QDialog() msg.setWindowTitle('Load overwrite') layout = QGridLayout() layout.addWidget(QLabel('Instrument %s already exists in memory. Overwrite this?'), 0, 0, 1, -1) buttons = [QPushButton(label) for label in ['Load and overwrite', 'Cancel Load', 'Load and rename to']] locations = [[1, 0], [1, 1], [2, 0]] self.overwrite_flag = 1 def overwriteCB(idx): self.overwrite_flag = idx msg.accept() for idx, button in enumerate(buttons): button.clicked.connect(lambda _, idx=idx: overwriteCB(idx)) layout.addWidget(button, locations[idx][0], locations[idx][1]) newname = QLineEdit() newname.editingFinished.connect(lambda: overwriteCB(2)) layout.addWidget(newname, 2, 1) msg.setLayout(layout) msg.exec_() newname = str(newname.text()) if not newname or newname in self.instruments: self.errormessage('Invalid instrument name. Cancelling load.') self.overwrite_flag = 1 return self.overwrite_flag, newname
class IntelligentSlider(QWidget): ''' A slider that adds a 'name' attribute and calls a callback with 'name' as an argument to the registerd callback. This allows you to create large groups of sliders in a loop, but still keep track of the individual events It also prints a label below the slider. The range of the slider is hardcoded from zero - 1000, but it supports a conversion factor so you can scale the results''' def __init__(self, name, a, b, callback): QWidget.__init__(self) self.name = name self.callback = callback self.a = a self.b = b self.manually_triggered = False self.slider = QSlider() self.slider.setRange(0, 1000) self.slider.setValue(500) self.slider.valueChanged.connect(self.slider_changed) self.name_label = QLabel() self.name_label.setText(self.name) self.name_label.setAlignment(QtCore.Qt.AlignCenter) self.value_label = QLabel() self.value_label.setText('%2.2f' % (self.slider.value() * self.a + self.b)) self.value_label.setAlignment(QtCore.Qt.AlignCenter) self.layout = QGridLayout(self) self.layout.addWidget(self.name_label, 0, 0) self.layout.addWidget(self.slider, 1, 0, QtCore.Qt.AlignHCenter) self.layout.addWidget(self.value_label, 2, 0) # bind this to the valueChanged signal of the slider def slider_changed(self, val): val = self.val() self.value_label.setText(str(val)[:4]) if not self.manually_triggered: self.callback(self.name, val) def set_conv_fac(self, a, b): self.a = a self.b = b def set_value(self, val): self.manually_triggered = True self.slider.setValue(int((val - self.b) / self.a)) self.value_label.setText('%2.2f' % val) self.manually_triggered = False def val(self): return self.slider.value() * self.a + self.b
class QuadHistogram(QFrame): '''A class which uses ColorHistogram to draw the 4 histograms of an image. R, G, B, and Value. The 4 histograms are layout out in a grid, and can be specified horizontal or vertical, and in which order ie. ['R', 'G', 'B', 'V'] ''' def __init__(self, img, layout='vertical', order=['R', 'G', 'B', 'V']): QFrame.__init__(self) r, g, b, v = histograms(img, 100) self.r_hist = ColorHistogram(r, (255, 0, 0)) self.g_hist = ColorHistogram(g, (0, 255, 0)) self.b_hist = ColorHistogram(b, (0, 0, 255)) self.v_hist = ColorHistogram(v, (0, 0, 0)) self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist, 'V': self.v_hist} if layout == 'vertical': for i in range(len(order)): self.layout.addWidget(order_map[order[i]], i, 0) elif layout == 'horizontal': for i in range(len(order)): self.layout.addWidget(order_map[order[i]], 0, i) def update_hists(self, img): r, g, b, v = histograms(img, 100) self.r_hist.update_hist(r, (255, 0, 0)) self.g_hist.update_hist(g, (0, 255, 0)) self.b_hist.update_hist(b, (0, 0, 255)) self.v_hist.update_hist(v, (0, 0, 0))
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 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 _build_GUI(self, linelist, table_view): panel_layout = QGridLayout() panel_layout.setSizeConstraint(QLayout.SetMaximumSize) self.setLayout(panel_layout) # GUI cannot be completely defined in a .ui file. # It has to be built on-the-fly here. self.button_pane = QWidget() loadUi(os.path.join(os.path.dirname(__file__), "ui", "linelists_panel_buttons.ui"), self.button_pane) # internal signals do not use Hub infrastructure. self.button_pane.create_set_button.clicked.connect(self._create_set) self.button_pane.deselect_button.clicked.connect(table_view.clearSelection) # header with line list metadata. info = QTextBrowser() info.setMaximumHeight(100) info.setAutoFillBackground(True) info.setStyleSheet("background-color: rgb(230,230,230);") for comment in linelist.meta['comments']: info.append(comment) # populate color picker model = self.button_pane.combo_box_color.model() for cname in ID_COLORS: item = QStandardItem(cname) item.setForeground(ID_COLORS[cname]) item.setData(QColor(ID_COLORS[cname]), role=Qt.UserRole) model.appendRow(item) # set validators validator = QDoubleValidator() validator.setRange(0.05, 0.95, decimals=2) self.button_pane.height_textbox.setValidator(validator) validator = QDoubleValidator() validator.setRange(-1.e5, 1.e10, decimals=4) self.button_pane.redshift_textbox.setValidator(validator) model = self.button_pane.combo_box_z_units.model() for uname in ['z', 'km/s']: item = QStandardItem(uname) model.appendRow(item) # put it all together panel_layout.addWidget(info,0,0) panel_layout.addWidget(table_view,1,0) panel_layout.addWidget(self.button_pane,2,0)
class CondaPackageActionDialog(QDialog): """ """ def __init__(self, parent, env, name, action, version, versions): super(CondaPackageActionDialog, self).__init__(parent) self._parent = parent self._env = env self._version_text = None self._name = name self._dependencies_dic = {} self._conda_process = \ conda_api_q.CondaProcess(self, self._on_process_finished) # widgets self.label = QLabel(self) self.combobox_version = QComboBox() self.label_version = QLabel(self) self.widget_version = None self.table_dependencies = None self.checkbox = QCheckBox(_('Install dependencies (recommended)')) self.bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.button_ok = self.bbox.button(QDialogButtonBox.Ok) self.button_cancel = self.bbox.button(QDialogButtonBox.Cancel) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) dialog_size = QSize(300, 90) # helper variable values action_title = {const.UPGRADE: _("Upgrade package"), const.DOWNGRADE: _("Downgrade package"), const.REMOVE: _("Remove package"), const.INSTALL: _("Install package")} # Versions might have duplicates from different builds versions = sort_versions(list(set(versions)), reverse=True) # FIXME: There is a bug, a package installed by anaconda has version # astropy 0.4 and the linked list 0.4 but the available versions # in the json file do not include 0.4 but 0.4rc1... so... # temporal fix is to check if inside list otherwise show full list if action == const.UPGRADE: if version in versions: index = versions.index(version) versions = versions[:index] else: versions = versions elif action == const.DOWNGRADE: if version in versions: index = versions.index(version) versions = versions[index+1:] else: versions = versions elif action == const.REMOVE: versions = [version] self.combobox_version.setEnabled(False) if len(versions) == 1: if action == const.REMOVE: labeltext = _('Package version to remove:') else: labeltext = _('Package version available:') self.label_version.setText(versions[0]) self.widget_version = self.label_version else: labeltext = _("Select package version:") self.combobox_version.addItems(versions) self.widget_version = self.combobox_version self.label.setText(labeltext) self.label_version.setAlignment(Qt.AlignLeft) self.table_dependencies = QWidget(self) self._layout = QGridLayout() self._layout.addWidget(self.label, 0, 0, Qt.AlignVCenter | Qt.AlignLeft) self._layout.addWidget(self.widget_version, 0, 1, Qt.AlignVCenter | Qt.AlignRight) self.widgets = [self.checkbox, self.button_ok, self.widget_version, self.table_dependencies] row_index = 1 # Create a Table if action in [const.INSTALL, const.UPGRADE, const.DOWNGRADE]: table = QTableView(self) dialog_size = QSize(dialog_size.width() + 40, 300) self.table_dependencies = table row_index = 1 self._layout.addItem(QSpacerItem(10, 5), row_index, 0) self._layout.addWidget(self.checkbox, row_index + 1, 0, 1, 2) self.checkbox.setChecked(True) self._changed_version(versions[0]) table.setSelectionBehavior(QAbstractItemView.SelectRows) table.verticalHeader().hide() table.horizontalHeader().hide() table.setAlternatingRowColors(True) table.setShowGrid(False) table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) table.horizontalHeader().setStretchLastSection(True) self._layout.addWidget(self.table_dependencies, row_index + 2, 0, 1, 2, Qt.AlignHCenter) self._layout.addItem(QSpacerItem(10, 5), row_index + 3, 0) self._layout.addWidget(self.bbox, row_index + 6, 0, 1, 2, Qt.AlignHCenter) title = "{0}: {1}".format(action_title[action], name) self.setLayout(self._layout) self.setMinimumSize(dialog_size) self.setFixedSize(dialog_size) self.setWindowTitle(title) self.setModal(True) # signals and slots self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.close) self.combobox_version.currentIndexChanged.connect( self._changed_version) self.checkbox.stateChanged.connect(self._changed_checkbox) def _changed_version(self, version, dependencies=True): """ """ self._set_gui_disabled(True) install_dependencies = (self.checkbox.checkState() == 2) self._version_text = to_text_string(version) self._get_dependencies(install_dependencies) def _get_dependencies(self, dependencies=True): """ """ name = [self._name + '=' + self._version_text] # Temporal fix env_name = self._env if env_name != 'root': env_name = osp.basename(env_name) self._conda_process.dependencies(name=env_name, pkgs=name, dep=dependencies) def _changed_checkbox(self, state): """ """ if state: self._changed_version(self._version_text) else: self._changed_version(self._version_text, dependencies=False) def _on_process_finished(self, boo): """ """ if self.isVisible(): dic = self._conda_process.output self.dependencies_dic = dic self._set_dependencies_table() self._set_gui_disabled(False) def _set_dependencies_table(self): """ """ table = self.table_dependencies dic = self.dependencies_dic table.setModel(CondaDependenciesModel(self, dic)) table.resizeColumnsToContents() table.resizeColumnToContents(1) def _set_gui_disabled(self, value): """ """ if value: table = self.table_dependencies table.setModel(CondaDependenciesModel(self, {})) table.resizeColumnsToContents() table.setDisabled(True) else: table = self.table_dependencies table.setDisabled(False) for widget in self.widgets: widget.setDisabled(value)
class Window(QMainWindow): def __init__(self, wintitle): super(Window, self).__init__() self.default_tool = None self.plots = [] self.itemlist = PlotItemList(None) self.contrast = ContrastAdjustment(None) self.xcsw = XCrossSection(None) self.ycsw = YCrossSection(None) self.manager = PlotManager(self) self.toolbar = QToolBar(_("Tools"), self) self.manager.add_toolbar(self.toolbar, "default") self.toolbar.setMovable(True) self.toolbar.setFloatable(True) self.addToolBar(Qt.TopToolBarArea, self.toolbar) frame = QFrame(self) self.setCentralWidget(frame) self.layout = QGridLayout() layout = QVBoxLayout(frame) frame.setLayout(layout) layout.addLayout(self.layout) self.frame = frame self.setWindowTitle(wintitle) self.setWindowIcon(get_icon("guiqwt.svg")) def closeEvent(self, event): global _figures, _current_fig, _current_axes figure_title = to_text_string(self.windowTitle()) if _figures.pop(figure_title) == _current_fig: _current_fig = None _current_axes = None self.itemlist.close() self.contrast.close() self.xcsw.close() self.ycsw.close() event.accept() def add_plot(self, i, j, plot): self.layout.addWidget(plot, i, j) self.manager.add_plot(plot) self.plots.append(plot) def replot(self): for plot in self.plots: plot.replot() item = plot.get_default_item() if item is not None: plot.set_active_item(item) item.unselect() def add_panels(self, images=False): self.manager.add_panel(self.itemlist) if images: for panel in (self.ycsw, self.xcsw, self.contrast): panel.hide() self.manager.add_panel(panel) def register_tools(self, images=False): if images: self.manager.register_all_image_tools() else: self.manager.register_all_curve_tools() def display(self): self.show() self.replot() self.manager.get_default_tool().activate() self.manager.update_tools_status()
def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, stdio=False, configurations={}, **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 # 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 = CodeEditor(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(LSP_LANGUAGES) self.button_ok.setEnabled(False) if language is not None: idx = LSP_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 x: 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.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=CONF.get('appearance', 'selected'), 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', folding=False ) self.conf_input.set_language('json', 'config.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)
class PyDMScaleIndicator(QFrame, TextFormatter, PyDMWidget): """ A bar-shaped indicator for scalar value with support for Channels and more from PyDM. Configurable features include indicator type (bar/pointer), scale tick marks and orientation (horizontal/vertical). Parameters ---------- parent : QWidget The parent widget for the Scale init_channel : str, optional The channel to be used by the widget. """ def __init__(self, parent=None, init_channel=None): QFrame.__init__(self, parent) PyDMWidget.__init__(self, init_channel=init_channel) self._show_value = True self._show_limits = True self.scale_indicator = QScale() self.value_label = QLabel() self.lower_label = QLabel() self.upper_label = QLabel() self.value_label.setText('<val>') self.lower_label.setText('<min>') self.upper_label.setText('<max>') self._value_position = Qt.TopEdge self._limits_from_channel = True self._user_lower_limit = 0 self._user_upper_limit = 0 self.value_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.setup_widgets_for_orientation(Qt.Horizontal, False, False, self._value_position) def update_labels(self): """ Update the limits and value labels with the correct values. """ self.lower_label.setText(str(self.scale_indicator._lower_limit)) self.upper_label.setText(str(self.scale_indicator._upper_limit)) self.value_label.setText(self.format_string.format(self.scale_indicator._value)) def value_changed(self, new_value): """ Callback invoked when the Channel value is changed. Parameters ---------- new_val : int or float The new value from the channel. """ super(PyDMScaleIndicator, self).value_changed(new_value) self.scale_indicator.set_value(new_value) self.update_labels() def upperCtrlLimitChanged(self, new_limit): """ PyQT Slot for changes on the upper control limit value of the Channel This slot sends the new limit value to the ```ctrl_limit_changed``` callback. Parameters ---------- new_limit : float """ super(PyDMScaleIndicator, self).upperCtrlLimitChanged(new_limit) if self.limitsFromChannel: self.scale_indicator.set_upper_limit(new_limit) self.update_labels() def lowerCtrlLimitChanged(self, new_limit): """ PyQT Slot for changes on the lower control limit value of the Channel This slot sends the new limit value to the ```ctrl_limit_changed``` callback. Parameters ---------- new_limit : float """ super(PyDMScaleIndicator, self).lowerCtrlLimitChanged(new_limit) if self.limitsFromChannel: self.scale_indicator.set_lower_limit(new_limit) self.update_labels() def setup_widgets_for_orientation(self, new_orientation, flipped, inverted, value_position): """ Reconstruct the widget given the orientation. Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical flipped : bool Indicates if scale tick marks are flipped to the other side inverted : bool Indicates if scale appearance is inverted """ self.limits_layout = None self.widget_layout = None if new_orientation == Qt.Horizontal: self.limits_layout = QHBoxLayout() if not inverted: self.limits_layout.addWidget(self.lower_label) self.limits_layout.addWidget(self.upper_label) else: self.limits_layout.addWidget(self.upper_label) self.limits_layout.addWidget(self.lower_label) self.widget_layout = QGridLayout() if not flipped: if value_position == Qt.LeftEdge: self.widget_layout.addWidget(self.value_label, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 0, 1) self.widget_layout.addItem(self.limits_layout, 1, 1) elif value_position == Qt.RightEdge: self.widget_layout.addWidget(self.value_label, 0, 1) self.widget_layout.addWidget(self.scale_indicator, 0, 0) self.widget_layout.addItem(self.limits_layout, 1, 0) elif value_position == Qt.TopEdge: self.widget_layout.addWidget(self.value_label, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 1, 0) self.widget_layout.addItem(self.limits_layout, 2, 0) elif value_position == Qt.BottomEdge: self.widget_layout.addWidget(self.scale_indicator, 0, 0) self.widget_layout.addItem(self.limits_layout, 1, 0) self.widget_layout.addWidget(self.value_label, 2, 0) if not inverted: self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignRight) elif inverted: self.lower_label.setAlignment(Qt.AlignTop | Qt.AlignRight) self.upper_label.setAlignment(Qt.AlignTop | Qt.AlignLeft) else: if value_position == Qt.LeftEdge: self.widget_layout.addItem(self.limits_layout, 0, 1) self.widget_layout.addWidget(self.scale_indicator, 1, 1) self.widget_layout.addWidget(self.value_label, 1, 0) elif value_position == Qt.RightEdge: self.widget_layout.addItem(self.limits_layout, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 1, 0) self.widget_layout.addWidget(self.value_label, 1, 1) elif value_position == Qt.TopEdge: self.widget_layout.addWidget(self.value_label, 0, 0) self.widget_layout.addItem(self.limits_layout, 1, 0) self.widget_layout.addWidget(self.scale_indicator, 2, 0) elif value_position == Qt.BottomEdge: self.widget_layout.addItem(self.limits_layout, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 1, 0) self.widget_layout.addWidget(self.value_label, 2, 0) if not inverted: self.lower_label.setAlignment(Qt.AlignBottom | Qt.AlignLeft) self.upper_label.setAlignment(Qt.AlignBottom | Qt.AlignRight) elif inverted: self.lower_label.setAlignment(Qt.AlignBottom | Qt.AlignRight) self.upper_label.setAlignment(Qt.AlignBottom | Qt.AlignLeft) elif new_orientation == Qt.Vertical: self.limits_layout = QVBoxLayout() if (value_position == Qt.RightEdge and flipped == False) or \ (value_position == Qt.LeftEdge and flipped == True): add_value_between_limits = True else: add_value_between_limits = False if not inverted: self.limits_layout.addWidget(self.upper_label) if add_value_between_limits: self.limits_layout.addWidget(self.value_label) self.limits_layout.addWidget(self.lower_label) self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom) self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop) else: self.limits_layout.addWidget(self.lower_label) if add_value_between_limits: self.limits_layout.addWidget(self.value_label) self.limits_layout.addWidget(self.upper_label) self.lower_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop) self.upper_label.setAlignment(Qt.AlignHCenter | Qt.AlignBottom) self.widget_layout = QGridLayout() if not flipped: if value_position == Qt.LeftEdge: self.widget_layout.addWidget(self.value_label, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 0, 1) self.widget_layout.addItem(self.limits_layout, 0, 2) elif value_position == Qt.RightEdge: self.widget_layout.addWidget(self.scale_indicator, 0, 0) self.widget_layout.addItem(self.limits_layout, 0, 1) elif value_position == Qt.TopEdge: self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2) self.widget_layout.addWidget(self.scale_indicator, 1, 0) self.widget_layout.addItem(self.limits_layout, 1, 1) elif value_position == Qt.BottomEdge: self.widget_layout.addWidget(self.scale_indicator, 0, 0) self.widget_layout.addItem(self.limits_layout, 0, 1) self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2) if not inverted: self.lower_label.setAlignment(Qt.AlignLeft | Qt.AlignBottom) self.upper_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) elif inverted: self.lower_label.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.upper_label.setAlignment(Qt.AlignLeft | Qt.AlignBottom) else: if value_position == Qt.LeftEdge: self.widget_layout.addItem(self.limits_layout, 0, 1) self.widget_layout.addWidget(self.scale_indicator, 0, 2) elif value_position == Qt.RightEdge: self.widget_layout.addItem(self.limits_layout, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 0, 1) self.widget_layout.addWidget(self.value_label, 0, 2) elif value_position == Qt.TopEdge: self.widget_layout.addWidget(self.value_label, 0, 0, 1, 2) self.widget_layout.addItem(self.limits_layout, 1, 0) self.widget_layout.addWidget(self.scale_indicator, 1, 1) elif value_position == Qt.BottomEdge: self.widget_layout.addItem(self.limits_layout, 0, 0) self.widget_layout.addWidget(self.scale_indicator, 0, 1) self.widget_layout.addWidget(self.value_label, 1, 0, 1, 2) if not inverted: self.lower_label.setAlignment(Qt.AlignRight | Qt.AlignBottom) self.upper_label.setAlignment(Qt.AlignRight | Qt.AlignTop) elif inverted: self.lower_label.setAlignment(Qt.AlignRight | Qt.AlignTop) self.upper_label.setAlignment(Qt.AlignRight | Qt.AlignBottom) self.value_label.setAlignment(Qt.AlignCenter) if self.layout() is not None: # Trick to remove the existing layout by re-parenting it in an empty widget. QWidget().setLayout(self.layout()) self.widget_layout.setContentsMargins(1, 1, 1, 1) self.setLayout(self.widget_layout) @Property(bool) def showValue(self): """ Whether or not the current value should be displayed on the scale. Returns ------- bool """ return self._show_value @showValue.setter def showValue(self, checked): """ Whether or not the current value should be displayed on the scale. Parameters ---------- checked : bool """ if self._show_value != bool(checked): self._show_value = checked if checked: self.value_label.show() else: self.value_label.hide() @Property(bool) def showLimits(self): """ Whether or not the high and low limits should be displayed on the scale. Returns ------- bool """ return self._show_limits @showLimits.setter def showLimits(self, checked): """ Whether or not the high and low limits should be displayed on the scale. Parameters ---------- checked : bool """ if self._show_limits != bool(checked): self._show_limits = checked if checked: self.lower_label.show() self.upper_label.show() else: self.lower_label.hide() self.upper_label.hide() @Property(bool) def showTicks(self): """ Whether or not the tick marks should be displayed on the scale. Returns ------- bool """ return self.scale_indicator.get_show_ticks() @showTicks.setter def showTicks(self, checked): """ Whether or not the tick marks should be displayed on the scale. Parameters ---------- checked : bool """ self.scale_indicator.set_show_ticks(checked) @Property(Qt.Orientation) def orientation(self): """ The scale orientation (Horizontal or Vertical) Returns ------- int Qt.Horizontal or Qt.Vertical """ return self.scale_indicator.get_orientation() @orientation.setter def orientation(self, orientation): """ The scale orientation (Horizontal or Vertical) Parameters ---------- new_orientation : int Qt.Horizontal or Qt.Vertical """ self.scale_indicator.set_orientation(orientation) self.setup_widgets_for_orientation(orientation, self.flipScale, self.invertedAppearance, self._value_position) @Property(bool) def flipScale(self): """ Whether or not the scale should be flipped. Returns ------- bool """ return self.scale_indicator.get_flip_scale() @flipScale.setter def flipScale(self, checked): """ Whether or not the scale should be flipped. Parameters ---------- checked : bool """ self.scale_indicator.set_flip_scale(checked) self.setup_widgets_for_orientation(self.orientation, checked, self.invertedAppearance, self._value_position) @Property(bool) def invertedAppearance(self): """ Whether or not the scale appearence should be inverted. Returns ------- bool """ return self.scale_indicator.get_inverted_appearance() @invertedAppearance.setter def invertedAppearance(self, inverted): """ Whether or not the scale appearence should be inverted. Parameters ---------- inverted : bool """ self.scale_indicator.set_inverted_appearance(inverted) self.setup_widgets_for_orientation(self.orientation, self.flipScale, inverted, self._value_position) @Property(bool) def barIndicator(self): """ Whether or not the scale indicator should be a bar instead of a pointer. Returns ------- bool """ return self.scale_indicator.get_bar_indicator() @barIndicator.setter def barIndicator(self, checked): """ Whether or not the scale indicator should be a bar instead of a pointer. Parameters ---------- checked : bool """ self.scale_indicator.set_bar_indicator(checked) @Property(QColor) def backgroundColor(self): """ The color of the scale background. Returns ------- QColor """ return self.scale_indicator.get_background_color() @backgroundColor.setter def backgroundColor(self, color): """ The color of the scale background. Parameters ------- color : QColor """ self.scale_indicator.set_background_color(color) @Property(QColor) def indicatorColor(self): """ The color of the scale indicator. Returns ------- QColor """ return self.scale_indicator.get_indicator_color() @indicatorColor.setter def indicatorColor(self, color): """ The color of the scale indicator. Parameters ------- color : QColor """ self.scale_indicator.set_indicator_color(color) @Property(QColor) def tickColor(self): """ The color of the scale tick marks. Returns ------- QColor """ return self.scale_indicator.get_tick_color() @tickColor.setter def tickColor(self, color): """ The color of the scale tick marks. Parameters ------- color : QColor """ self.scale_indicator.set_tick_color(color) @Property(float) def backgroundSizeRate(self): """ The rate of background height size (from top to bottom). Returns ------- float """ return self.scale_indicator.get_background_size_rate() @backgroundSizeRate.setter def backgroundSizeRate(self, rate): """ The rate of background height size (from top to bottom). Parameters ------- rate : float Between 0 and 1. """ self.scale_indicator.set_background_size_rate(rate) @Property(float) def tickSizeRate(self): """ The rate of tick marks height size (from bottom to top). Returns ------- float """ return self.scale_indicator.get_tick_size_rate() @tickSizeRate.setter def tickSizeRate(self, rate): """ The rate of tick marks height size (from bottom to top). Parameters ------- rate : float Between 0 and 1. """ self.scale_indicator.set_tick_size_rate(rate) @Property(int) def numDivisions(self): """ The number in which the scale is divided. Returns ------- int """ return self.scale_indicator.get_num_divisions() @numDivisions.setter def numDivisions(self, divisions): """ The number in which the scale is divided. Parameters ------- divisions : int The number of scale divisions. """ self.scale_indicator.set_num_divisions(divisions) @Property(int) def scaleHeight(self): """ The scale height, fixed so it do not wiggle when value label resizes. Returns ------- int """ return self.scale_indicator.get_scale_height() @scaleHeight.setter def scaleHeight(self, value): """ The scale height, fixed so it do not wiggle when value label resizes. Parameters ------- divisions : int The scale height. """ self.scale_indicator.set_scale_height(value) @Property(Qt.Edge) def valuePosition(self): """ The position of the value label (Top, Bottom, Left or Right). Returns ------- int Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge """ return self._value_position @valuePosition.setter def valuePosition(self, position): """ The position of the value label (Top, Bottom, Left or Right). Parameters ---------- position : int Qt.TopEdge, Qt.BottomEdge, Qt.LeftEdge or Qt.RightEdge """ self._value_position = position self.setup_widgets_for_orientation(self.orientation, self.flipScale, self.invertedAppearance, position) @Property(bool) def originAtZero(self): """ Whether or not the scale indicator should start at zero value. Applies only for bar indicator. Returns ------- bool """ return self.scale_indicator.get_origin_at_zero() @originAtZero.setter def originAtZero(self, checked): """ Whether or not the scale indicator should start at zero value. Applies only for bar indicator. Parameters ---------- checked : bool """ self.scale_indicator.set_origin_at_zero(checked) @Property(bool) def limitsFromChannel(self): """ Whether or not the scale indicator should use the limits information from the channel. Returns ------- bool """ return self._limits_from_channel @limitsFromChannel.setter def limitsFromChannel(self, checked): """ Whether or not the scale indicator should use the limits information from the channel. Parameters ---------- checked : bool True to use the limits from the Channel, False to use the user-defined values. """ if self._limits_from_channel != checked: self._limits_from_channel = checked if checked: if self._lower_ctrl_limit: self.scale_indicator.set_lower_limit(self._lower_ctrl_limit) if self._upper_ctrl_limit: self.scale_indicator.set_upper_limit(self._upper_ctrl_limit) else: self.scale_indicator.set_lower_limit(self._user_lower_limit) self.scale_indicator.set_upper_limit(self._user_upper_limit) self.update_labels() @Property(float) def userLowerLimit(self): """ The user-defined lower limit for the scale. Returns ------- float """ return self._user_lower_limit @userLowerLimit.setter def userLowerLimit(self, value): """ The user-defined lower limit for the scale. Parameters ---------- value : float The new lower limit value. """ if self._limits_from_channel: return self._user_lower_limit = value self.scale_indicator.set_lower_limit(self._user_lower_limit) self.update_labels() @Property(float) def userUpperLimit(self): """ The user-defined upper limit for the scale. Returns ------- float """ return self._user_upper_limit @userUpperLimit.setter def userUpperLimit(self, value): """ The user-defined upper limit for the scale. Parameters ---------- value : float The new upper limit value. """ if self._limits_from_channel: return self._user_upper_limit = value self.scale_indicator.set_upper_limit(self._user_upper_limit) self.update_labels()
class PyChopGui(QMainWindow): """ GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments at spallation sources by calculating the resolution and flux at a given neutron energies. """ instruments = {} choppers = {} minE = {} maxE = {} hyspecS2 = 35. def __init__(self, parent=None, window_flags=None): super(PyChopGui, self).__init__(parent) if window_flags: self.setWindowFlags(window_flags) self.folder = os.path.dirname(sys.modules[self.__module__].__file__) for fname in os.listdir(self.folder): if fname.endswith('.yaml'): instobj = Instrument(os.path.join(self.folder, fname)) self.instruments[instobj.name] = instobj self.choppers[instobj.name] = instobj.getChopperNames() self.minE[instobj.name] = max([instobj.emin, 0.01]) self.maxE[instobj.name] = instobj.emax self.drawLayout() self.setInstrument(list(self.instruments.keys())[0]) self.resaxes_xlim = 0 self.qeaxes_xlim = 0 self.isFramePlotted = 0 # help self.assistant_process = QProcess(self) # pylint: disable=protected-access self.mantidplot_name = 'PyChop' def closeEvent(self, event): self.assistant_process.close() self.assistant_process.waitForFinished() event.accept() def setInstrument(self, instname): """ Defines the instrument parameters by the name of the instrument. """ self.engine = self.instruments[str(instname)] self.tabs.setTabEnabled(self.tdtabID, False) self.widgets['ChopperCombo']['Combo'].clear() self.widgets['FrequencyCombo']['Combo'].clear() self.widgets['FrequencyCombo']['Label'].setText('Frequency') self.widgets['PulseRemoverCombo']['Combo'].clear() for item in self.choppers[str(instname)]: self.widgets['ChopperCombo']['Combo'].addItem(item) rep = self.engine.moderator.source_rep maxfreq = self.engine.chopper_system.max_frequencies # At the moment, the GUI only supports up to two independent frequencies if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1: self.widgets['PulseRemoverCombo']['Combo'].hide() self.widgets['PulseRemoverCombo']['Label'].hide() for fq in range( rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep): self.widgets['FrequencyCombo']['Combo'].addItem(str(fq)) if hasattr(self.engine.chopper_system, 'frequency_names'): self.widgets['FrequencyCombo']['Label'].setText( self.engine.chopper_system.frequency_names[0]) else: self.widgets['PulseRemoverCombo']['Combo'].show() self.widgets['PulseRemoverCombo']['Label'].show() if hasattr(self.engine.chopper_system, 'frequency_names'): for idx, chp in enumerate([ self.widgets['FrequencyCombo']['Label'], self.widgets['PulseRemoverCombo']['Label'] ]): chp.setText( self.engine.chopper_system.frequency_names[idx]) for fq in range(rep, maxfreq[0] + 1, rep): self.widgets['FrequencyCombo']['Combo'].addItem(str(fq)) for fq in range(rep, maxfreq[1] + 1, rep): self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq)) if len(self.engine.chopper_system.choppers) > 1: self.widgets['MultiRepCheck'].setEnabled(True) self.tabs.setTabEnabled(self.tdtabID, True) else: self.widgets['MultiRepCheck'].setEnabled(False) self.widgets['MultiRepCheck'].setChecked(False) self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() if self.engine.chopper_system.isPhaseIndependent: self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() self.widgets['Chopper2Phase']['Edit'].setText( str(self.engine.chopper_system.defaultPhase[0])) self.widgets['Chopper2Phase']['Label'].setText( self.engine.chopper_system.phaseNames[0]) # Special case for MERLIN - hide phase control from normal users if 'MERLIN' in str(instname) and not self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() self.engine.setChopper( str(self.widgets['ChopperCombo']['Combo'].currentText())) self.engine.setFrequency( float(self.widgets['FrequencyCombo']['Combo'].currentText())) val = self.flxslder.val * self.maxE[self.engine.instname] / 100 self.flxedt.setText('%3.2f' % (val)) nframe = self.engine.moderator.n_frame if hasattr( self.engine.moderator, 'n_frame') else 1 self.repfig_nframe_edit.setText(str(nframe)) self.repfig_nframe_rep1only.setChecked(False) if hasattr(self.engine.chopper_system, 'default_frequencies'): cb = [ self.widgets['FrequencyCombo']['Combo'], self.widgets['PulseRemoverCombo']['Combo'] ] for idx, freq in enumerate( self.engine.chopper_system.default_frequencies): cb[idx].setCurrentIndex([ i for i in range(cb[idx].count()) if str(freq) in cb[idx].itemText(i) ][0]) if idx > 1: break self.tabs.setTabEnabled(self.qetabID, False) if self.engine.has_detector and hasattr(self.engine.detector, 'tthlims'): self.tabs.setTabEnabled(self.qetabID, True) # show s2 for HYSPEC only if 'HYSPEC' in str(instname): self.widgets[f"S2Edit"]['Edit'].show() self.widgets[f"S2Edit"]['Edit'].setText(str(self.hyspecS2)) self.widgets[f"S2Edit"]['Label'].show() else: self.widgets[f"S2Edit"]['Edit'].hide() self.widgets[f"S2Edit"]['Label'].hide() def setChopper(self, choppername): """ Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant. """ self.engine.setChopper(str(choppername)) self.engine.setFrequency( float(self.widgets['FrequencyCombo']['Combo'].currentText())) # Special case for MERLIN - only enable multirep for 'G' chopper if 'MERLIN' in self.engine.instname: if 'G' in str(choppername): self.widgets['MultiRepCheck'].setEnabled(True) self.tabs.setTabEnabled(self.tdtabID, True) self.widgets['Chopper2Phase']['Edit'].setText('1500') self.widgets['Chopper2Phase']['Label'].setText( 'Disk chopper phase delay time') if self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() else: self.widgets['MultiRepCheck'].setEnabled(False) self.widgets['MultiRepCheck'].setChecked(False) self.tabs.setTabEnabled(self.tdtabID, False) self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() def setFreq(self, freqtext=None, **kwargs): """ Sets the chopper frequency(ies), in Hz. """ freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText()) freq_in = kwargs['manual_freq'] if ('manual_freq' in kwargs.keys()) else freq_gui if len(self.engine.getFrequency()) > 1 and ( not hasattr(freq_in, '__len__') or len(freq_in) == 1): freqpr = float( self.widgets['PulseRemoverCombo']['Combo'].currentText()) freq_in = [freq_in, freqpr] if not self.widgets['Chopper2Phase']['Label'].isHidden(): chop2phase = self.widgets['Chopper2Phase']['Edit'].text() if isinstance(self.engine.chopper_system.defaultPhase[0], str): chop2phase = str(chop2phase) else: chop2phase = float(chop2phase) % ( 1e6 / self.engine.moderator.source_rep) self.engine.setFrequency(freq_in, phase=chop2phase) else: self.engine.setFrequency(freq_in) def setEi(self): """ Sets the incident energy (or focused incident energy for multi-rep case). """ try: eitxt = float(self.widgets['EiEdit']['Edit'].text()) except ValueError: raise ValueError('No Ei specified, or Ei string not understood') self.engine.setEi(eitxt) if self.eiPlots.isChecked(): self.calc_callback() def setS2(self): """ Sets the S2 tank rotation for HYSPEC instrument """ try: S2txt = float(self.widgets['S2Edit']['Edit'].text()) except: raise ValueError('No S2 specified, or S2 string not understood') if np.abs(S2txt) > 150: raise ValueError('S2 must be between -150 and 150 degrees') self.hyspecS2 = S2txt def calc_callback(self): """ Calls routines to calculate the resolution / flux and to update the Matplotlib graphs. """ try: if self.engine.getChopper() is None: self.setChopper( self.widgets['ChopperCombo']['Combo'].currentText()) self.setEi() if self.engine.name == 'HYSPEC': self.setS2() self.setFreq() self.calculate() if self.errormess: idx = [ i for i, ei in enumerate(self.eis) if np.abs(ei - self.engine.getEi()) < 1.e-4 ] if idx and self.flux[idx[0]] == 0: raise ValueError(self.errormess) self.errormessage(self.errormess) self.plot_res() self.plot_frame() if self.instSciAct.isChecked(): self.update_script() except ValueError as err: self.errormessage(err) else: self.plot_flux_ei() self.plot_flux_hz() def calculate(self): """ Performs the resolution and flux calculations. """ self.errormess = None if self.engine.getEi() is None: self.setEi() if self.widgets['MultiRepCheck'].isChecked(): en = np.linspace(0, 0.95, 200) self.eis = self.engine.getAllowedEi() with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', UserWarning) self.res = self.engine.getMultiRepResolution(en) self.flux = self.engine.getMultiRepFlux() if len(w) > 0: mess = [str(w[i].message) for i in range(len(w))] self.errormess = '\n'.join( [m for m in mess if 'tchop' in m]) else: en = np.linspace(0, 0.95 * self.engine.getEi(), 200) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', UserWarning) self.res = self.engine.getResolution(en) self.flux = self.engine.getFlux() if len(w) > 0: raise ValueError(w[0].message) def _set_overplot(self, overplot, axisname): axis = getattr(self, axisname) if overplot: if matplotlib.compare_versions('2.1.0', matplotlib.__version__): axis.hold(True) else: setattr(self, axisname + '_xlim', 0) axis.clear() axis.axhline(color='k') def plot_res(self): """ Plots the resolution in the resolution tab """ overplot = self.widgets['HoldCheck'].isChecked() multiplot = self.widgets['MultiRepCheck'].isChecked() self._set_overplot(overplot, 'resaxes') self._set_overplot(overplot, 'qeaxes') inst = self.engine.instname freq = self.engine.getFrequency() if hasattr(freq, '__len__'): freq = freq[0] if multiplot: if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.resaxes.hold(True) for ie, Ei in enumerate(self.eis): en = np.linspace(0, 0.95 * Ei, 200) if any(self.res[ie]): if not self.flux[ie]: continue line, = self.resaxes.plot(en, self.res[ie]) label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % ( inst, Ei, freq, self.flux[ie]) line.set_label(label_text) if self.tabs.isTabEnabled(self.qetabID): self.plot_qe(Ei, label_text, hold=True) self.resaxes_xlim = max(Ei, self.resaxes_xlim) if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.resaxes.hold(False) else: ei = self.engine.getEi() en = np.linspace(0, 0.95 * ei, 200) line, = self.resaxes.plot(en, self.res) chopper = self.engine.getChopper() label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % ( inst, chopper, ei, freq, self.flux) line.set_label(label_text) if self.tabs.isTabEnabled(self.qetabID): self.plot_qe(ei, label_text, overplot) self.resaxes_xlim = max(ei, self.resaxes_xlim) self.resaxes.set_xlim([0, self.resaxes_xlim]) legend_set_draggable(self.resaxes.legend(), True) self.resaxes.set_xlabel('Energy Transfer (meV)') self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)') self.rescanvas.draw() def plot_qe(self, Ei, label_text, hold=False): """ Plots the Q-E diagram """ from scipy import constants E2q, meV2J = (2. * constants.m_n / (constants.hbar**2), constants.e / 1000.) en = np.linspace(-Ei / 5., Ei, 100) q2 = [] if self.engine.name == 'HYSPEC': if abs(self.hyspecS2) <= 30: self.engine.detector.tthlims = [0, abs(self.hyspecS2) + 30] else: self.engine.detector.tthlims = [ abs(self.hyspecS2) - 30, abs(self.hyspecS2) + 30 ] label_text += '_S2={}'.format(self.hyspecS2) for tth in self.engine.detector.tthlims: q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) * np.cos(np.deg2rad(tth))) * meV2J) / 1e10 q2.append(np.concatenate((np.flipud(q), q))) self._set_overplot(hold, 'qeaxes') self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim) line, = self.qeaxes.plot( np.hstack(q2), np.concatenate((np.flipud(en), en)).tolist() * len(self.engine.detector.tthlims)) line.set_label(label_text) self.qeaxes.set_xlim([0, self.qeaxes_xlim]) legend_set_draggable(self.qeaxes.legend(), True) self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$') self.qeaxes.set_ylabel('Energy Transfer (meV)') self.qecanvas.draw() def plot_flux_ei(self, **kwargs): """ Plots the flux vs Ei in the middle tab """ inst = self.engine.instname chop = self.engine.getChopper() freq = self.engine.getFrequency() overplot = self.widgets['HoldCheck'].isChecked() if hasattr(freq, '__len__'): freq = freq[0] update = kwargs['update'] if 'update' in kwargs.keys() else False # Do not recalculate if all relevant parameters still the same. _, labels = self.flxaxes2.get_legend_handles_labels() searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz' tmpinst = [] if (labels and (overplot or len(labels) == 1)) or update: for prevtitle in labels: prevInst, prevChop, prevFreq = re.search(searchStr, prevtitle).groups() if update: tmpinst.append( copy.deepcopy( Instrument(self.instruments[prevInst], prevChop, float(prevFreq)))) else: if inst == prevInst and chop == prevChop and freq == float( prevFreq): return ne = 25 mn = self.minE[inst] mx = (self.flxslder.val / 100) * self.maxE[inst] eis = np.linspace(mn, mx, ne) flux = eis * 0 elres = eis * 0 if update: self.flxaxes1.clear() self.flxaxes2.clear() if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.flxaxes1.hold(True) self.flxaxes2.hold(True) for ii, instrument in enumerate(tmpinst): for ie, ei in enumerate(eis): with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = instrument.getFlux(ei) elres[ie] = instrument.getResolution(0., ei)[0] self.flxaxes1.plot(eis, flux) line, = self.flxaxes2.plot(eis, elres) line.set_label(labels[ii]) else: for ie, ei in enumerate(eis): with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = self.engine.getFlux(ei) elres[ie] = self.engine.getResolution(0., ei)[0] if overplot: if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.flxaxes1.hold(True) self.flxaxes2.hold(True) else: self.flxaxes1.clear() self.flxaxes2.clear() self.flxaxes1.plot(eis, flux) line, = self.flxaxes2.plot(eis, elres) line.set_label('%s "%s" %d Hz' % (inst, chop, freq)) self.flxaxes1.set_xlim([mn, mx]) self.flxaxes2.set_xlim([mn, mx]) self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)') lg = self.flxaxes2.legend() legend_set_draggable(lg, True) self.flxcanvas.draw() def update_slider(self, val=None): """ Callback function for the x-axis slider of the flux tab """ if val is None: val = float( self.flxedt.text()) / self.maxE[self.engine.instname] * 100 if val < self.minE[self.engine.instname]: self.errormessage("Max Ei must be greater than %2.1f" % (self.minE[self.engine.instname])) val = (self.minE[self.engine.instname] + 0.1) / self.maxE[self.engine.instname] * 100 self.flxslder.set_val(val) else: val = self.flxslder.val * self.maxE[self.engine.instname] / 100 self.flxedt.setText('%3.2f' % (val)) self.plot_flux_ei(update=True) self.flxcanvas.draw() def plot_flux_hz(self): """ Plots the flux vs freq in the middle tab """ inst = self.engine.instname chop = self.engine.getChopper() ei = float(self.widgets['EiEdit']['Edit'].text()) overplot = self.widgets['HoldCheck'].isChecked() # Do not recalculate if one of the plots has the same parametersc _, labels = self.frqaxes2.get_legend_handles_labels() searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV' if labels and (overplot or len(labels) == 1): for prevtitle in labels: prevInst, prevChop, prevEi = re.search(searchStr, prevtitle).groups() if inst == prevInst and chop == prevChop and abs( ei - float(prevEi)) < 0.01: return freq0 = self.engine.getFrequency() rep = self.engine.moderator.source_rep maxfreq = self.engine.chopper_system.max_frequencies freqs = range( rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep) flux = np.zeros(len(freqs)) elres = np.zeros(len(freqs)) for ie, freq in enumerate(freqs): if hasattr(freq0, '__len__'): self.setFreq(manual_freq=[freq] + freq0[1:]) else: self.setFreq(manual_freq=freq) with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = self.engine.getFlux(ei) elres[ie] = self.engine.getResolution(0., ei)[0] if overplot: if matplotlib.compare_versions('2.1.0', matplotlib.__version__): self.frqaxes1.hold(True) self.frqaxes2.hold(True) else: self.frqaxes1.clear() self.frqaxes2.clear() self.setFreq(manual_freq=freq0) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)') line, = self.frqaxes1.plot(freqs, flux, 'o-') self.frqaxes1.set_xlim([0, np.max(freqs)]) self.frqaxes2.set_xlabel('Chopper Frequency (Hz)') self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)') line, = self.frqaxes2.plot(freqs, elres, 'o-') line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei)) lg = self.frqaxes2.legend() legend_set_draggable(lg, True) self.frqaxes2.set_xlim([0, np.max(freqs)]) self.frqcanvas.draw() def instSciCB(self): """ Callback function for the "Instrument Scientist Mode" menu option """ # MERLIN is a special case - want to hide ability to change phase from users if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper( ): if self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() self.widgets['Chopper2Phase']['Edit'].setText('1500') self.widgets['Chopper2Phase']['Label'].setText( 'Disk chopper phase delay time') else: self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() if self.instSciAct.isChecked(): self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput') self.scrtab.show() else: self.tabs.removeTab(self.scrtabID) self.scrtab.hide() def errormessage(self, message): msg = QMessageBox() msg.setText(str(message)) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def loadYaml(self): yaml_file = QFileDialog().getOpenFileName(self.mainWidget, 'Open Instrument YAML File', self.folder, 'Files (*.yaml)') if isinstance(yaml_file, tuple): yaml_file = yaml_file[0] yaml_file = str(yaml_file) new_folder = os.path.dirname(yaml_file) if new_folder != self.folder: self.folder = new_folder try: new_inst = Instrument(yaml_file) except (RuntimeError, AttributeError, ValueError) as err: self.errormessage(err) newname = new_inst.name if newname in self.instruments.keys( ) and not self.overwriteload.isChecked(): overwrite, newname = self._ask_overwrite() if overwrite == 1: return elif overwrite == 0: newname = new_inst.name self.instruments[newname] = new_inst self.choppers[newname] = new_inst.getChopperNames() self.minE[newname] = max([new_inst.emin, 0.01]) self.maxE[newname] = new_inst.emax self.updateInstrumentList() combo = self.widgets['InstrumentCombo']['Combo'] idx = [ i for i in range(combo.count()) if str(combo.itemText(i)) == newname ] combo.setCurrentIndex(idx[0]) self.setInstrument(newname) def _ask_overwrite(self): msg = QDialog() msg.setWindowTitle('Load overwrite') layout = QGridLayout() layout.addWidget( QLabel('Instrument %s already exists in memory. Overwrite this?'), 0, 0, 1, -1) buttons = [ QPushButton(label) for label in ['Load and overwrite', 'Cancel Load', 'Load and rename to'] ] locations = [[1, 0], [1, 1], [2, 0]] self.overwrite_flag = 1 def overwriteCB(idx): self.overwrite_flag = idx msg.accept() for idx, button in enumerate(buttons): button.clicked.connect(lambda _, idx=idx: overwriteCB(idx)) layout.addWidget(button, locations[idx][0], locations[idx][1]) newname = QLineEdit() newname.editingFinished.connect(lambda: overwriteCB(2)) layout.addWidget(newname, 2, 1) msg.setLayout(layout) msg.exec_() newname = str(newname.text()) if not newname or newname in self.instruments: self.errormessage('Invalid instrument name. Cancelling load.') self.overwrite_flag = 1 return self.overwrite_flag, newname def updateInstrumentList(self): combo = self.widgets['InstrumentCombo']['Combo'] old_instruments = [ str(combo.itemText(i)) for i in range(combo.count()) ] new_instruments = [ inst for inst in self.instruments if inst not in old_instruments ] for inst in new_instruments: combo.addItem(inst) def plot_frame(self): """ Plots the distance-time diagram in the right tab """ if len(self.engine.chopper_system.choppers) > 1: self.engine.n_frame = int(self.repfig_nframe_edit.text()) self.repaxes.clear() self.engine.plotMultiRepFrame( self.repaxes, first_rep=self.repfig_nframe_rep1only.isChecked()) self.repcanvas.draw() def _gen_text_ei(self, ei, obj_in): obj = Instrument(obj_in) obj.setEi(ei) en = np.linspace(0, 0.95 * ei, 10) try: flux = self.engine.getFlux() res = self.engine.getResolution(en) except ValueError as err: self.errormessage(err) raise ValueError(err) tsqvan, tsqdic, tsqmodchop = obj.getVanVar() v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6) x0, _, x1, x2, _ = obj.chopper_system.getDistances() first_component = 'moderator' if x0 != tsqmodchop[2]: x0 = tsqmodchop[2] first_component = 'chopper 1' txt = '# ------------------------------------------------------------- #\n' txt += '# Ei = %8.2f meV\n' % (ei) txt += '# Flux = %8.2f n/cm2/s\n' % (flux) txt += '# Elastic resolution = %6.2f meV\n' % (res[0]) txt += '# Time width at sample = %6.2f us, of which:\n' % ( 1e6 * np.sqrt(tsqvan)) for ky, val in list(tsqdic.items()): txt += '# %20s : %6.2f us\n' % (ky, 1e6 * np.sqrt(val)) txt += '# %s distances:\n' % (obj.instname) txt += '# x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component) txt += '# x1 = %6.2f m (Fermi to sample)\n' % (x1) txt += '# x2 = %6.2f m (sample to detector)\n' % (x2) txt += '# Approximate inelastic resolution is given by:\n' txt += '# dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n' txt += '# where: E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n' txt += '# t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n' txt += '# geom = (x1 + x2*(ei/ef)**1.5) / x0\n' txt += '# and t_mod and t_chop are the moderator and chopper time widths at the\n' txt += '# moderator and chopper positions (not at the sample as listed above).\n' txt += '# Which in this case is:\n' txt += '# %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % ( 874.78672e-6 / x2, v_mod, x1 / x0, x2 / x0) txt += '# + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % ( v_chop, 1 + x1 / x0, x2 / x0) txt += '# EN (meV) Full dE (meV) Approx dE (meV)\n' for ii in range(len(res)): ef = ei - en[ii] approx = (874.78672e-6 / x2) * np.sqrt(ef**3 * ( (v_mod * ((x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2 + (v_chop * (1 + (x1 / x0) + (x2 / x0) * (ei / ef)**1.5))**2)) txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx) return txt def genText(self): """ Generates text output of the resolution function versus energy transfer and other information. """ multiplot = self.widgets['MultiRepCheck'].isChecked() obj = self.engine if obj.getChopper() is None: self.setChopper( self.widgets['ChopperCombo']['Combo'].currentText()) if obj.getEi() is None: self.setEi() instname, chtyp, freqs, ei_in = tuple( [obj.instname, obj.getChopper(), obj.getFrequency(), obj.getEi()]) txt = '# ------------------------------------------------------------- #\n' txt += '# Chop calculation for instrument %s\n' % (instname) if obj.isFermi: txt += '# with chopper %s at %3i Hz\n' % (chtyp, freqs[0]) else: txt += '# in %s mode with:\n' % (chtyp) freq_names = obj.chopper_system.frequency_names for idx in range(len(freq_names)): txt += '# %s at %3i Hz\n' % (freq_names[idx], freqs[idx]) txt += self._gen_text_ei(ei_in, obj) if multiplot: for ei in sorted(self.engine.getAllowedEi()): if np.abs(ei - ei_in) > 0.001: txt += self._gen_text_ei(ei, obj) return txt def showText(self): """ Creates a dialog to show the generated text output. """ try: generatedText = self.genText() except ValueError: return self.txtwin = QDialog() self.txtedt = QTextEdit() self.txtbtn = QPushButton('OK') self.txtwin.layout = QVBoxLayout(self.txtwin) self.txtwin.layout.addWidget(self.txtedt) self.txtwin.layout.addWidget(self.txtbtn) self.txtbtn.clicked.connect(self.txtwin.deleteLater) self.txtedt.setText(generatedText) self.txtedt.setReadOnly(True) self.txtwin.setWindowTitle('Resolution information') self.txtwin.setWindowModality(Qt.ApplicationModal) self.txtwin.setAttribute(Qt.WA_DeleteOnClose) self.txtwin.setMinimumSize(400, 600) self.txtwin.resize(400, 600) self.txtwin.show() self.txtloop = QEventLoop() self.txtloop.exec_() def saveText(self): """ Saves the generated text to a file (opens file dialog). """ fname = QFileDialog.getSaveFileName(self, 'Open file', '') if isinstance(fname, tuple): fname = fname[0] fid = open(fname, 'w') fid.write(self.genText()) fid.close() def update_script(self): """ Updates the text window with information about the previous calculation. """ if self.widgets['MultiRepCheck'].isChecked(): out = self.engine.getMultiWidths() new_str = '\n' for ie, ee in enumerate(out['Eis']): res = out['Energy'][ie] percent = res / ee * 100 chop_width = out['chopper'][ie] mod_width = out['moderator'][ie] new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % ( ee, res * 1000, percent) new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % ( chop_width, mod_width) else: ei = self.engine.getEi() out = self.engine.getWidths() res = out['Energy'] percent = res / ei * 100 chop_width = out['chopper'] mod_width = out['moderator'] new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % ( ei, res * 1000, percent) new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % ( chop_width, mod_width) self.scredt.append(new_str) def onHelp(self): """ Shows the help page """ try: from mantidqt.gui_helper import show_interface_help show_interface_help(self.mantidplot_name, self.assistant_process, area='direct') except ImportError: helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n" helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n" helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n" helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n" helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help " helpTxt += "can be obtained from the\nMantid help pages." self.hlpwin = QDialog() self.hlpedt = QLabel(helpTxt) self.hlpbtn = QPushButton('OK') self.hlpwin.layout = QVBoxLayout(self.hlpwin) self.hlpwin.layout.addWidget(self.hlpedt) self.hlpwin.layout.addWidget(self.hlpbtn) self.hlpbtn.clicked.connect(self.hlpwin.deleteLater) self.hlpwin.setWindowTitle('Help') self.hlpwin.setWindowModality(Qt.ApplicationModal) self.hlpwin.setAttribute(Qt.WA_DeleteOnClose) self.hlpwin.setMinimumSize(370, 300) self.hlpwin.resize(370, 300) self.hlpwin.show() self.hlploop = QEventLoop() self.hlploop.exec_() def drawLayout(self): """ Draws the GUI layout. """ self.widgetslist = [ [ 'pair', 'show', 'Instrument', 'combo', self.instruments, self.setInstrument, 'InstrumentCombo' ], [ 'pair', 'show', 'Chopper', 'combo', '', self.setChopper, 'ChopperCombo' ], [ 'pair', 'show', 'Frequency', 'combo', '', self.setFreq, 'FrequencyCombo' ], [ 'pair', 'hide', 'Pulse remover chopper freq', 'combo', '', self.setFreq, 'PulseRemoverCombo' ], ['pair', 'show', 'Ei', 'edit', '', self.setEi, 'EiEdit'], [ 'pair', 'hide', 'Chopper 2 phase delay time', 'edit', '5', self.setFreq, 'Chopper2Phase' ], ['pair', 'hide', 'S2', 'edit', '', self.setS2, 'S2Edit'], ['spacer'], [ 'single', 'show', 'Calculate and Plot', 'button', self.calc_callback, 'CalculateButton' ], [ 'single', 'show', 'Hold current plot', 'check', lambda: None, 'HoldCheck' ], [ 'single', 'show', 'Show multi-reps', 'check', lambda: None, 'MultiRepCheck' ], ['spacer'], [ 'single', 'show', 'Show data ascii window', 'button', self.showText, 'ShowAsciiButton' ], [ 'single', 'show', 'Save data as ascii', 'button', self.saveText, 'SaveAsciiButton' ] ] self.droplabels = [] self.dropboxes = [] self.singles = [] self.widgets = {} self.leftPanel = QVBoxLayout() self.rightPanel = QVBoxLayout() self.tabs = QTabWidget(self) self.fullWindow = QGridLayout() for widget in self.widgetslist: if 'pair' in widget[0]: self.droplabels.append(QLabel(widget[2])) if 'combo' in widget[3]: self.dropboxes.append(QComboBox(self)) self.dropboxes[-1].activated['QString'].connect(widget[5]) for item in widget[4]: self.dropboxes[-1].addItem(item) self.widgets[widget[-1]] = { 'Combo': self.dropboxes[-1], 'Label': self.droplabels[-1] } elif 'edit' in widget[3]: self.dropboxes.append(QLineEdit(self)) self.dropboxes[-1].returnPressed.connect(widget[5]) self.widgets[widget[-1]] = { 'Edit': self.dropboxes[-1], 'Label': self.droplabels[-1] } else: raise RuntimeError( 'Bug in code - widget %s is not recognised.' % (widget[3])) self.leftPanel.addWidget(self.droplabels[-1]) self.leftPanel.addWidget(self.dropboxes[-1]) if 'hide' in widget[1]: self.droplabels[-1].hide() self.dropboxes[-1].hide() elif 'single' in widget[0]: if 'check' in widget[3]: self.singles.append(QCheckBox(widget[2], self)) self.singles[-1].stateChanged.connect(widget[4]) elif 'button' in widget[3]: self.singles.append(QPushButton(widget[2])) self.singles[-1].clicked.connect(widget[4]) else: raise RuntimeError( 'Bug in code - widget %s is not recognised.' % (widget[3])) self.leftPanel.addWidget(self.singles[-1]) if 'hide' in widget[1]: self.singles[-1].hide() self.widgets[widget[-1]] = self.singles[-1] elif 'spacer' in widget[0]: self.leftPanel.addItem(QSpacerItem(0, 35)) else: raise RuntimeError( 'Bug in code - widget class %s is not recognised.' % (widget[0])) # Right panel, matplotlib figures self.resfig = Figure() self.resfig.patch.set_facecolor('white') self.rescanvas = FigureCanvas(self.resfig) self.resaxes = self.resfig.add_subplot(111) self.resaxes.axhline(color='k') self.resaxes.set_xlabel('Energy Transfer (meV)') self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)') self.resfig_controls = NavigationToolbar(self.rescanvas, self) self.restab = QWidget(self.tabs) self.restabbox = QVBoxLayout() self.restabbox.addWidget(self.rescanvas) self.restabbox.addWidget(self.resfig_controls) self.restab.setLayout(self.restabbox) self.flxfig = Figure() self.flxfig.patch.set_facecolor('white') self.flxcanvas = FigureCanvas(self.flxfig) self.flxaxes1 = self.flxfig.add_subplot(121) self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.flxaxes2 = self.flxfig.add_subplot(122) self.flxaxes2.set_xlabel('Incident Energy (meV)') self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)') self.flxfig_controls = NavigationToolbar(self.flxcanvas, self) self.flxsldfg = Figure() self.flxsldfg.patch.set_facecolor('white') self.flxsldcv = FigureCanvas(self.flxsldfg) self.flxsldax = self.flxsldfg.add_subplot(111) self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100) self.flxslder.valtext.set_visible(False) self.flxslder.on_changed(self.update_slider) self.flxedt = QLineEdit() self.flxedt.setText('1000') self.flxedt.returnPressed.connect(self.update_slider) self.flxtab = QWidget(self.tabs) self.flxsldbox = QHBoxLayout() self.flxsldbox.addWidget(self.flxsldcv) self.flxsldbox.addWidget(self.flxedt) self.flxsldwdg = QWidget() self.flxsldwdg.setLayout(self.flxsldbox) sz = self.flxsldwdg.maximumSize() sz.setHeight(50) self.flxsldwdg.setMaximumSize(sz) self.flxtabbox = QVBoxLayout() self.flxtabbox.addWidget(self.flxcanvas) self.flxtabbox.addWidget(self.flxsldwdg) self.flxtabbox.addWidget(self.flxfig_controls) self.flxtab.setLayout(self.flxtabbox) self.frqfig = Figure() self.frqfig.patch.set_facecolor('white') self.frqcanvas = FigureCanvas(self.frqfig) self.frqaxes1 = self.frqfig.add_subplot(121) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.frqaxes2 = self.frqfig.add_subplot(122) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)') self.frqfig_controls = NavigationToolbar(self.frqcanvas, self) self.frqtab = QWidget(self.tabs) self.frqtabbox = QVBoxLayout() self.frqtabbox.addWidget(self.frqcanvas) self.frqtabbox.addWidget(self.frqfig_controls) self.frqtab.setLayout(self.frqtabbox) self.repfig = Figure() self.repfig.patch.set_facecolor('white') self.repcanvas = FigureCanvas(self.repfig) self.repaxes = self.repfig.add_subplot(111) self.repaxes.axhline(color='k') self.repaxes.set_xlabel(r'TOF ($\mu$sec)') self.repaxes.set_ylabel('Distance (m)') self.repfig_controls = NavigationToolbar(self.repcanvas, self) self.repfig_nframe_label = QLabel('Number of frames to plot') self.repfig_nframe_edit = QLineEdit('1') self.repfig_nframe_button = QPushButton('Replot') self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame()) self.repfig_nframe_rep1only = QCheckBox('First Rep Only') self.repfig_nframe_box = QHBoxLayout() self.repfig_nframe_box.addWidget(self.repfig_nframe_label) self.repfig_nframe_box.addWidget(self.repfig_nframe_edit) self.repfig_nframe_box.addWidget(self.repfig_nframe_button) self.repfig_nframe_box.addWidget(self.repfig_nframe_rep1only) self.reptab = QWidget(self.tabs) self.repfig_nframe = QWidget(self.reptab) self.repfig_nframe.setLayout(self.repfig_nframe_box) self.repfig_nframe.setSizePolicy( QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.reptabbox = QVBoxLayout() self.reptabbox.addWidget(self.repcanvas) self.reptabbox.addWidget(self.repfig_nframe) self.reptabbox.addWidget(self.repfig_controls) self.reptab.setLayout(self.reptabbox) self.qefig = Figure() self.qefig.patch.set_facecolor('white') self.qecanvas = FigureCanvas(self.qefig) self.qeaxes = self.qefig.add_subplot(111) self.qeaxes.axhline(color='k') self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$') self.qeaxes.set_ylabel('Energy Transfer (meV)') self.qefig_controls = NavigationToolbar(self.qecanvas, self) self.qetabbox = QVBoxLayout() self.qetabbox.addWidget(self.qecanvas) self.qetabbox.addWidget(self.qefig_controls) self.qetab = QWidget(self.tabs) self.qetab.setLayout(self.qetabbox) self.scrtab = QWidget(self.tabs) self.scredt = QTextEdit() self.scrcls = QPushButton("Clear") self.scrcls.clicked.connect(lambda: self.scredt.clear()) self.scrbox = QVBoxLayout() self.scrbox.addWidget(self.scredt) self.scrbox.addWidget(self.scrcls) self.scrtab.setLayout(self.scrbox) self.scrtab.hide() self.tabs.addTab(self.restab, 'Resolution') self.tabs.addTab(self.flxtab, 'Flux-Ei') self.tabs.addTab(self.frqtab, 'Flux-Freq') self.tabs.addTab(self.reptab, 'Time-Distance') self.tdtabID = 3 self.tabs.setTabEnabled(self.tdtabID, False) self.tabs.addTab(self.qetab, 'Q-E') self.qetabID = 4 self.tabs.setTabEnabled(self.qetabID, False) self.scrtabID = 5 self.rightPanel.addWidget(self.tabs) self.menuLoad = QMenu('Load') self.loadAct = QAction('Load YAML', self.menuLoad) self.loadAct.triggered.connect(self.loadYaml) self.menuLoad.addAction(self.loadAct) self.menuOptions = QMenu('Options') self.instSciAct = QAction('Instrument Scientist Mode', self.menuOptions, checkable=True) self.instSciAct.triggered.connect(self.instSciCB) self.menuOptions.addAction(self.instSciAct) self.eiPlots = QAction('Press Enter in Ei box updates plots', self.menuOptions, checkable=True) self.menuOptions.addAction(self.eiPlots) self.overwriteload = QAction('Always overwrite instruments in memory', self.menuOptions, checkable=True) self.menuOptions.addAction(self.overwriteload) self.menuBar().addMenu(self.menuLoad) self.menuBar().addMenu(self.menuOptions) self.leftPanelWidget = QWidget() self.leftPanelWidget.setLayout(self.leftPanel) self.leftPanelWidget.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)) self.fullWindow.addWidget(self.leftPanelWidget, 0, 0) self.fullWindow.addLayout(self.rightPanel, 0, 1) self.helpbtn = QPushButton("?", self) self.helpbtn.setMaximumWidth(30) self.helpbtn.clicked.connect(self.onHelp) self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1) self.mainWidget = QWidget() self.mainWidget.setLayout(self.fullWindow) self.setCentralWidget(self.mainWidget) self.setWindowTitle('PyChopGUI') self.show()
def create_layout(self): cancel_box = QHBoxLayout() cancel_box.addWidget(self.close_button) grid1 = QGridLayout() grid2 = QGridLayout() #----------------------------------------- # setup self.radio_rectangular = QRadioButton('Rectangular') self.radio_cylindrical = QRadioButton('Cylindrical') self.radio_spherical = QRadioButton('Spherical') coord_type_layout = QHBoxLayout() coord_type_layout.addWidget(self.radio_rectangular) coord_type_layout.addWidget(self.radio_cylindrical) coord_type_layout.addWidget(self.radio_spherical) checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_region_all) checkboxs.addButton(self.checkbox_region_ids) vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_region_all) vbox1.addWidget(self.checkbox_region_ids) #vbox1.addLayout(checkboxs) #----------------------------------------- irow = 0 grid2.addWidget(self.name, irow, 0) grid2.addWidget(self.name_edit, irow, 1) irow += 1 #grid2.addWidget(self.name, irow, 0) grid2.addWidget(self.description_edit, irow, 1) irow += 1 grid2.addWidget(self.location_x, irow, 0) grid2.addWidget(self.location_x_edit, irow, 1) irow += 1 grid2.addWidget(self.location_y, irow, 0) grid2.addWidget(self.location_y_edit, irow, 1) irow += 1 grid2.addWidget(self.location_z, irow, 0) grid2.addWidget(self.location_z_edit, irow, 1) irow += 1 #| Name EngineInlet | #| RegionMode * RegionID | #| * All | #| | #| AllowedRegions: | #| Region ID 3 | #| | #| PickMode * All | #| Pick Mode x On/Off | #| Pick Angle 20 deg | #| | #| Revert | #| RenumberRegions | #| Close | grid2.addWidget(self.region_id, irow, 0) grid2.addWidget(self.region_id_edit, irow, 1) irow += 1 #grid2.addWidget(self.pick_mode, irow, 0) grid2.addWidget(self.checkbox_pick_mode, irow, 0) irow += 1 grid2.addWidget(self.pick_angle, irow, 0) grid2.addWidget(self.pick_angle_edit, irow, 1) irow += 1 #grid2.addWidget(self.pi, irow, 0) #grid2.addLayout(coord_type_layout, irow, 1) #irow += 1 #grid2.addWidget(self.location, irow, 0) #grid2.addLayout(location_layout, irow, 1) #irow += 1 #------------------------------------ vbox = QVBoxLayout() vbox.addLayout(grid1) #vbox.addLayout(vbox1) #vbox.addStretch() vbox.addWidget(self.table) vbox.addLayout(grid2) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(cancel_box) self.setLayout(vbox)
def __init__(self, parent, enable_replace=False): QWidget.__init__(self, parent) self.enable_replace = enable_replace self.editor = None self.is_code_editor = None glayout = QGridLayout() glayout.setContentsMargins(0, 0, 0, 0) self.setLayout(glayout) self.close_button = create_toolbutton(self, triggered=self.hide, icon=ima.icon('DialogCloseButton')) glayout.addWidget(self.close_button, 0, 0) # Find layout self.search_text = PatternComboBox(self, tip=_("Search string"), adjust_to_minimum=False) self.search_text.valid.connect( lambda state: self.find(changed=False, forward=True, rehighlight=False)) self.search_text.lineEdit().textEdited.connect( self.text_has_been_edited) self.previous_button = create_toolbutton(self, triggered=self.find_previous, icon=ima.icon('ArrowUp')) self.next_button = create_toolbutton(self, triggered=self.find_next, icon=ima.icon('ArrowDown')) self.next_button.clicked.connect(self.update_search_combo) self.previous_button.clicked.connect(self.update_search_combo) self.re_button = create_toolbutton(self, icon=ima.icon('advanced'), tip=_("Regular expression")) self.re_button.setCheckable(True) self.re_button.toggled.connect(lambda state: self.find()) self.case_button = create_toolbutton(self, icon=get_icon("upper_lower.png"), tip=_("Case Sensitive")) self.case_button.setCheckable(True) self.case_button.toggled.connect(lambda state: self.find()) self.words_button = create_toolbutton(self, icon=get_icon("whole_words.png"), tip=_("Whole words")) self.words_button.setCheckable(True) self.words_button.toggled.connect(lambda state: self.find()) self.highlight_button = create_toolbutton(self, icon=get_icon("highlight.png"), tip=_("Highlight matches")) self.highlight_button.setCheckable(True) self.highlight_button.toggled.connect(self.toggle_highlighting) hlayout = QHBoxLayout() self.widgets = [self.close_button, self.search_text, self.previous_button, self.next_button, self.re_button, self.case_button, self.words_button, self.highlight_button] for widget in self.widgets[1:]: hlayout.addWidget(widget) glayout.addLayout(hlayout, 0, 1) # Replace layout replace_with = QLabel(_("Replace with:")) self.replace_text = PatternComboBox(self, adjust_to_minimum=False, tip=_('Replace string')) self.replace_button = create_toolbutton(self, text=_('Replace/find'), icon=ima.icon('DialogApplyButton'), triggered=self.replace_find, text_beside_icon=True) self.replace_button.clicked.connect(self.update_replace_combo) self.replace_button.clicked.connect(self.update_search_combo) self.all_check = QCheckBox(_("Replace all")) self.replace_layout = QHBoxLayout() widgets = [replace_with, self.replace_text, self.replace_button, self.all_check] for widget in widgets: self.replace_layout.addWidget(widget) glayout.addLayout(self.replace_layout, 1, 1) self.widgets.extend(widgets) self.replace_widgets = widgets self.hide_replace() self.search_text.setTabOrder(self.search_text, self.replace_text) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.shortcuts = self.create_shortcuts(parent) self.highlight_timer = QTimer(self) self.highlight_timer.setSingleShot(True) self.highlight_timer.setInterval(1000) self.highlight_timer.timeout.connect(self.highlight_matches)
def __init__(self, parent, text): QWidget.__init__(self, parent) self.text_editor = QTextEdit(self) self.text_editor.setText(text) self.text_editor.setReadOnly(True) # Type frame type_layout = QHBoxLayout() type_label = QLabel(_("Import as")) type_layout.addWidget(type_label) data_btn = QRadioButton(_("data")) data_btn.setChecked(True) self._as_data = True type_layout.addWidget(data_btn) code_btn = QRadioButton(_("code")) self._as_code = False type_layout.addWidget(code_btn) txt_btn = QRadioButton(_("text")) type_layout.addWidget(txt_btn) h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) type_layout.addItem(h_spacer) type_frame = QFrame() type_frame.setLayout(type_layout) # Opts frame grid_layout = QGridLayout() grid_layout.setSpacing(0) col_label = QLabel(_("Column separator:")) grid_layout.addWidget(col_label, 0, 0) col_w = QWidget() col_btn_layout = QHBoxLayout() self.tab_btn = QRadioButton(_("Tab")) self.tab_btn.setChecked(False) col_btn_layout.addWidget(self.tab_btn) self.ws_btn = QRadioButton(_("Whitespace")) self.ws_btn.setChecked(False) col_btn_layout.addWidget(self.ws_btn) other_btn_col = QRadioButton(_("other")) other_btn_col.setChecked(True) col_btn_layout.addWidget(other_btn_col) col_w.setLayout(col_btn_layout) grid_layout.addWidget(col_w, 0, 1) self.line_edt = QLineEdit(",") self.line_edt.setMaximumWidth(30) self.line_edt.setEnabled(True) other_btn_col.toggled.connect(self.line_edt.setEnabled) grid_layout.addWidget(self.line_edt, 0, 2) row_label = QLabel(_("Row separator:")) grid_layout.addWidget(row_label, 1, 0) row_w = QWidget() row_btn_layout = QHBoxLayout() self.eol_btn = QRadioButton(_("EOL")) self.eol_btn.setChecked(True) row_btn_layout.addWidget(self.eol_btn) other_btn_row = QRadioButton(_("other")) row_btn_layout.addWidget(other_btn_row) row_w.setLayout(row_btn_layout) grid_layout.addWidget(row_w, 1, 1) self.line_edt_row = QLineEdit(";") self.line_edt_row.setMaximumWidth(30) self.line_edt_row.setEnabled(False) other_btn_row.toggled.connect(self.line_edt_row.setEnabled) grid_layout.addWidget(self.line_edt_row, 1, 2) grid_layout.setRowMinimumHeight(2, 15) other_group = QGroupBox(_("Additional options")) other_layout = QGridLayout() other_group.setLayout(other_layout) skiprows_label = QLabel(_("Skip rows:")) other_layout.addWidget(skiprows_label, 0, 0) self.skiprows_edt = QLineEdit('0') self.skiprows_edt.setMaximumWidth(30) intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), self.skiprows_edt) self.skiprows_edt.setValidator(intvalid) other_layout.addWidget(self.skiprows_edt, 0, 1) other_layout.setColumnMinimumWidth(2, 5) comments_label = QLabel(_("Comments:")) other_layout.addWidget(comments_label, 0, 3) self.comments_edt = QLineEdit('#') self.comments_edt.setMaximumWidth(30) other_layout.addWidget(self.comments_edt, 0, 4) self.trnsp_box = QCheckBox(_("Transpose")) #self.trnsp_box.setEnabled(False) other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) grid_layout.addWidget(other_group, 3, 0, 2, 0) opts_frame = QFrame() opts_frame.setLayout(grid_layout) data_btn.toggled.connect(opts_frame.setEnabled) data_btn.toggled.connect(self.set_as_data) code_btn.toggled.connect(self.set_as_code) # self.connect(txt_btn, SIGNAL("toggled(bool)"), # self, SLOT("is_text(bool)")) # Final layout layout = QVBoxLayout() layout.addWidget(type_frame) layout.addWidget(self.text_editor) layout.addWidget(opts_frame) self.setLayout(layout)
class ArrayEditor(QDialog): """Array Editor Dialog""" def __init__(self, parent=None): QDialog.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.data = None self.arraywidget = None self.stack = None self.layout = None # Values for 3d array editor self.dim_indexes = [{}, {}, {}] self.last_dim = 0 # Adjust this for changing the startup dimension def setup_and_check(self, data, title='', readonly=False, xlabels=None, ylabels=None): """ Setup ArrayEditor: return False if data is not supported, True otherwise """ self.data = data #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 def current_widget_changed(self, index): self.arraywidget = self.stack.widget(index) def change_active_widget(self, index): """ This is implemented for handling negative values in index for 3d arrays, to give the same behavior as slicing """ string_index = [':'] * 3 string_index[self.last_dim] = '<font color=red>%i</font>' self.slicing_label.setText( (r"Slicing: [" + ", ".join(string_index) + "]") % index) if index < 0: data_index = self.data.shape[self.last_dim] + index else: data_index = index slice_index = [slice(None)] * 3 slice_index[self.last_dim] = data_index stack_index = self.dim_indexes[self.last_dim].get(data_index) if stack_index == None: stack_index = self.stack.count() try: self.stack.addWidget( ArrayEditorWidget(self, self.data[slice_index])) except IndexError: # Handle arrays of size 0 in one axis self.stack.addWidget(ArrayEditorWidget(self, self.data)) self.dim_indexes[self.last_dim][data_index] = stack_index self.stack.update() self.stack.setCurrentIndex(stack_index) def current_dim_changed(self, index): """ This change the active axis the array editor is plotting over in 3D """ self.last_dim = index string_size = ['%i'] * 3 string_size[index] = '<font color=red>%i</font>' self.shape_label.setText( ('Shape: (' + ', '.join(string_size) + ') ') % self.data.shape) if self.index_spin.value() != 0: self.index_spin.setValue(0) else: # this is done since if the value is currently 0 it does not emit # currentIndexChanged(int) self.change_active_widget(0) self.index_spin.setRange(-self.data.shape[index], self.data.shape[index] - 1) @Slot() def accept(self): """Reimplement Qt method""" for index in range(self.stack.count()): self.stack.widget(index).accept_changes() QDialog.accept(self) def get_value(self): """Return modified array -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute return self.data def error(self, message): """An error occured, closing the dialog box""" QMessageBox.critical(self, _("Array editor"), message) self.setAttribute(Qt.WA_DeleteOnClose) self.reject() @Slot() def reject(self): """Reimplement Qt method""" if self.arraywidget is not None: for index in range(self.stack.count()): self.stack.widget(index).reject_changes() QDialog.reject(self)
def __init__(self, presenter, dims_info, can_normalise, parent=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots = False self.can_normalise = can_normalise self.nonortho_tr = None self.ws_type = dims_info[0]['type'] # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect( self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self.ws_type, self) self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") if self.ws_type == 'MDE': self.colorbar_layout.addWidget(self.image_info_widget, alignment=Qt.AlignCenter) self.colorbar_layout.addWidget(self.track_cursor) else: self.dimensions_layout.setHorizontalSpacing(10) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) self.track_cursor.setChecked(True) # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.axx, self.axy = None, None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('motion_notify_event', self.mouse_move) self.canvas.mpl_connect('axes_leave_event', self.mouse_outside_image) self.canvas.mpl_connect('button_press_event', self.mouse_click) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) self.colorbar = ColorbarWidget(self) self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.colorbarChanged.connect(self.update_line_plot_limits) # make width larger to fit image readout table if self.ws_type == 'MDE': self.colorbar.setMaximumWidth(155) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar( self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.plotOptionsChanged.connect( self.colorbar.mappable_changed) self.mpl_toolbar.nonOrthogonalClicked.connect( self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 2) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 2, 1) layout.setRowStretch(2, 1)
class SliceViewerDataView(QWidget): """The view for the data portion of the sliceviewer""" def __init__(self, presenter, dims_info, can_normalise, parent=None): super().__init__(parent) self.presenter = presenter self.image = None self.line_plots = False self.can_normalise = can_normalise self.nonortho_tr = None self.ws_type = dims_info[0]['type'] # Dimension widget self.dimensions_layout = QGridLayout() self.dimensions = DimensionWidget(dims_info, parent=self) self.dimensions.dimensionsChanged.connect( self.presenter.dimensions_changed) self.dimensions.valueChanged.connect(self.presenter.slicepoint_changed) self.dimensions_layout.addWidget(self.dimensions, 1, 0, 1, 1) self.colorbar_layout = QVBoxLayout() self.colorbar_layout.setContentsMargins(0, 0, 0, 0) self.colorbar_layout.setSpacing(0) self.image_info_widget = ImageInfoWidget(self.ws_type, self) self.track_cursor = QCheckBox("Track Cursor", self) self.track_cursor.setToolTip( "Update the image readout table when the cursor is over the plot. " "If unticked the table will update only when the plot is clicked") if self.ws_type == 'MDE': self.colorbar_layout.addWidget(self.image_info_widget, alignment=Qt.AlignCenter) self.colorbar_layout.addWidget(self.track_cursor) else: self.dimensions_layout.setHorizontalSpacing(10) self.dimensions_layout.addWidget(self.track_cursor, 0, 1, Qt.AlignRight) self.dimensions_layout.addWidget(self.image_info_widget, 1, 1) self.track_cursor.setChecked(True) # normalization options if can_normalise: self.norm_label = QLabel("Normalization") self.colorbar_layout.addWidget(self.norm_label) self.norm_opts = QComboBox() self.norm_opts.addItems(["None", "By bin width"]) self.norm_opts.setToolTip("Normalization options") self.colorbar_layout.addWidget(self.norm_opts) # MPL figure + colorbar self.fig = Figure() self.ax = None self.axx, self.axy = None, None self.image = None self._grid_on = False self.fig.set_facecolor(self.palette().window().color().getRgbF()) self.canvas = SliceViewerCanvas(self.fig) self.canvas.mpl_connect('motion_notify_event', self.mouse_move) self.canvas.mpl_connect('axes_leave_event', self.mouse_outside_image) self.canvas.mpl_connect('button_press_event', self.mouse_click) self.canvas.mpl_connect('button_release_event', self.mouse_release) self.colorbar_label = QLabel("Colormap") self.colorbar_layout.addWidget(self.colorbar_label) self.colorbar = ColorbarWidget(self) self.colorbar_layout.addWidget(self.colorbar) self.colorbar.colorbarChanged.connect(self.update_data_clim) self.colorbar.colorbarChanged.connect(self.update_line_plot_limits) # make width larger to fit image readout table if self.ws_type == 'MDE': self.colorbar.setMaximumWidth(155) # MPL toolbar self.toolbar_layout = QHBoxLayout() self.mpl_toolbar = SliceViewerNavigationToolbar( self.canvas, self, False) self.mpl_toolbar.gridClicked.connect(self.toggle_grid) self.mpl_toolbar.linePlotsClicked.connect(self.on_line_plots_toggle) self.mpl_toolbar.homeClicked.connect(self.on_home_clicked) self.mpl_toolbar.plotOptionsChanged.connect( self.colorbar.mappable_changed) self.mpl_toolbar.nonOrthogonalClicked.connect( self.on_non_orthogonal_axes_toggle) self.mpl_toolbar.zoomPanFinished.connect(self.on_data_limits_changed) self.toolbar_layout.addWidget(self.mpl_toolbar) # layout layout = QGridLayout(self) layout.setSpacing(1) layout.addLayout(self.dimensions_layout, 0, 0, 1, 2) layout.addLayout(self.toolbar_layout, 1, 0, 1, 2) layout.addWidget(self.canvas, 2, 0, 1, 1) layout.addLayout(self.colorbar_layout, 1, 1, 2, 1) layout.setRowStretch(2, 1) @property def grid_on(self): return self._grid_on @property def nonorthogonal_mode(self): return self.nonortho_tr is not None def create_axes_orthogonal(self, redraw_on_zoom=False): """Create a standard set of orthogonal axes :param redraw_on_zoom: If True then when scroll zooming the canvas is redrawn immediately """ self.clear_figure() self.nonortho_tr = None self.ax = self.fig.add_subplot(111, projection='mantid') self.enable_zoom_on_mouse_scroll(redraw_on_zoom) if self.grid_on: self.ax.grid(self.grid_on) if self.line_plots: self.add_line_plots() self.plot_MDH = self.plot_MDH_orthogonal self.canvas.draw_idle() def create_axes_nonorthogonal(self, transform): self.clear_figure() self.set_nonorthogonal_transform(transform) self.ax = CurveLinearSubPlot(self.fig, 1, 1, 1, grid_helper=GridHelperCurveLinear( (self.nonortho_tr, transform.inv_tr))) # don't redraw on zoom as the data is rebinned and has to be redrawn again anyway self.enable_zoom_on_mouse_scroll(redraw=False) self.set_grid_on() self.fig.add_subplot(self.ax) self.plot_MDH = self.plot_MDH_nonorthogonal self.canvas.draw_idle() def enable_zoom_on_mouse_scroll(self, redraw): """Enable zoom on scroll the mouse wheel for the created axes :param redraw: Pass through to redraw option in enable_zoom_on_scroll """ self.canvas.enable_zoom_on_scroll(self.ax, redraw=redraw, toolbar=self.mpl_toolbar, callback=self.on_data_limits_changed) def add_line_plots(self): """Assuming line plots are currently disabled, enable them on the current figure The image axes must have been created first. """ if self.line_plots: return self.line_plots = True image_axes = self.ax if image_axes is None: return # Create a new GridSpec and reposition the existing image Axes gs = gridspec.GridSpec(2, 2, width_ratios=[1, 4], height_ratios=[4, 1], wspace=0.0, hspace=0.0) image_axes.set_position(gs[1].get_position(self.fig)) set_artist_property(image_axes.get_xticklabels(), visible=False) set_artist_property(image_axes.get_yticklabels(), visible=False) self.axx = self.fig.add_subplot(gs[3], sharex=image_axes) self.axx.yaxis.tick_right() self.axy = self.fig.add_subplot(gs[0], sharey=image_axes) self.axy.xaxis.tick_top() self.update_line_plot_labels() self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def remove_line_plots(self): """Assuming line plots are currently enabled, remove them from the current figure """ if not self.line_plots: return self.line_plots = False image_axes = self.ax if image_axes is None: return self.delete_line_plot_lines() all_axes = self.fig.axes # The order is defined by the order of the add_subplot calls so we always want to remove # the last two Axes. Do it backwards to cope with the container size change all_axes[2].remove() all_axes[1].remove() gs = gridspec.GridSpec(1, 1) image_axes.set_position(gs[0].get_position(self.fig)) image_axes.xaxis.tick_bottom() image_axes.yaxis.tick_left() self.axx, self.axy = None, None self.mpl_toolbar.update() # sync list of axes in navstack self.canvas.draw_idle() def plot_MDH_orthogonal(self, ws, **kwargs): """ clears the plot and creates a new one using a MDHistoWorkspace """ self.clear_image() self.image = self.ax.imshow(ws, origin='lower', aspect='auto', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # ensure the axes data limits are updated to match the # image. For example if the axes were zoomed and the # swap dimensions was clicked we need to restore the # appropriate extents to see the image in the correct place extent = self.image.get_extent() self.ax.set_xlim(extent[0], extent[1]) self.ax.set_ylim(extent[2], extent[3]) self.draw_plot() def plot_MDH_nonorthogonal(self, ws, **kwargs): self.clear_image() self.image = pcolormesh_nonorthogonal( self.ax, ws, self.nonortho_tr, transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), **kwargs) # swapping dimensions in nonorthogonal mode currently resets back to the # full data limits as the whole axes has been recreated so we don't have # access to the original limits # pcolormesh clears any grid that was previously visible if self.grid_on: self.ax.grid(self.grid_on) self.draw_plot() def plot_matrix(self, ws, **kwargs): """ clears the plot and creates a new one using a MatrixWorkspace keeping the axes limits that have already been set """ # ensure view is correct if zoomed in while swapping dimensions # compute required extent and just have resampling imshow deal with it old_extent = None if self.image is not None: old_extent = self.image.get_extent() if self.image.transpose != self.dimensions.transpose: e1, e2, e3, e4 = old_extent old_extent = e3, e4, e1, e2 self.clear_image() self.image = imshow_sampling(self.ax, ws, origin='lower', aspect='auto', interpolation='none', transpose=self.dimensions.transpose, norm=self.colorbar.get_norm(), extent=old_extent, **kwargs) self.draw_plot() def clear_image(self): """Removes any image from the axes""" if self.image is not None: if self.line_plots: self.delete_line_plot_lines() self.image_info_widget.updateTable(DBLMAX, DBLMAX, DBLMAX) self.image.remove() self.image = None def clear_figure(self): """Removes everything from the figure""" if self.line_plots: self.delete_line_plot_lines() self.axx, self.axy = None, None self.image = None self.canvas.disable_zoom_on_scroll() self.fig.clf() self.ax = None def draw_plot(self): self.ax.set_title('') self.colorbar.set_mappable(self.image) self.colorbar.update_clim() self.mpl_toolbar.update() # clear nav stack self.delete_line_plot_lines() self.update_line_plot_labels() self.canvas.draw_idle() def select_zoom(self): """Select the zoom control on the toolbar""" self.mpl_toolbar.zoom() def update_plot_data(self, data): """ This just updates the plot data without creating a new plot. The extents can change if the data has been rebinned """ if self.nonortho_tr: self.image.set_array(data.T.ravel()) else: self.image.set_data(data.T) self.colorbar.update_clim() def on_home_clicked(self): """Reset the view to encompass all of the data""" self.presenter.show_all_data_requested() def on_line_plots_toggle(self, state): self.presenter.line_plots(state) def on_non_orthogonal_axes_toggle(self, state): """ Switch state of the non-orthognal axes on/off """ self.presenter.nonorthogonal_axes(state) def on_data_limits_changed(self): """ React to when the data limits have changed """ self.presenter.data_limits_changed() def enable_lineplots_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, True) def disable_lineplots_button(self): """ Disabled line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.LINEPLOTS, False) def enable_peaks_button(self): """ Enables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, True) def disable_peaks_button(self): """ Disables line plots functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.OVERLAYPEAKS, False) def enable_nonorthogonal_axes_button(self): """ Enables access to non-orthogonal axes functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.NONORTHOGONAL_AXES, True) def disable_nonorthogonal_axes_button(self): """ Disables non-orthorognal axes functionality """ self.mpl_toolbar.set_action_enabled(ToolItemText.NONORTHOGONAL_AXES, state=False) def delete_line_plot_lines(self): try: # clear old plots try: self.xfig.remove() self.yfig.remove() except ValueError: pass del self.xfig del self.yfig except AttributeError: pass def get_axes_limits(self): """ Return the limits of the image axes or None if no image yet exists """ if self.image is None: return None else: return self.ax.get_xlim(), self.ax.get_ylim() def set_axes_limits(self, xlim, ylim): """ Set the view limits on the image axes to the given extents :param xlim: 2-tuple of (xmin, xmax) :param ylim: 2-tuple of (ymin, ymax) """ self.ax.set_xlim(xlim) self.ax.set_ylim(ylim) def set_grid_on(self): """ If not visible sets the grid visibility """ if not self._grid_on: self._grid_on = True self.mpl_toolbar.set_action_checked(ToolItemText.GRID, state=self._grid_on) def set_nonorthogonal_transform(self, transform): """ Set the transform for nonorthogonal axes mode :param transform: An object with a tr method to transform from nonorthognal coordinates to display coordinates """ self.nonortho_tr = transform.tr def toggle_grid(self, state): """ Toggle the visibility of the grid on the axes """ self._grid_on = state self.ax.grid(self._grid_on) self.canvas.draw_idle() def mouse_move(self, event): if event.inaxes == self.ax: signal = self.update_image_data(event.xdata, event.ydata, self.line_plots) if self.track_cursor.checkState() == Qt.Checked: self.update_image_table_widget(event.xdata, event.ydata, signal) def mouse_outside_image(self, _): """ Indicates that the mouse have moved outside of an axes. We clear the line plots so that it is not confusing what they mean. """ if self.line_plots: self.delete_line_plot_lines() self.canvas.draw_idle() def mouse_click(self, event): if self.track_cursor.checkState() == Qt.Unchecked \ and event.inaxes == self.ax and event.button == 1: signal = self.update_image_data(event.xdata, event.ydata) self.update_image_table_widget(event.xdata, event.ydata, signal) def mouse_release(self, event): if event.button == 3 and event.inaxes == self.ax: self.on_home_clicked() def update_image_table_widget(self, xdata, ydata, signal): if signal is not None: if self.dimensions.transpose and self.ws_type == "MATRIX": self.image_info_widget.updateTable(ydata, xdata, signal) else: self.image_info_widget.updateTable(xdata, ydata, signal) def plot_x_line(self, x, y): try: self.xfig.set_data(x, y) except (AttributeError, IndexError): self.axx.clear() self.xfig = self.axx.plot(x, y, scalex=False)[0] self.update_line_plot_labels() self.update_line_plot_limits() self.canvas.draw_idle() def plot_y_line(self, x, y): try: self.yfig.set_data(y, x) except (AttributeError, IndexError): self.axy.clear() self.yfig = self.axy.plot(y, x, scaley=False)[0] self.update_line_plot_labels() self.update_line_plot_limits() self.canvas.draw_idle() def update_data_clim(self): self.image.set_clim(self.colorbar.colorbar.mappable.get_clim()) self.canvas.draw_idle() def update_line_plot_limits(self): try: # set line plot intensity axes to match colorbar limits self.axx.set_ylim(self.colorbar.cmin_value, self.colorbar.cmax_value) self.axy.set_xlim(self.colorbar.cmin_value, self.colorbar.cmax_value) except AttributeError: pass def update_line_plot_labels(self): try: # ensure plot labels are in sync with main axes self.axx.set_xlabel(self.ax.get_xlabel()) self.axy.set_ylabel(self.ax.get_ylabel()) except AttributeError: pass def update_image_data(self, x, y, update_line_plot=False): xmin, xmax, ymin, ymax = self.image.get_extent() arr = self.image.get_array() data_extent = Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = Bbox([[0, 0], arr.shape[:2]]) trans = BboxTransform(boxin=data_extent, boxout=array_extent) point = trans.transform_point([y, x]) if any(np.isnan(point)): return i, j = point.astype(int) if update_line_plot: if 0 <= i < arr.shape[0]: self.plot_x_line(np.linspace(xmin, xmax, arr.shape[1]), arr[i, :]) if 0 <= j < arr.shape[1]: self.plot_y_line(np.linspace(ymin, ymax, arr.shape[0]), arr[:, j]) # Clip the coordinates at array bounds if not (0 <= i < arr.shape[0]) or not (0 <= j < arr.shape[1]): return None else: return arr[i, j] def set_normalization(self, ws, **kwargs): normalize_by_bin_width, _ = get_normalize_by_bin_width( ws, self.ax, **kwargs) is_normalized = normalize_by_bin_width or ws.isDistribution() if is_normalized: self.presenter.normalization = mantid.api.MDNormalization.VolumeNormalization self.norm_opts.setCurrentIndex(1) else: self.presenter.normalization = mantid.api.MDNormalization.NoNormalization self.norm_opts.setCurrentIndex(0)
def create_layout(self): cancel_box = QHBoxLayout() cancel_box.addWidget(self.close_button) grid1 = QGridLayout() grid2 = QGridLayout() #----------------------------------------- # setup self.radio_rectangular = QRadioButton('Rectangular') self.radio_cylindrical = QRadioButton('Cylindrical') self.radio_spherical = QRadioButton('Spherical') coord_type_layout = QHBoxLayout() coord_type_layout.addWidget(self.radio_rectangular) coord_type_layout.addWidget(self.radio_cylindrical) coord_type_layout.addWidget(self.radio_spherical) location_layout = QHBoxLayout() location_layout.addWidget(self.location_x_edit) location_layout.addWidget(self.location_y_edit) location_layout.addWidget(self.location_z_edit) checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_show) vbox1.addWidget(self.checkbox_hide) #vbox1.addLayout(checkboxs) #----------------------------------------- irow = 0 grid1.addWidget(self.all_nodes_header, irow, 0) irow += 1 grid1.addWidget(self.color, irow, 0) grid1.addWidget(self.color_edit, irow, 1) irow += 1 grid1.addWidget(self.opacity, irow, 0) grid1.addWidget(self.opacity_edit, irow, 1) irow += 1 grid1.addWidget(self.point_size, irow, 0) grid1.addWidget(self.point_size_edit, irow, 1) irow += 1 #----------------------------------------- irow = 0 grid2.addWidget(self.nodes_header, irow, 0) irow += 1 grid2.addWidget(self.name, irow, 0) grid2.addWidget(self.name_edit, irow, 1) irow += 1 grid2.addWidget(self.description, irow, 0) grid2.addWidget(self.description_edit, irow, 1) irow += 1 #| All Nodes: | #| Color red | #| PointSize 3 | #| Opacity 0.3 | #| Show/Hide | #| | #| Name LEwingTip | #| Location X Y Z | #| Coord 0 | #| CoordType R, C, S | #| | #| Previous Next | grid2.addWidget(self.coord, irow, 0) grid2.addWidget(self.coord_edit, irow, 1) irow += 1 grid2.addWidget(self.coord_type, irow, 0) grid2.addLayout(coord_type_layout, irow, 1) irow += 1 grid2.addWidget(self.location, irow, 0) grid2.addLayout(location_layout, irow, 1) irow += 1 #------------------------------------ vbox = QVBoxLayout() vbox.addLayout(grid1) vbox.addLayout(vbox1) vbox.addStretch() vbox.addWidget(self.table) vbox.addLayout(grid2) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(cancel_box) self.setLayout(vbox)
def create_layout(self): grid = QGridLayout() grid.addWidget(self.font_size, 0, 0) grid.addWidget(self.font_size_edit, 0, 1) grid.addWidget(self.font_size_button, 0, 2) grid.addWidget(self.background_color, 1, 0) grid.addWidget(self.background_color_edit, 1, 1) grid.addWidget(self.text_color, 2, 0) grid.addWidget(self.text_color_edit, 2, 1) grid.addWidget(self.annotation_color, 3, 0) grid.addWidget(self.annotation_color_edit, 3, 1) grid.addWidget(self.annotation_size, 4, 0) grid.addWidget(self.annotation_size_edit, 4, 1) if USE_ANNOTATION_INT: grid.addWidget(self.annotation_size_button, 4, 2) grid.addWidget(self.picker_size, 5, 0) grid.addWidget(self.picker_size_edit, 5, 1) #grid.addWidget(self.clipping_min, 6, 0) #grid.addWidget(self.clipping_min_edit, 6, 1) #grid.addWidget(self.clipping_min_button, 6, 2) #grid.addWidget(self.clipping_max, 7, 0) #grid.addWidget(self.clipping_max_edit, 7, 1) #grid.addWidget(self.clipping_max_button, 7, 2) ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.ok_button) ok_cancel_box.addWidget(self.cancel_button) vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox)
class RawPlots(QWidget): def __init__(self, plot_config, *args, **kwargs): super(RawPlots, self).__init__(*args, **kwargs) self.plot_config = plot_config self.pen_color = QColor(pen_colors[self.plot_config['color_iterator']]) # Create and add GraphicsLayoutWidget self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) # create GLW for the depth plot depth_sett = { **DEPTH, 'pen_color': pen_colors[self.plot_config['color_iterator']] } self.depth_plot = BasePlotWidget(depth_sett) self.layout.addWidget(self.depth_plot, 0, 0, NPLOTSRAW, 1) # extra depth_plot settings self.depth_plot.plot.invertY() self.fill_bar = self.depth_plot.plot.plot( [-5, 5], [depth_sett['y_range'][0], depth_sett['y_range'][0]], fillLevel=depth_sett['y_range'][0], brush=(255, 255, 255, 100), movable=True) self.fill_bar_position = [] self.depth_bar = pg.InfiniteLine( angle=0, # bounds=[depth_sett['y_range'][0], # depth_sett['y_range'][1]], pos=depth_sett['y_range'][0], movable=True, pen='w') self.depth_bar.sigDragged.connect(self.depth_bar_drag) self.depth_plot.plot.addItem(self.depth_bar) self.depth_text = pg.TextItem(text='', color='w', fill=(0, 0, 0, 0)) self.depth_text.setAnchor((.5, 1.5)) self.depth_plot.plot.addItem(self.depth_text) self.depth_text.setZValue(1) # Prepare plot data self.data_layout = QVBoxLayout() self.layout.addLayout(self.data_layout, 0, 1, NPLOTSRAW, 5) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 5) self.data_figures = [] self.depth_pdi = {} # all depth data self.data_texts = [] raw_sett = { **DEFAULTPLOT, 'x_axis': False, # Is Visible 'y_axis': False, 'x_range': self.plot_config['x_range'], # None for auto-scale, list otherwise 'y_range': [-self.plot_config['y_range'], self.plot_config['y_range']], 'auto_scale': False, 'interactive': False, 'marker_line': None, 'error_bars': False } for i in range(NPLOTSRAW): tmp = BasePlotWidget(raw_sett) self.data_layout.addWidget(tmp) self.data_figures.append(tmp) tmp_txt = pg.TextItem(text="", angle=90, color='w') tmp_txt.setX(0) tmp_txt.setY(0) tmp_txt.setAnchor((0.5, 1)) tmp_txt.setZValue(1) tmp.plot.addItem(tmp_txt) self.data_texts.append(tmp_txt) def depth_bar_drag(self): # hide text field self.depth_plot.clear_text_line() # set fill area to be the first 8 depths found above the line all_depths = np.sort([x for x in self.depth_pdi.keys()]) if len(all_depths) > 0: curr_value = self.depth_bar.value() diffs = abs(all_depths - curr_value) # lock to closest depth value idx, = np.where(diffs == min(diffs))[0] self.depth_bar.setValue(all_depths[idx]) self.plot_depth_values() # Update plot is only for new datum def update_plot(self, all_data): # new_depth = -20 new_depth = DEPTHRANGE[0] if all_data is not None: # all_data is a dict {datum_id: [depth, np array of data]} for _, depth_data in all_data.items(): # append data if depth_data[0] not in self.depth_pdi.keys(): # instead of saving Plot Data Item, save the data self.depth_pdi[depth_data[0]] = depth_data[1] else: # overwritten depth data if not all(depth_data[1] == self.depth_pdi[depth_data[0]]): self.depth_pdi[depth_data[0]] = depth_data[1] # plot depth symbol_brush = self.pen_color if depth_data[2] else None self.depth_plot.plot.plot(x=[0], y=[depth_data[0]], symbol='o', symbolBrush=symbol_brush, symbolPen=self.pen_color, symbolSize=6) if new_depth == DEPTHRANGE[0] and new_depth > depth_data[0]: new_depth = depth_data[0] else: new_depth = max(depth_data[0], new_depth) # move draggable bar to new depth self.depth_bar.setValue(new_depth) self.plot_depth_values() def plot_depth_values(self): # get current index of selected depth all_depths = np.sort([x for x in self.depth_pdi.keys()]) curr_value = self.depth_bar.value() idx, = np.where(all_depths == curr_value)[0] # plot last 8 depth data # make fill bar around 8 depths above the currently selected one top_idx = max(0, idx - (NPLOTSRAW - 1)) self.fill_bar.setData(x=[-5, 5], y=[all_depths[idx], all_depths[idx]], fillLevel=all_depths[top_idx]) plot_idx = 1 while plot_idx <= NPLOTSRAW: if idx >= top_idx: to_plot = self.depth_pdi[all_depths[idx]] # data if len(self.data_figures[-plot_idx].plot.dataItems) == 0: self.data_figures[-plot_idx].plot.addItem( pg.PlotDataItem(to_plot, pen=self.pen_color, autoDownsample=True)) else: self.data_figures[-plot_idx].plot.dataItems[0].setData( to_plot) self.data_texts[-plot_idx].setText("{0:.3f}".format( all_depths[idx])) else: self.data_texts[-plot_idx].setText("") if len(self.data_figures[-plot_idx].plot.dataItems) > 0: self.data_figures[-plot_idx].plot.dataItems[0].setData([0]) self.data_figures[-plot_idx].plot.setYRange( -self.plot_config['y_range'], self.plot_config['y_range']) idx -= 1 plot_idx += 1 def clear_plot(self): self.depth_pdi = {} # clear depth plot # self.depth_bar.setValue(-20) self.depth_bar.setValue(DEPTHRANGE[0]) self.fill_bar.setData(x=[-5, 5], y=[DEPTHRANGE[0], DEPTHRANGE[0]], fillLevel=DEPTHRANGE[0]) for it in self.depth_plot.plot.listDataItems(): if it.opts['symbol'] == 'o': self.depth_plot.plot.removeItem(it) # clear data plots [ x.plot.removeItem(x.plot.dataItems[0]) for x in self.data_figures if x.plot.dataItems ] for txt in self.data_texts: txt.setText("")
class ArrayEditor(QDialog): """Array Editor Dialog""" def __init__(self, parent=None): QDialog.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.data = None self.arraywidget = None self.stack = None self.layout = None # Values for 3d array editor self.dim_indexes = [{}, {}, {}] self.last_dim = 0 # Adjust this for changing the startup dimension def setup_and_check(self, data, title='', readonly=False, xlabels=None, ylabels=None): """ Setup ArrayEditor: return False if data is not supported, True otherwise """ self.data = data is_record_array = data.dtype.names is not None is_masked_array = isinstance(data, np.ma.MaskedArray) if data.size == 0: self.error(_("Array is empty")) return False if data.ndim > 3: self.error(_("Arrays with more than 3 dimensions are not supported")) return False if xlabels is not None and len(xlabels) != self.data.shape[1]: self.error(_("The 'xlabels' argument length do no match array " "column number")) return False if ylabels is not None and len(ylabels) != self.data.shape[0]: self.error(_("The 'ylabels' argument length do no match array row " "number")) return False if not is_record_array: dtn = data.dtype.name if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \ and not dtn.startswith('unicode'): arr = _("%s arrays") % data.dtype.name self.error(_("%s are currently not supported") % arr) return False self.layout = QGridLayout() self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - " + _("NumPy array") else: title = _("Array editor") if readonly: title += ' (' + _('read only') + ')' self.setWindowTitle(title) self.resize(600, 500) # Stack widget self.stack = QStackedWidget(self) if is_record_array: for name in data.dtype.names: self.stack.addWidget(ArrayEditorWidget(self, data[name], readonly, xlabels, ylabels)) elif is_masked_array: self.stack.addWidget(ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly, xlabels, ylabels)) self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly, xlabels, ylabels)) elif data.ndim == 3: pass else: self.stack.addWidget(ArrayEditorWidget(self, data, readonly, xlabels, ylabels)) self.arraywidget = self.stack.currentWidget() self.stack.currentChanged.connect(self.current_widget_changed) self.layout.addWidget(self.stack, 1, 0) # Buttons configuration btn_layout = QHBoxLayout() if is_record_array or is_masked_array or data.ndim == 3: if is_record_array: btn_layout.addWidget(QLabel(_("Record array fields:"))) names = [] for name in data.dtype.names: field = data.dtype.fields[name] text = name if len(field) >= 3: title = field[2] if not is_text_string(title): title = repr(title) text += ' - '+title names.append(text) else: names = [_('Masked data'), _('Data'), _('Mask')] if data.ndim == 3: # QSpinBox self.index_spin = QSpinBox(self, keyboardTracking=False) self.index_spin.valueChanged.connect(self.change_active_widget) # QComboBox names = [str(i) for i in range(3)] ra_combo = QComboBox(self) ra_combo.addItems(names) ra_combo.currentIndexChanged.connect(self.current_dim_changed) # Adding the widgets to layout label = QLabel(_("Axis:")) btn_layout.addWidget(label) btn_layout.addWidget(ra_combo) self.shape_label = QLabel() btn_layout.addWidget(self.shape_label) label = QLabel(_("Index:")) btn_layout.addWidget(label) btn_layout.addWidget(self.index_spin) self.slicing_label = QLabel() btn_layout.addWidget(self.slicing_label) # set the widget to display when launched self.current_dim_changed(self.last_dim) else: ra_combo = QComboBox(self) ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex) ra_combo.addItems(names) btn_layout.addWidget(ra_combo) if is_masked_array: label = QLabel(_("<u>Warning</u>: changes are applied separately")) label.setToolTip(_("For performance reasons, changes applied "\ "to masked array won't be reflected in "\ "array's data (and vice-versa).")) btn_layout.addWidget(label) btn_layout.addStretch() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) btn_layout.addWidget(bbox) self.layout.addLayout(btn_layout, 2, 0) self.setMinimumSize(400, 300) # Make the dialog act as a window self.setWindowFlags(Qt.Window) return True def current_widget_changed(self, index): self.arraywidget = self.stack.widget(index) def change_active_widget(self, index): """ This is implemented for handling negative values in index for 3d arrays, to give the same behavior as slicing """ string_index = [':']*3 string_index[self.last_dim] = '<font color=red>%i</font>' self.slicing_label.setText((r"Slicing: [" + ", ".join(string_index) + "]") % index) if index < 0: data_index = self.data.shape[self.last_dim] + index else: data_index = index slice_index = [slice(None)]*3 slice_index[self.last_dim] = data_index stack_index = self.dim_indexes[self.last_dim].get(data_index) if stack_index == None: stack_index = self.stack.count() self.stack.addWidget(ArrayEditorWidget(self, self.data[slice_index])) self.dim_indexes[self.last_dim][data_index] = stack_index self.stack.update() self.stack.setCurrentIndex(stack_index) def current_dim_changed(self, index): """ This change the active axis the array editor is plotting over in 3D """ self.last_dim = index string_size = ['%i']*3 string_size[index] = '<font color=red>%i</font>' self.shape_label.setText(('Shape: (' + ', '.join(string_size) + ') ') % self.data.shape) if self.index_spin.value() != 0: self.index_spin.setValue(0) else: # this is done since if the value is currently 0 it does not emit # currentIndexChanged(int) self.change_active_widget(0) self.index_spin.setRange(-self.data.shape[index], self.data.shape[index]-1) @Slot() def accept(self): """Reimplement Qt method""" for index in range(self.stack.count()): self.stack.widget(index).accept_changes() QDialog.accept(self) def get_value(self): """Return modified array -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute return self.data def error(self, message): """An error occured, closing the dialog box""" QMessageBox.critical(self, _("Array editor"), message) self.setAttribute(Qt.WA_DeleteOnClose) self.reject() @Slot() def reject(self): """Reimplement Qt method""" if self.arraywidget is not None: for index in range(self.stack.count()): self.stack.widget(index).reject_changes() QDialog.reject(self)
class STNPlots(QWidget): def __init__(self, plot_config, *args, **kwargs): super(STNPlots, self).__init__(*args, **kwargs) self.plot_config = plot_config pen_color = QColor(pen_colors[plot_config['color_iterator']]) # Create and add GraphicsLayoutWidget self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) # create GLW for the depth plot rms_sett = { **FEAT_VS_DEPTH, 'title': 'Noise RMS (' + u'\u03BC' + 'V)', 'y_name': 'NoiseRMS', 'pen_color': pen_color } self.rms_plot = BasePlotWidget(rms_sett) self.layout.addWidget(self.rms_plot, 0, 0, 1, 1) beta_sett = { **FEAT_VS_DEPTH, 'title': 'Beta power (dB)', 'y_name': 'BetaPower', 'post_processing': self.beta_process, 'pen_color': pen_color } self.bp_plot = BasePlotWidget(beta_sett) self.layout.addWidget(self.bp_plot, 1, 0, 1, 1) # https://gist.github.com/beniwohli/765262 pac_sett = { **FEAT_VS_DEPTH, 'title': 'Peak ' + u'\u03B2' + '-' + u'\u03B3' + ' PAC (± var)', 'y_name': 'PAC', 'y_range': [-.1, 1], 'x_ticks': True, 'post_processing': self.pac_process, 'error_bars': True, 'pen_color': pen_color } self.pac_plot = BasePlotWidget(pac_sett) self.layout.addWidget(self.pac_plot, 2, 0, 1, 1) self.layout.setRowStretch(0, 1) self.layout.setRowStretch(1, 1) self.layout.setRowStretch(2, 1) @staticmethod def beta_process(x, data): return 10 * np.log10(data[1]) @staticmethod def pac_process(x, data): # data contains peak, mean, variance return data[1][:1], data[1][-1:] def update_plot(self, all_data): self.rms_plot.update_plot(all_data) self.bp_plot.update_plot(all_data) self.pac_plot.update_plot(all_data) def clear_plot(self): self.rms_plot.clear_plot() self.bp_plot.clear_plot() self.pac_plot.clear_plot()
def create_layout(self): """displays the menu objects""" grid = QGridLayout() grid.addWidget(self.icase, 0, 0) grid.addWidget(self.icase_edit, 0, 1) grid.addWidget(self.scale, 1, 0) grid.addWidget(self.scale_edit, 1, 1) grid.addWidget(self.scale_button, 1, 2) grid.addWidget(self.time, 2, 0) grid.addWidget(self.time_edit, 2, 1) grid.addWidget(self.time_button, 2, 2) # spacer spacer = QLabel('') grid.addWidget(self.fps, 3, 0) grid.addWidget(self.fps_edit, 3, 1) grid.addWidget(self.fps_button, 3, 2) grid.addWidget(self.resolution, 4, 0) grid.addWidget(self.resolution_edit, 4, 1) grid.addWidget(self.resolution_button, 4, 2) grid.addWidget(self.browse_folder, 5, 0) grid.addWidget(self.browse_folder_edit, 5, 1) grid.addWidget(self.browse_folder_button, 5, 2) grid.addWidget(self.gif, 6, 0) grid.addWidget(self.gif_edit, 6, 1) grid.addWidget(self.gif_button, 6, 2) grid.addWidget(self.animation_type, 7, 0) grid.addWidget(self.animation_type_edit, 7, 1) grid.addWidget(spacer, 8, 0) #---------- #Time grid_time = QGridLayout() grid_time.addWidget(self.icase_start, 0, 0) grid_time.addWidget(self.icase_start_edit, 0, 1) #grid_time.addWidget(self.icase_start_button, 0, 2) grid_time.addWidget(self.icase_end, 1, 0) grid_time.addWidget(self.icase_end_edit, 1, 1) #grid_time.addWidget(self.icase_end_button, 1, 2) grid_time.addWidget(self.icase_delta, 2, 0) grid_time.addWidget(self.icase_delta_edit, 2, 1) #grid_time.addWidget(self.icase_delta_button, 2, 2) #grid_time.addWidget(self.min_value, 3, 0) #grid_time.addWidget(self.min_value_edit, 3, 1) #grid_time.addWidget(self.min_value_button, 3, 2) #grid_time.addWidget(self.max_value, 4, 0) #grid_time.addWidget(self.max_value_edit, 4, 1) #grid_time.addWidget(self.max_value_button, 4, 2) grid_time.addWidget(spacer, 5, 0) #-------------- grid_scale = QGridLayout() grid_scale.addWidget(self.animation_profile, 0, 0) grid_scale.addWidget(self.animation_profile_edit, 0, 1) #grid_scale.addWidget(self.csv_profile, 1, 0) #grid_scale.addWidget(self.csv_profile_edit, 1, 1) #grid_scale.addWidget(self.csv_profile_browse_button, 1, 2) self.csv_profile = QLabel("CSV profile:") self.csv_profile_edit = QLineEdit() self.csv_profile_button = QPushButton('Browse') #box_time = QVBoxLayout() # TODO: It's super annoying that the animate time box doesn't # line up with the previous box box_scale = QGroupBox('Animate Scale') box_scale.setLayout(grid_scale) box_time = QGroupBox('Animate Time') box_time.setLayout(grid_time) #---------- grid2 = QGridLayout() #grid2.addWidget(self.animate_scale_radio, 8, 0) #grid2.addWidget(self.animate_phase_radio, 8, 1) #grid2.addWidget(self.animate_time_radio, 8, 2) #grid2.addWidget(self.animate_freq_sweeep_radio, 8, 3) grid2.addWidget(self.animate_in_gui_checkbox, 10, 0) grid2.addWidget(self.make_images_checkbox, 11, 0) #grid2.addWidget(self.overwrite_images_checkbox, 11, 0) grid2.addWidget(self.delete_images_checkbox, 11, 1) grid2.addWidget(self.make_gif_checkbox, 11, 2) grid2.addWidget(self.repeat_checkbox, 12, 0) grid2.addWidget(spacer, 13, 0) grid_hbox = QHBoxLayout() grid_hbox.addWidget(spacer) grid_hbox.addLayout(grid2) grid_hbox.addWidget(spacer) # bottom buttons step_run_box = QHBoxLayout() step_run_box.addWidget(self.step_button) step_run_box.addWidget(self.wipe_button) step_run_box.addWidget(self.stop_button) step_run_box.addWidget(self.run_button) ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.cancel_button) vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addWidget(box_scale) vbox.addWidget(box_time) #vbox.addLayout(checkboxes) vbox.addLayout(grid_hbox) vbox.addStretch() vbox.addLayout(step_run_box) vbox.addLayout(ok_cancel_box) self.setLayout(vbox)
class LFPPlots(QWidget): def __init__(self, plot_config, *args, **kwargs): super(LFPPlots, self).__init__(*args, **kwargs) self.plot_config = plot_config self.pen_color = QColor(pen_colors[self.plot_config['color_iterator']]) # Create and add GraphicsLayoutWidget self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) # create GLW for the depth plot pwr_sett = { **SPECTRUM, 'title': 'Power Spectrum (depth vs frequency; dB)', 'x_ticks': False, 'x_name': 'depth', 'y_name': 'LFPSpectrumAndEpisodes', 'y_range': [0, 21], 'y_tick_labels': [[(1, 4), (6, 8), (11, 16), (16, 32), (21, 64), (26, 128), (31, 256)]], 'c_lim': [50, 150], 'post_processing': self.spectrum_process, 'pen_color': self.pen_color } self.spectro_plot = BasePlotWidget(pwr_sett) self.layout.addWidget(self.spectro_plot, 0, 0, 1, 1) beta_sett = { **FEAT_VS_DEPTH, 'interactive': True, 'title': 'Beta power (dB)', 'y_name': 'LFPSpectrumAndEpisodes', 'post_processing': self.beta_process, 'pen_color': self.pen_color } self.bp_plot = BasePlotWidget(beta_sett) self.layout.addWidget(self.bp_plot, 1, 0, 1, 1) pep_sett = { **SPECTRUM, 'title': 'P_episodes (depth vs frequency; %)', 'x_ticks': False, 'x_name': 'depth', 'y_name': 'LFPSpectrumAndEpisodes', 'y_range': [0, 21], 'y_tick_labels': [[(1, 4), (6, 8), (11, 16), (16, 32), (21, 64)]], 'c_lim': [0, 1], 'post_processing': self.episodes_process, 'pen_color': self.pen_color } self.episodes_plot = BasePlotWidget(pep_sett) self.layout.addWidget(self.episodes_plot, 2, 0, 1, 1) beta_ep_sett = { **FEAT_VS_DEPTH, 'x_ticks': True, 'interactive': True, 'title': 'Beta episodes', 'y_name': 'LFPSpectrumAndEpisodes', 'post_processing': self.beta_ep_process, 'pen_color': self.pen_color } self.b_ep_plot = BasePlotWidget(beta_ep_sett) self.layout.addWidget(self.b_ep_plot, 3, 0, 1, 1) # self.layout.addWidget(QLabel(), 4, 0, 1, 1) self.layout.setRowStretch(0, 1) self.layout.setRowStretch(1, 1) self.layout.setRowStretch(2, 1) self.layout.setRowStretch(3, 1) # 0.001 mm steps self.spectrum_x = np.arange(pwr_sett['x_range'][0] * 1000, pwr_sett['x_range'][1] * 1000, 1, dtype=np.int) self.spectrum_depths = np.array(np.round(pwr_sett['x_range'][0] * 1000), dtype=np.int) self.spectrum_data = np.zeros( (pwr_sett['y_range'][1], self.spectrum_x.shape[0])) self.episodes_depths = np.array(np.round(pep_sett['x_range'][0] * 1000), dtype=np.int) self.episodes_data = np.zeros( (pep_sett['y_range'][1], self.spectrum_x.shape[0])) self.depth_data = {} def spectrum_process(self, x, data): # data is a chan x values array where n values=62 representing 31 power frequency points and 31 p_episode points new_depth = np.round(x * 1000) new_values = 10 * np.log10(data[1][:data[0].shape[0]]) new_values = new_values[:21] # limit to 64 Hz prev_depths = self.spectrum_depths[self.spectrum_depths < new_depth] if prev_depths.shape[0] > 0: prev_depth = prev_depths.max() prev_values = self.spectrum_data[:, self.spectrum_x == prev_depth].flatten() depth_diff = new_depth - prev_depth values_diff = new_values - prev_values delta = np.arange(0, depth_diff + 1, 1) / depth_diff self.spectrum_data[:, np.logical_and(self.spectrum_x >= prev_depth, self.spectrum_x <= new_depth)] = \ np.add(np.atleast_2d(prev_values).T, (np.atleast_2d(values_diff).T * delta)) else: prev_depth = DEPTHRANGE[0] # -20.000 next_depths = self.spectrum_depths[self.spectrum_depths > new_depth] if next_depths.shape[0] > 0: next_depth = next_depths.min() next_values = self.spectrum_data[:, self.spectrum_x == next_depth].flatten() depth_diff = next_depth - new_depth values_diff = next_values - new_values delta = np.arange(0, depth_diff + 1, 1) / depth_diff self.spectrum_data[:, np.logical_and(self.spectrum_x >= new_depth, self.spectrum_x <= next_depth)] = \ np.add(np.atleast_2d(new_values).T, (np.atleast_2d(values_diff).T * delta)) # append new depth self.spectrum_depths = np.insert( self.spectrum_depths, np.where(self.spectrum_depths == prev_depth)[0] + 1, new_depth) return self.spectrum_data.T @staticmethod def beta_ep_process(x, data): new_values = data[1][data[0].shape[0]:] ep = np.mean(new_values[np.logical_and(data[0] >= 13, data[0] <= 30)]) return [ep] @staticmethod def beta_process(x, data): new_values = data[1][:data[0].shape[0]] pwr = np.mean(new_values[np.logical_and(data[0] >= 13, data[0] <= 30)]) return [np.log10(pwr)] def episodes_process(self, x, data): # data is a chan x values array where n values=62 representing 31 power frequency points and 31 p_episode points new_depth = np.round(x * 1000) new_values = data[1][data[0].shape[0]:] new_values = new_values[:21] # new_values = 10*np.log10(data[1][:data[0].shape[0]]) prev_depths = self.episodes_depths[self.episodes_depths < new_depth] if prev_depths.shape[0] > 0: prev_depth = prev_depths.max() prev_values = self.episodes_data[:, self.spectrum_x == prev_depth].flatten() depth_diff = new_depth - prev_depth values_diff = new_values - prev_values delta = np.arange(0, depth_diff + 1, 1) / depth_diff self.episodes_data[:, np.logical_and(self.spectrum_x >= prev_depth, self.spectrum_x <= new_depth)] = \ np.add(np.atleast_2d(prev_values).T, (np.atleast_2d(values_diff).T * delta)) else: prev_depth = DEPTHRANGE[0] # -20.000 next_depths = self.episodes_depths[self.episodes_depths > new_depth] if next_depths.shape[0] > 0: next_depth = next_depths.min() next_values = self.episodes_data[:, self.spectrum_x == next_depth].flatten() depth_diff = next_depth - new_depth values_diff = next_values - new_values delta = np.arange(0, depth_diff + 1, 1) / depth_diff self.episodes_data[:, np.logical_and(self.spectrum_x >= new_depth, self.spectrum_x <= next_depth)] = \ np.add(np.atleast_2d(new_values).T, (np.atleast_2d(values_diff).T * delta)) # append new depth self.episodes_depths = np.insert( self.episodes_depths, np.where(self.episodes_depths == prev_depth)[0] + 1, new_depth) return self.episodes_data.T def update_plot(self, all_data): self.spectro_plot.update_plot(all_data) self.bp_plot.update_plot(all_data) self.episodes_plot.update_plot(all_data) self.b_ep_plot.update_plot(all_data) def clear_plot(self): self.spectro_plot.clear_plot() self.bp_plot.clear_plot() self.episodes_plot.clear_plot() self.b_ep_plot.clear_plot() self.depth_data = {}
def __init__(self, *args, **kwargs): super(MainWidget, self).__init__(*args, **kwargs) #Widgets self.label_Title1 = QLabel() self.label_Title1.setText("INGRESE LOS DATOS") self.label_Title2 = QLabel() self.label_Title2.setText("DATOS BOMBILLOS ESTANDAR") self.label_Title3 = QLabel() self.label_Title3.setText("DATOS BOMBILLOS LED") self.label_Data1 = QLabel() self.label_Data1.setText("Costo de la electricidad (kW/hora)") self.label_Data2 = QLabel() self.label_Data2.setText("Numero de bombillos a reemplazar") self.label_Data3 = QLabel() self.label_Data3.setText("Vatios del bombillo existente") self.label_Data4 = QLabel() self.label_Data4.setText("Numero de Horas por Dia encendido") self.label_Data5 = QLabel() self.label_Data5.setText("Vida util bombillo actual") self.label_Data6 = QLabel() self.label_Data6.setText("Costo Bombillo actual") self.label_Data7 = QLabel() self.label_Data7.setText("Costo estimado mantenimiento por bombillo") self.label_Data8 = QLabel() self.label_Data8.setText("Vatios del bombillo LED") self.label_Data9 = QLabel() self.label_Data9.setText("Vida util estimada del bombillo") self.label_Data10 = QLabel() self.label_Data10.setText("Costo del bombillo led") #Widgets Titulos del Form self.label_Titlef1 = QLabel() self.label_Titlef1.setText("INFORMACION DE COSTOS CALCULADOS") self.label_Titlef2 = QLabel() self.label_Titlef2.setText("INFORMACION DE AHORROS CALCULADOS") self.line_Data1 = QLineEdit() #self.line_Data1.setValidator() self.line_Data2 = QLineEdit() self.line_Data2.setValidator(QIntValidator()) self.line_Data3 = QLineEdit() self.line_Data3.setValidator(QIntValidator()) self.line_Data4 = QLineEdit() self.line_Data4.setValidator(QIntValidator()) self.line_Data5 = QLineEdit() self.line_Data5.setValidator(QIntValidator()) self.line_Data6 = QLineEdit() #self.line_Data6.setValidator() self.line_Data7 = QLineEdit() #self.line_Data7.setValidator() self.line_Data8 = QLineEdit() #self.line_Data8.setValidator() self.line_Data9 = QLineEdit() #self.line_Data9.setValidator() self.line_Data10 = QLineEdit() #self.line_Data10.setValidator() #Widget Informacion Calculada Form1 Costos self.line_Inf1 = QLineEdit() self.line_Inf1.setValidator(QIntValidator()) self.line_Inf2 = QLineEdit() self.line_Inf2.setValidator(QIntValidator()) self.line_Inf3 = QLineEdit() self.line_Inf3.setValidator(QIntValidator()) self.line_Inf4 = QLineEdit() self.line_Inf4.setValidator(QIntValidator()) self.line_Inf5 = QLineEdit() self.line_Inf5.setValidator(QIntValidator()) self.line_Inf6 = QLineEdit() self.line_Inf6.setValidator(QIntValidator()) self.line_Inf7 = QLineEdit() self.line_Inf7.setValidator(QIntValidator()) self.line_Inf8 = QLineEdit() self.line_Inf8.setValidator(QIntValidator()) self.line_Inf9 = QLineEdit() self.line_Inf9.setValidator(QIntValidator()) #Widget Informacion Calculada Form2 Ahorros self.line_Sav1 = QLineEdit() self.line_Sav1.setValidator(QIntValidator()) self.line_Sav2 = QLineEdit() self.line_Sav2.setValidator(QIntValidator()) self.line_Sav3 = QLineEdit() self.line_Sav3.setValidator(QIntValidator()) self.line_Sav4 = QLineEdit() self.line_Sav4.setValidator(QIntValidator()) self.line_Sav5 = QLineEdit() self.line_Sav5.setValidator(QIntValidator()) self.line_Sav6 = QLineEdit() self.line_Sav6.setValidator(QIntValidator()) self.button_Calc = QPushButton('Calcular Ahorro', parent=self) self.button_Impr = QPushButton('Imprimir', parent=self) #Layouts layout = QGridLayout() layout.addWidget(self.label_Title1,0,0) layout.addWidget(self.label_Data1,1,0) layout.addWidget(self.line_Data1,1,1) layout.addWidget(self.label_Title2,2,0) layout.addWidget(self.label_Data2,3,0) layout.addWidget(self.line_Data2,3,1) layout.addWidget(self.label_Data5,3,2) layout.addWidget(self.line_Data5,3,3) layout.addWidget(self.label_Data3,4,0) layout.addWidget(self.line_Data3,4,1) layout.addWidget(self.label_Data6,4,2) layout.addWidget(self.line_Data6,4,3) layout.addWidget(self.label_Data4,5,0) layout.addWidget(self.line_Data4,5,1) layout.addWidget(self.label_Data7,5,2) layout.addWidget(self.line_Data7,5,3) layout.addWidget(self.label_Title3,6,0) layout.addWidget(self.label_Data8,7,0) layout.addWidget(self.line_Data8,7,1) layout.addWidget(self.label_Data10,7,2) layout.addWidget(self.line_Data10,7,3) layout.addWidget(self.label_Data9,8,0) layout.addWidget(self.line_Data9,8,1) layout.addWidget(self.button_Calc,8,3) layout1 = QFormLayout() layout1.addRow("Horas por año",self.line_Inf1) layout1.addRow("Costo de Energia por Dia",self.line_Inf2) layout1.addRow("Costo de Energia por Año",self.line_Inf3) layout1.addRow("Costo total de la Compra",self.line_Inf4) layout1.addRow("Costo Total de la compra con mantenimiento",self.line_Inf5) layout1.addRow("Costo Reemplazo Actuales Año",self.line_Inf6) layout1.addRow("Costo Energia por Dia LED",self.line_Inf7) layout1.addRow("Costo de Energia por Año LED",self.line_Inf8) layout1.addRow("Costo Total de la Compra LED",self.line_Inf9) layout2 = QFormLayout() layout2.addRow("Ahorro Energia Anual",self.line_Sav1) layout2.addRow("Ahorro Energia Mensual",self.line_Sav2) layout2.addRow("Periodo Retorno Inversion en Años",self.line_Sav3) layout2.addRow("Periodo Retorno Inversion en Meses",self.line_Sav4) layout2.addWidget(self.button_Impr) #Layout Horizontal Titulos del Form self.LayoutTitle = QHBoxLayout() self.LayoutTitle.addWidget(self.label_Titlef1) self.LayoutTitle.addWidget(self.label_Titlef2) ### Layout HOrizontal que une los dos Form self.LayoutForm = QHBoxLayout() self.LayoutForm.addLayout(layout1) self.LayoutForm.addLayout(layout2) ## LAyout Vertical Final self.myLayout = QVBoxLayout() self.myLayout.setMargin(20) self.myLayout.addLayout(layout) self.myLayout.addLayout(self.LayoutTitle) self.myLayout.addLayout(self.LayoutForm) self.setLayout(self.myLayout) # Signals self.button_Calc.clicked.connect(self.result_method)
class SpikePlots(QWidget): def __init__(self, plot_config, *args, **kwargs): super(SpikePlots, self).__init__(*args, **kwargs) self.plot_config = plot_config self.pen_color = QColor(pen_colors[self.plot_config['color_iterator']]) # Create and add GraphicsLayoutWidget self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) # rms settings rms_sett = { **FEAT_VS_DEPTH, 'title': 'Noise RMS (' + u'\u03BC' + 'V)', 'y_name': 'DBSSpikeFeatures', 'post_processing': lambda x, y: [y[1][0]], 'pen_color': self.pen_color } self.rms_plot = BasePlotWidget(rms_sett) self.layout.addWidget(self.rms_plot, 0, 0, 1, 1) # rate rate_sett = { **FEAT_VS_DEPTH, 'title': 'Rate (Hz)', 'y_name': 'DBSSpikeFeatures', 'post_processing': lambda x, y: [y[1][1]], 'pen_color': self.pen_color } self.rate_plot = BasePlotWidget(rate_sett) self.layout.addWidget(self.rate_plot, 1, 0, 1, 1) # rate burst_sett = { **FEAT_VS_DEPTH, 'title': 'Burst Index', 'y_name': 'DBSSpikeFeatures', 'post_processing': lambda x, y: [y[1][2]], 'pen_color': self.pen_color } self.burst_plot = BasePlotWidget(burst_sett) self.layout.addWidget(self.burst_plot, 2, 0, 1, 1) # FF ff_sett = { **FEAT_VS_DEPTH, 'title': 'Fano Factor', 'x_ticks': True, 'y_name': 'DBSSpikeFeatures', 'post_processing': lambda x, y: [y[1][3]], 'pen_color': self.pen_color } self.ff_plot = BasePlotWidget(ff_sett) self.layout.addWidget(self.ff_plot, 3, 0, 1, 1) self.layout.setRowStretch(0, 1) self.layout.setRowStretch(1, 1) self.layout.setRowStretch(2, 1) self.layout.setRowStretch(3, 1) self.depth_data = {} def update_plot(self, all_data): self.rms_plot.update_plot(all_data) self.rate_plot.update_plot(all_data) self.burst_plot.update_plot(all_data) self.ff_plot.update_plot(all_data) def clear_plot(self): # rms settings self.rms_plot.clear_plot() self.rate_plot.clear_plot() self.burst_plot.clear_plot() self.ff_plot.clear_plot() self.depth_data = {}
def setup_page(self): newcb = self.create_checkbox # --- Display tab --- showtabbar_box = newcb(_("Show tab bar"), 'show_tab_bar') showclassfuncdropdown_box = newcb( _("Show selector for classes and functions"), 'show_class_func_dropdown') showindentguides_box = newcb(_("Show Indent Guides"), 'indent_guides') linenumbers_box = newcb(_("Show line numbers"), 'line_numbers') blanks_box = newcb(_("Show blank spaces"), 'blank_spaces') currentline_box = newcb(_("Highlight current line"), 'highlight_current_line') currentcell_box = newcb(_("Highlight current cell"), 'highlight_current_cell') wrap_mode_box = newcb(_("Wrap lines"), 'wrap') scroll_past_end_box = newcb(_("Scroll past the end"), 'scroll_past_end') edgeline_box = newcb(_("Show vertical lines at"), 'edge_line') edgeline_edit = self.create_lineedit( "", 'edge_line_columns', tip=("Enter values separated by commas"), alignment=Qt.Horizontal, regex="[0-9]+(,[0-9]+)*") edgeline_edit_label = QLabel(_("characters")) edgeline_box.toggled.connect(edgeline_edit.setEnabled) edgeline_box.toggled.connect(edgeline_edit_label.setEnabled) edgeline_edit.setEnabled(self.get_option('edge_line')) edgeline_edit_label.setEnabled(self.get_option('edge_line')) occurrence_box = newcb(_("Highlight occurrences after"), 'occurrence_highlighting') occurrence_spin = self.create_spinbox( "", _(" ms"), 'occurrence_highlighting/timeout', min_=100, max_=1000000, step=100) occurrence_box.toggled.connect(occurrence_spin.spinbox.setEnabled) occurrence_box.toggled.connect(occurrence_spin.slabel.setEnabled) occurrence_spin.spinbox.setEnabled( self.get_option('occurrence_highlighting')) occurrence_spin.slabel.setEnabled( self.get_option('occurrence_highlighting')) display_g_layout = QGridLayout() display_g_layout.addWidget(edgeline_box, 0, 0) display_g_layout.addWidget(edgeline_edit.textbox, 0, 1) display_g_layout.addWidget(edgeline_edit_label, 0, 2) display_g_layout.addWidget(occurrence_box, 1, 0) display_g_layout.addWidget(occurrence_spin.spinbox, 1, 1) display_g_layout.addWidget(occurrence_spin.slabel, 1, 2) display_h_layout = QHBoxLayout() display_h_layout.addLayout(display_g_layout) display_h_layout.addStretch(1) display_layout = QVBoxLayout() display_layout.addWidget(showtabbar_box) display_layout.addWidget(showclassfuncdropdown_box) display_layout.addWidget(showindentguides_box) display_layout.addWidget(linenumbers_box) display_layout.addWidget(blanks_box) display_layout.addWidget(currentline_box) display_layout.addWidget(currentcell_box) display_layout.addWidget(wrap_mode_box) display_layout.addWidget(scroll_past_end_box) display_layout.addLayout(display_h_layout) display_widget = QWidget() display_widget.setLayout(display_layout) # --- Source code tab --- closepar_box = newcb( _("Automatic insertion of parentheses, braces and brackets"), 'close_parentheses') close_quotes_box = newcb( _("Automatic insertion of closing quotes"), 'close_quotes') add_colons_box = newcb( _("Automatic insertion of colons after 'for', 'if', 'def', etc"), 'add_colons') autounindent_box = newcb( _("Automatic indentation after 'else', 'elif', etc."), 'auto_unindent') tab_mode_box = newcb( _("Tab always indent"), 'tab_always_indent', default=False, tip=_("If enabled, pressing Tab will always indent,\n" "even when the cursor is not at the beginning\n" "of a line (when this option is enabled, code\n" "completion may be triggered using the alternate\n" "shortcut: Ctrl+Space)")) ibackspace_box = newcb( _("Intelligent backspace"), 'intelligent_backspace', default=True) removetrail_box = newcb( _("Automatically remove trailing spaces when saving files"), 'always_remove_trailing_spaces', default=False) indent_chars_box = self.create_combobox( _("Indentation characters: "), ((_("2 spaces"), '* *'), (_("3 spaces"), '* *'), (_("4 spaces"), '* *'), (_("5 spaces"), '* *'), (_("6 spaces"), '* *'), (_("7 spaces"), '* *'), (_("8 spaces"), '* *'), (_("Tabulations"), '*\t*')), 'indent_chars') tabwidth_spin = self.create_spinbox( _("Tab stop width:"), _("spaces"), 'tab_stop_width_spaces', 4, 1, 8, 1) def enable_tabwidth_spin(index): if index == 7: # Tabulations tabwidth_spin.plabel.setEnabled(True) tabwidth_spin.spinbox.setEnabled(True) else: tabwidth_spin.plabel.setEnabled(False) tabwidth_spin.spinbox.setEnabled(False) indent_chars_box.combobox.currentIndexChanged.connect( enable_tabwidth_spin) indent_tab_grid_layout = QGridLayout() indent_tab_grid_layout.addWidget(indent_chars_box.label, 0, 0) indent_tab_grid_layout.addWidget(indent_chars_box.combobox, 0, 1) indent_tab_grid_layout.addWidget(tabwidth_spin.plabel, 1, 0) indent_tab_grid_layout.addWidget(tabwidth_spin.spinbox, 1, 1) indent_tab_grid_layout.addWidget(tabwidth_spin.slabel, 1, 2) indent_tab_layout = QHBoxLayout() indent_tab_layout.addLayout(indent_tab_grid_layout) indent_tab_layout.addStretch(1) sourcecode_layout = QVBoxLayout() sourcecode_layout.addWidget(closepar_box) sourcecode_layout.addWidget(autounindent_box) sourcecode_layout.addWidget(add_colons_box) sourcecode_layout.addWidget(close_quotes_box) sourcecode_layout.addWidget(tab_mode_box) sourcecode_layout.addWidget(ibackspace_box) sourcecode_layout.addWidget(removetrail_box) sourcecode_layout.addLayout(indent_tab_layout) sourcecode_widget = QWidget() sourcecode_widget.setLayout(sourcecode_layout) # --- Run code tab --- saveall_box = newcb(_("Save all files before running script"), 'save_all_before_run') focus_box = newcb(_("Maintain focus in the Editor after running cells " "or selections"), 'focus_to_editor') run_cell_box = newcb(_("Copy full cell contents to the console when " "running code cells"), 'run_cell_copy') run_layout = QVBoxLayout() run_layout.addWidget(saveall_box) run_layout.addWidget(focus_box) run_layout.addWidget(run_cell_box) run_widget = QWidget() run_widget.setLayout(run_layout) # --- Advanced tab --- # -- Templates template_btn = self.create_button(_("Edit template for new files"), self.plugin.edit_template) # -- Autosave autosave_group = QGroupBox(_('Autosave')) autosave_checkbox = newcb( _('Automatically save a copy of files with unsaved changes'), 'autosave_enabled') autosave_spinbox = self.create_spinbox( _('Autosave interval: '), _('seconds'), 'autosave_interval', min_=1, max_=3600) autosave_checkbox.toggled.connect(autosave_spinbox.setEnabled) autosave_layout = QVBoxLayout() autosave_layout.addWidget(autosave_checkbox) autosave_layout.addWidget(autosave_spinbox) autosave_group.setLayout(autosave_layout) # -- Docstring docstring_group = QGroupBox(_('Docstring type')) numpy_url = "<a href='{}'>Numpy</a>".format(NUMPYDOC) googledoc_url = "<a href='{}'>Google</a>".format(GOOGLEDOC) docstring_label = QLabel( _("Here you can select the type of docstrings ({} or {}) you " "want the editor to automatically introduce when pressing " "<tt>{}</tt> after a function/method/class " "declaration.").format( numpy_url, googledoc_url, DOCSTRING_SHORTCUT)) docstring_label.setOpenExternalLinks(True) docstring_label.setWordWrap(True) docstring_combo_choices = ((_("Numpy"), 'Numpydoc'), (_("Google"), 'Googledoc'),) docstring_combo = self.create_combobox( "Type:", docstring_combo_choices, 'docstring_type') docstring_layout = QVBoxLayout() docstring_layout.addWidget(docstring_label) docstring_layout.addWidget(docstring_combo) docstring_group.setLayout(docstring_layout) # -- Annotations annotations_group = QGroupBox(_("Annotations")) annotations_label = QLabel( _("Display a marker to the left of line numbers when the " "following annotations appear at the beginning of a comment: " "<tt>TODO, FIXME, XXX, HINT, TIP, @todo, HACK, BUG, OPTIMIZE, " "!!!, ???</tt>")) annotations_label.setWordWrap(True) todolist_box = newcb( _("Display code annotations"), 'todo_list') annotations_layout = QVBoxLayout() annotations_layout.addWidget(annotations_label) annotations_layout.addWidget(todolist_box) annotations_group.setLayout(annotations_layout) # -- EOL eol_group = QGroupBox(_("End-of-line characters")) eol_label = QLabel(_("When opening a text file containing " "mixed end-of-line characters (this may " "raise syntax errors in the consoles " "on Windows platforms), Spyder may fix the " "file automatically.")) eol_label.setWordWrap(True) check_eol_box = newcb(_("Fix automatically and show warning " "message box"), 'check_eol_chars', default=True) convert_eol_on_save_box = newcb(_("On Save: convert EOL characters" " to"), 'convert_eol_on_save', default=False) eol_combo_choices = ((_("LF (UNIX)"), 'LF'), (_("CRLF (Windows)"), 'CRLF'), (_("CR (Mac)"), 'CR'), ) convert_eol_on_save_combo = self.create_combobox("", eol_combo_choices, ('convert_eol_on_' 'save_to'), ) convert_eol_on_save_box.toggled.connect( convert_eol_on_save_combo.setEnabled) convert_eol_on_save_combo.setEnabled( self.get_option('convert_eol_on_save')) eol_on_save_layout = QHBoxLayout() eol_on_save_layout.addWidget(convert_eol_on_save_box) eol_on_save_layout.addWidget(convert_eol_on_save_combo) eol_layout = QVBoxLayout() eol_layout.addWidget(eol_label) eol_layout.addWidget(check_eol_box) eol_layout.addLayout(eol_on_save_layout) eol_group.setLayout(eol_layout) # --- Tabs --- tabs = QTabWidget() tabs.addTab(self.create_tab(display_widget), _("Display")) tabs.addTab(self.create_tab(sourcecode_widget), _("Source code")) tabs.addTab(self.create_tab(run_widget), _('Run Code')) tabs.addTab(self.create_tab(template_btn, autosave_group, docstring_group, annotations_group, eol_group), _("Advanced settings")) vlayout = QVBoxLayout() vlayout.addWidget(tabs) self.setLayout(vlayout)
class MappingPlots(QWidget): def __init__(self, plot_config, *args, **kwargs): super(MappingPlots, self).__init__(*args, **kwargs) self.plot_config = plot_config pen_color = QColor(pen_colors[plot_config['color_iterator']]) # Create and add GraphicsLayoutWidget self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) # create GLW for the Kinesthetic kin_sett = { **FEAT_VS_DEPTH, 'title': 'Kinesthetic Response', 'y_name': 'Kinesthetic', 'y_range': [-.1, 6.1], 'y_tick_labels': [[(0, "No"), (1, "Un"), (2, "Fo"), (3, "Le"), (4, "Ha"), (5, "Ar"), (6, 'He')]], 'auto_scale': False, 'post_processing': self.fill_empty, 'pen_color': pen_color } self.kin_plot = BasePlotWidget(kin_sett) self.add_dashed_lines(self.kin_plot.plot) self.layout.addWidget(self.kin_plot, 0, 0, 1, 1) tact_sett = { **FEAT_VS_DEPTH, 'title': 'Tactile Response', 'y_name': 'Tactile', 'y_range': [-.1, 6.1], 'y_tick_labels': [[(0, "No"), (1, "Un"), (2, "Fo"), (3, "Le"), (4, "Ha"), (5, "Ar"), (6, 'He')]], 'auto_scale': False, 'post_processing': self.fill_empty, 'pen_color': pen_color } self.tact_plot = BasePlotWidget(tact_sett) self.add_dashed_lines(self.tact_plot.plot) self.layout.addWidget(self.tact_plot, 1, 0, 1, 1) custom_sett = { **FEAT_VS_DEPTH, 'title': 'Custom Response', 'y_name': 'Custom', 'y_range': [-.1, 6.1], 'y_tick_labels': [[(0, "No"), (1, "Un"), (2, "Fo"), (3, "Le"), (4, "Ha"), (5, "Ar"), (6, 'He')]], 'auto_scale': False, 'post_processing': self.fill_empty, 'pen_color': pen_color } self.custom_plot = BasePlotWidget(custom_sett) self.add_dashed_lines(self.custom_plot.plot) self.layout.addWidget(self.custom_plot, 2, 0, 1, 1) self.layout.setRowStretch(0, 1) self.layout.setRowStretch(1, 1) self.layout.setRowStretch(2, 1) @staticmethod def add_dashed_lines(plot): # add separators for head, ul, ll, plot.addItem( pg.InfiniteLine(angle=0, pos=5.5, movable=False, pen=pg.mkPen('w', width=0.5, style=Qt.DashLine))) plot.addItem( pg.InfiniteLine(angle=0, pos=3.5, movable=False, pen=pg.mkPen('w', width=0.5, style=Qt.DashLine))) plot.addItem( pg.InfiniteLine(angle=0, pos=1.5, movable=False, pen=pg.mkPen('w', width=0.5, style=Qt.DashLine))) @staticmethod def fill_empty(x, data): if len(data[1]) == 0: data[1] = [0] return data[1] def update_plot(self, all_data): self.kin_plot.update_plot(all_data) self.tact_plot.update_plot(all_data) self.custom_plot.update_plot(all_data) def clear_plot(self): self.kin_plot.clear_plot() self.tact_plot.clear_plot() self.custom_plot.clear_plot() self.add_dashed_lines(self.kin_plot.plot) self.add_dashed_lines(self.tact_plot.plot) self.add_dashed_lines(self.custom_plot.plot)
class PyChopGui(QMainWindow): """ GUI Class using PyQT for PyChop to help users plan inelastic neutron experiments at spallation sources by calculating the resolution and flux at a given neutron energies. """ instruments = {} choppers = {} minE = {} maxE = {} def __init__(self): super(PyChopGui, self).__init__() self.folder = os.path.dirname(sys.modules[self.__module__].__file__) for fname in os.listdir(self.folder): if fname.endswith('.yaml'): instobj = Instrument(os.path.join(self.folder, fname)) self.instruments[instobj.name] = instobj self.choppers[instobj.name] = instobj.getChopperNames() self.minE[instobj.name] = max([instobj.emin, 0.01]) self.maxE[instobj.name] = instobj.emax self.drawLayout() self.setInstrument(list(self.instruments.keys())[0]) self.resaxes_xlim = 0 self.qeaxes_xlim = 0 self.isFramePlotted = 0 def setInstrument(self, instname): """ Defines the instrument parameters by the name of the instrument. """ self.engine = self.instruments[str(instname)] self.tabs.setTabEnabled(self.tdtabID, False) self.widgets['ChopperCombo']['Combo'].clear() self.widgets['FrequencyCombo']['Combo'].clear() self.widgets['FrequencyCombo']['Label'].setText('Frequency') self.widgets['PulseRemoverCombo']['Combo'].clear() for item in self.choppers[str(instname)]: self.widgets['ChopperCombo']['Combo'].addItem(item) rep = self.engine.moderator.source_rep maxfreq = self.engine.chopper_system.max_frequencies # At the moment, the GUI only supports up to two independent frequencies if not hasattr(maxfreq, '__len__') or len(maxfreq) == 1: self.widgets['PulseRemoverCombo']['Combo'].hide() self.widgets['PulseRemoverCombo']['Label'].hide() for fq in range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep): self.widgets['FrequencyCombo']['Combo'].addItem(str(fq)) if hasattr(self.engine.chopper_system, 'frequency_names'): self.widgets['FrequencyCombo']['Label'].setText(self.engine.chopper_system.frequency_names[0]) else: self.widgets['PulseRemoverCombo']['Combo'].show() self.widgets['PulseRemoverCombo']['Label'].show() if hasattr(self.engine.chopper_system, 'frequency_names'): for idx, chp in enumerate([self.widgets['FrequencyCombo']['Label'], self.widgets['PulseRemoverCombo']['Label']]): chp.setText(self.engine.chopper_system.frequency_names[idx]) for fq in range(rep, maxfreq[0] + 1, rep): self.widgets['FrequencyCombo']['Combo'].addItem(str(fq)) for fq in range(rep, maxfreq[1] + 1, rep): self.widgets['PulseRemoverCombo']['Combo'].addItem(str(fq)) if len(self.engine.chopper_system.choppers) > 1: self.widgets['MultiRepCheck'].setEnabled(True) self.tabs.setTabEnabled(self.tdtabID, True) else: self.widgets['MultiRepCheck'].setEnabled(False) self.widgets['MultiRepCheck'].setChecked(False) self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() if self.engine.chopper_system.isPhaseIndependent: self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() self.widgets['Chopper2Phase']['Edit'].setText(str(self.engine.chopper_system.defaultPhase[0])) self.widgets['Chopper2Phase']['Label'].setText(self.engine.chopper_system.phaseNames[0]) # Special case for MERLIN - hide phase control from normal users if 'MERLIN' in str(instname) and not self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() self.engine.setChopper(str(self.widgets['ChopperCombo']['Combo'].currentText())) self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText())) val = self.flxslder.val * self.maxE[self.engine.instname] / 100 self.flxedt.setText('%3.2f' % (val)) nframe = self.engine.moderator.n_frame if hasattr(self.engine.moderator, 'n_frame') else 1 self.repfig_nframe_edit.setText(str(nframe)) self.repfig_nframe_rep1only.setChecked(False) if hasattr(self.engine.chopper_system, 'default_frequencies'): cb = [self.widgets['FrequencyCombo']['Combo'], self.widgets['PulseRemoverCombo']['Combo']] for idx, freq in enumerate(self.engine.chopper_system.default_frequencies): cb[idx].setCurrentIndex([i for i in range(cb[idx].count()) if str(freq) in cb[idx].itemText(i)][0]) if idx > 1: break self.tabs.setTabEnabled(self.qetabID, False) if self.engine.has_detector and hasattr(self.engine.detector, 'tthlims'): self.tabs.setTabEnabled(self.qetabID, True) def setChopper(self, choppername): """ Defines the Fermi chopper slit package type by name, or the disk chopper arrangement variant. """ self.engine.setChopper(str(choppername)) self.engine.setFrequency(float(self.widgets['FrequencyCombo']['Combo'].currentText())) # Special case for MERLIN - only enable multirep for 'G' chopper if 'MERLIN' in self.engine.instname: if 'G' in str(choppername): self.widgets['MultiRepCheck'].setEnabled(True) self.tabs.setTabEnabled(self.tdtabID, True) self.widgets['Chopper2Phase']['Edit'].setText('1500') self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time') if self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() else: self.widgets['MultiRepCheck'].setEnabled(False) self.widgets['MultiRepCheck'].setChecked(False) self.tabs.setTabEnabled(self.tdtabID, False) self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() def setFreq(self, freqtext=None, **kwargs): """ Sets the chopper frequency(ies), in Hz. """ freq_gui = float(self.widgets['FrequencyCombo']['Combo'].currentText()) freq_in = kwargs['manual_freq'] if ('manual_freq' in kwargs.keys()) else freq_gui if len(self.engine.getFrequency()) > 1 and (not hasattr(freq_in, '__len__') or len(freq_in)==1): freqpr = float(self.widgets['PulseRemoverCombo']['Combo'].currentText()) freq_in = [freq_in, freqpr] if not self.widgets['Chopper2Phase']['Label'].isHidden(): chop2phase = self.widgets['Chopper2Phase']['Edit'].text() if isinstance(self.engine.chopper_system.defaultPhase[0], string_types): chop2phase = str(chop2phase) else: chop2phase = float(chop2phase) % (1e6 / self.engine.moderator.source_rep) self.engine.setFrequency(freq_in, phase=chop2phase) else: self.engine.setFrequency(freq_in) def setEi(self): """ Sets the incident energy (or focused incident energy for multi-rep case). """ try: eitxt = float(self.widgets['EiEdit']['Edit'].text()) self.engine.setEi(eitxt) if self.eiPlots.isChecked(): self.calc_callback() except ValueError: raise ValueError('No Ei specified, or Ei string not understood') def calc_callback(self): """ Calls routines to calculate the resolution / flux and to update the Matplotlib graphs. """ try: if self.engine.getChopper() is None: self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText()) self.setEi() self.setFreq() self.calculate() if self.errormess: idx = [i for i, ei in enumerate(self.eis) if np.abs(ei - self.engine.getEi()) < 1.e-4] if idx and self.flux[idx[0]] == 0: raise ValueError(self.errormess) self.errormessage(self.errormess) self.plot_res() self.plot_frame() if self.instSciAct.isChecked(): self.update_script() except ValueError as err: self.errormessage(err) self.plot_flux_ei() self.plot_flux_hz() def calculate(self): """ Performs the resolution and flux calculations. """ self.errormess = None if self.engine.getEi() is None: self.setEi() if self.widgets['MultiRepCheck'].isChecked(): en = np.linspace(0, 0.95, 200) self.eis = self.engine.getAllowedEi() with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', UserWarning) self.res = self.engine.getMultiRepResolution(en) self.flux = self.engine.getMultiRepFlux() if len(w) > 0: mess = [str(w[i].message) for i in range(len(w))] self.errormess = '\n'.join([m for m in mess if 'tchop' in m]) else: en = np.linspace(0, 0.95*self.engine.getEi(), 200) with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', UserWarning) self.res = self.engine.getResolution(en) self.flux = self.engine.getFlux() if len(w) > 0: raise ValueError(w[0].message) def _set_overplot(self, overplot, axisname): axis = getattr(self, axisname) if overplot: if matplotlib.compare_versions('2.1.0',matplotlib.__version__): axis.hold(True) else: setattr(self, axisname+'_xlim', 0) axis.clear() axis.axhline(color='k') def plot_res(self): """ Plots the resolution in the resolution tab """ overplot = self.widgets['HoldCheck'].isChecked() multiplot = self.widgets['MultiRepCheck'].isChecked() self._set_overplot(overplot, 'resaxes') self._set_overplot(overplot, 'qeaxes') inst = self.engine.instname freq = self.engine.getFrequency() if hasattr(freq, '__len__'): freq = freq[0] if multiplot: if matplotlib.compare_versions('2.1.0',matplotlib.__version__): self.resaxes.hold(True) for ie, Ei in enumerate(self.eis): en = np.linspace(0, 0.95*Ei, 200) if any(self.res[ie]): if not self.flux[ie]: continue line, = self.resaxes.plot(en, self.res[ie]) label_text = '%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, Ei, freq, self.flux[ie]) line.set_label(label_text) if self.tabs.isTabEnabled(self.qetabID): self.plot_qe(Ei, label_text, hold=True) self.resaxes_xlim = max(Ei, self.resaxes_xlim) if matplotlib.compare_versions('2.1.0',matplotlib.__version__): self.resaxes.hold(False) else: ei = self.engine.getEi() en = np.linspace(0, 0.95*ei, 200) line, = self.resaxes.plot(en, self.res) chopper = self.engine.getChopper() label_text = '%s_%s_%3.2fmeV_%dHz_Flux=%fn/cm2/s' % (inst, chopper, ei, freq, self.flux) line.set_label(label_text) if self.tabs.isTabEnabled(self.qetabID): self.plot_qe(ei, label_text, overplot) self.resaxes_xlim = max(ei, self.resaxes_xlim) self.resaxes.set_xlim([0, self.resaxes_xlim]) self.resaxes.legend().draggable() self.resaxes.set_xlabel('Energy Transfer (meV)') self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)') self.rescanvas.draw() def plot_qe(self, Ei, label_text, hold=False): """ Plots the Q-E diagram """ from scipy import constants E2q, meV2J = (2. * constants.m_n / (constants.hbar ** 2), constants.e / 1000.) en = np.linspace(-Ei / 5., Ei, 100) q2 = [] for tth in self.engine.detector.tthlims: q = np.sqrt(E2q * (2 * Ei - en - 2 * np.sqrt(Ei * (Ei - en)) * np.cos(np.deg2rad(tth))) * meV2J) / 1e10 q2.append(np.concatenate((np.flipud(q), q))) self._set_overplot(hold, 'qeaxes') self.qeaxes_xlim = max(np.max(q2), self.qeaxes_xlim) line, = self.qeaxes.plot(np.hstack(q2), np.concatenate((np.flipud(en), en)).tolist() * len(self.engine.detector.tthlims)) line.set_label(label_text) self.qeaxes.set_xlim([0, self.qeaxes_xlim]) self.qeaxes.legend().draggable() self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$') self.qeaxes.set_ylabel('Energy Transfer (meV)') self.qecanvas.draw() def plot_flux_ei(self, **kwargs): """ Plots the flux vs Ei in the middle tab """ inst = self.engine.instname chop = self.engine.getChopper() freq = self.engine.getFrequency() overplot = self.widgets['HoldCheck'].isChecked() if hasattr(freq, '__len__'): freq = freq[0] update = kwargs['update'] if 'update' in kwargs.keys() else False # Do not recalculate if all relevant parameters still the same. _, labels = self.flxaxes2.get_legend_handles_labels() searchStr = '([A-Z]+) "(.+)" ([0-9]+) Hz' tmpinst = [] if (labels and (overplot or len(labels) == 1)) or update: for prevtitle in labels: prevInst, prevChop, prevFreq = re.search(searchStr, prevtitle).groups() if update: tmpinst.append(copy.deepcopy(Instrument(self.instruments[prevInst], prevChop, float(prevFreq)))) else: if inst == prevInst and chop == prevChop and freq == float(prevFreq): return ne = 25 mn = self.minE[inst] mx = (self.flxslder.val/100)*self.maxE[inst] eis = np.linspace(mn, mx, ne) flux = eis*0 elres = eis*0 if update: self.flxaxes1.clear() self.flxaxes2.clear() if matplotlib.compare_versions('2.1.0',matplotlib.__version__): self.flxaxes1.hold(True) self.flxaxes2.hold(True) for ii, instrument in enumerate(tmpinst): for ie, ei in enumerate(eis): with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = instrument.getFlux(ei) elres[ie] = instrument.getResolution(0., ei)[0] self.flxaxes1.plot(eis, flux) line, = self.flxaxes2.plot(eis, elres) line.set_label(labels[ii]) else: for ie, ei in enumerate(eis): with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = self.engine.getFlux(ei) elres[ie] = self.engine.getResolution(0., ei)[0] if overplot: if matplotlib.compare_versions('2.1.0',matplotlib.__version__): self.flxaxes1.hold(True) self.flxaxes2.hold(True) else: self.flxaxes1.clear() self.flxaxes2.clear() self.flxaxes1.plot(eis, flux) line, = self.flxaxes2.plot(eis, elres) line.set_label('%s "%s" %d Hz' % (inst, chop, freq)) self.flxaxes1.set_xlim([mn, mx]) self.flxaxes2.set_xlim([mn, mx]) self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)') lg = self.flxaxes2.legend() lg.draggable() self.flxcanvas.draw() def update_slider(self, val=None): """ Callback function for the x-axis slider of the flux tab """ if val is None: val = float(self.flxedt.text()) / self.maxE[self.engine.instname] * 100 if val < self.minE[self.engine.instname]: self.errormessage("Max Ei must be greater than %2.1f" % (self.minE[self.engine.instname])) val = (self.minE[self.engine.instname]+0.1) / self.maxE[self.engine.instname] * 100 self.flxslder.set_val(val) else: val = self.flxslder.val * self.maxE[self.engine.instname] / 100 self.flxedt.setText('%3.2f' % (val)) self.plot_flux_ei(update=True) self.flxcanvas.draw() def plot_flux_hz(self): """ Plots the flux vs freq in the middle tab """ inst = self.engine.instname chop = self.engine.getChopper() ei = float(self.widgets['EiEdit']['Edit'].text()) overplot = self.widgets['HoldCheck'].isChecked() # Do not recalculate if one of the plots has the same parametersc _, labels = self.frqaxes2.get_legend_handles_labels() searchStr = '([A-Z]+) "(.+)" Ei = ([0-9.-]+) meV' if labels and (overplot or len(labels) == 1): for prevtitle in labels: prevInst, prevChop, prevEi = re.search(searchStr, prevtitle).groups() if inst == prevInst and chop == prevChop and abs(ei-float(prevEi)) < 0.01: return freq0 = self.engine.getFrequency() rep = self.engine.moderator.source_rep maxfreq = self.engine.chopper_system.max_frequencies freqs = range(rep, (maxfreq[0] if hasattr(maxfreq, '__len__') else maxfreq) + 1, rep) flux = np.zeros(len(freqs)) elres = np.zeros(len(freqs)) for ie, freq in enumerate(freqs): if hasattr(freq0, '__len__'): self.setFreq(manual_freq=[freq] + freq0[1:]) else: self.setFreq(manual_freq=freq) with warnings.catch_warnings(record=True): warnings.simplefilter('always', UserWarning) flux[ie] = self.engine.getFlux(ei) elres[ie] = self.engine.getResolution(0., ei)[0] if overplot: if matplotlib.compare_versions('2.1.0',matplotlib.__version__): self.frqaxes1.hold(True) self.frqaxes2.hold(True) else: self.frqaxes1.clear() self.frqaxes2.clear() self.setFreq(manual_freq=freq0) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)') line, = self.frqaxes1.plot(freqs, flux, 'o-') self.frqaxes1.set_xlim([0, np.max(freqs)]) self.frqaxes2.set_xlabel('Chopper Frequency (Hz)') self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)') line, = self.frqaxes2.plot(freqs, elres, 'o-') line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei)) lg = self.frqaxes2.legend() lg.draggable() self.frqaxes2.set_xlim([0, np.max(freqs)]) self.frqcanvas.draw() def instSciCB(self): """ Callback function for the "Instrument Scientist Mode" menu option """ # MERLIN is a special case - want to hide ability to change phase from users if 'MERLIN' in self.engine.instname and 'G' in self.engine.getChopper(): if self.instSciAct.isChecked(): self.widgets['Chopper2Phase']['Edit'].show() self.widgets['Chopper2Phase']['Label'].show() self.widgets['Chopper2Phase']['Edit'].setText('1500') self.widgets['Chopper2Phase']['Label'].setText('Disk chopper phase delay time') else: self.widgets['Chopper2Phase']['Edit'].hide() self.widgets['Chopper2Phase']['Label'].hide() if self.instSciAct.isChecked(): self.tabs.insertTab(self.scrtabID, self.scrtab, 'ScriptOutput') self.scrtab.show() else: self.tabs.removeTab(self.scrtabID) self.scrtab.hide() def errormessage(self, message): msg = QMessageBox() msg.setText(str(message)) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def loadYaml(self): yaml_file = QFileDialog().getOpenFileName(self.mainWidget, 'Open Instrument YAML File', self.folder, 'Files (*.yaml)') if isinstance(yaml_file, tuple): yaml_file = yaml_file[0] yaml_file = str(yaml_file) new_folder = os.path.dirname(yaml_file) if new_folder != self.folder: self.folder = new_folder try: new_inst = Instrument(yaml_file) except (RuntimeError, AttributeError, ValueError) as err: self.errormessage(err) newname = new_inst.name if newname in self.instruments.keys() and not self.overwriteload.isChecked(): overwrite, newname = self._ask_overwrite() if overwrite == 1: return elif overwrite == 0: newname = new_inst.name self.instruments[newname] = new_inst self.choppers[newname] = new_inst.getChopperNames() self.minE[newname] = max([new_inst.emin, 0.01]) self.maxE[newname] = new_inst.emax self.updateInstrumentList() combo = self.widgets['InstrumentCombo']['Combo'] idx = [i for i in range(combo.count()) if str(combo.itemText(i)) == newname] combo.setCurrentIndex(idx[0]) self.setInstrument(newname) def _ask_overwrite(self): msg = QDialog() msg.setWindowTitle('Load overwrite') layout = QGridLayout() layout.addWidget(QLabel('Instrument %s already exists in memory. Overwrite this?'), 0, 0, 1, -1) buttons = [QPushButton(label) for label in ['Load and overwrite', 'Cancel Load', 'Load and rename to']] locations = [[1, 0], [1, 1], [2, 0]] self.overwrite_flag = 1 def overwriteCB(idx): self.overwrite_flag = idx msg.accept() for idx, button in enumerate(buttons): button.clicked.connect(lambda _, idx=idx: overwriteCB(idx)) layout.addWidget(button, locations[idx][0], locations[idx][1]) newname = QLineEdit() newname.editingFinished.connect(lambda: overwriteCB(2)) layout.addWidget(newname, 2, 1) msg.setLayout(layout) msg.exec_() newname = str(newname.text()) if not newname or newname in self.instruments: self.errormessage('Invalid instrument name. Cancelling load.') self.overwrite_flag = 1 return self.overwrite_flag, newname def updateInstrumentList(self): combo = self.widgets['InstrumentCombo']['Combo'] old_instruments = [str(combo.itemText(i)) for i in range(combo.count())] new_instruments = [inst for inst in self.instruments if inst not in old_instruments] for inst in new_instruments: combo.addItem(inst) def plot_frame(self): """ Plots the distance-time diagram in the right tab """ if len(self.engine.chopper_system.choppers) > 1: self.engine.n_frame = int(self.repfig_nframe_edit.text()) self.repaxes.clear() self.engine.plotMultiRepFrame(self.repaxes, first_rep=self.repfig_nframe_rep1only.isChecked()) self.repcanvas.draw() def _gen_text_ei(self, ei, obj_in): obj = Instrument(obj_in) obj.setEi(ei) en = np.linspace(0, 0.95*ei, 10) try: flux = self.engine.getFlux() res = self.engine.getResolution(en) except ValueError as err: self.errormessage(err) raise ValueError(err) tsqvan, tsqdic, tsqmodchop = obj.getVanVar() v_mod, v_chop = tuple(np.sqrt(tsqmodchop[:2]) * 1e6) x0, _, x1, x2, _ = obj.chopper_system.getDistances() first_component = 'moderator' if x0 != tsqmodchop[2]: x0 = tsqmodchop[2] first_component = 'chopper 1' txt = '# ------------------------------------------------------------- #\n' txt += '# Ei = %8.2f meV\n' % (ei) txt += '# Flux = %8.2f n/cm2/s\n' % (flux) txt += '# Elastic resolution = %6.2f meV\n' % (res[0]) txt += '# Time width at sample = %6.2f us, of which:\n' % (1e6*np.sqrt(tsqvan)) for ky, val in list(tsqdic.items()): txt += '# %20s : %6.2f us\n' % (ky, 1e6*np.sqrt(val)) txt += '# %s distances:\n' % (obj.instname) txt += '# x0 = %6.2f m (%s to Fermi)\n' % (x0, first_component) txt += '# x1 = %6.2f m (Fermi to sample)\n' % (x1) txt += '# x2 = %6.2f m (sample to detector)\n' % (x2) txt += '# Approximate inelastic resolution is given by:\n' txt += '# dE = 2 * E2V * sqrt(ef**3 * t_van**2) / x2\n' txt += '# where: E2V = 4.373e-4 meV/(m/us) conversion from energy to speed\n' txt += '# t_van**2 = (geom*t_mod)**2 + ((1+geom)*t_chop)**2\n' txt += '# geom = (x1 + x2*(ei/ef)**1.5) / x0\n' txt += '# and t_mod and t_chop are the moderator and chopper time widths at the\n' txt += '# moderator and chopper positions (not at the sample as listed above).\n' txt += '# Which in this case is:\n' txt += '# %.4e*sqrt(ef**3 * ( (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2 \n' % (874.78672e-6/x2, v_mod, x1/x0, x2/x0) txt += '# + (%6.5f*(%.3f+%.3f*(ei/ef)**1.5))**2) )\n' % (v_chop, 1+x1/x0, x2/x0) txt += '# EN (meV) Full dE (meV) Approx dE (meV)\n' for ii in range(len(res)): ef = ei-en[ii] approx = (874.78672e-6/x2)*np.sqrt(ef**3 * ((v_mod*((x1/x0)+(x2/x0)*(ei/ef)**1.5))**2 + (v_chop*(1+(x1/x0)+(x2/x0)*(ei/ef)**1.5))**2)) txt += '%12.5f %12.5f %12.5f\n' % (en[ii], res[ii], approx) return txt def genText(self): """ Generates text output of the resolution function versus energy transfer and other information. """ multiplot = self.widgets['MultiRepCheck'].isChecked() obj = self.engine if obj.getChopper() is None: self.setChopper(self.widgets['ChopperCombo']['Combo'].currentText()) if obj.getEi() is None: self.setEi() instname, chtyp, freqs, ei_in = tuple([obj.instname, obj.getChopper(), obj.getFrequency(), obj.getEi()]) txt = '# ------------------------------------------------------------- #\n' txt += '# Chop calculation for instrument %s\n' % (instname) if obj.isFermi: txt += '# with chopper %s at %3i Hz\n' % (chtyp, freqs[0]) else: txt += '# in %s mode with:\n' % (chtyp) freq_names = obj.chopper_system.frequency_names for idx in range(len(freq_names)): txt += '# %s at %3i Hz\n' % (freq_names[idx], freqs[idx]) txt += self._gen_text_ei(ei_in, obj) if multiplot: for ei in sorted(self.engine.getAllowedEi()): if np.abs(ei - ei_in) > 0.001: txt += self._gen_text_ei(ei, obj) return txt def showText(self): """ Creates a dialog to show the generated text output. """ try: generatedText = self.genText() except ValueError: return self.txtwin = QDialog() self.txtedt = QTextEdit() self.txtbtn = QPushButton('OK') self.txtwin.layout = QVBoxLayout(self.txtwin) self.txtwin.layout.addWidget(self.txtedt) self.txtwin.layout.addWidget(self.txtbtn) self.txtbtn.clicked.connect(self.txtwin.deleteLater) self.txtedt.setText(generatedText) self.txtedt.setReadOnly(True) self.txtwin.setWindowTitle('Resolution information') self.txtwin.setWindowModality(Qt.ApplicationModal) self.txtwin.setAttribute(Qt.WA_DeleteOnClose) self.txtwin.setMinimumSize(400, 600) self.txtwin.resize(400, 600) self.txtwin.show() self.txtloop = QEventLoop() self.txtloop.exec_() def saveText(self): """ Saves the generated text to a file (opens file dialog). """ fname = QFileDialog.getSaveFileName(self, 'Open file', '') if isinstance(fname, tuple): fname = fname[0] fid = open(fname, 'w') fid.write(self.genText()) fid.close() def update_script(self): """ Updates the text window with information about the previous calculation. """ if self.widgets['MultiRepCheck'].isChecked(): out = self.engine.getMultiWidths() new_str = '\n' for ie, ee in enumerate(out['Eis']): res = out['Energy'][ie] percent = res / ee * 100 chop_width = out['chopper'][ie] mod_width = out['moderator'][ie] new_str += 'Ei is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ee, res * 1000, percent) new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width) else: ei = self.engine.getEi() out = self.engine.getWidths() res = out['Energy'] percent = res / ei * 100 chop_width = out['chopper'] mod_width = out['moderator'] new_str = '\nEi is %6.2f meV, resolution is %6.2f ueV, percentage resolution is %6.3f\n' % (ei, res * 1000, percent) new_str += 'FWHM at sample from chopper and moderator are %6.2f us, %6.2f us\n' % (chop_width, mod_width) self.scredt.append(new_str) def onHelp(self): """ Shows the help page """ try: from pymantidplot.proxies import showCustomInterfaceHelp showCustomInterfaceHelp("PyChop") except ImportError: helpTxt = "PyChop is a tool to allow direct inelastic neutron\nscattering users to estimate the inelastic resolution\n" helpTxt += "and incident flux for a given spectrometer setting.\n\nFirst select the instrument, chopper settings and\n" helpTxt += "Ei, and then click 'Calculate and Plot'. Data for all\nthe graphs will be generated (may take 1-2s) and\n" helpTxt += "all graphs will be updated. If the 'Hold current plot'\ncheck box is ticked, additional settings will be\n" helpTxt += "overplotted on the existing graphs if they are\ndifferent from previous settings.\n\nMore in-depth help " helpTxt += "can be obtained from the\nMantid help pages." self.hlpwin = QDialog() self.hlpedt = QLabel(helpTxt) self.hlpbtn = QPushButton('OK') self.hlpwin.layout = QVBoxLayout(self.hlpwin) self.hlpwin.layout.addWidget(self.hlpedt) self.hlpwin.layout.addWidget(self.hlpbtn) self.hlpbtn.clicked.connect(self.hlpwin.deleteLater) self.hlpwin.setWindowTitle('Help') self.hlpwin.setWindowModality(Qt.ApplicationModal) self.hlpwin.setAttribute(Qt.WA_DeleteOnClose) self.hlpwin.setMinimumSize(370, 300) self.hlpwin.resize(370, 300) self.hlpwin.show() self.hlploop = QEventLoop() self.hlploop.exec_() def drawLayout(self): """ Draws the GUI layout. """ self.widgetslist = [ ['pair', 'show', 'Instrument', 'combo', self.instruments, self.setInstrument, 'InstrumentCombo'], ['pair', 'show', 'Chopper', 'combo', '', self.setChopper, 'ChopperCombo'], ['pair', 'show', 'Frequency', 'combo', '', self.setFreq, 'FrequencyCombo'], ['pair', 'hide', 'Pulse remover chopper freq', 'combo', '', self.setFreq, 'PulseRemoverCombo'], ['pair', 'show', 'Ei', 'edit', '', self.setEi, 'EiEdit'], ['pair', 'hide', 'Chopper 2 phase delay time', 'edit', '5', self.setFreq, 'Chopper2Phase'], ['spacer'], ['single', 'show', 'Calculate and Plot', 'button', self.calc_callback, 'CalculateButton'], ['single', 'show', 'Hold current plot', 'check', lambda: None, 'HoldCheck'], ['single', 'show', 'Show multi-reps', 'check', lambda: None, 'MultiRepCheck'], ['spacer'], ['single', 'show', 'Show data ascii window', 'button', self.showText, 'ShowAsciiButton'], ['single', 'show', 'Save data as ascii', 'button', self.saveText, 'SaveAsciiButton'] ] self.droplabels = [] self.dropboxes = [] self.singles = [] self.widgets = {} self.leftPanel = QVBoxLayout() self.rightPanel = QVBoxLayout() self.tabs = QTabWidget(self) self.fullWindow = QGridLayout() for widget in self.widgetslist: if 'pair' in widget[0]: self.droplabels.append(QLabel(widget[2])) if 'combo' in widget[3]: self.dropboxes.append(QComboBox(self)) self.dropboxes[-1].activated['QString'].connect(widget[5]) for item in widget[4]: self.dropboxes[-1].addItem(item) self.widgets[widget[-1]] = {'Combo':self.dropboxes[-1], 'Label':self.droplabels[-1]} elif 'edit' in widget[3]: self.dropboxes.append(QLineEdit(self)) self.dropboxes[-1].returnPressed.connect(widget[5]) self.widgets[widget[-1]] = {'Edit':self.dropboxes[-1], 'Label':self.droplabels[-1]} else: raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3])) self.leftPanel.addWidget(self.droplabels[-1]) self.leftPanel.addWidget(self.dropboxes[-1]) if 'hide' in widget[1]: self.droplabels[-1].hide() self.dropboxes[-1].hide() elif 'single' in widget[0]: if 'check' in widget[3]: self.singles.append(QCheckBox(widget[2], self)) self.singles[-1].stateChanged.connect(widget[4]) elif 'button' in widget[3]: self.singles.append(QPushButton(widget[2])) self.singles[-1].clicked.connect(widget[4]) else: raise RuntimeError('Bug in code - widget %s is not recognised.' % (widget[3])) self.leftPanel.addWidget(self.singles[-1]) if 'hide' in widget[1]: self.singles[-1].hide() self.widgets[widget[-1]] = self.singles[-1] elif 'spacer' in widget[0]: self.leftPanel.addItem(QSpacerItem(0, 35)) else: raise RuntimeError('Bug in code - widget class %s is not recognised.' % (widget[0])) # Right panel, matplotlib figures self.resfig = Figure() self.resfig.patch.set_facecolor('white') self.rescanvas = FigureCanvas(self.resfig) self.resaxes = self.resfig.add_subplot(111) self.resaxes.axhline(color='k') self.resaxes.set_xlabel('Energy Transfer (meV)') self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)') self.resfig_controls = NavigationToolbar(self.rescanvas, self) self.restab = QWidget(self.tabs) self.restabbox = QVBoxLayout() self.restabbox.addWidget(self.rescanvas) self.restabbox.addWidget(self.resfig_controls) self.restab.setLayout(self.restabbox) self.flxfig = Figure() self.flxfig.patch.set_facecolor('white') self.flxcanvas = FigureCanvas(self.flxfig) self.flxaxes1 = self.flxfig.add_subplot(121) self.flxaxes1.set_xlabel('Incident Energy (meV)') self.flxaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.flxaxes2 = self.flxfig.add_subplot(122) self.flxaxes2.set_xlabel('Incident Energy (meV)') self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)') self.flxfig_controls = NavigationToolbar(self.flxcanvas, self) self.flxsldfg = Figure() self.flxsldfg.patch.set_facecolor('white') self.flxsldcv = FigureCanvas(self.flxsldfg) self.flxsldax = self.flxsldfg.add_subplot(111) self.flxslder = Slider(self.flxsldax, 'Ei (meV)', 0, 100, valinit=100) self.flxslder.valtext.set_visible(False) self.flxslder.on_changed(self.update_slider) self.flxedt = QLineEdit() self.flxedt.setText('1000') self.flxedt.returnPressed.connect(self.update_slider) self.flxtab = QWidget(self.tabs) self.flxsldbox = QHBoxLayout() self.flxsldbox.addWidget(self.flxsldcv) self.flxsldbox.addWidget(self.flxedt) self.flxsldwdg = QWidget() self.flxsldwdg.setLayout(self.flxsldbox) sz = self.flxsldwdg.maximumSize() sz.setHeight(50) self.flxsldwdg.setMaximumSize(sz) self.flxtabbox = QVBoxLayout() self.flxtabbox.addWidget(self.flxcanvas) self.flxtabbox.addWidget(self.flxsldwdg) self.flxtabbox.addWidget(self.flxfig_controls) self.flxtab.setLayout(self.flxtabbox) self.frqfig = Figure() self.frqfig.patch.set_facecolor('white') self.frqcanvas = FigureCanvas(self.frqfig) self.frqaxes1 = self.frqfig.add_subplot(121) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes1.set_ylabel('Flux (n/cm$^2$/s)') self.frqaxes2 = self.frqfig.add_subplot(122) self.frqaxes1.set_xlabel('Chopper Frequency (Hz)') self.frqaxes2.set_ylabel('Elastic Resolution FWHM (meV)') self.frqfig_controls = NavigationToolbar(self.frqcanvas, self) self.frqtab = QWidget(self.tabs) self.frqtabbox = QVBoxLayout() self.frqtabbox.addWidget(self.frqcanvas) self.frqtabbox.addWidget(self.frqfig_controls) self.frqtab.setLayout(self.frqtabbox) self.repfig = Figure() self.repfig.patch.set_facecolor('white') self.repcanvas = FigureCanvas(self.repfig) self.repaxes = self.repfig.add_subplot(111) self.repaxes.axhline(color='k') self.repaxes.set_xlabel(r'TOF ($\mu$sec)') self.repaxes.set_ylabel('Distance (m)') self.repfig_controls = NavigationToolbar(self.repcanvas, self) self.repfig_nframe_label = QLabel('Number of frames to plot') self.repfig_nframe_edit = QLineEdit('1') self.repfig_nframe_button = QPushButton('Replot') self.repfig_nframe_button.clicked.connect(lambda: self.plot_frame()) self.repfig_nframe_rep1only = QCheckBox('First Rep Only') self.repfig_nframe_box = QHBoxLayout() self.repfig_nframe_box.addWidget(self.repfig_nframe_label) self.repfig_nframe_box.addWidget(self.repfig_nframe_edit) self.repfig_nframe_box.addWidget(self.repfig_nframe_button) self.repfig_nframe_box.addWidget(self.repfig_nframe_rep1only) self.reptab = QWidget(self.tabs) self.repfig_nframe = QWidget(self.reptab) self.repfig_nframe.setLayout(self.repfig_nframe_box) self.repfig_nframe.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) self.reptabbox = QVBoxLayout() self.reptabbox.addWidget(self.repcanvas) self.reptabbox.addWidget(self.repfig_nframe) self.reptabbox.addWidget(self.repfig_controls) self.reptab.setLayout(self.reptabbox) self.qefig = Figure() self.qefig.patch.set_facecolor('white') self.qecanvas = FigureCanvas(self.qefig) self.qeaxes = self.qefig.add_subplot(111) self.qeaxes.axhline(color='k') self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$') self.qeaxes.set_ylabel('Energy Transfer (meV)') self.qefig_controls = NavigationToolbar(self.qecanvas, self) self.qetabbox = QVBoxLayout() self.qetabbox.addWidget(self.qecanvas) self.qetabbox.addWidget(self.qefig_controls) self.qetab = QWidget(self.tabs) self.qetab.setLayout(self.qetabbox) self.scrtab = QWidget(self.tabs) self.scredt = QTextEdit() self.scrcls = QPushButton("Clear") self.scrcls.clicked.connect(lambda: self.scredt.clear()) self.scrbox = QVBoxLayout() self.scrbox.addWidget(self.scredt) self.scrbox.addWidget(self.scrcls) self.scrtab.setLayout(self.scrbox) self.scrtab.hide() self.tabs.addTab(self.restab, 'Resolution') self.tabs.addTab(self.flxtab, 'Flux-Ei') self.tabs.addTab(self.frqtab, 'Flux-Freq') self.tabs.addTab(self.reptab, 'Time-Distance') self.tdtabID = 3 self.tabs.setTabEnabled(self.tdtabID, False) self.tabs.addTab(self.qetab, 'Q-E') self.qetabID = 4 self.tabs.setTabEnabled(self.qetabID, False) self.scrtabID = 5 self.rightPanel.addWidget(self.tabs) self.menuLoad = QMenu('Load') self.loadAct = QAction('Load YAML', self.menuLoad) self.loadAct.triggered.connect(self.loadYaml) self.menuLoad.addAction(self.loadAct) self.menuOptions = QMenu('Options') self.instSciAct = QAction('Instrument Scientist Mode', self.menuOptions, checkable=True) self.instSciAct.triggered.connect(self.instSciCB) self.menuOptions.addAction(self.instSciAct) self.eiPlots = QAction('Press Enter in Ei box updates plots', self.menuOptions, checkable=True) self.menuOptions.addAction(self.eiPlots) self.overwriteload = QAction('Always overwrite instruments in memory', self.menuOptions, checkable=True) self.menuOptions.addAction(self.overwriteload) self.menuBar().addMenu(self.menuLoad) self.menuBar().addMenu(self.menuOptions) self.leftPanelWidget = QWidget() self.leftPanelWidget.setLayout(self.leftPanel) self.leftPanelWidget.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)) self.fullWindow.addWidget(self.leftPanelWidget, 0, 0) self.fullWindow.addLayout(self.rightPanel, 0, 1) self.helpbtn = QPushButton("?", self) self.helpbtn.setMaximumWidth(30) self.helpbtn.clicked.connect(self.onHelp) self.fullWindow.addWidget(self.helpbtn, 1, 0, 1, -1) self.mainWidget = QWidget() self.mainWidget.setLayout(self.fullWindow) self.setCentralWidget(self.mainWidget) self.setWindowTitle('PyChopGUI') self.show()
def __init__(self, *args, **kwargs): super(Arboretum, self).__init__(*args, **kwargs) layout = QVBoxLayout() # add some buttons self.load_button = QPushButton('Load...', self) self.config_button = QPushButton('Configure...', self) self.localize_button = QPushButton('Localize', self) self.track_button = QPushButton('Track', self) self.save_button = QPushButton('Save...', self) # checkboxes self.optimize_checkbox = QCheckBox() self.optimize_checkbox.setChecked(True) # self.use_states_checkbox = QCheckBox() # self.use_states_checkbox.setChecked(True) # combo boxes self.tracking_mode_combobox = QComboBox() for mode in BayesianUpdates: self.tracking_mode_combobox.addItem(mode.name.lower()) default_mode = BayesianUpdates.EXACT self.tracking_mode_combobox.setCurrentIndex(default_mode.value) # # sliders self.search_radius_slider = QSlider(Qt.Horizontal) self.search_radius_slider.setFocusPolicy(Qt.NoFocus) self.search_radius_slider.setMinimum(1) self.search_radius_slider.setMaximum(300) self.search_radius_slider.setSingleStep(1) # self.search_radius_slider.setEnabled(False) # dynamic labels self.config_filename_label = QLabel() self.localizations_label = QLabel() self.tracks_label = QLabel() self.status_label = QLabel() self.search_radius_label = QLabel() self.search_radius_label.setAlignment(Qt.AlignRight) # load/save buttons io_panel = QWidget() io_layout = QHBoxLayout() io_layout.addWidget(self.load_button) io_layout.addWidget(self.save_button) io_panel.setLayout(io_layout) io_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) layout.addWidget(io_panel) # tracking panel tracking_panel = QGroupBox('tracking') tracking_layout = QGridLayout() tracking_layout.addWidget(QLabel('method: '), 0, 0) tracking_layout.addWidget(self.tracking_mode_combobox, 0, 1) tracking_layout.addWidget(self.search_radius_label, 1, 0) tracking_layout.addWidget(self.search_radius_slider, 1, 1) tracking_layout.addWidget(QLabel('optimize: '), 2, 0) tracking_layout.addWidget(self.optimize_checkbox, 2, 1) tracking_layout.addWidget(self.config_button, 3, 0) tracking_layout.addWidget(self.config_filename_label, 3, 1) tracking_layout.addWidget(self.localize_button, 4, 0) tracking_layout.addWidget(self.localizations_label, 4, 1) tracking_layout.addWidget(self.track_button, 5, 0) tracking_layout.addWidget(self.tracks_label, 5, 1) tracking_layout.setColumnMinimumWidth(1, 150) tracking_layout.setSpacing(4) tracking_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) tracking_panel.setLayout(tracking_layout) layout.addWidget(tracking_panel) # status panel status_panel = QGroupBox('status') status_layout = QHBoxLayout() status_layout.addWidget(self.status_label) status_panel.setMaximumWidth(GUI_MAXIMUM_WIDTH) status_panel.setLayout(status_layout) layout.addWidget(status_panel) # set the layout layout.setAlignment(Qt.AlignTop) layout.setSpacing(4) self.setLayout(layout) self.setMaximumHeight(GUI_MAXIMUM_HEIGHT) self.setMaximumWidth(GUI_MAXIMUM_WIDTH) # callbacks self.load_button.clicked.connect(self.load_data) self.save_button.clicked.connect(self.export_data) self.config_button.clicked.connect(self.load_config) self.tracking_mode_combobox.currentTextChanged.connect( self._on_mode_change) self.search_radius_slider.valueChanged.connect(self._on_radius_change) self._tracker_state = None self._segmentation = None self._localizations = None self._tracks = None self._btrack_cfg = None self._active_layer = None # TODO(arl): this is the working filename for the dataset self.filename = None self._search_radius = None self._on_mode_change() self.search_radius_slider.setValue(100)
def __init__(self, parent=None): QWidget.__init__(self, parent) self.current_radio = None self.dedicated_radio = None self.systerm_radio = None self.runconf = RunConfiguration() firstrun_o = CONF.get("run", ALWAYS_OPEN_FIRST_RUN_OPTION, False) # --- General settings ---- common_group = QGroupBox(_("General settings")) common_layout = QGridLayout() common_group.setLayout(common_layout) self.clo_cb = QCheckBox(_("Command line options:")) common_layout.addWidget(self.clo_cb, 0, 0) self.clo_edit = QLineEdit() self.clo_cb.toggled.connect(self.clo_edit.setEnabled) self.clo_edit.setEnabled(False) common_layout.addWidget(self.clo_edit, 0, 1) self.wd_cb = QCheckBox(_("Working directory:")) common_layout.addWidget(self.wd_cb, 1, 0) wd_layout = QHBoxLayout() self.wd_edit = QLineEdit() self.wd_cb.toggled.connect(self.wd_edit.setEnabled) self.wd_edit.setEnabled(False) wd_layout.addWidget(self.wd_edit) browse_btn = QPushButton(ima.icon("DirOpenIcon"), "", self) browse_btn.setToolTip(_("Select directory")) browse_btn.clicked.connect(self.select_directory) wd_layout.addWidget(browse_btn) common_layout.addLayout(wd_layout, 1, 1) self.post_mortem_cb = QCheckBox(_("Enter debugging mode when " "errors appear during execution")) common_layout.addWidget(self.post_mortem_cb) # --- Interpreter --- interpreter_group = QGroupBox(_("Console")) interpreter_layout = QVBoxLayout() interpreter_group.setLayout(interpreter_layout) self.current_radio = QRadioButton(CURRENT_INTERPRETER) interpreter_layout.addWidget(self.current_radio) self.dedicated_radio = QRadioButton(DEDICATED_INTERPRETER) interpreter_layout.addWidget(self.dedicated_radio) self.systerm_radio = QRadioButton(SYSTERM_INTERPRETER) interpreter_layout.addWidget(self.systerm_radio) # --- Dedicated interpreter --- new_group = QGroupBox(_("Dedicated Python console")) self.current_radio.toggled.connect(new_group.setDisabled) new_layout = QGridLayout() new_group.setLayout(new_layout) self.interact_cb = QCheckBox(_("Interact with the Python " "console after execution")) new_layout.addWidget(self.interact_cb, 1, 0, 1, -1) self.show_kill_warning_cb = QCheckBox(_("Show warning when killing" " running process")) new_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1) self.pclo_cb = QCheckBox(_("Command line options:")) new_layout.addWidget(self.pclo_cb, 3, 0) self.pclo_edit = QLineEdit() self.pclo_cb.toggled.connect(self.pclo_edit.setEnabled) self.pclo_edit.setEnabled(False) self.pclo_edit.setToolTip(_("<b>-u</b> is added to the " "other options you set here")) new_layout.addWidget(self.pclo_edit, 3, 1) # Checkbox to preserve the old behavior, i.e. always open the dialog # on first run hline = QFrame() hline.setFrameShape(QFrame.HLine) hline.setFrameShadow(QFrame.Sunken) self.firstrun_cb = QCheckBox(ALWAYS_OPEN_FIRST_RUN % _("this dialog")) self.firstrun_cb.clicked.connect(self.set_firstrun_o) self.firstrun_cb.setChecked(firstrun_o) layout = QVBoxLayout() layout.addWidget(interpreter_group) layout.addWidget(common_group) layout.addWidget(new_group) layout.addWidget(hline) layout.addWidget(self.firstrun_cb) self.setLayout(layout)
def __init__(self, config_folder=CONFIG_FOLDER, title="PartSeg", settings=None, signal_fun=None, initial_image=None): super().__init__(config_folder, title, settings, load_functions.load_dict, signal_fun) self.channel_info = "result_image" self.files_num = 2 self.setMinimumWidth(600) # thi isinstance is only for hinting in IDE assert isinstance(self.settings, PartSettings) # nosec self.main_menu = MainMenu(self.settings, self) self.channel_control2 = ChannelProperty(self.settings, start_name="result_image") self.raw_image = CompareImageView(self.settings, self.channel_control2, "raw_image") self.measurements = MeasurementWidget(self.settings) self.left_stack = StackedWidgetWithSelector() self.left_stack.addWidget(self.raw_image, "Image") self.left_stack.addWidget(self.measurements, "Measurements") self.result_image = ResultImageView(self.settings, self.channel_control2, "result_image") self.color_bar = ColorBar(self.settings, [self.raw_image, self.result_image]) self.info_text = QLabel() self.info_text.setMinimumHeight(25) self.raw_image.text_info_change.connect(self.info_text.setText) self.result_image.text_info_change.connect(self.info_text.setText) self.synchronize_tool = SynchronizeView(self.raw_image, self.result_image, self) self.options_panel = Options(self.settings, self.channel_control2, self.raw_image, self.synchronize_tool) # self.main_menu.image_loaded.connect(self.image_read) self.settings.image_changed.connect(self.image_read) self.advanced_window = SegAdvancedWindow(self.settings, reload_list=[self.reload]) self.batch_window = None # BatchWindow(self.settings) self.multiple_files = MultipleFileWidget(self.settings, load_functions.load_dict, True) self.multiple_files.setVisible( self.settings.get("multiple_files_widget", False)) if initial_image is None: reader = TiffImageReader() im = reader.read(self.initial_image_path) im.file_path = "" self.settings.image = im elif initial_image is not False: self.settings.image = initial_image icon = QIcon(os.path.join(PartSegData.icons_dir, "icon.png")) self.setWindowIcon(icon) menu_bar = self.menuBar() file_menu = menu_bar.addMenu("File") file_menu.addAction("&Open").triggered.connect( self.main_menu.load_data) file_menu.addMenu(self.recent_file_menu) file_menu.addAction("&Save").triggered.connect( self.main_menu.save_file) file_menu.addAction("Batch processing").triggered.connect( self.main_menu.batch_window) view_menu = menu_bar.addMenu("View") view_menu.addAction("Settings and Measurement").triggered.connect( self.main_menu.advanced_window_show) view_menu.addAction("Additional output").triggered.connect( self.additional_layers_show) view_menu.addAction("Additional output with data").triggered.connect( lambda: self.additional_layers_show(True)) view_menu.addAction("Napari viewer").triggered.connect( self.napari_viewer_show) view_menu.addAction("Toggle Multiple Files").triggered.connect( self.toggle_multiple_files) view_menu.addAction("Toggle left panel").triggered.connect( self.toggle_left_panel) view_menu.addAction("Toggle console").triggered.connect( self._toggle_console) action = view_menu.addAction("Screenshot right panel") action.triggered.connect(self.screenshot(self.result_image)) action.setShortcut(QKeySequence.Print) view_menu.addAction("Screenshot left panel").triggered.connect( self.screenshot(self.raw_image)) image_menu = menu_bar.addMenu("Image operations") image_menu.addAction("Image adjustment").triggered.connect( self.image_adjust_exec) image_menu.addAction("Mask manager").triggered.connect( self.main_menu.mask_manager) help_menu = menu_bar.addMenu("Help") help_menu.addAction("State directory").triggered.connect( self.show_settings_directory) help_menu.addAction("About").triggered.connect(self.show_about_dialog) layout = QGridLayout() layout.setSpacing(0) info_layout = QHBoxLayout() info_layout.addWidget(self.left_stack.selector) info_layout.addWidget(self.options_panel.compare_btn) info_layout.addWidget(self.info_text, 1, Qt.AlignHCenter) image_layout = EqualColumnLayout() image_layout.addWidget(self.left_stack) image_layout.addWidget(self.result_image) layout.setSpacing(0) layout.addWidget(self.main_menu, 0, 0, 1, 3) layout.addLayout(info_layout, 3, 1, 1, 2) layout.addWidget(self.multiple_files, 2, 0) layout.addWidget(self.color_bar, 2, 1) layout.addLayout(image_layout, 2, 2, 1, 1) layout.addWidget(self.options_panel, 0, 3, 3, 1) layout.setColumnStretch(2, 1) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget) with suppress(KeyError): geometry = self.settings.get_from_profile("main_window_geometry") self.restoreGeometry(QByteArray.fromHex(bytes(geometry, "ascii")))
def __init__(self, parent, context, name, sequence, shortcuts): super(ShortcutEditor, self).__init__(parent) self._parent = parent self.context = context self.npressed = 0 self.keys = set() self.key_modifiers = set() self.key_non_modifiers = list() self.key_text = list() self.sequence = sequence self.new_sequence = None self.edit_state = True self.shortcuts = shortcuts # Widgets self.label_info = QLabel() self.label_info.setText(_("Press the new shortcut and select 'Ok': \n" "(Press 'Tab' once to switch focus between the shortcut entry \n" "and the buttons below it)")) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = CustomLineEdit(self) self.text_new_sequence.setPlaceholderText(sequence) self.helper_button = HelperToolButton() self.helper_button.hide() self.label_warning = QLabel() self.label_warning.hide() bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(name)) self.button_ok.setFocusPolicy(Qt.NoFocus) self.button_ok.setEnabled(False) self.button_cancel.setFocusPolicy(Qt.NoFocus) self.helper_button.setToolTip('') self.helper_button.setFocusPolicy(Qt.NoFocus) style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) self.text_new_sequence.setFocusPolicy(Qt.NoFocus) self.label_warning.setFocusPolicy(Qt.NoFocus) # Layout spacing = 5 layout_sequence = QGridLayout() layout_sequence.addWidget(self.label_info, 0, 0, 1, 3) layout_sequence.addItem(QSpacerItem(spacing, spacing), 1, 0, 1, 2) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2) layout = QVBoxLayout() layout.addLayout(layout_sequence) layout.addSpacing(spacing) layout.addWidget(bbox) self.setLayout(layout) # Signals bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject)
def __init__(self, parent=None, parentList=(), currIndex=0): QDialog.__init__( self, parent, flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle('Create New Space') self.treeview = parent self.reply = None parentLabel = QLabel(_("Parent")) self.parentBox = QComboBox(self) self.parentBox.addItems(parentList) self.parentBox.setCurrentIndex(currIndex) nameLabel = QLabel(_("Space Name")) self.nameEdit = QLineEdit(self) self.importWidget = ImportAsWidget(self, self.nameEdit) basesTitle = QLabel(_("Base Spaces")) self.basesLine = QLineEdit() self.basesLine.setReadOnly(True) self.basesLine.setEnabled(False) self.basesEditButton = QPushButton(_("Edit")) self.basesEditButton.clicked.connect(self.on_base_edit) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Cancel|QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) mainLayout = QGridLayout(self) mainLayout.addWidget(parentLabel, 0, 0) mainLayout.addWidget(self.parentBox, 0, 1) mainLayout.addWidget(nameLabel, 1, 0) mainLayout.addWidget(self.nameEdit, 1, 1) mainLayout.addWidget(self.importWidget, 2, 0, 1, 2) mainLayout.addWidget(basesTitle, 3, 0) mainLayout.addWidget(self.basesLine, 3, 1) mainLayout.addWidget(self.basesEditButton, 3, 2) mainLayout.addWidget(self.buttonBox, 4, 0, 1, 2) self.setLayout(mainLayout)
def __init__(self, parent, text): QWidget.__init__(self, parent) self.text_editor = QTextEdit(self) self.text_editor.setText(text) self.text_editor.setReadOnly(True) # Type frame type_layout = QHBoxLayout() type_label = QLabel(_("Import as")) type_layout.addWidget(type_label) data_btn = QRadioButton(_("data")) data_btn.setChecked(True) self._as_data= True type_layout.addWidget(data_btn) code_btn = QRadioButton(_("code")) self._as_code = False type_layout.addWidget(code_btn) txt_btn = QRadioButton(_("text")) type_layout.addWidget(txt_btn) h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) type_layout.addItem(h_spacer) type_frame = QFrame() type_frame.setLayout(type_layout) # Opts frame grid_layout = QGridLayout() grid_layout.setSpacing(0) col_label = QLabel(_("Column separator:")) grid_layout.addWidget(col_label, 0, 0) col_w = QWidget() col_btn_layout = QHBoxLayout() self.tab_btn = QRadioButton(_("Tab")) self.tab_btn.setChecked(False) col_btn_layout.addWidget(self.tab_btn) other_btn_col = QRadioButton(_("other")) other_btn_col.setChecked(True) col_btn_layout.addWidget(other_btn_col) col_w.setLayout(col_btn_layout) grid_layout.addWidget(col_w, 0, 1) self.line_edt = QLineEdit(",") self.line_edt.setMaximumWidth(30) self.line_edt.setEnabled(True) other_btn_col.toggled.connect(self.line_edt.setEnabled) grid_layout.addWidget(self.line_edt, 0, 2) row_label = QLabel(_("Row separator:")) grid_layout.addWidget(row_label, 1, 0) row_w = QWidget() row_btn_layout = QHBoxLayout() self.eol_btn = QRadioButton(_("EOL")) self.eol_btn.setChecked(True) row_btn_layout.addWidget(self.eol_btn) other_btn_row = QRadioButton(_("other")) row_btn_layout.addWidget(other_btn_row) row_w.setLayout(row_btn_layout) grid_layout.addWidget(row_w, 1, 1) self.line_edt_row = QLineEdit(";") self.line_edt_row.setMaximumWidth(30) self.line_edt_row.setEnabled(False) other_btn_row.toggled.connect(self.line_edt_row.setEnabled) grid_layout.addWidget(self.line_edt_row, 1, 2) grid_layout.setRowMinimumHeight(2, 15) other_group = QGroupBox(_("Additional options")) other_layout = QGridLayout() other_group.setLayout(other_layout) skiprows_label = QLabel(_("Skip rows:")) other_layout.addWidget(skiprows_label, 0, 0) self.skiprows_edt = QLineEdit('0') self.skiprows_edt.setMaximumWidth(30) intvalid = QIntValidator(0, len(to_text_string(text).splitlines()), self.skiprows_edt) self.skiprows_edt.setValidator(intvalid) other_layout.addWidget(self.skiprows_edt, 0, 1) other_layout.setColumnMinimumWidth(2, 5) comments_label = QLabel(_("Comments:")) other_layout.addWidget(comments_label, 0, 3) self.comments_edt = QLineEdit('#') self.comments_edt.setMaximumWidth(30) other_layout.addWidget(self.comments_edt, 0, 4) self.trnsp_box = QCheckBox(_("Transpose")) #self.trnsp_box.setEnabled(False) other_layout.addWidget(self.trnsp_box, 1, 0, 2, 0) grid_layout.addWidget(other_group, 3, 0, 2, 0) opts_frame = QFrame() opts_frame.setLayout(grid_layout) data_btn.toggled.connect(opts_frame.setEnabled) data_btn.toggled.connect(self.set_as_data) code_btn.toggled.connect(self.set_as_code) # self.connect(txt_btn, SIGNAL("toggled(bool)"), # self, SLOT("is_text(bool)")) # Final layout layout = QVBoxLayout() layout.addWidget(type_frame) layout.addWidget(self.text_editor) layout.addWidget(opts_frame) self.setLayout(layout)
def __init__(self, parent=None, parentList=(), currIndex=0): QDialog.__init__( self, parent, flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle('Create New Cells') self.treeview = parent parentLabel = QLabel(_("Parent")) self.parentBox = QComboBox(self) self.parentBox.addItems(parentList) self.parentBox.setCurrentIndex(currIndex) nameLabel = QLabel(_("Cells Name")) self.nameEdit = QLineEdit(self) self.importWidget = ImportAsWidget(self, self.nameEdit) self.formulaPane = BaseCodePane(parent, title='Formula') self.formulaPane.editor.setReadOnly(False) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Cancel|QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) mainLayout = QGridLayout(self) mainLayout.addWidget(parentLabel, 0, 0) mainLayout.addWidget(self.parentBox, 0, 1) mainLayout.addWidget(nameLabel, 1, 0) mainLayout.addWidget(self.nameEdit, 1, 1) mainLayout.addWidget(self.importWidget, 2, 0, 1, 2) mainLayout.addWidget(self.formulaPane, 3, 0, 1, 2) mainLayout.addWidget(self.buttonBox, 4, 0, 1, 2) mainLayout.setRowStretch(3, 1) self.setLayout(mainLayout)
def _setupIntlkGenLayout(self): self._ld_genenbl = QLabel('Enable: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) self._sb_genenbl = PyDMStateButton( self, self.devpref.substitute(propty='IntlkEn-Sel')) self._led_genenbl = SiriusLedState( self, self.devpref.substitute(propty='IntlkEn-Sts')) self._ld_genclr = QLabel('Reset: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) self._bt_genclr = PyDMPushButton( self, init_channel=self.devpref.substitute(propty='IntlkClr-Sel'), pressValue=1) self._bt_genclr.setIcon(qta.icon('fa5s.sync')) self._bt_genclr.setObjectName('clr') self._bt_genclr.setStyleSheet( '#clr{min-width:25px; max-width:25px; icon-size:20px;}') self._ld_intlkinst = QLabel('Intantaneous Interlock: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) self._led_intlkinst = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk-Mon')) self._ld_intlkltc = QLabel('Latch Interlock: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) self._led_intlkltc = SiriusLedAlert( self, self.devpref.substitute(propty='IntlkLtc-Mon')) self._ld_minsumenbl = QLabel('Min.Sum.Thres. Enable: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) self._ld_minsumenbl.setToolTip( 'If enabled, enable interlock only if minimum sum' ' threshold is exceeded.') self._sb_minsumenbl = PyDMStateButton( self, self.devpref.substitute(propty='IntlkMinSumEn-Sel')) self._led_minsumenbl = SiriusLedState( self, self.devpref.substitute(propty='IntlkMinSumEn-Sts')) self._ld_minsumlim = QLabel('Min.Sum.Thres.[sum count]: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) self._sb_minsumlim = SiriusSpinbox( self, self.devpref.substitute(propty='IntlkLmtMinSum-SP')) self._sb_minsumlim.showStepExponent = False self._sb_minsumlim.limitsFromChannel = False self._sb_minsumlim.setMinimum(-1e12) self._sb_minsumlim.setMaximum(+1e12) self._lb_minsumlim = PyDMLabel( self, self.devpref.substitute(propty='IntlkLmtMinSum-RB')) lay = QGridLayout() lay.setAlignment(Qt.AlignCenter) lay.addWidget(self._ld_genenbl, 0, 0) lay.addWidget(self._sb_genenbl, 0, 1) lay.addWidget(self._led_genenbl, 0, 2) lay.addWidget(self._ld_genclr, 1, 0) lay.addWidget(self._bt_genclr, 1, 1, alignment=Qt.AlignCenter) lay.addWidget(self._ld_intlkinst, 2, 0) lay.addWidget(self._led_intlkinst, 2, 1) lay.addWidget(self._ld_intlkltc, 3, 0) lay.addWidget(self._led_intlkltc, 3, 1) lay.addItem(QSpacerItem(1, 15, QSzPlc.Ignored, QSzPlc.Fixed), 4, 0) lay.addWidget(self._ld_minsumenbl, 5, 0) lay.addWidget(self._sb_minsumenbl, 5, 1) lay.addWidget(self._led_minsumenbl, 5, 2) lay.addWidget(self._ld_minsumlim, 6, 0) lay.addWidget(self._sb_minsumlim, 6, 1) lay.addWidget(self._lb_minsumlim, 6, 2) return lay
class ScriptTab(QWidget,): autocomplete_lst = [] def __init__(self, parent, name, script="", filemodified=0): QWidget.__init__(self) self.parent = parent self.tabWidget = self.parent.ui.tabWidget self.gridlayout = QGridLayout(self) self._dirty = False self.initialize_editor() self.gridlayout.addWidget(self.editor) self.setAcceptDrops(True) self.filename = name self.filemodified = filemodified if self.is_file(): self.title = os.path.basename(name) # if self.equals_saved(): # self.filemodified = os.path.getmtime(self.filename) else: self.filename = "" self.title = name # Show this file in the self.editor self.editor.setText(script) self.clean_txt = self.saved() self.update_dirty() self.editor.keyPressEvent = self.key_press_event @property def scriptRunner(self): return self.parent.controller.scriptRunner def wheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: if event.delta() > 0: self.editor.zoomIn() else: self.editor.zoomOut() return QWidget.wheelEvent(self, event) def initialize_editor(self): self.editor = QsciScintilla() # self.editor.cursorPositionChanged.connect(self.e) # self.editor.copyAvailable.connect(self.e) # self.editor.indicatorClicked.connect(self.e) # self.editor.indicatorReleased.connect(self.e) # self.editor.linesChanged.connect(self.e) # self.editor.marginClicked.connect(self.e) # self.editor.modificationAttempted.connect(self.e) # self.editor.modificationChanged.connect(self.e) # self.editor.selectionChanged.connect(self.e) # self.editor.textChanged.connect(self.e) # self.editor.userListActivated.connect(self.e) if self.editor.__class__.__name__ == "LineTextWidget": return # When using PySide without QSciScintilla # define the font to use font = QFont() font.setFamily("Consolas") font.setFixedPitch(True) font.setPointSize(10) # the font metrics here will help # building the margin width later fm = QFontMetrics(font) # set the default font of the self.editor # and take the same font for line numbers self.editor.setFont(font) self.editor.setMarginsFont(font) # Line numbers # conventionnaly, margin 0 is for line numbers self.editor.setMarginWidth(0, fm.width("00000") + 5) self.editor.setMarginLineNumbers(0, True) self.editor.setTabWidth(4) # Folding visual : we will use boxes self.editor.setFolding(QsciScintilla.BoxedTreeFoldStyle) self.editor.setAutoIndent(True) # Braces matching self.editor.setBraceMatching(QsciScintilla.SloppyBraceMatch) # Editing line color self.editor.setCaretLineVisible(True) self.editor.setCaretLineBackgroundColor(QColor("#CDA869")) # Margins colors # line numbers margin self.editor.setMarginsBackgroundColor(QColor("#333333")) self.editor.setMarginsForegroundColor(QColor("#CCCCCC")) # folding margin colors (foreground,background) self.editor.setFoldMarginColors(QColor("#99CC66"), QColor("#333300")) # Choose a lexer self.lexer = QsciLexerPython() self.lexer.setDefaultFont(font) # Set the length of the string before the editor tries to autocomplete # In practise this would be higher than 1 # But its set lower here to make the autocompletion more obvious self.editor.setAutoCompletionThreshold(1) # Tell the editor we are using a QsciAPI for the autocompletion self.editor.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.editor.setLexer(self.lexer) self.editor.setCallTipsStyle(QsciScintilla.CallTipsContext) # self.editor.setCallTipsVisible(0) # Create an API for us to populate with our autocomplete terms self.api = QsciAPIs(self.lexer) # Compile the api for use in the lexer self.api.prepare() # def tefocusInEvent(self, event): # self.parent.focusInEvent(event) # return QTextEdit.focusInEvent(self.textEditScript, event) def is_file(self): return os.path.isfile(self.filename) def is_modified(self): if self.is_file(): if self.equals_saved(): self.filemodified = os.path.getmtime(self.filename) return str(os.path.getmtime(self.filename)) != str(self.filemodified) else: return False def saved(self): if self.is_file(): f = open(self.filename) saved = f.read() f.close() return saved.strip() else: return "" def equals_saved(self): curr_lines = self.get_script().strip().splitlines() saved_lines = self.saved().strip().splitlines() if len(curr_lines) != len(saved_lines): return False for cl, sl in zip(curr_lines, saved_lines): if cl.strip() != sl.strip(): return False return True @property def dirty(self): if not self._dirty: self.dirty = self.clean_txt != self.get_script() return self._dirty @dirty.setter def dirty(self, dirty): if dirty is False: self.clean_txt = self.get_script() if self._dirty != dirty: self._dirty = dirty self.filename_changed() def update_dirty(self): return self.dirty def index(self): return self.tabWidget.indexOf(self) def filename_changed(self): index = self.parent.ui.tabWidget.indexOf(self) if self.dirty: self.tabWidget.setTabText(index, "%s*" % self.title) else: self.tabWidget.setTabText(index, self.title) def reload(self): self.set_script(self.parent._load_script(self.filename)) self.filemodified = os.path.getmtime(self.filename) def close(self): while self.dirty is True: # While avoids data loss, in case save operation is aborted if self.filename == "": text = "Save unsaved changes?" else: text = "Save %s?" % self.filename ans = QMessageBox.question(self, 'Save', text, QMessageBox.Yes, QMessageBox.Cancel, QMessageBox.No) if ans == QMessageBox.Cancel: return elif ans == QMessageBox.Yes: self.parent.actionSave(False) elif ans == QMessageBox.No: break self.tabWidget.removeTab(self.index()) def _save(self,): f = open(self.filename, 'w') f.write(self.get_script()) f.close() self.title = os.path.basename(self.filename) self.dirty = False def key_press_event(self, event): self.update_dirty() if self.editor.__class__.__name__ == "LineTextWidget": return self.editor.edit.keyReleaseEvent(event) # When using PySide without QSciScintilla QsciScintilla.keyPressEvent(self.editor, event) linenr, pos_in_line = self.editor.getCursorPosition() line = str(self.editor.text(linenr)[:pos_in_line]) if event.key() == Qt.Key_F1: try: self.parent.show_documentation(getattr(self.scriptRunner, str(self.editor.selectedText()))) except AttributeError: pass if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: for tip in self.autocomplete_lst: try: if line.endswith(tip[:tip.index('(') ]): self.editor.insert(tip[tip.index('('):]) parent, residual = self.scriptRunner.split_line(line) self.parent.show_documentation(self.scriptRunner.get_function_dict(parent, residual)[residual]) return except ValueError: pass if event.key() < 100 or event.key() in [Qt.Key_Backspace, Qt.Key_Shift]: linenr, pos_in_line = self.editor.getCursorPosition() line = str(self.editor.text(linenr)[:pos_in_line]) lst = self.scriptRunner.get_autocomplete_list(line) if lst is not None: self.api.clear() list(map(self.api.add, lst)) self.api.prepare() if len(lst) > 0: self.autocomplete_lst = lst self.editor.autoCompleteFromAll() shift_event = QKeyEvent(QEvent.KeyPress, Qt.Key_Shift, Qt.NoModifier) QsciScintilla.keyPressEvent(self.editor, shift_event) # show autocomplete list def set_script(self, script): self.editor.setText(script) def get_script(self): return str(self.editor.text()).replace("\r", "").replace(">>> ", "").replace("\t", " ") def dropHandler(self, dataItemList, ctrl, shift, event): pass
def _setupIntlkTypeLayout(self, intlk): unit = 'nm' ld_enbl = QLabel('Enable: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) sb_enbl = PyDMStateButton( self, self.devpref.substitute(propty='Intlk' + intlk + 'En-Sel')) led_enbl = SiriusLedState( self, self.devpref.substitute(propty='Intlk' + intlk + 'En-Sts')) ld_clr = QLabel('Reset: ', self, alignment=Qt.AlignRight | Qt.AlignBottom) bt_clr = PyDMPushButton(self, init_channel=self.devpref.substitute( propty='Intlk' + intlk + 'Clr-Sel'), pressValue=1) bt_clr.setIcon(qta.icon('fa5s.sync')) bt_clr.setObjectName('clr') bt_clr.setStyleSheet( '#clr{min-width:25px; max-width:25px; icon-size:20px;}') ld_minx = QLabel('Min.X Thres.[' + unit + ']: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_minx = SiriusSpinbox( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MinX-SP')) sb_minx.showStepExponent = False sb_minx.limitsFromChannel = False sb_minx.setMinimum(-1e9) sb_minx.setMaximum(+1e9) lb_minx = PyDMLabel( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MinX-RB')) ld_maxx = QLabel('Max.X Thres.[' + unit + ']: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_maxx = SiriusSpinbox( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MaxX-SP')) sb_maxx.showStepExponent = False sb_maxx.limitsFromChannel = False sb_maxx.setMinimum(-1e9) sb_maxx.setMaximum(+1e9) lb_maxx = PyDMLabel( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MaxX-RB')) ld_miny = QLabel('Min.Y Thres.[' + unit + ']: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_miny = SiriusSpinbox( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MinY-SP')) sb_miny.showStepExponent = False sb_miny.limitsFromChannel = False sb_miny.setMinimum(-1e9) sb_miny.setMaximum(+1e9) lb_miny = PyDMLabel( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MinY-RB')) ld_maxy = QLabel('Max.Y Thres.[' + unit + ']: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_maxy = SiriusSpinbox( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MaxY-SP')) sb_maxy.showStepExponent = False sb_maxy.limitsFromChannel = False sb_maxy.setMinimum(-1e9) sb_maxy.setMaximum(+1e9) lb_maxy = PyDMLabel( self, self.devpref.substitute(propty='IntlkLmt' + intlk + 'MaxY-RB')) ld_leglow = QLabel('Smaller', self, alignment=Qt.AlignCenter) ld_leghigh = QLabel('Bigger', self, alignment=Qt.AlignCenter) ld_legmask = QLabel('<h4>Masked By Enable</h4>', self, alignment=Qt.AlignCenter) ld_legmask_any = QLabel('X or Y', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_mask_anylow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'Smaller-Mon')) led_mask_anyhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'Bigger-Mon')) ld_leginst = QLabel('<h4>Instantaneous</h4>', self, alignment=Qt.AlignCenter) ld_leginst_any = QLabel('X or Y', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_inst_anylow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerAny-Mon')) led_inst_anyhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerAny-Mon')) ld_leginst_x = QLabel('X', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_inst_xlow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerX-Mon')) led_inst_xhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerX-Mon')) ld_leginst_y = QLabel('Y', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_inst_ylow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerY-Mon')) led_inst_yhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerY-Mon')) ld_legltc = QLabel('<h4>Latch</h4>', self, alignment=Qt.AlignCenter) ld_legltc_any = QLabel('X or Y', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_ltc_anylow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerLtc-Mon')) led_ltc_anyhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerLtc-Mon')) ld_legltc_x = QLabel('X', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_ltc_xlow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerLtcX-Mon')) led_ltc_xhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerLtcX-Mon')) ld_legltc_y = QLabel('Y', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_ltc_ylow = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'SmallerLtcY-Mon')) led_ltc_yhigh = SiriusLedAlert( self, self.devpref.substitute(propty='Intlk' + intlk + 'BiggerLtcY-Mon')) lay_mon = QGridLayout() lay_mon.setAlignment(Qt.AlignCenter) lay_mon.addWidget(ld_leglow, 0, 1) lay_mon.addWidget(ld_leghigh, 0, 2) lay_mon.addWidget(ld_legmask, 1, 0) lay_mon.addWidget(ld_legmask_any, 2, 0) lay_mon.addWidget(led_mask_anylow, 2, 1) lay_mon.addWidget(led_mask_anyhigh, 2, 2) lay_mon.addWidget(ld_leginst, 3, 0) lay_mon.addWidget(ld_leginst_any, 4, 0) lay_mon.addWidget(led_inst_anylow, 4, 1) lay_mon.addWidget(led_inst_anyhigh, 4, 2) lay_mon.addWidget(ld_leginst_x, 5, 0) lay_mon.addWidget(led_inst_xlow, 5, 1) lay_mon.addWidget(led_inst_xhigh, 5, 2) lay_mon.addWidget(ld_leginst_y, 6, 0) lay_mon.addWidget(led_inst_ylow, 6, 1) lay_mon.addWidget(led_inst_yhigh, 6, 2) lay_mon.addWidget(ld_legltc, 7, 0) lay_mon.addWidget(ld_legltc_any, 8, 0) lay_mon.addWidget(led_ltc_anylow, 8, 1) lay_mon.addWidget(led_ltc_anyhigh, 8, 2) lay_mon.addWidget(ld_legltc_x, 9, 0) lay_mon.addWidget(led_ltc_xlow, 9, 1) lay_mon.addWidget(led_ltc_xhigh, 9, 2) lay_mon.addWidget(ld_legltc_y, 10, 0) lay_mon.addWidget(led_ltc_ylow, 10, 1) lay_mon.addWidget(led_ltc_yhigh, 10, 2) lay = QGridLayout() lay.setAlignment(Qt.AlignTop) lay.addWidget(ld_enbl, 0, 0) lay.addWidget(sb_enbl, 0, 1) lay.addWidget(led_enbl, 0, 2) lay.addWidget(ld_clr, 1, 0) lay.addWidget(bt_clr, 1, 1, alignment=Qt.AlignCenter) lay.addWidget(ld_minx, 2, 0) lay.addWidget(sb_minx, 2, 1) lay.addWidget(lb_minx, 2, 2) lay.addWidget(ld_maxx, 3, 0) lay.addWidget(sb_maxx, 3, 1) lay.addWidget(lb_maxx, 3, 2) lay.addWidget(ld_miny, 4, 0) lay.addWidget(sb_miny, 4, 1) lay.addWidget(lb_miny, 4, 2) lay.addWidget(ld_maxy, 5, 0) lay.addWidget(sb_maxy, 5, 1) lay.addWidget(lb_maxy, 5, 2) lay.addItem(QSpacerItem(1, 15, QSzPlc.Ignored, QSzPlc.Fixed), 6, 0) lay.addLayout(lay_mon, 7, 0, 1, 3) return lay
class ThumbnailScrollBar(QFrame): """ A widget that manages the display of the FigureThumbnails that are created when a figure is sent to the IPython console by the kernel and that controls what is displayed in the FigureViewer. """ redirect_stdio = Signal(bool) def __init__(self, figure_viewer, parent=None, background_color=None): super(ThumbnailScrollBar, self).__init__(parent) self._thumbnails = [] self.background_color = background_color self.current_thumbnail = None self.set_figureviewer(figure_viewer) self.setup_gui() def setup_gui(self): """Setup the main layout of the widget.""" scrollarea = self.setup_scrollarea() up_btn, down_btn = self.setup_arrow_buttons() self.setFixedWidth(150) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(up_btn) layout.addWidget(scrollarea) layout.addWidget(down_btn) def setup_scrollarea(self): """Setup the scrollarea that will contain the FigureThumbnails.""" self.view = QWidget() self.scene = QGridLayout(self.view) self.scene.setColumnStretch(0, 100) self.scene.setColumnStretch(2, 100) self.scrollarea = QScrollArea() self.scrollarea.setWidget(self.view) self.scrollarea.setWidgetResizable(True) self.scrollarea.setFrameStyle(0) self.scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scrollarea.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)) # Set the vertical scrollbar explicitely : # This is required to avoid a "RuntimeError: no access to protected # functions or signals for objects not created from Python" in Linux. self.scrollarea.setVerticalScrollBar(QScrollBar()) return self.scrollarea def setup_arrow_buttons(self): """ Setup the up and down arrow buttons that are placed at the top and bottom of the scrollarea. """ # Get the height of the up/down arrow of the default vertical # scrollbar : vsb = self.scrollarea.verticalScrollBar() style = vsb.style() opt = QStyleOptionSlider() vsb.initStyleOption(opt) vsb_up_arrow = style.subControlRect( QStyle.CC_ScrollBar, opt, QStyle.SC_ScrollBarAddLine, self) # Setup the up and down arrow button : up_btn = up_btn = QPushButton(icon=ima.icon('last_edit_location')) up_btn.setFlat(True) up_btn.setFixedHeight(vsb_up_arrow.size().height()) up_btn.clicked.connect(self.go_up) down_btn = QPushButton(icon=ima.icon('folding.arrow_down_on')) down_btn.setFlat(True) down_btn.setFixedHeight(vsb_up_arrow.size().height()) down_btn.clicked.connect(self.go_down) return up_btn, down_btn def set_figureviewer(self, figure_viewer): """Set the bamespace for the FigureViewer.""" self.figure_viewer = figure_viewer # ---- Save Figure def save_all_figures_as(self): """Save all the figures to a file.""" self.redirect_stdio.emit(False) dirname = getexistingdirectory(self, caption='Save all figures', basedir=getcwd_or_home()) self.redirect_stdio.emit(True) if dirname: return self.save_all_figures_todir(dirname) def save_all_figures_todir(self, dirname): """Save all figure in dirname.""" fignames = [] for thumbnail in self._thumbnails: fig = thumbnail.canvas.fig fmt = thumbnail.canvas.fmt fext = {'image/png': '.png', 'image/jpeg': '.jpg', 'image/svg+xml': '.svg'}[fmt] figname = get_unique_figname(dirname, 'Figure', fext) save_figure_tofile(fig, fmt, figname) fignames.append(figname) return fignames def save_current_figure_as(self): """Save the currently selected figure.""" if self.current_thumbnail is not None: self.save_figure_as(self.current_thumbnail.canvas.fig, self.current_thumbnail.canvas.fmt) def save_figure_as(self, fig, fmt): """Save the figure to a file.""" fext, ffilt = { 'image/png': ('.png', 'PNG (*.png)'), 'image/jpeg': ('.jpg', 'JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)'), 'image/svg+xml': ('.svg', 'SVG (*.svg);;PNG (*.png)')}[fmt] figname = get_unique_figname(getcwd_or_home(), 'Figure', fext) self.redirect_stdio.emit(False) fname, fext = getsavefilename( parent=self.parent(), caption='Save Figure', basedir=figname, filters=ffilt, selectedfilter='', options=None) self.redirect_stdio.emit(True) if fname: save_figure_tofile(fig, fmt, fname) # ---- Thumbails Handlers def add_thumbnail(self, fig, fmt): thumbnail = FigureThumbnail(background_color=self.background_color) thumbnail.canvas.load_figure(fig, fmt) # Scale the thumbnail size, while respecting the figure # dimension ratio. fwidth = thumbnail.canvas.fwidth fheight = thumbnail.canvas.fheight max_length = 100 if fwidth/fheight > 1: canvas_width = max_length canvas_height = canvas_width / fwidth * fheight else: canvas_height = max_length canvas_width = canvas_height / fheight * fwidth thumbnail.canvas.setFixedSize(canvas_width, canvas_height) thumbnail.sig_canvas_clicked.connect(self.set_current_thumbnail) thumbnail.sig_remove_figure.connect(self.remove_thumbnail) thumbnail.sig_save_figure.connect(self.save_figure_as) self._thumbnails.append(thumbnail) self.scene.setRowStretch(self.scene.rowCount()-1, 0) self.scene.addWidget(thumbnail, self.scene.rowCount()-1, 1) self.scene.setRowStretch(self.scene.rowCount(), 100) self.set_current_thumbnail(thumbnail) def remove_current_thumbnail(self): """Remove the currently selected thumbnail.""" if self.current_thumbnail is not None: self.remove_thumbnail(self.current_thumbnail) def remove_all_thumbnails(self): """Remove all thumbnails.""" for thumbnail in self._thumbnails: self.layout().removeWidget(thumbnail) thumbnail.sig_canvas_clicked.disconnect() thumbnail.sig_remove_figure.disconnect() thumbnail.sig_save_figure.disconnect() thumbnail.deleteLater() self._thumbnails = [] self.current_thumbnail = None self.figure_viewer.figcanvas.clear_canvas() def remove_thumbnail(self, thumbnail): """Remove thumbnail.""" if thumbnail in self._thumbnails: index = self._thumbnails.index(thumbnail) self._thumbnails.remove(thumbnail) self.layout().removeWidget(thumbnail) thumbnail.deleteLater() thumbnail.sig_canvas_clicked.disconnect() thumbnail.sig_remove_figure.disconnect() thumbnail.sig_save_figure.disconnect() # Select a new thumbnail if any : if thumbnail == self.current_thumbnail: if len(self._thumbnails) > 0: self.set_current_index(min(index, len(self._thumbnails)-1)) else: self.current_thumbnail = None self.figure_viewer.figcanvas.clear_canvas() def set_current_index(self, index): """Set the currently selected thumbnail by its index.""" self.set_current_thumbnail(self._thumbnails[index]) def get_current_index(self): """Return the index of the currently selected thumbnail.""" try: return self._thumbnails.index(self.current_thumbnail) except ValueError: return -1 def set_current_thumbnail(self, thumbnail): """Set the currently selected thumbnail.""" self.current_thumbnail = thumbnail self.figure_viewer.load_figure( thumbnail.canvas.fig, thumbnail.canvas.fmt) for thumbnail in self._thumbnails: thumbnail.highlight_canvas(thumbnail == self.current_thumbnail) def go_previous_thumbnail(self): """Select the thumbnail previous to the currently selected one.""" if self.current_thumbnail is not None: index = self._thumbnails.index(self.current_thumbnail) - 1 index = index if index >= 0 else len(self._thumbnails) - 1 self.set_current_index(index) self.scroll_to_item(index) def go_next_thumbnail(self): """Select thumbnail next to the currently selected one.""" if self.current_thumbnail is not None: index = self._thumbnails.index(self.current_thumbnail) + 1 index = 0 if index >= len(self._thumbnails) else index self.set_current_index(index) self.scroll_to_item(index) def scroll_to_item(self, index): """Scroll to the selected item of ThumbnailScrollBar.""" spacing_between_items = self.scene.verticalSpacing() height_view = self.scrollarea.viewport().height() height_item = self.scene.itemAt(index).sizeHint().height() height_view_excluding_item = max(0, height_view - height_item) height_of_top_items = spacing_between_items for i in range(index): item = self.scene.itemAt(i) height_of_top_items += item.sizeHint().height() height_of_top_items += spacing_between_items pos_scroll = height_of_top_items - height_view_excluding_item // 2 vsb = self.scrollarea.verticalScrollBar() vsb.setValue(pos_scroll) # ---- ScrollBar Handlers def go_up(self): """Scroll the scrollbar of the scrollarea up by a single step.""" vsb = self.scrollarea.verticalScrollBar() vsb.setValue(int(vsb.value() - vsb.singleStep())) def go_down(self): """Scroll the scrollbar of the scrollarea down by a single step.""" vsb = self.scrollarea.verticalScrollBar() vsb.setValue(int(vsb.value() + vsb.singleStep()))
def __init__(self, parent=None): QWidget.__init__(self, parent) self.dir = None self.runconf = RunConfiguration() firstrun_o = CONF.get('run', ALWAYS_OPEN_FIRST_RUN_OPTION, False) # --- Interpreter --- interpreter_group = QGroupBox(_("Console")) interpreter_layout = QVBoxLayout() interpreter_group.setLayout(interpreter_layout) self.current_radio = QRadioButton(CURRENT_INTERPRETER) interpreter_layout.addWidget(self.current_radio) self.dedicated_radio = QRadioButton(DEDICATED_INTERPRETER) interpreter_layout.addWidget(self.dedicated_radio) self.systerm_radio = QRadioButton(SYSTERM_INTERPRETER) interpreter_layout.addWidget(self.systerm_radio) # --- General settings ---- common_group = QGroupBox(_("General settings")) common_layout = QGridLayout() common_group.setLayout(common_layout) self.clear_var_cb = QCheckBox(CLEAR_ALL_VARIABLES) common_layout.addWidget(self.clear_var_cb, 0, 0) self.post_mortem_cb = QCheckBox(POST_MORTEM) common_layout.addWidget(self.post_mortem_cb, 1, 0) self.clo_cb = QCheckBox(_("Command line options:")) common_layout.addWidget(self.clo_cb, 2, 0) self.clo_edit = QLineEdit() self.clo_cb.toggled.connect(self.clo_edit.setEnabled) self.clo_edit.setEnabled(False) common_layout.addWidget(self.clo_edit, 2, 1) # --- Working directory --- wdir_group = QGroupBox(_("Working Directory settings")) wdir_layout = QVBoxLayout() wdir_group.setLayout(wdir_layout) self.file_dir_radio = QRadioButton(FILE_DIR) wdir_layout.addWidget(self.file_dir_radio) self.cwd_radio = QRadioButton(CW_DIR) wdir_layout.addWidget(self.cwd_radio) fixed_dir_layout = QHBoxLayout() self.fixed_dir_radio = QRadioButton(FIXED_DIR) fixed_dir_layout.addWidget(self.fixed_dir_radio) self.wd_edit = QLineEdit() self.fixed_dir_radio.toggled.connect(self.wd_edit.setEnabled) self.wd_edit.setEnabled(False) fixed_dir_layout.addWidget(self.wd_edit) browse_btn = QPushButton(ima.icon('DirOpenIcon'), '', self) browse_btn.setToolTip(_("Select directory")) browse_btn.clicked.connect(self.select_directory) fixed_dir_layout.addWidget(browse_btn) wdir_layout.addLayout(fixed_dir_layout) # --- System terminal --- external_group = QGroupBox(_("External system terminal")) external_group.setDisabled(True) self.systerm_radio.toggled.connect(external_group.setEnabled) external_layout = QGridLayout() external_group.setLayout(external_layout) self.interact_cb = QCheckBox(INTERACT) external_layout.addWidget(self.interact_cb, 1, 0, 1, -1) self.pclo_cb = QCheckBox(_("Command line options:")) external_layout.addWidget(self.pclo_cb, 3, 0) self.pclo_edit = QLineEdit() self.pclo_cb.toggled.connect(self.pclo_edit.setEnabled) self.pclo_edit.setEnabled(False) self.pclo_edit.setToolTip(_("<b>-u</b> is added to the " "other options you set here")) external_layout.addWidget(self.pclo_edit, 3, 1) # Checkbox to preserve the old behavior, i.e. always open the dialog # on first run hline = QFrame() hline.setFrameShape(QFrame.HLine) hline.setFrameShadow(QFrame.Sunken) self.firstrun_cb = QCheckBox(ALWAYS_OPEN_FIRST_RUN % _("this dialog")) self.firstrun_cb.clicked.connect(self.set_firstrun_o) self.firstrun_cb.setChecked(firstrun_o) layout = QVBoxLayout() layout.addWidget(interpreter_group) layout.addWidget(common_group) layout.addWidget(wdir_group) layout.addWidget(external_group) layout.addWidget(hline) layout.addWidget(self.firstrun_cb) self.setLayout(layout)
def __init__(self, parent, project_types): """Project creation dialog.""" super(ProjectDialog, self).__init__(parent=parent) self.plugin = parent self._project_types = project_types self.project_data = {} self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # Variables current_python_version = '.'.join([ to_text_string(sys.version_info[0]), to_text_string(sys.version_info[1]) ]) python_versions = ['2.7', '3.4', '3.5'] if current_python_version not in python_versions: python_versions.append(current_python_version) python_versions = sorted(python_versions) self.project_name = None self.location = get_home_dir() # Widgets self.groupbox = QGroupBox() self.radio_new_dir = QRadioButton(_("New directory")) self.radio_from_dir = QRadioButton(_("Existing directory")) self.label_project_name = QLabel(_('Project name')) self.label_location = QLabel(_('Location')) self.label_project_type = QLabel(_('Project type')) self.label_python_version = QLabel(_('Python version')) self.text_project_name = QLineEdit() self.text_location = QLineEdit(get_home_dir()) self.combo_project_type = QComboBox() self.combo_python_version = QComboBox() self.label_information = QLabel("") self.button_select_location = create_toolbutton( self, triggered=self.select_location, icon=ima.icon('DirOpenIcon'), tip=_("Select directory")) self.button_cancel = QPushButton(_('Cancel')) self.button_create = QPushButton(_('Create')) self.bbox = QDialogButtonBox(Qt.Horizontal) self.bbox.addButton(self.button_cancel, QDialogButtonBox.ActionRole) self.bbox.addButton(self.button_create, QDialogButtonBox.ActionRole) # Widget setup self.combo_python_version.addItems(python_versions) self.radio_new_dir.setChecked(True) self.text_location.setEnabled(True) self.text_location.setReadOnly(True) self.button_cancel.setDefault(True) self.button_cancel.setAutoDefault(True) self.button_create.setEnabled(False) for (id_, name) in [(pt_id, pt.get_name()) for pt_id, pt in project_types.items()]: self.combo_project_type.addItem(name, id_) self.combo_python_version.setCurrentIndex( python_versions.index(current_python_version)) self.setWindowTitle(_('Create new project')) self.setFixedWidth(500) self.label_python_version.setVisible(False) self.combo_python_version.setVisible(False) # Layouts layout_top = QHBoxLayout() layout_top.addWidget(self.radio_new_dir) layout_top.addWidget(self.radio_from_dir) layout_top.addStretch(1) self.groupbox.setLayout(layout_top) layout_grid = QGridLayout() layout_grid.addWidget(self.label_project_name, 0, 0) layout_grid.addWidget(self.text_project_name, 0, 1, 1, 2) layout_grid.addWidget(self.label_location, 1, 0) layout_grid.addWidget(self.text_location, 1, 1) layout_grid.addWidget(self.button_select_location, 1, 2) layout_grid.addWidget(self.label_project_type, 2, 0) layout_grid.addWidget(self.combo_project_type, 2, 1, 1, 2) layout_grid.addWidget(self.label_python_version, 3, 0) layout_grid.addWidget(self.combo_python_version, 3, 1, 1, 2) layout_grid.addWidget(self.label_information, 4, 0, 1, 3) layout = QVBoxLayout() layout.addWidget(self.groupbox) layout.addSpacing(10) layout.addLayout(layout_grid) layout.addStretch() layout.addSpacing(20) layout.addWidget(self.bbox) self.setLayout(layout) # Signals and slots self.button_create.clicked.connect(self.create_project) self.button_cancel.clicked.connect(self.close) self.radio_from_dir.clicked.connect(self.update_location) self.radio_new_dir.clicked.connect(self.update_location) self.text_project_name.textChanged.connect(self.update_location)
def setup_page(self): self.ICON = ima.icon('genprefs') newcb = self.create_checkbox # --- Interface general_group = QGroupBox(_("General")) languages = LANGUAGE_CODES.items() language_choices = sorted([(val, key) for key, val in languages]) language_combo = self.create_combobox(_('Language:'), language_choices, 'interface_language', restart=True) opengl_options = ['Automatic', 'Desktop', 'Software', 'GLES'] opengl_choices = list(zip(opengl_options, [c.lower() for c in opengl_options])) opengl_combo = self.create_combobox(_('Rendering engine:'), opengl_choices, 'opengl', restart=True) single_instance_box = newcb(_("Use a single instance"), 'single_instance', tip=_("Set this to open external<br> " "Python files in an already running " "instance (Requires a restart)")) prompt_box = newcb(_("Prompt when exiting"), 'prompt_on_exit') popup_console_box = newcb(_("Show internal Spyder errors to report " "them to Github"), 'show_internal_errors') check_updates = newcb(_("Check for updates on startup"), 'check_updates_on_startup') # Decide if it's possible to activate or not single instance mode if running_in_mac_app(): self.set_option("single_instance", True) single_instance_box.setEnabled(False) comboboxes_advanced_layout = QHBoxLayout() cbs_adv_grid = QGridLayout() cbs_adv_grid.addWidget(language_combo.label, 0, 0) cbs_adv_grid.addWidget(language_combo.combobox, 0, 1) cbs_adv_grid.addWidget(opengl_combo.label, 1, 0) cbs_adv_grid.addWidget(opengl_combo.combobox, 1, 1) comboboxes_advanced_layout.addLayout(cbs_adv_grid) comboboxes_advanced_layout.addStretch(1) general_layout = QVBoxLayout() general_layout.addLayout(comboboxes_advanced_layout) general_layout.addWidget(single_instance_box) general_layout.addWidget(prompt_box) general_layout.addWidget(popup_console_box) general_layout.addWidget(check_updates) general_group.setLayout(general_layout) # --- Theme interface_group = QGroupBox(_("Interface")) vertdock_box = newcb(_("Vertical title bars in panes"), 'vertical_dockwidget_titlebars') verttabs_box = newcb(_("Vertical tabs in panes"), 'vertical_tabs') animated_box = newcb(_("Animated toolbars and panes"), 'animated_docks') tear_off_box = newcb(_("Tear off menus"), 'tear_off_menus', tip=_("Set this to detach any<br> " "menu from the main window")) margin_box = newcb(_("Custom margin for panes:"), 'use_custom_margin') margin_spin = self.create_spinbox("", _("pixels"), 'custom_margin', 0, 0, 30) margin_box.toggled.connect(margin_spin.spinbox.setEnabled) margin_box.toggled.connect(margin_spin.slabel.setEnabled) margin_spin.spinbox.setEnabled(self.get_option('use_custom_margin')) margin_spin.slabel.setEnabled(self.get_option('use_custom_margin')) cursor_box = newcb(_("Cursor blinking:"), 'use_custom_cursor_blinking') cursor_spin = self.create_spinbox( "", _("ms"), 'custom_cursor_blinking', default=QApplication.cursorFlashTime(), min_=0, max_=5000, step=100) cursor_box.toggled.connect(cursor_spin.spinbox.setEnabled) cursor_box.toggled.connect(cursor_spin.slabel.setEnabled) cursor_spin.spinbox.setEnabled( self.get_option('use_custom_cursor_blinking')) cursor_spin.slabel.setEnabled( self.get_option('use_custom_cursor_blinking')) margins_cursor_layout = QGridLayout() margins_cursor_layout.addWidget(margin_box, 0, 0) margins_cursor_layout.addWidget(margin_spin.spinbox, 0, 1) margins_cursor_layout.addWidget(margin_spin.slabel, 0, 2) margins_cursor_layout.addWidget(cursor_box, 1, 0) margins_cursor_layout.addWidget(cursor_spin.spinbox, 1, 1) margins_cursor_layout.addWidget(cursor_spin.slabel, 1, 2) margins_cursor_layout.setColumnStretch(2, 100) # Layout interface interface_layout = QVBoxLayout() interface_layout.addWidget(vertdock_box) interface_layout.addWidget(verttabs_box) interface_layout.addWidget(animated_box) interface_layout.addWidget(tear_off_box) interface_layout.addLayout(margins_cursor_layout) interface_group.setLayout(interface_layout) # --- Status bar sbar_group = QGroupBox(_("Status bar")) show_status_bar = newcb(_("Show status bar"), 'show_status_bar') memory_box = newcb(_("Show memory usage every"), 'memory_usage/enable', tip=self.main.mem_status.toolTip()) memory_spin = self.create_spinbox("", _(" ms"), 'memory_usage/timeout', min_=100, max_=1000000, step=100) memory_box.toggled.connect(memory_spin.setEnabled) memory_spin.setEnabled(self.get_option('memory_usage/enable')) memory_box.setEnabled(self.main.mem_status.is_supported()) memory_spin.setEnabled(self.main.mem_status.is_supported()) cpu_box = newcb(_("Show CPU usage every"), 'cpu_usage/enable', tip=self.main.cpu_status.toolTip()) cpu_spin = self.create_spinbox("", _(" ms"), 'cpu_usage/timeout', min_=100, max_=1000000, step=100) cpu_box.toggled.connect(cpu_spin.setEnabled) cpu_spin.setEnabled(self.get_option('cpu_usage/enable')) cpu_box.setEnabled(self.main.cpu_status.is_supported()) cpu_spin.setEnabled(self.main.cpu_status.is_supported()) status_bar_o = self.get_option('show_status_bar') show_status_bar.toggled.connect(memory_box.setEnabled) show_status_bar.toggled.connect(memory_spin.setEnabled) show_status_bar.toggled.connect(cpu_box.setEnabled) show_status_bar.toggled.connect(cpu_spin.setEnabled) memory_box.setEnabled(status_bar_o) memory_spin.setEnabled(status_bar_o) cpu_box.setEnabled(status_bar_o) cpu_spin.setEnabled(status_bar_o) # Layout status bar cpu_memory_layout = QGridLayout() cpu_memory_layout.addWidget(memory_box, 0, 0) cpu_memory_layout.addWidget(memory_spin, 0, 1) cpu_memory_layout.addWidget(cpu_box, 1, 0) cpu_memory_layout.addWidget(cpu_spin, 1, 1) sbar_layout = QVBoxLayout() sbar_layout.addWidget(show_status_bar) sbar_layout.addLayout(cpu_memory_layout) sbar_group.setLayout(sbar_layout) # --- Screen resolution Group (hidpi) screen_resolution_group = QGroupBox(_("Screen resolution")) screen_resolution_bg = QButtonGroup(screen_resolution_group) screen_resolution_label = QLabel(_("Configuration for high DPI " "screens<br><br>" "Please see " "<a href=\"{0}\">{0}</a><> " "for more information about " "these options (in " "English).").format(HDPI_QT_PAGE)) screen_resolution_label.setWordWrap(True) normal_radio = self.create_radiobutton( _("Normal"), 'normal_screen_resolution', button_group=screen_resolution_bg) auto_scale_radio = self.create_radiobutton( _("Enable auto high DPI scaling"), 'high_dpi_scaling', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays"), restart=True) custom_scaling_radio = self.create_radiobutton( _("Set a custom high DPI scaling"), 'high_dpi_custom_scale_factor', button_group=screen_resolution_bg, tip=_("Set this for high DPI displays when " "auto scaling does not work"), restart=True) custom_scaling_edit = self.create_lineedit( "", 'high_dpi_custom_scale_factors', tip=_("Enter values for different screens " "separated by semicolons ';', " "float values are supported"), alignment=Qt.Horizontal, regex=r"[0-9]+(?:\.[0-9]*)(;[0-9]+(?:\.[0-9]*))*", restart=True) normal_radio.toggled.connect(custom_scaling_edit.setDisabled) auto_scale_radio.toggled.connect(custom_scaling_edit.setDisabled) custom_scaling_radio.toggled.connect(custom_scaling_edit.setEnabled) # Layout Screen resolution screen_resolution_layout = QVBoxLayout() screen_resolution_layout.addWidget(screen_resolution_label) screen_resolution_inner_layout = QGridLayout() screen_resolution_inner_layout.addWidget(normal_radio, 0, 0) screen_resolution_inner_layout.addWidget(auto_scale_radio, 1, 0) screen_resolution_inner_layout.addWidget(custom_scaling_radio, 2, 0) screen_resolution_inner_layout.addWidget(custom_scaling_edit, 2, 1) screen_resolution_layout.addLayout(screen_resolution_inner_layout) screen_resolution_group.setLayout(screen_resolution_layout) tabs = QTabWidget() tabs.addTab(self.create_tab(screen_resolution_group, interface_group), _("Interface")) tabs.addTab(self.create_tab(general_group, sbar_group), _("Advanced Settings")) vlayout = QVBoxLayout() vlayout.addWidget(tabs) self.setLayout(vlayout)
def setup(self): """Setup the ShortcutEditor with the provided arguments.""" # Widgets icon_info = HelperToolButton() icon_info.setIcon(get_std_icon('MessageBoxInformation')) layout_icon_info = QVBoxLayout() layout_icon_info.setContentsMargins(0, 0, 0, 0) layout_icon_info.setSpacing(0) layout_icon_info.addWidget(icon_info) layout_icon_info.addStretch(100) self.label_info = QLabel() self.label_info.setText( _("Press the new shortcut and select 'Ok' to confirm, " "click 'Cancel' to revert to the previous state, " "or use 'Clear' to unbind the command from a shortcut.")) self.label_info.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.label_info.setWordWrap(True) layout_info = QHBoxLayout() layout_info.setContentsMargins(0, 0, 0, 0) layout_info.addLayout(layout_icon_info) layout_info.addWidget(self.label_info) layout_info.setStretch(1, 100) self.label_current_sequence = QLabel(_("Current shortcut:")) self.text_current_sequence = QLabel(self.current_sequence) self.label_new_sequence = QLabel(_("New shortcut:")) self.text_new_sequence = ShortcutLineEdit(self) self.text_new_sequence.setPlaceholderText(_("Press shortcut.")) self.helper_button = HelperToolButton() self.helper_button.setIcon(QIcon()) self.label_warning = QLabel() self.label_warning.setWordWrap(True) self.label_warning.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.button_default = QPushButton(_('Default')) self.button_ok = QPushButton(_('Ok')) self.button_ok.setEnabled(False) self.button_clear = QPushButton(_('Clear')) self.button_cancel = QPushButton(_('Cancel')) button_box = QHBoxLayout() button_box.addWidget(self.button_default) button_box.addStretch(100) button_box.addWidget(self.button_ok) button_box.addWidget(self.button_clear) button_box.addWidget(self.button_cancel) # New Sequence button box self.btn_clear_sequence = create_toolbutton( self, icon=ima.icon('editclear'), tip=_("Clear all entered key sequences"), triggered=self.clear_new_sequence) self.button_back_sequence = create_toolbutton( self, icon=ima.icon('ArrowBack'), tip=_("Remove last key sequence entered"), triggered=self.back_new_sequence) newseq_btnbar = QHBoxLayout() newseq_btnbar.setSpacing(0) newseq_btnbar.setContentsMargins(0, 0, 0, 0) newseq_btnbar.addWidget(self.button_back_sequence) newseq_btnbar.addWidget(self.btn_clear_sequence) # Setup widgets self.setWindowTitle(_('Shortcut: {0}').format(self.name)) self.helper_button.setToolTip('') style = """ QToolButton { margin:1px; border: 0px solid grey; padding:0px; border-radius: 0px; }""" self.helper_button.setStyleSheet(style) icon_info.setToolTip('') icon_info.setStyleSheet(style) # Layout layout_sequence = QGridLayout() layout_sequence.setContentsMargins(0, 0, 0, 0) layout_sequence.addLayout(layout_info, 0, 0, 1, 4) layout_sequence.addItem(QSpacerItem(15, 15), 1, 0, 1, 4) layout_sequence.addWidget(self.label_current_sequence, 2, 0) layout_sequence.addWidget(self.text_current_sequence, 2, 2) layout_sequence.addWidget(self.label_new_sequence, 3, 0) layout_sequence.addWidget(self.helper_button, 3, 1) layout_sequence.addWidget(self.text_new_sequence, 3, 2) layout_sequence.addLayout(newseq_btnbar, 3, 3) layout_sequence.addWidget(self.label_warning, 4, 2, 1, 2) layout_sequence.setColumnStretch(2, 100) layout_sequence.setRowStretch(4, 100) layout = QVBoxLayout(self) layout.addLayout(layout_sequence) layout.addSpacing(10) layout.addLayout(button_box) layout.setSizeConstraint(layout.SetFixedSize) # Signals self.button_ok.clicked.connect(self.accept_override) self.button_clear.clicked.connect(self.unbind_shortcut) self.button_cancel.clicked.connect(self.reject) self.button_default.clicked.connect(self.set_sequence_to_default) # Set all widget to no focus so that we can register <Tab> key # press event. widgets = (self.label_warning, self.helper_button, self.text_new_sequence, self.button_clear, self.button_default, self.button_cancel, self.button_ok, self.btn_clear_sequence, self.button_back_sequence) for w in widgets: w.setFocusPolicy(Qt.NoFocus) w.clearFocus()
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')
class TestLauncher(QMainWindow): """PythonQwt Test Launcher main window""" ROWS = 5 def __init__(self, parent=None): super(TestLauncher, self).__init__(parent) from qwt import __version__ self.setObjectName("TestLauncher") self.setWindowIcon(self.get_std_icon("FileDialogListView")) self.setWindowTitle("PythonQwt %s - Test Launcher" % __version__) self.setCentralWidget(QWidget()) self.grid_layout = QGridLayout() self.centralWidget().setLayout(self.grid_layout) self.test_nb = None self.fill_layout() self.statusBar().show() self.setStatusTip("Click on any button to run a test") def get_std_icon(self, name): """Return Qt standard icon""" return self.style().standardIcon(getattr(QStyle, "SP_" + name)) def fill_layout(self): """Fill grid layout""" import qwt for fname in get_tests(qwt): self.add_test(fname) toolbar = QToolBar(self) all_act = QAction(self.get_std_icon("DialogYesButton"), "", self) all_act.setIconText("Run all tests") all_act.triggered.connect(lambda checked: run_all_tests(wait=False)) folder_act = QAction(self.get_std_icon("DirOpenIcon"), "", self) folder_act.setIconText("Open tests folder") open_test_folder = lambda checked: os.startfile(TEST_PATH) folder_act.triggered.connect(open_test_folder) about_act = QAction(self.get_std_icon("FileDialogInfoView"), "", self) about_act.setIconText("About") about_act.triggered.connect(self.about) for action in (all_act, folder_act, None, about_act): if action is None: toolbar.addSeparator() else: toolbar.addAction(action) toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.addToolBar(toolbar) def add_test(self, fname): """Add new test""" if self.test_nb is None: self.test_nb = 0 self.test_nb += 1 row = (self.test_nb - 1) % self.ROWS column = (self.test_nb - 1) // self.ROWS bname = osp.basename(fname) button = QToolButton(self) button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) shot = osp.join(TEST_PATH, "data", bname.replace(".py", ".png")) if osp.isfile(shot): button.setIcon(QIcon(shot)) else: button.setIcon(self.get_std_icon("DialogYesButton")) button.setText(bname) button.setToolTip(fname) button.setIconSize(QSize(130, 80)) button.clicked.connect( lambda checked=None, fname=fname: run_test(fname)) self.grid_layout.addWidget(button, row, column) def about(self): """About test launcher""" from qtpy.QtCore import __version__ as qt_version QMessageBox.about( self, "About " + self.windowTitle(), """<b>%s</b><p>Developped by Pierre Raybaut <br>Copyright © 2020 Pierre Raybaut <p>Python %s, Qt %s on %s""" % ( self.windowTitle(), platform.python_version(), qt_version, platform.system(), ), )
def _make_grid_layout(self): """builds the QGridLayout""" grid = QGridLayout() irow = 0 #------------------------- grid.addWidget(self.location_label, irow, 0) grid.addWidget(self.cid_label, irow, 1) grid.addWidget(self.x_label, irow, 2) grid.addWidget(self.y_label, irow, 3) grid.addWidget(self.z_label, irow, 4) irow += 1 add_row(irow, grid, self.p1_label, self.p1_cid_pulldown, self.p1_x_edit, self.p1_y_edit, self.p1_z_edit) irow += 1 add_row(irow, grid, self.p3_label, self.p3_cid_pulldown, self.p3_x_edit, self.p3_y_edit, self.p3_z_edit) irow += 1 grid.addWidget(self.plane_label, irow, 0) irow += 1 grid.addWidget(self.method_label, irow, 0) grid.addWidget(self.method_pulldown, irow, 1) irow += 1 grid.addWidget(self.zaxis_method_label, irow, 0) grid.addWidget(self.zaxis_method_pulldown, irow, 1) irow += 1 add_row(irow, grid, self.zaxis_label, self.zaxis_cid_pulldown, self.zaxis_x_edit, self.zaxis_y_edit, self.zaxis_z_edit) irow += 1 add_row(irow, grid, self.p2_label, self.p2_cid_pulldown, self.p2_x_edit, self.p2_y_edit, self.p2_z_edit) irow += 1 #----------------------------------------- grid.addWidget(self.case_info_label, irow, 0) irow += 1 grid.addWidget(self.time_label, irow, 0) grid.addWidget(self.times_pulldown, irow, 1) irow += 1 grid.addWidget(self.nplanes_label, irow, 0) grid.addWidget(self.nplanes_spinner, irow, 1) irow += 1 #----------------------------------------- grid.addWidget(self.additional_params_label, irow, 0) irow += 1 grid.addWidget(self.plane_color_label, irow, 0) grid.addWidget(self.plane_color_edit, irow, 1) irow += 1 grid.addWidget(self.plane_opacity_label, irow, 0) grid.addWidget(self.plane_opacity_edit, irow, 1) irow += 1 #---------------------------------------------- return grid
def setup_page(self): newcb = self.create_checkbox # --- Introspection --- # Basic features group basic_features_group = QGroupBox(_("Basic features")) completion_box = newcb(_("Enable code completion"), 'code_completion') code_snippets_box = newcb(_("Enable code snippets"), 'code_snippets') enable_hover_hints_box = newcb( _("Enable hover hints"), 'enable_hover_hints', tip=_("If enabled, hovering the mouse pointer over an object\n" "name will display that object's signature and/or\n" "docstring (if present).")) goto_definition_box = newcb( _("Enable Go to definition"), 'jedi_definition', tip=_("If enabled, left-clicking on an object name while \n" "pressing the {} key will go to that object's definition\n" "(if resolved).".format(self.CTRL))) follow_imports_box = newcb(_("Follow imports when going to a " "definition"), 'jedi_definition/follow_imports') show_signature_box = newcb(_("Show calltips"), 'jedi_signature_help') basic_features_layout = QVBoxLayout() basic_features_layout.addWidget(completion_box) basic_features_layout.addWidget(code_snippets_box) basic_features_layout.addWidget(enable_hover_hints_box) basic_features_layout.addWidget(goto_definition_box) basic_features_layout.addWidget(follow_imports_box) basic_features_layout.addWidget(show_signature_box) basic_features_group.setLayout(basic_features_layout) # Advanced group advanced_group = QGroupBox(_("Advanced")) modules_textedit = self.create_textedit( _("Preload the following modules to make completion faster " "and more accurate:"), 'preload_modules' ) if is_dark_interface(): modules_textedit.textbox.setStyleSheet( "border: 1px solid #32414B;" ) advanced_layout = QVBoxLayout() advanced_layout.addWidget(modules_textedit) advanced_group.setLayout(advanced_layout) # --- Linting --- # Linting options linting_label = QLabel(_("Spyder can optionally highlight syntax " "errors and possible problems with your " "code in the editor.")) linting_label.setOpenExternalLinks(True) linting_label.setWordWrap(True) linting_check = self.create_checkbox( _("Enable basic linting"), 'pyflakes') linting_complexity_box = self.create_checkbox( _("Enable complexity linting with " "the Mccabe package"), 'mccabe') # Linting layout linting_layout = QVBoxLayout() linting_layout.addWidget(linting_label) linting_layout.addWidget(linting_check) linting_layout.addWidget(linting_complexity_box) linting_widget = QWidget() linting_widget.setLayout(linting_layout) # --- Code style tab --- # Code style label pep_url = ( '<a href="https://www.python.org/dev/peps/pep-0008">PEP 8</a>') code_style_codes_url = _( "<a href='http://pycodestyle.pycqa.org/en/stable" "/intro.html#error-codes'>pycodestyle error codes</a>") code_style_label = QLabel( _("Spyder can use pycodestyle to analyze your code for " "conformance to the {} convention. You can also " "manually show or hide specific warnings by their " "{}.").format(pep_url, code_style_codes_url)) code_style_label.setOpenExternalLinks(True) code_style_label.setWordWrap(True) # Code style checkbox self.code_style_check = self.create_checkbox( _("Enable code style linting"), 'pycodestyle') # Code style options self.code_style_filenames_match = self.create_lineedit( _("Only check filenames matching these patterns:"), 'pycodestyle/filename', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Check Python files: *.py")) self.code_style_exclude = self.create_lineedit( _("Exclude files or directories matching these patterns:"), 'pycodestyle/exclude', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Exclude all test files: (?!test_).*\\.py")) code_style_select = self.create_lineedit( _("Show the following errors or warnings:").format( code_style_codes_url), 'pycodestyle/select', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: E113, W391")) code_style_ignore = self.create_lineedit( _("Ignore the following errors or warnings:"), 'pycodestyle/ignore', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: E201, E303")) code_style_max_line_length = self.create_spinbox( _("Maximum allowed line length:"), None, 'pycodestyle/max_line_length', min_=10, max_=500, step=1, tip=_("Default is 79")) # Code style layout code_style_g_layout = QGridLayout() code_style_g_layout.addWidget( self.code_style_filenames_match.label, 1, 0) code_style_g_layout.addWidget( self.code_style_filenames_match.textbox, 1, 1) code_style_g_layout.addWidget(self.code_style_exclude.label, 2, 0) code_style_g_layout.addWidget(self.code_style_exclude.textbox, 2, 1) code_style_g_layout.addWidget(code_style_select.label, 3, 0) code_style_g_layout.addWidget(code_style_select.textbox, 3, 1) code_style_g_layout.addWidget(code_style_ignore.label, 4, 0) code_style_g_layout.addWidget(code_style_ignore.textbox, 4, 1) code_style_g_layout.addWidget(code_style_max_line_length.plabel, 5, 0) code_style_g_layout.addWidget( code_style_max_line_length.spinbox, 5, 1) # Set Code style options enabled/disabled code_style_g_widget = QWidget() code_style_g_widget.setLayout(code_style_g_layout) code_style_g_widget.setEnabled(self.get_option('pycodestyle')) self.code_style_check.toggled.connect(code_style_g_widget.setEnabled) # Code style layout code_style_layout = QVBoxLayout() code_style_layout.addWidget(code_style_label) code_style_layout.addWidget(self.code_style_check) code_style_layout.addWidget(code_style_g_widget) code_style_widget = QWidget() code_style_widget.setLayout(code_style_layout) # --- Docstring tab --- # Docstring style label numpy_url = ( "<a href='https://numpydoc.readthedocs.io/en/" "latest/format.html'>Numpy</a>") pep257_url = ( "<a href='https://www.python.org/dev/peps/pep-0257/'>PEP 257</a>") docstring_style_codes = _( "<a href='http://www.pydocstyle.org/en/stable" "/error_codes.html'>page</a>") docstring_style_label = QLabel( _("Here you can decide if you want to perform style analysis on " "your docstrings according to the {} or {} conventions. You can " "also decide if you want to show or ignore specific errors, " "according to the codes found on this {}.").format( numpy_url, pep257_url, docstring_style_codes)) docstring_style_label.setOpenExternalLinks(True) docstring_style_label.setWordWrap(True) # Docstring style checkbox self.docstring_style_check = self.create_checkbox( _("Enable docstring style linting"), 'pydocstyle') # Docstring style options docstring_style_convention = self.create_combobox( _("Choose the convention used to lint docstrings: "), (("Numpy", 'numpy'), ("PEP 257", 'pep257'), ("Custom", 'custom')), 'pydocstyle/convention') self.docstring_style_select = self.create_lineedit( _("Show the following errors:"), 'pydocstyle/select', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: D413, D414")) self.docstring_style_ignore = self.create_lineedit( _("Ignore the following errors:"), 'pydocstyle/ignore', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Example codes: D107, D402")) self.docstring_style_match = self.create_lineedit( _("Only check filenames matching these patterns:"), 'pydocstyle/match', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Skip test files: (?!test_).*\\.py")) self.docstring_style_match_dir = self.create_lineedit( _("Only check in directories matching these patterns:"), 'pydocstyle/match_dir', alignment=Qt.Horizontal, word_wrap=False, placeholder=_("Skip dot directories: [^\\.].*")) # Custom option handling docstring_style_convention.combobox.currentTextChanged.connect( self.setup_docstring_style_convention) current_convention = docstring_style_convention.combobox.currentText() self.setup_docstring_style_convention(current_convention) # Docstring style layout docstring_style_g_layout = QGridLayout() docstring_style_g_layout.addWidget( docstring_style_convention.label, 1, 0) docstring_style_g_layout.addWidget( docstring_style_convention.combobox, 1, 1) docstring_style_g_layout.addWidget( self.docstring_style_select.label, 2, 0) docstring_style_g_layout.addWidget( self.docstring_style_select.textbox, 2, 1) docstring_style_g_layout.addWidget( self.docstring_style_ignore.label, 3, 0) docstring_style_g_layout.addWidget( self.docstring_style_ignore.textbox, 3, 1) docstring_style_g_layout.addWidget( self.docstring_style_match.label, 4, 0) docstring_style_g_layout.addWidget( self.docstring_style_match.textbox, 4, 1) docstring_style_g_layout.addWidget( self.docstring_style_match_dir.label, 5, 0) docstring_style_g_layout.addWidget( self.docstring_style_match_dir.textbox, 5, 1) # Set Docstring style options enabled/disabled docstring_style_g_widget = QWidget() docstring_style_g_widget.setLayout(docstring_style_g_layout) docstring_style_g_widget.setEnabled(self.get_option('pydocstyle')) self.docstring_style_check.toggled.connect( docstring_style_g_widget.setEnabled) # Docstring style layout docstring_style_layout = QVBoxLayout() docstring_style_layout.addWidget(docstring_style_label) docstring_style_layout.addWidget(self.docstring_style_check) docstring_style_layout.addWidget(docstring_style_g_widget) docstring_style_widget = QWidget() docstring_style_widget.setLayout(docstring_style_layout) # --- Advanced tab --- # Advanced label advanced_label = QLabel( _("<b>Warning</b>: Only modify these values if " "you know what you're doing!")) advanced_label.setWordWrap(True) advanced_label.setAlignment(Qt.AlignJustify) # Advanced settings checkbox self.advanced_options_check = self.create_checkbox( _("Enable advanced settings"), 'advanced/enabled') # Advanced options self.advanced_module = self.create_lineedit( _("Module for the Python language server: "), 'advanced/module', alignment=Qt.Horizontal, word_wrap=False) self.advanced_host = self.create_lineedit( _("IP Address and port to bind the server to: "), 'advanced/host', alignment=Qt.Horizontal, word_wrap=False) self.advanced_port = self.create_spinbox( ":", "", 'advanced/port', min_=1, max_=65535, step=1) self.external_server = self.create_checkbox( _("This is an external server"), 'advanced/external') self.use_stdio = self.create_checkbox( _("Use stdio pipes to communicate with server"), 'advanced/stdio') self.use_stdio.stateChanged.connect(self.disable_tcp) self.external_server.stateChanged.connect(self.disable_stdio) # Advanced layout advanced_g_layout = QGridLayout() advanced_g_layout.addWidget(self.advanced_module.label, 1, 0) advanced_g_layout.addWidget(self.advanced_module.textbox, 1, 1) advanced_g_layout.addWidget(self.advanced_host.label, 2, 0) advanced_host_port_g_layout = QGridLayout() advanced_host_port_g_layout.addWidget(self.advanced_host.textbox, 1, 0) advanced_host_port_g_layout.addWidget(self.advanced_port.plabel, 1, 1) advanced_host_port_g_layout.addWidget(self.advanced_port.spinbox, 1, 2) advanced_g_layout.addLayout(advanced_host_port_g_layout, 2, 1) # External server and stdio options layout advanced_server_layout = QVBoxLayout() advanced_server_layout.addWidget(self.external_server) advanced_server_layout.addWidget(self.use_stdio) advanced_options_layout = QVBoxLayout() advanced_options_layout.addLayout(advanced_g_layout) advanced_options_layout.addLayout(advanced_server_layout) # Set advanced options enabled/disabled advanced_options_widget = QWidget() advanced_options_widget.setLayout(advanced_options_layout) advanced_options_widget.setEnabled(self.get_option('advanced/enabled')) self.advanced_options_check.toggled.connect( advanced_options_widget.setEnabled) self.advanced_options_check.toggled.connect( self.show_advanced_warning) # Advanced options layout advanced_layout = QVBoxLayout() advanced_layout.addWidget(advanced_label) advanced_layout.addWidget(self.advanced_options_check) advanced_layout.addWidget(advanced_options_widget) advanced_widget = QWidget() advanced_widget.setLayout(advanced_layout) # --- Other servers tab --- # Section label servers_label = QLabel( _("Spyder uses the <a href=\"{lsp_url}\">Language Server " "Protocol</a> to provide code completion and linting " "for its Editor. Here, you can setup and configure LSP servers " "for languages other than Python, so Spyder can provide such " "features for those languages as well." ).format(lsp_url=LSP_URL)) servers_label.setOpenExternalLinks(True) servers_label.setWordWrap(True) servers_label.setAlignment(Qt.AlignJustify) # Servers table table_group = QGroupBox(_('Available servers:')) self.table = LSPServerTable(self, text_color=ima.MAIN_FG_COLOR) self.table.setMaximumHeight(150) table_layout = QVBoxLayout() table_layout.addWidget(self.table) table_group.setLayout(table_layout) # Buttons self.reset_btn = QPushButton(_("Reset to default values")) self.new_btn = QPushButton(_("Set up a new server")) self.delete_btn = QPushButton(_("Delete currently selected server")) self.delete_btn.setEnabled(False) # Slots connected to buttons self.new_btn.clicked.connect(self.create_new_server) self.reset_btn.clicked.connect(self.reset_to_default) self.delete_btn.clicked.connect(self.delete_server) # Buttons layout btns = [self.new_btn, self.delete_btn, self.reset_btn] buttons_layout = QGridLayout() for i, btn in enumerate(btns): buttons_layout.addWidget(btn, i, 1) buttons_layout.setColumnStretch(0, 1) buttons_layout.setColumnStretch(1, 2) buttons_layout.setColumnStretch(2, 1) # Combined layout servers_widget = QWidget() servers_layout = QVBoxLayout() servers_layout.addSpacing(-10) servers_layout.addWidget(servers_label) servers_layout.addWidget(table_group) servers_layout.addSpacing(10) servers_layout.addLayout(buttons_layout) servers_widget.setLayout(servers_layout) # --- Tabs organization --- self.tabs = QTabWidget() self.tabs.addTab(self.create_tab(basic_features_group, advanced_group), _('Introspection')) self.tabs.addTab(self.create_tab(linting_widget), _('Linting')) self.tabs.addTab(self.create_tab(code_style_widget), _('Code style')) self.tabs.addTab(self.create_tab(docstring_style_widget), _('Docstring style')) self.tabs.addTab(self.create_tab(advanced_widget), _('Advanced')) self.tabs.addTab(self.create_tab(servers_widget), _('Other languages')) vlayout = QVBoxLayout() vlayout.addWidget(self.tabs) self.setLayout(vlayout)
def __init__(self, parent=None): super(KernelConnectionDialog, self).__init__(parent) self.setWindowTitle(_('Connect to an existing kernel')) main_label = QLabel(_( "<p>Please select the JSON connection file (<i>e.g.</i> " "<tt>kernel-1234.json</tt>) of the existing kernel, and enter " "the SSH information if connecting to a remote machine. " "To learn more about starting external kernels and connecting " "to them, see <a href=\"https://docs.spyder-ide.org/" "ipythonconsole.html#connect-to-an-external-kernel\">" "our documentation</a>.</p>")) main_label.setWordWrap(True) main_label.setAlignment(Qt.AlignJustify) main_label.setOpenExternalLinks(True) # Connection file cf_label = QLabel(_('Connection file:')) self.cf = QLineEdit() self.cf.setPlaceholderText(_('Kernel connection file path')) self.cf.setMinimumWidth(350) cf_open_btn = QPushButton(_('Browse')) cf_open_btn.clicked.connect(self.select_connection_file) cf_layout = QHBoxLayout() cf_layout.addWidget(cf_label) cf_layout.addWidget(self.cf) cf_layout.addWidget(cf_open_btn) # Remote kernel groupbox self.rm_group = QGroupBox(_("This is a remote kernel (via SSH)")) # SSH connection hn_label = QLabel(_('Hostname:')) self.hn = QLineEdit() pn_label = QLabel(_('Port:')) self.pn = QLineEdit() self.pn.setMaximumWidth(75) self.pn.setText('22') un_label = QLabel(_('Username:'******'Password:'******'SSH keyfile:')) self.pw = QLineEdit() self.pw.setEchoMode(QLineEdit.Password) self.pw_radio.toggled.connect(self.pw.setEnabled) self.kf_radio.toggled.connect(self.pw.setDisabled) self.kf = QLineEdit() kf_open_btn = QPushButton(_('Browse')) kf_open_btn.clicked.connect(self.select_ssh_key) kf_layout = QHBoxLayout() kf_layout.addWidget(self.kf) kf_layout.addWidget(kf_open_btn) kfp_label = QLabel(_('Passphase:')) self.kfp = QLineEdit() self.kfp.setPlaceholderText(_('Optional')) self.kfp.setEchoMode(QLineEdit.Password) self.kf_radio.toggled.connect(self.kf.setEnabled) self.kf_radio.toggled.connect(self.kfp.setEnabled) self.kf_radio.toggled.connect(kf_open_btn.setEnabled) self.kf_radio.toggled.connect(kfp_label.setEnabled) self.pw_radio.toggled.connect(self.kf.setDisabled) self.pw_radio.toggled.connect(self.kfp.setDisabled) self.pw_radio.toggled.connect(kf_open_btn.setDisabled) self.pw_radio.toggled.connect(kfp_label.setDisabled) # SSH layout ssh_layout = QGridLayout() ssh_layout.addWidget(hn_label, 0, 0, 1, 2) ssh_layout.addWidget(self.hn, 0, 2) ssh_layout.addWidget(pn_label, 0, 3) ssh_layout.addWidget(self.pn, 0, 4) ssh_layout.addWidget(un_label, 1, 0, 1, 2) ssh_layout.addWidget(self.un, 1, 2, 1, 3) # SSH authentication layout auth_layout = QGridLayout() auth_layout.addWidget(self.pw_radio, 1, 0) auth_layout.addWidget(pw_label, 1, 1) auth_layout.addWidget(self.pw, 1, 2) auth_layout.addWidget(self.kf_radio, 2, 0) auth_layout.addWidget(kf_label, 2, 1) auth_layout.addLayout(kf_layout, 2, 2) auth_layout.addWidget(kfp_label, 3, 1) auth_layout.addWidget(self.kfp, 3, 2) auth_group.setLayout(auth_layout) # Remote kernel layout rm_layout = QVBoxLayout() rm_layout.addLayout(ssh_layout) rm_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) rm_layout.addWidget(auth_group) self.rm_group.setLayout(rm_layout) self.rm_group.setCheckable(True) self.rm_group.setChecked(False) self.rm_group.toggled.connect(self.pw_radio.setChecked) # Ok and Cancel buttons self.accept_btns = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.accept_btns.accepted.connect(self.accept) self.accept_btns.rejected.connect(self.reject) # Dialog layout layout = QVBoxLayout(self) layout.addWidget(main_label) layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) layout.addLayout(cf_layout) layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 12))) layout.addWidget(self.rm_group) layout.addWidget(self.accept_btns)
def setup_page(self): names = self.get_option("names") try: names.pop(names.index(u'Custom')) except ValueError: pass custom_names = self.get_option("custom_names", []) # Interface options theme_group = QGroupBox(_("Main interface")) # Interface Widgets ui_themes = ['Automatic', 'Light', 'Dark'] ui_theme_choices = list( zip(ui_themes, [ui_theme.lower() for ui_theme in ui_themes])) ui_theme_combo = self.create_combobox(_('Interface theme'), ui_theme_choices, 'ui_theme', restart=True) styles = [str(txt) for txt in list(QStyleFactory.keys())] # Don't offer users the possibility to change to a different # style in Gtk-based desktops # See spyder-ide/spyder#2036. if is_gtk_desktop() and ('GTK+' in styles): styles = ['GTK+'] choices = list(zip(styles, [style.lower() for style in styles])) style_combo = self.create_combobox(_('Qt windows style'), choices, 'windows_style', default=self.main.default_style) self.style_combobox = style_combo.combobox themes = ['Spyder 2', 'Spyder 3'] icon_choices = list(zip(themes, [theme.lower() for theme in themes])) icons_combo = self.create_combobox(_('Icon theme'), icon_choices, 'icon_theme', restart=True) theme_comboboxes_layout = QGridLayout() theme_comboboxes_layout.addWidget(ui_theme_combo.label, 0, 0) theme_comboboxes_layout.addWidget(ui_theme_combo.combobox, 0, 1) theme_comboboxes_layout.addWidget(style_combo.label, 1, 0) theme_comboboxes_layout.addWidget(self.style_combobox, 1, 1) theme_comboboxes_layout.addWidget(icons_combo.label, 2, 0) theme_comboboxes_layout.addWidget(icons_combo.combobox, 2, 1) theme_layout = QVBoxLayout() theme_layout.addLayout(theme_comboboxes_layout) theme_group.setLayout(theme_layout) # Syntax coloring options syntax_group = QGroupBox(_("Syntax highlighting theme")) # Syntax Widgets edit_button = QPushButton(_("Edit selected scheme")) create_button = QPushButton(_("Create new scheme")) self.delete_button = QPushButton(_("Delete scheme")) self.reset_button = QPushButton(_("Reset to defaults")) self.preview_editor = SimpleCodeEditor(self) self.stacked_widget = QStackedWidget(self) self.scheme_editor_dialog = SchemeEditor(parent=self, stack=self.stacked_widget) self.scheme_choices_dict = {} schemes_combobox_widget = self.create_combobox('', [('', '')], 'selected') self.schemes_combobox = schemes_combobox_widget.combobox # Syntax layout syntax_layout = QGridLayout(syntax_group) btns = [ self.schemes_combobox, edit_button, self.reset_button, create_button, self.delete_button ] for i, btn in enumerate(btns): syntax_layout.addWidget(btn, i, 1) syntax_layout.setColumnStretch(0, 1) syntax_layout.setColumnStretch(1, 2) syntax_layout.setColumnStretch(2, 1) syntax_layout.setContentsMargins(0, 12, 0, 12) # Fonts options fonts_group = QGroupBox(_("Fonts")) # Fonts widgets self.plain_text_font = self.create_fontgroup( option='font', title=_("Plain text"), fontfilters=QFontComboBox.MonospacedFonts, without_group=True) self.rich_text_font = self.create_fontgroup(option='rich_font', title=_("Rich text"), without_group=True) # Fonts layouts fonts_layout = QGridLayout(fonts_group) fonts_layout.addWidget(self.plain_text_font.fontlabel, 0, 0) fonts_layout.addWidget(self.plain_text_font.fontbox, 0, 1) fonts_layout.addWidget(self.plain_text_font.sizelabel, 0, 2) fonts_layout.addWidget(self.plain_text_font.sizebox, 0, 3) fonts_layout.addWidget(self.rich_text_font.fontlabel, 1, 0) fonts_layout.addWidget(self.rich_text_font.fontbox, 1, 1) fonts_layout.addWidget(self.rich_text_font.sizelabel, 1, 2) fonts_layout.addWidget(self.rich_text_font.sizebox, 1, 3) fonts_layout.setRowStretch(fonts_layout.rowCount(), 1) # Left options layout options_layout = QVBoxLayout() options_layout.addWidget(theme_group) options_layout.addWidget(syntax_group) options_layout.addWidget(fonts_group) # Right preview layout preview_group = QGroupBox(_("Preview")) preview_layout = QVBoxLayout() preview_layout.addWidget(self.preview_editor) preview_group.setLayout(preview_layout) # Combined layout combined_layout = QGridLayout() combined_layout.setRowStretch(0, 1) combined_layout.setColumnStretch(1, 100) combined_layout.addLayout(options_layout, 0, 0) combined_layout.addWidget(preview_group, 0, 1) self.setLayout(combined_layout) # Signals and slots create_button.clicked.connect(self.create_new_scheme) edit_button.clicked.connect(self.edit_scheme) self.reset_button.clicked.connect(self.reset_to_default) self.delete_button.clicked.connect(self.delete_scheme) self.schemes_combobox.currentIndexChanged.connect(self.update_preview) self.schemes_combobox.currentIndexChanged.connect(self.update_buttons) # Setup for name in names: self.scheme_editor_dialog.add_color_scheme_stack(name) for name in custom_names: self.scheme_editor_dialog.add_color_scheme_stack(name, custom=True) self.update_combobox() self.update_preview() self.update_qt_style_combobox()
def setup_layout(self): self.instantiated = False layout = QGridLayout() self.load_cellfinder_project_button = add_button( "Load cellfinder project", layout, self.load_cellfinder_directory, 0, 0, minimum_width=200, ) self.load_registration_project_button = add_button( "Load amap project", layout, self.load_registration_directory, 1, 0, minimum_width=200, ) self.load_raw_data_directory_button = add_button( "Load data directory", layout, self.load_raw_data_directory, 2, 0, visibility=False, ) self.load_raw_data_single_button = add_button( "Load single image", layout, self.load_raw_data_single, 3, 0, visibility=False, ) self.load_downsampled_data_button = add_button( "Load downsampled_data", layout, self.load_downsampled_data, 4, 0, visibility=False, ) self.load_registration_button = add_button( "Load registration", layout, self.load_registration, 5, 0, visibility=False, ) self.load_heatmap_button = add_button( "Load heatmap", layout, self.load_heatmap, 6, 0, visibility=False, ) self.load_cells_button = add_button( "Load cells", layout, self.load_cells, 7, 0, visibility=False, ) self.save_cells_button = add_button( "Save cells", layout, self.save_cells, 8, 0, visibility=False, ) layout.setAlignment(QtCore.Qt.AlignTop) layout.setSpacing(4) self.status_label = QLabel() self.status_label.setText("Ready") layout.addWidget(self.status_label, 9, 0) self.viewer._status = "TESTING" self.setLayout(layout)
def initUI(self): labelMName = QLabel(u'被削材種') labelTN1 = QLabel(u'上面ダレ量(1箇所目)') labelTN2 = QLabel(u'上面ダレ量(2箇所目)') labelSN1 = QLabel(u'側面ダレ量(1箇所目)') labelSN2 = QLabel(u'側面ダレ量(2箇所目)') labelN1S = QLabel(u'側面ダレ面積(1箇所目)') labelN2S = QLabel(u'側面ダレ面積(2箇所目)') labelImg = QLabel(u'ダレ形状画像') self.image = QLabel() self.image.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.image.setFixedSize(200, 200) sel_img_btn = QPushButton(u'ファイル選択') comboMName = QComboBox(self) comboMName.addItem(u'材料A') comboMName.addItem(u'材料B') editTN1 = QLineEdit() editTN2 = QLineEdit() editSN1 = QLineEdit() editSN2 = QLineEdit() editN1S = QLineEdit() editN2S = QLineEdit() grid = QGridLayout() grid.setSpacing(10) # ラベルと入力フォームの作成 grid.addWidget(labelMName, 1, 0) grid.addWidget(comboMName, 1, 1) grid.addWidget(labelTN1, 2, 0) grid.addWidget(editTN1, 2, 1) grid.addWidget(labelTN2, 3, 0) grid.addWidget(editTN2, 3, 1) grid.addWidget(labelSN1, 4, 0) grid.addWidget(editSN1, 4, 1) grid.addWidget(labelSN2, 5, 0) grid.addWidget(editSN2, 5, 1) grid.addWidget(labelN1S, 6, 0) grid.addWidget(editN1S, 6, 1) grid.addWidget(labelN2S, 7, 0) grid.addWidget(editN2S, 7, 1) # 画像ファイルの入力・閲覧 hbox_img = QHBoxLayout() hbox_img.addWidget(labelImg) hbox_img.addWidget(self.image) hbox_img.addWidget(sel_img_btn) sel_img_btn.clicked.connect(self.showFileDialog) recomBtn = QPushButton(u"条件回答") recomBtn.clicked.connect(self.showResult) exitBtn = QPushButton(u"終了") exitBtn.clicked.connect(self.closeWindow) hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(recomBtn) hbox.addWidget(exitBtn) vbox = QVBoxLayout() vbox.addStretch(1) vbox.addLayout(grid) vbox.addLayout(hbox_img) vbox.addLayout(hbox) self.setLayout(vbox) self.resize(400, 150) self.setWindowTitle(u'Xebec Recommendation System') self.show()