class ParamCheckBox(ParamWidget): """ QCheckBox for operator control of boolean values. Parameters ---------- parameter : str Name of parameter this widget controls. default : bool, optional Default state of the box. parent : QWidget, optional """ def __init__(self, parameter, default=inspect._empty, parent=None): super().__init__(parameter, default=default, parent=parent) self.param_control = QCheckBox(parent=self) self.layout().addWidget(self.param_control) # Configure default QCheckBox position if default != inspect._empty: self.param_control.setChecked(default) def get_param_value(self): """ Return the checked state of the QCheckBox. """ return self.param_control.isChecked()
def dialog(title, text, icon, setting=None, default=None): if not getattr(settings, setting.upper()): return True check = QCheckBox() check.setText( 'Dont show this message again (can be reset via the preferences)') info = QMessageBox() info.setIcon(icon) info.setText(title) info.setInformativeText(text) info.setCheckBox(check) info.setStandardButtons(info.Cancel | info.Ok) if default == 'Cancel': info.setDefaultButton(info.Cancel) result = info.exec_() if result == info.Cancel: return False if check.isChecked(): setattr(settings, setting.upper(), False) save_settings() return True
class TermsFrame(ConfigBaseFrame): def __init__(self, parent=None): super(TermsFrame, self).__init__(parent) self.setObjectName("botnet_termsframe_start") self.disable_next_on_enter = True self.widget_layout = QVBoxLayout(self) self.setLayout(self.widget_layout) self.terms = QTextEdit(self) self.terms.setReadOnly(True) self.terms.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.terms.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.terms.setLineWrapMode(QTextEdit.WidgetWidth) self.terms.setWordWrapMode(QTextOption.WrapAnywhere) with open(os.path.join(sys.path[0], "../LICENSE")) as f: self.terms.setText(f.read()) self.widget_layout.addWidget(self.terms) self.accept = QCheckBox(self) self.accept.setChecked(False) self.accept.setText("Accept license") self.accept.setObjectName("license_checkbox") self.widget_layout.addWidget(self.accept) QMetaObject.connectSlotsByName(self) @Slot(bool) def on_license_checkbox_clicked(self, checked): self.set_next_enabled.emit(checked) self.disable_next_on_enter = not checked @Slot() def collect_info(self): return {"terms_accepted": self.accept.isChecked()}
class ImportAsWidget(QWidget): def __init__(self, parent, sourceWidget: QLineEdit): super().__init__(parent) self.sourceWidget = sourceWidget self.shouldImport = QCheckBox(_("Import As")) self.shouldImport.setCheckState(Qt.Checked) self.shouldImport.stateChanged.connect(self.activateName) self.nameEdit = ImportNameEdit(self) sourceWidget.textChanged.connect(self.syncText) self.layout = QHBoxLayout(self) self.layout.addWidget(self.shouldImport) self.layout.addWidget(self.nameEdit) self.layout.setContentsMargins(0, 0, 0, 0) # https://forum.qt.io/topic/87226/synchronize-2-qlineedit def syncText(self, name): if self.shouldImport.isChecked() and self.nameEdit.synced: self.nameEdit.setText(name) def activateName(self, state): if state: self.nameEdit.setReadOnly(False) self.nameEdit.setEnabled(True) else: self.nameEdit.setReadOnly(True) self.nameEdit.setEnabled(False)
def dialog(title, text, icon, setting=None, default=None): if not getattr(settings, setting.upper()): return True check = QCheckBox() check.setText('Dont show this message again (can be reset via the preferences)') info = QMessageBox() info.setIcon(icon) info.setText(title) info.setInformativeText(text) info.setCheckBox(check) info.setStandardButtons(info.Cancel | info.Ok) if default == 'Cancel': info.setDefaultButton(info.Cancel) result = info.exec_() if result == info.Cancel: return False if check.isChecked(): setattr(settings, setting.upper(), False) save_settings() return True
class DxfOutputDialog(OutputDialog): """Dialog for DXF format.""" format_name = "DXF" format_icon = "dxf.png" assembly_description = "The sketch of the parts will include in the file." frame_description = "There is only wire frame will be generated." def __init__(self, *args): """Type name: "DXF module".""" super(DxfOutputDialog, self).__init__(*args) # DXF version option version_label = QLabel("DXF version:", self) self.version_option = QComboBox(self) self.version_option.addItems( sorted((f"{name} - {DXF_VERSIONS_MAP[name]}" for name in DXF_VERSIONS), key=lambda v: v.split()[-1])) self.version_option.setCurrentIndex(self.version_option.count() - 1) self.version_option.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) layout = QHBoxLayout() layout.addWidget(version_label) layout.addWidget(self.version_option) self.main_layout.insertLayout(3, layout) # Parts interval self.use_interval = QCheckBox("Parts interval:", self) self.use_interval.setCheckState(Qt.Checked) self.use_interval.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred) self.interval_option = QDoubleSpinBox(self) self.interval_option.setValue(10) self.use_interval.stateChanged.connect(self.interval_option.setEnabled) layout = QHBoxLayout() layout.addWidget(self.use_interval) layout.addWidget(self.interval_option) layout.addItem( QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Preferred)) self.assembly_layout.insertLayout(2, layout) def do(self, dir_str: QDir) -> bool: """Output types: + Boundary + Frame """ file_name = dir_str.filePath(_get_name(self.filename_edit) + '.dxf') if isfile(file_name) and self.warn_radio.isChecked(): self.exist_warning(file_name) return False version = self.version_option.currentText().split()[0] if self.frame_radio.isChecked(): # Frame dxf_frame(self.vpoints, self.v_to_slvs, version, file_name) elif self.assembly_radio.isChecked(): # Boundary dxf_boundary( self.vpoints, self.link_radius.value(), self.interval_option.value() if self.use_interval.isChecked() else 0., version, file_name) return True
class CSVExport(QGroupBox): def __init__(self, parent=None): self._parent = parent super().__init__(parent=parent) self.setTitle("CSV Export") layout = QHBoxLayout() self.export = QPushButton("Export Grid Information") self.export.clicked.connect(self.save_CSV) self.detailed = QCheckBox("Detailed") layout.addWidget(self.export) layout.addWidget(self.detailed) self.setLayout(layout) self.setEnabled(False) def setEnabled(self, enabled): self.export.setEnabled(enabled) self.detailed.setEnabled(enabled) def save_CSV(self): self._parent.calculate_stress() filename, _ = QFileDialog.getSaveFileName( self, "Export Grid Information", self._parent.model.get_default_csv_filename(), "CSV (*.csv);;All Files (*)") if not filename: return self._parent.controller.write_stress_to_csv(filename, self.detailed.isChecked())
class ImportNamesDialog(QDialog): def __init__(self, parent=None): QDialog.__init__( self, parent, flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.setWindowTitle('Import Names') self.treeview = parent self.setAttribute(Qt.WA_DeleteOnClose) self.importSelected = QCheckBox(_("Import selected"), self) self.importSelected.setCheckState(Qt.Checked) self.importChildren = QCheckBox(_("Import children"), self) self.importChildren.setCheckState(Qt.Checked) self.replaceExisting = QCheckBox(_("Replace existing names"), self) self.replaceExisting.setCheckState(Qt.Checked) 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(self.importSelected) mainLayout.addWidget(self.importChildren) mainLayout.addWidget(self.replaceExisting) mainLayout.addWidget(self.buttonBox) self.setLayout(mainLayout) def accept(self) -> None: reply = { 'accepted': True, 'import_selected': self.importSelected.isChecked(), 'import_children': self.importChildren.isChecked(), 'replace_existing': self.replaceExisting.isChecked() } self.treeview.reply = reply super().accept() def reject(self) -> None: self.treeview.reply = {'accepted': False} super().reject()
class InterpolateBadsDialog(QDialog): def __init__(self, parent): super().__init__(parent) self.setWindowTitle("Interpolate bad channels") vbox = QVBoxLayout(self) grid = QGridLayout() grid.addWidget(QLabel("Reset bads:"), 0, 0) self.reset_bads_checkbox = QCheckBox() self.reset_bads_checkbox.setChecked(True) grid.addWidget(self.reset_bads_checkbox, 0, 1) grid.addWidget(QLabel("Mode:"), 1, 0) self.mode_select = QComboBox() self.modes = {"Accurate": "accurate", "Fast": "fast"} self.mode_select.addItems(self.modes.keys()) self.mode_select.setCurrentText("Accurate") grid.addWidget(self.mode_select, 1, 1) grid.addWidget(QLabel("Origin (x, y, z):"), 2, 0) hbox = QHBoxLayout() self.x = QDoubleSpinBox() self.x.setValue(0) self.x.setDecimals(3) hbox.addWidget(self.x) self.y = QDoubleSpinBox() self.y.setValue(0) self.y.setDecimals(3) hbox.addWidget(self.y) self.z = QDoubleSpinBox() self.z.setValue(0.04) self.z.setDecimals(3) hbox.addWidget(self.z) grid.addLayout(hbox, 2, 1) vbox.addLayout(grid) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(buttonbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) @property def origin(self): x = float(self.x.value()) y = float(self.y.value()) z = float(self.z.value()) return x, y, z @property def mode(self): return self.mode_select.currentText() @property def reset_bads(self): return self.reset_bads_checkbox.isChecked()
class FontLayout(QGridLayout): """Font selection""" def __init__(self, value, parent=None): QGridLayout.__init__(self) font = tuple_to_qfont(value) assert font is not None # Font family self.family = QFontComboBox(parent) self.family.setCurrentFont(font) self.addWidget(self.family, 0, 0, 1, -1) # Font size self.size = QComboBox(parent) self.size.setEditable(True) sizelist = list(range(6, 12)) + list(range(12, 30, 2)) + [36, 48, 72] size = font.pointSize() if size not in sizelist: sizelist.append(size) sizelist.sort() self.size.addItems([str(s) for s in sizelist]) self.size.setCurrentIndex(sizelist.index(size)) self.addWidget(self.size, 1, 0) # Italic or not self.italic = QCheckBox(_("Italic"), parent) self.italic.setChecked(font.italic()) self.addWidget(self.italic, 1, 1) # Bold or not self.bold = QCheckBox(_("Bold"), parent) self.bold.setChecked(font.bold()) self.addWidget(self.bold, 1, 2) def get_font(self): font = self.family.currentFont() font.setItalic(self.italic.isChecked()) font.setBold(self.bold.isChecked()) font.setPointSize(int(self.size.currentText())) return qfont_to_tuple(font)
class FeaturesWidget(QWidget): def __init__(self, features_settings): super(FeaturesWidget, self).__init__() # Settings self.feature_categories = DBWrapper().all_features.keys() self.features_settings = features_settings if not self.features_settings: self.features_settings['features'] = {} # Check if default values are defined for cat in self.feature_categories: # defaults to true, compute all features self.features_settings['features'][cat] = True self.features_widgets = {} features_layout = QGridLayout(self) # Add an option to toggle all features self.all_features = QCheckBox('All') self.all_features.setChecked(False) self.all_features.clicked.connect(self.toggle_all) features_layout.addWidget(self.all_features, 0, 0, 1, 1) if 'features' in self.features_settings.keys(): for idx, (label, sett) in enumerate( self.features_settings['features'].items()): self.features_widgets[label] = QCheckBox(label) self.features_widgets[label].setChecked(sett) self.features_widgets[label].clicked.connect(self.toggle) features_layout.addWidget(self.features_widgets[label], idx + 1, 0, 1, 1) def toggle_all(self): for label, sett in self.features_widgets.items(): self.features_widgets[label].setChecked( self.all_features.isChecked()) def toggle(self): if any([not x.isChecked() for x in self.features_widgets.values()]): self.all_features.setChecked(False) def to_dict(self): for key, value in self.features_widgets.items(): self.features_settings['features'][key] = value.isChecked()
class CheckBoxWidget(AbstractDataSetWidget): """ BoolItem widget """ def __init__(self, item, parent_layout): super(CheckBoxWidget, self).__init__(item, parent_layout) self.checkbox = QCheckBox(self.item.get_prop_value("display", "text")) self.checkbox.setToolTip(item.get_help()) self.group = self.checkbox self.store = self.item.get_prop("display", "store", None) if self.store: self.checkbox.stateChanged.connect(self.do_store) def get(self): """Override AbstractDataSetWidget method""" value = self.item.get() if value is not None: self.checkbox.setChecked(value) def set(self): """Override AbstractDataSetWidget method""" self.item.set(self.value()) def value(self): return self.checkbox.isChecked() def place_on_grid(self, layout, row, label_column, widget_column, row_span=1, column_span=1): """Override AbstractDataSetWidget method""" if not self.item.get_prop_value("display", "label"): widget_column = label_column column_span += 1 else: self.place_label(layout, row, label_column) layout.addWidget(self.group, row, widget_column, row_span, column_span) def do_store(self, state): self.store.set(self.item.instance, self.item.item, state) self.parent_layout.refresh_widgets()
class MessageCheckBox(QMessageBox): """ A QMessageBox derived widget that includes a QCheckBox aligned to the right under the message and on top of the buttons. """ def __init__(self, *args, **kwargs): super(MessageCheckBox, self).__init__(*args, **kwargs) self.setWindowModality(Qt.NonModal) self._checkbox = QCheckBox(self) # Set layout to include checkbox size = 9 check_layout = QVBoxLayout() check_layout.addItem(QSpacerItem(size, size)) check_layout.addWidget(self._checkbox, 0, Qt.AlignRight) check_layout.addItem(QSpacerItem(size, size)) # Access the Layout of the MessageBox to add the Checkbox layout = self.layout() if PYQT5: layout.addLayout(check_layout, 1, 2) else: layout.addLayout(check_layout, 1, 1) # --- Public API # Methods to access the checkbox def is_checked(self): return self._checkbox.isChecked() def set_checked(self, value): return self._checkbox.setChecked(value) def set_check_visible(self, value): self._checkbox.setVisible(value) def is_check_visible(self): self._checkbox.isVisible() def checkbox_text(self): self._checkbox.text() def set_checkbox_text(self, text): self._checkbox.setText(text)
class MessageCheckBox(QMessageBox): """ A QMessageBox derived widget that includes a QCheckBox aligned to the right under the message and on top of the buttons. """ def __init__(self, *args, **kwargs): super(MessageCheckBox, self).__init__(*args, **kwargs) self._checkbox = QCheckBox() # Set layout to include checkbox size = 9 check_layout = QVBoxLayout() check_layout.addItem(QSpacerItem(size, size)) check_layout.addWidget(self._checkbox, 0, Qt.AlignRight) check_layout.addItem(QSpacerItem(size, size)) # Access the Layout of the MessageBox to add the Checkbox layout = self.layout() if PYQT5: layout.addLayout(check_layout, 1, 2) else: layout.addLayout(check_layout, 1, 1) # --- Public API # Methods to access the checkbox def is_checked(self): return self._checkbox.isChecked() def set_checked(self, value): return self._checkbox.setChecked(value) def set_check_visible(self, value): self._checkbox.setVisible(value) def is_check_visible(self): self._checkbox.isVisible() def checkbox_text(self): self._checkbox.text() def set_checkbox_text(self, text): self._checkbox.setText(text)
class _SaveDialog(QFileDialog): def __init__(self, parent): QFileDialog.__init__(self, parent) self.setFileMode(QFileDialog.AnyFile) self.setAcceptMode(QFileDialog.AcceptSave) # Widgets self._chk_tight = QCheckBox('Tight layout') self._txt_dpi = QSpinBox() self._txt_dpi.setRange(1, 10000) self._txt_dpi.setSingleStep(50) self._txt_dpi.setSuffix('dpi') self._txt_dpi.setValue(100) # Layouts layout = self.layout() lyt_extras = QHBoxLayout() lyt_extras.addWidget(QLabel('Extra options')) lyt_extras.addWidget(self._chk_tight) lyt_extras.addWidget(QLabel('Resolution')) lyt_extras.addWidget(self._txt_dpi) layout.addLayout(lyt_extras, layout.rowCount(), 0, 1, layout.columnCount()) self.setLayout(layout) def tightLayout(self): return self._chk_tight.isChecked() def setTightLayout(self, tight): self._chk_tight.setChecked(tight) def dpi(self): return self._txt_dpi.value() def setDpi(self, dpi): self._txt_dpi.setValue(dpi)
class CanvasExportDialog(EasyDialog): NAME = _("Export canvas to PNG picture") sig_start = Signal(str, bool) def __init__(self, parent=None): EasyDialog.__init__(self, parent) self.setup_page() def setup_page(self): self.output = self.create_browsefile(_("File"), default="canvas.png", new=True) self.layout.addWidget(self.output) self.cbClipboard = QCheckBox("Copy to clipboard") self.cbClipboard.setChecked(True) self.layout.addWidget(self.cbClipboard) action = self.create_action() self.layout.addWidget(action) def apply(self): fn = self.output.lineedit.edit.text() copy2cb = True if self.cbClipboard.isChecked() else False self.sig_start.emit(fn, copy2cb)
class RunConfigOptions(QWidget): """Run configuration options""" 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 select_directory(self): """Select directory""" basedir = to_text_string(self.wd_edit.text()) if not osp.isdir(basedir): basedir = getcwd() directory = getexistingdirectory(self, _("Select directory"), basedir) if directory: self.wd_edit.setText(directory) self.wd_cb.setChecked(True) def set(self, options): self.runconf.set(options) self.clo_cb.setChecked(self.runconf.args_enabled) self.clo_edit.setText(self.runconf.args) self.wd_cb.setChecked(self.runconf.wdir_enabled) self.wd_edit.setText(self.runconf.wdir) if self.runconf.current: self.current_radio.setChecked(True) elif self.runconf.systerm: self.systerm_radio.setChecked(True) else: self.dedicated_radio.setChecked(True) self.interact_cb.setChecked(self.runconf.interact) self.show_kill_warning_cb.setChecked(self.runconf.show_kill_warning) self.post_mortem_cb.setChecked(self.runconf.post_mortem) self.pclo_cb.setChecked(self.runconf.python_args_enabled) self.pclo_edit.setText(self.runconf.python_args) def get(self): self.runconf.args_enabled = self.clo_cb.isChecked() self.runconf.args = to_text_string(self.clo_edit.text()) self.runconf.wdir_enabled = self.wd_cb.isChecked() self.runconf.wdir = to_text_string(self.wd_edit.text()) self.runconf.current = self.current_radio.isChecked() self.runconf.systerm = self.systerm_radio.isChecked() self.runconf.interact = self.interact_cb.isChecked() self.runconf.show_kill_warning = self.show_kill_warning_cb.isChecked() self.runconf.post_mortem = self.post_mortem_cb.isChecked() self.runconf.python_args_enabled = self.pclo_cb.isChecked() self.runconf.python_args = to_text_string(self.pclo_edit.text()) return self.runconf.get() def is_valid(self): wdir = to_text_string(self.wd_edit.text()) if not self.wd_cb.isChecked() or osp.isdir(wdir): return True else: QMessageBox.critical( self, _("Run configuration"), _("The following working directory is " "not valid:<br><b>%s</b>") % wdir ) return False def set_firstrun_o(self): CONF.set("run", ALWAYS_OPEN_FIRST_RUN_OPTION, self.firstrun_cb.isChecked())
class AnimationWindow(PyDialog): """ +-------------------+ | Animation | +-------------------------+ | icase ______ | | scale ______ Default | | time ______ Default | | | | nframes ______ Default | | resolu. ______ Default | | Dir ______ Browse | | iFrame ______ | | | | Animations: | | o Scale, Phase, Time | | | | x delete images | | x repeat | # TODO: change to an integer | x make gif | | | | Step, RunAll | | Close | +-------------------------+ TODO: add key-frame support """ def __init__(self, data, win_parent=None): PyDialog.__init__(self, data, win_parent) self.set_font_size(data['font_size']) self.istep = 0 self._animate_type = 'time' self._updated_animation = False self._active_deformation = 0. self._icase_fringe = data['icase_fringe'] self._icase_disp = data['icase_disp'] self._icase_vector = data['icase_vector'] self._default_name = data['name'] self._default_time = data['time'] self._default_fps = data['frames/sec'] self._default_resolution = data['resolution'] self._scale = data['scale'] self._default_scale = data['default_scale'] self._default_is_scale = data['is_scale'] self._arrow_scale = data['arrow_scale'] self._default_arrow_scale = data['default_arrow_scale'] self._phase = data['phase'] self._default_phase = data['default_phase'] self._default_dirname = data['dirname'] self._default_gif_name = os.path.join(self._default_dirname, data['name'] + '.gif') self.animation_types = [ 'Animate Scale', ] #'Animate Phase', #'Animate Time', #'Animate Frequency Sweep' #] self.setWindowTitle('Animate Model') self.create_widgets() self.create_layout() self.set_connections() self.is_gui = False if hasattr(self.win_parent, '_updated_legend'): self.win_parent.is_animate_open = True self.is_gui = True self.on_update_min_max_defaults() def create_widgets(self): """creates the menu objects""" self.box_scale = QGroupBox('Animate Scale') self.box_time = QGroupBox('Animate Time') icase_max = 1000 # TODO: update 1000 self.checkbox_fringe = QCheckBox('Animate') self.checkbox_fringe.setToolTip( 'Animate the fringe in addition to the deflection') #self.checkbox_disp = QCheckBox('Animate') self.checkbox_fringe.setEnabled(False) self.icase_fringe_label = QLabel("iCase (Fringe):") self.icase_fringe_edit = QSpinBox(self) self.icase_fringe_edit.setRange(0, icase_max) self.icase_fringe_edit.setSingleStep(1) if self._icase_fringe is not None: self.icase_fringe_edit.setValue(self._icase_fringe) self.icase_fringe_edit.setToolTip( 'Case Number for the Scale/Phase Animation Type.\n' 'Defaults to the result you had shown when you clicked "Create Animation".\n' 'iCase can be seen by clicking "Apply" on a result.') self.icase_disp_label = QLabel("iCase (Disp):") self.icase_disp_edit = QSpinBox(self) self.icase_disp_edit.setRange(1, icase_max) self.icase_disp_edit.setSingleStep(1) if self._icase_disp is not None: self.icase_disp_edit.setValue(self._icase_disp) self.checkbox_vector = QCheckBox('Animate') self.checkbox_vector.setToolTip( 'Animate the vector in addition to the deflection') self.checkbox_vector.hide() #self.checkbox_disp = QCheckBox('Animate') self.icase_vector_label = QLabel("iCase (Vector):") self.icase_vector_edit = QSpinBox(self) self.icase_vector_edit.setRange(1, icase_max) self.icase_vector_edit.setSingleStep(1) if self._icase_vector is not None: self.icase_vector_edit.setValue(self._icase_vector) self.scale_label = QLabel("True Scale:") self.scale_edit = QLineEdit(str(self._scale)) self.scale_button = QPushButton("Default") self.scale_edit.setToolTip('Scale factor of the "deflection"') self.scale_button.setToolTip('Sets the scale factor of the gif to %s' % self._scale) self.arrow_scale_label = QLabel("Arrow Scale:") self.arrow_scale_edit = QLineEdit(str(self._scale)) self.arrow_scale_button = QPushButton("Default") self.arrow_scale_edit.setToolTip('Scale factor of the "arrows"') self.arrow_scale_button.setToolTip( 'Sets the arrow scale factor of the gif to %s' % self._arrow_scale) self.arrow_scale_label.setVisible(False) self.arrow_scale_edit.setVisible(False) self.arrow_scale_button.setVisible(False) self.time_label = QLabel("Total Time (sec):") self.time_edit = QDoubleSpinBox(self) self.time_edit.setValue(self._default_time) self.time_edit.setRange(0.1, 5. * 60.) self.time_edit.setDecimals(2) self.time_edit.setSingleStep(0.1) self.time_button = QPushButton("Default") self.time_edit.setToolTip("Total time of the gif") self.time_button.setToolTip('Sets the total time of the gif to %.2f' % self._default_time) self.fps_label = QLabel("Frames/Second:") self.fps_edit = QSpinBox(self) self.fps_edit.setRange(1, 60) self.fps_edit.setSingleStep(1) self.fps_edit.setValue(self._default_fps) self.fps_button = QPushButton("Default") self.fps_edit.setToolTip( "A higher FPS is smoother, but may not play well for large gifs") self.fps_button.setToolTip('Sets the FPS to %s' % self._default_fps) self.resolution_label = QLabel("Resolution Scale:") self.resolution_edit = QSpinBox(self) self.resolution_edit.setRange(1, 5) self.resolution_edit.setSingleStep(1) self.resolution_edit.setValue(self._default_resolution) self.resolution_button = QPushButton("Default") self.resolution_edit.setToolTip( 'Scales the window resolution by an integer factor') self.resolution_button.setToolTip('Sets the resolution to %s' % self._default_resolution) #----------------- # Time plot self.fringe_label = QLabel("Fringe") self.icase_fringe_start_edit = QSpinBox(self) self.icase_fringe_start_edit.setRange(0, icase_max) self.icase_fringe_start_edit.setSingleStep(1) self.icase_fringe_start_edit.setValue(self._icase_fringe) self.icase_fringe_start_button = QPushButton("Default") self.icase_fringe_end_edit = QSpinBox(self) self.icase_fringe_end_edit.setRange(0, icase_max) self.icase_fringe_end_edit.setSingleStep(1) self.icase_fringe_end_edit.setValue(self._icase_fringe) self.icase_fringe_end_button = QPushButton("Default") self.icase_fringe_delta_edit = QSpinBox(self) self.icase_fringe_delta_edit.setRange(1, icase_max) self.icase_fringe_delta_edit.setSingleStep(1) self.icase_fringe_delta_edit.setValue(1) self.icase_fringe_delta_button = QPushButton("Default") self.displacement_label = QLabel("Displacement") self.icase_start = QLabel("iCase Start:") self.icase_disp_start_edit = QSpinBox(self) self.icase_disp_start_edit.setRange(0, icase_max) self.icase_disp_start_edit.setSingleStep(1) self.icase_disp_start_edit.setValue(self._icase_fringe) self.icase_disp_start_button = QPushButton("Default") self.icase_end_label = QLabel("iCase End:") self.icase_disp_end_edit = QSpinBox(self) self.icase_disp_end_edit.setRange(0, icase_max) self.icase_disp_end_edit.setSingleStep(1) self.icase_disp_end_edit.setValue(self._icase_fringe) self.icase_disp_end_button = QPushButton("Default") self.icase_delta_label = QLabel("iCase Delta:") self.icase_disp_delta_edit = QSpinBox(self) self.icase_disp_delta_edit.setRange(1, icase_max) self.icase_disp_delta_edit.setSingleStep(1) self.icase_disp_delta_edit.setValue(1) self.icase_disp_delta_button = QPushButton("Default") self.min_value_enable = QCheckBox() self.min_value_label = QLabel("Min Value:") self.min_value_edit = QLineEdit('') #self.min_value_edit.setRange(1, 1000) #self.min_value_edit.setSingleStep(1) #self.min_value_edit.setValue(1) self.min_value_button = QPushButton("Default") self.max_value_enable = QCheckBox() self.max_value_label = QLabel("Max Value:") self.max_value_edit = QLineEdit('') #self.min_value_edit.setRange(1, 1000) # TODO: update 1000 #self.min_value_edit.setSingleStep(1) #self.min_value_edit.setValue(1) self.max_value_button = QPushButton("Default") # TODO: enable this (uncomment) ------------------------------------------ #self.min_value_enable.hide() #self.min_value.hide() #self.min_value_edit.hide() #self.min_value_button.hide() #self.max_value_enable.hide() #self.max_value.hide() #self.max_value_edit.hide() #self.max_value_button.hide() # TODO: enable this (uncomment) ------------------------------------------ self.icase_disp_start_edit.setToolTip( 'The first frame of the animation') self.icase_disp_end_edit.setToolTip( 'The last frame of the animation\n' 'Assumes icase_start + nframes * icase_delta = icase_end') self.icase_disp_delta_edit.setToolTip( 'The frame step size (to skip non-consecutive results).\n' 'Frame skipping can be used to:\n' " - skip across results that you don't want to plot\n" ' - adjust the FPS') self.min_value_edit.setToolTip( 'Min value of the legend (not supported)') self.max_value_edit.setToolTip( 'Max value of the legend (not supported)') #'time' : 0., #'default_time' : 0, #'icase_start' : 10, #'icase_delta' : 3, #'min_value' : 0., #'max_value' : 1000., self.browse_folder_label = QLabel('Output Directory:') self.browse_folder_edit = QLineEdit(str(self._default_dirname)) self.browse_folder_button = QPushButton('Browse') self.browse_folder_edit.setToolTip( 'Location to save the png/gif files') self.gif_label = QLabel("Gif Filename:") self.gif_edit = QLineEdit(str(self._default_name + '.gif')) self.gif_button = QPushButton('Default') self.gif_edit.setToolTip('Name of the gif') self.gif_button.setToolTip('Sets the name of the gif to %s.gif' % self._default_name) # scale / phase if 1: # pragma: no cover self.animate_scale_radio = QRadioButton("Animate Scale") self.animate_phase_radio = QRadioButton("Animate Phase") self.animate_time_radio = QRadioButton("Animate Time") self.animate_freq_sweeep_radio = QRadioButton( "Animate Frequency Sweep") self.animate_scale_radio.setToolTip( 'Animates the scale factor based on the "Animation Type"') self.animate_time_radio.setToolTip( 'Animates the time/load/mode step') self.animate_scale_radio.setChecked(self._default_is_scale) self.animate_phase_radio.setChecked(not self._default_is_scale) self.animate_time_radio.setChecked(False) msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n' if self._default_phase is None: self.animate_phase_radio.setDisabled(True) self.animate_phase_radio.setToolTip( 'Animates the phase angle ' '(only for complex results)') msg += 'Phase : Animates the phase angle (only for complex results)\n' else: self.animate_phase_radio.setToolTip("Animates the phase angle") msg += 'Phase : Animates the phase angle\n' msg += ( 'Time : Animates the time/load/mode step\n' 'Freq Sweep : Animates a complex result across a range of frequencies ' '(not supported)\n') self.animate_freq_sweeep_radio.setDisabled(True) self.animate_freq_sweeep_radio.setToolTip( 'Animates a complex result across a range of frequencies (not supported)' ) else: msg = 'Scale : Animates the scale factor based on the "Animation Profile"\n' if self._default_phase is None: #self.animate_phase_radio.setDisabled(True) #self.animate_phase_radio.setToolTip('Animates the phase angle ' #'(only for complex results)') msg += 'Phase : Animates the phase angle (only for complex results)\n' else: #self.animate_phase_radio.setToolTip("Animates the phase angle") msg += 'Phase : Animates the phase angle\n' msg += ( 'Time : Animates the time/load/mode step\n' 'Freq Sweep : Animates a complex result across a range of frequencies ' '(not supported)\n') self.animation_type = QLabel("Animation Type:") animation_type = OrderedDict() #scale_msg = 'Scale\n' #phase_msg = 'Phase\n' #time_msg = 'Time\n' #animation_types = [ #('Animate Scale', scale_msg), #('Animate Phase', phase_msg), #('Animate Time', time_msg), ##'Animate Frequency Sweep' #] if self._phase is not None: self.animation_types.append('Animate Phase') self.animation_types.append('Animate Time') self.animation_profile_label = QLabel("Animation Profile:") self.animation_profile_edit = QComboBox() for animation_profile in ANIMATION_PROFILES: self.animation_profile_edit.addItem(animation_profile) self.animation_profile_edit.setToolTip('The profile for a scaled GIF') self.animation_type_edit = QComboBox() # TODO: add a tooltip for each item for animation_type in self.animation_types: self.animation_type_edit.addItem(animation_type) #self.animation_type_edit.setToolTip('The profile for a scaled GIF') self.animation_type_edit.setToolTip(msg.rstrip()) self.csv_profile_label = QLabel("CSV profile:") self.csv_profile_edit = QLineEdit() self.csv_profile_browse_button = QPushButton('Browse') self.csv_profile_edit.setToolTip( 'The path to the CSV file of (Scale1, Scale2, Scale3, ...)') #widget = QWidget(self) #horizontal_vertical_group = QButtonGroup(widget) #horizontal_vertical_group.addButton(self.animate_scale_radio) #horizontal_vertical_group.addButton(self.animate_phase_radio) #horizontal_vertical_group.addButton(self.animate_time_radio) #horizontal_vertical_group.addButton(self.animate_freq_sweeep_radio) # animate in gui self.animate_in_gui_checkbox = QCheckBox("Animate In GUI?") self.animate_in_gui_checkbox.setChecked(True) # make images self.make_images_checkbox = QCheckBox("Make images?") self.make_images_checkbox.setChecked(True) # make images self.overwrite_images_checkbox = QCheckBox("Overwrite images?") self.overwrite_images_checkbox.setChecked(True) # delete images when finished self.delete_images_checkbox = QCheckBox("Delete images when finished?") self.delete_images_checkbox.setChecked(True) # endless loop self.repeat_checkbox = QCheckBox("Repeat?") self.repeat_checkbox.setChecked(True) self.repeat_checkbox.setToolTip( "Repeating creates an infinitely looping gif") # endless loop self.make_gif_checkbox = QCheckBox("Make Gif?") if IS_IMAGEIO: self.make_gif_checkbox.setChecked(True) else: self.make_gif_checkbox.setChecked(False) self.make_gif_checkbox.setEnabled(False) self.make_gif_checkbox.setToolTip( 'imageio is not available; install it') # bottom buttons self.step_button = QPushButton("Step") self.wipe_button = QPushButton("Wipe Deformed Shape") self.stop_button = QPushButton("Stop") self.run_button = QPushButton("Run") self.step_button.setToolTip( 'Steps through the animation (for testing)') self.wipe_button.setToolTip( 'Removes the existing "deflecton" from the animation') self.stop_button.setToolTip('Stops the animation') self.run_button.setToolTip('Creates the animation') self.step_button.hide() self.wipe_button.hide() self.wipe_button.setEnabled(False) #self.wipe_button.hide() self.stop_button.setEnabled(False) self.cancel_button = QPushButton("Close") #self.set_grid_time(enabled=False) #self.set_grid_scale(enabled=self._default_is_scale) if self._default_phase: self.on_animate_phase(force=True) set_combo_box_text(self.animation_type_edit, 'Animate Phase') else: self.on_animate_scale(force=True) def set_connections(self): """creates button actions""" self.checkbox_vector.clicked.connect(self.on_checkbox_vector) self.scale_button.clicked.connect(self.on_default_scale) self.arrow_scale_button.clicked.connect(self.on_default_arrow_scale) self.time_button.clicked.connect(self.on_default_time) self.fps_button.clicked.connect(self.on_default_fps) self.resolution_button.clicked.connect(self.on_default_resolution) self.browse_folder_button.clicked.connect(self.on_browse_folder) self.csv_profile_browse_button.clicked.connect(self.on_browse_csv) self.gif_button.clicked.connect(self.on_default_name) self.step_button.clicked.connect(self.on_step) self.wipe_button.clicked.connect(self.on_wipe) self.stop_button.clicked.connect(self.on_stop) self.run_button.clicked.connect(self.on_run) self.min_value_enable.clicked.connect(self.on_min_value_enable) self.max_value_enable.clicked.connect(self.on_max_value_enable) self.min_value_button.clicked.connect(self.on_min_value_default) self.max_value_button.clicked.connect(self.on_max_value_default) self.icase_disp_start_button.clicked.connect( self.on_update_min_max_defaults) #self.animate_scale_radio.clicked.connect(self.on_animate_scale) #self.animate_phase_radio.clicked.connect(self.on_animate_phase) #self.animate_time_radio.clicked.connect(self.on_animate_time) self.animation_type_edit.currentIndexChanged.connect(self.on_animate) #self.animate_freq_sweeep_radio self.cancel_button.clicked.connect(self.on_cancel) self.animate_in_gui_checkbox.clicked.connect(self.on_animate_in_gui) self.animate_in_gui_checkbox.setChecked(True) self.on_animate_in_gui() def on_checkbox_vector(self): is_enabled = self.checkbox_vector.isEnabled() is_checked = self.checkbox_vector.isChecked() enable_edit = is_enabled and is_checked self.icase_vector_label.setEnabled(is_checked) self.icase_vector_edit.setEnabled(enable_edit) def on_animate_in_gui(self): animate_in_gui = self.animate_in_gui_checkbox.isChecked() enable = not animate_in_gui if HIDE_WHEN_INACTIVE: self.make_images_checkbox.setVisible(enable) self.delete_images_checkbox.setVisible(enable) self.make_gif_checkbox.setVisible(enable) self.repeat_checkbox.setVisible(enable) self.resolution_button.setVisible(enable) self.resolution_label.setVisible(enable) self.resolution_edit.setVisible(enable) self.gif_label.setVisible(enable) self.gif_edit.setVisible(enable) self.gif_button.setVisible(enable) self.browse_folder_label.setVisible(enable) self.browse_folder_button.setVisible(enable) self.browse_folder_edit.setVisible(enable) self.step_button.setEnabled(enable) self.make_images_checkbox.setEnabled(enable) self.delete_images_checkbox.setEnabled(enable) self.make_gif_checkbox.setEnabled(enable) self.repeat_checkbox.setEnabled(enable) self.resolution_button.setEnabled(enable) self.resolution_edit.setEnabled(enable) self.gif_edit.setEnabled(enable) self.gif_button.setEnabled(enable) self.browse_folder_button.setEnabled(enable) self.browse_folder_edit.setEnabled(enable) self.step_button.setEnabled(enable) #wipe_button def on_animate(self, value): """ animate pulldown Parameters ---------- value : int index in animation_types """ #animation_types = ['Animate Scale', 'Animate Phase', 'Animate Time', #'Animate Frequency Sweep'] animation_type = self.animation_types[value] if animation_type == 'Animate Scale': self.on_animate_scale() elif animation_type == 'Animate Phase': self.on_animate_phase() elif animation_type == 'Animate Time': self.on_animate_time() else: raise NotImplementedError('value = ', value) def on_animate_time(self, force=False): """enables the secondary input""" #print('on_animate_time') if self._animate_type == 'scale' or force: self.set_grid_scale(False, 'time') self.set_grid_time(True, 'time') self._animate_type = 'time' def on_animate_scale(self, force=False): """enables the secondary input""" #print('on_animate_scale') self.set_grid_scale(True, 'scale') if self._animate_type == 'time' or force: self.set_grid_time(False, 'scale') self._animate_type = 'scale' def on_animate_phase(self, force=False): """enables the secondary input""" #print('on_animate_phase') if self._animate_type == 'scale' or force: self.set_grid_scale(False, 'phase') if self._animate_type == 'time' or force: self.set_grid_time(False, 'phase') self._animate_type = 'phase' def set_grid_scale(self, enabled=True, word=''): """enables/disables the secondary input""" #print('%s-set_grid_scale; enabled = %r' % (word, enabled)) if HIDE_WHEN_INACTIVE: self.box_scale.setVisible(enabled) self.animation_profile_label.setVisible(enabled) self.animation_profile_edit.setVisible(enabled) #self.min_value_enable.setVisible(enabled) #self.max_value_enable.setVisible(enabled) self.animation_profile_label.setEnabled(enabled) self.animation_profile_edit.setEnabled(enabled) # TODO: doesn't work... #self.csv_profile.setEnabled(enabled) #self.csv_profile_edit.setEnabled(enabled) #self.csv_profile_button.setEnabled(enabled) self.min_value_enable.setEnabled(enabled) self.max_value_enable.setEnabled(enabled) self.on_min_value_enable() self.on_max_value_enable() def set_grid_time(self, enabled=True, word=''): """enables/disables the secondary input""" #print('%s-set_grid_time; enabled = %r' % (word, enabled)) if HIDE_WHEN_INACTIVE: self.box_time.setVisible(enabled) self.checkbox_fringe.setVisible(not enabled) self.icase_fringe_label.setVisible(not enabled) self.icase_fringe_edit.setVisible(not enabled) self.icase_disp_label.setVisible(not enabled) self.icase_disp_edit.setVisible(not enabled) self.icase_vector_label.setVisible(not enabled) self.icase_vector_edit.setVisible(not enabled) #self.icase_fringe_delta_edit.setVisible(enabled) self.icase_disp_delta_edit.setVisible(enabled) self.icase_disp_delta_edit.setVisible(enabled) self.fps_label.setVisible(enabled) self.fps_edit.setVisible(enabled) self.fps_button.setVisible(enabled) self.displacement_label.setEnabled(enabled) self.fringe_label.setEnabled(enabled) self.icase_start.setEnabled(enabled) self.icase_disp_start_edit.setEnabled(enabled) self.icase_disp_start_button.setEnabled(enabled) self.icase_fringe_start_edit.setEnabled(enabled) self.icase_fringe_start_button.setEnabled(enabled) self.icase_end_label.setEnabled(enabled) self.icase_disp_end_edit.setEnabled(enabled) self.icase_disp_end_button.setEnabled(enabled) self.icase_fringe_end_edit.setEnabled(enabled) self.icase_fringe_end_button.setEnabled(enabled) self.icase_delta_label.setEnabled(enabled) self.icase_disp_delta_edit.setEnabled(enabled) self.icase_disp_delta_button.setEnabled(enabled) self.icase_fringe_delta_edit.setEnabled(enabled) self.icase_fringe_delta_button.setEnabled(enabled) #----------------------------------------------------------------------- is_min_enabled = self.min_value_enable.isChecked() self.min_value_label.setEnabled(is_min_enabled) self.min_value_edit.setEnabled(is_min_enabled) self.min_value_button.setEnabled(is_min_enabled) is_max_enabled = self.max_value_enable.isChecked() self.max_value_label.setEnabled(is_max_enabled) self.max_value_edit.setEnabled(is_max_enabled) self.max_value_button.setEnabled(is_max_enabled) self.min_value_enable.setEnabled(enabled) self.on_min_value_enable() #self.min_value.setEnabled(enabled) #self.min_value_edit.setEnabled(enabled) #self.min_value_button.setEnabled(enabled) self.max_value_enable.setEnabled(enabled) self.on_max_value_enable() #self.max_value.setEnabled(enabled) #self.max_value_edit.setEnabled(enabled) #self.max_value_button.setEnabled(enabled) self.icase_fringe_label.setEnabled(not enabled) self.icase_fringe_edit.setEnabled(not enabled) self.checkbox_fringe.setEnabled(not enabled) self.icase_disp_label.setEnabled(not enabled) self.icase_disp_edit.setEnabled(not enabled) self.icase_vector_label.setEnabled(not enabled) self.icase_vector_edit.setEnabled(not enabled) self.checkbox_vector.setEnabled(not enabled) self.on_checkbox_vector() self.fps_label.setEnabled(not enabled) self.fps_edit.setEnabled(not enabled) self.fps_button.setEnabled(not enabled) def on_min_value_enable(self): """ The min edit value box is enabled when we switch to time and the box is checked """ is_min_enabled = self.min_value_enable.isChecked( ) and self.min_value_enable.isEnabled() self.min_value_label.setEnabled(is_min_enabled) self.min_value_edit.setEnabled(is_min_enabled) self.min_value_button.setEnabled(is_min_enabled) def on_max_value_enable(self): """ The max edit value box is enabled when we switch to time and the box is checked """ is_max_enabled = self.max_value_enable.isChecked( ) and self.max_value_enable.isEnabled() self.max_value_label.setEnabled(is_max_enabled) self.max_value_edit.setEnabled(is_max_enabled) self.max_value_button.setEnabled(is_max_enabled) def on_update_min_max_defaults(self): """ When the icase is changed, the min/max value default message is changed """ icase = self.icase_disp_start_edit.value() min_value, max_value = self.get_min_max(icase) self.min_value_button.setToolTip('Sets the min value to %g' % min_value) self.max_value_button.setToolTip('Sets the max value to %g' % max_value) def on_min_value_default(self): """When min default icase is pressued, update the value""" icase = self.icase_disp_start_edit.value() min_value = self.get_min_max(icase)[0] self.min_value_edit.setText(str(min_value)) self.min_value_edit.setStyleSheet("QLineEdit{background: white;}") def on_max_value_default(self): """When max default icase is pressued, update the value""" icase = self.icase_disp_start_edit.value() max_value = self.get_min_max(icase)[1] self.max_value_edit.setText(str(max_value)) self.max_value_edit.setStyleSheet("QLineEdit{background: white;}") def on_browse_folder(self): """opens a folder dialog""" dirname = getexistingdirectory(parent=self, caption='Select a Directory', basedir='', options=QFileDialog.ShowDirsOnly) if not dirname: return self.browse_folder_edit.setText(dirname) def on_browse_csv(self): """opens a file dialog""" default_filename = '' file_types = 'Delimited Text (*.txt; *.dat; *.csv)' dirname = open_file_dialog(self, 'Select a CSV File', default_filename, file_types) if not dirname: return self.csv_profile_browse_button.setText(dirname) def on_default_name(self): """sets the default gif name""" self.gif_edit.setText(self._default_name + '.gif') def on_default_scale(self): """sets the default displacement scale factor""" self.scale_edit.setText(str(self._default_scale)) self.scale_edit.setStyleSheet("QLineEdit{background: white;}") def on_default_arrow_scale(self): """sets the default arrow scale factor""" self.arrow_scale_edit.setText(str(self._default_arrow_scale)) self.arrow_scale_edit.setStyleSheet("QLineEdit{background: white;}") def on_default_time(self): """sets the default gif time""" self.time_edit.setValue(self._default_time) def on_default_fps(self): """sets the default FPS""" self.fps_edit.setValue(self._default_fps) def on_default_resolution(self): """sets the default image resolution scale factor""" self.resolution_edit.setValue(self._default_resolution) def create_layout(self): """displays the menu objects""" grid = QGridLayout() irow = 0 grid.addWidget(self.icase_fringe_label, irow, 0) grid.addWidget(self.icase_fringe_edit, irow, 1) grid.addWidget(self.checkbox_fringe, irow, 2) irow += 1 grid.addWidget(self.icase_disp_label, irow, 0) grid.addWidget(self.icase_disp_edit, irow, 1) #grid.addWidget(self.checkbox_disp, irow, 2) irow += 1 grid.addWidget(self.icase_vector_label, irow, 0) grid.addWidget(self.icase_vector_edit, irow, 1) grid.addWidget(self.checkbox_vector, irow, 2) irow += 1 grid.addWidget(self.scale_label, irow, 0) grid.addWidget(self.scale_edit, irow, 1) grid.addWidget(self.scale_button, irow, 2) irow += 1 grid.addWidget(self.arrow_scale_label, irow, 0) grid.addWidget(self.arrow_scale_edit, irow, 1) grid.addWidget(self.arrow_scale_button, irow, 2) irow += 1 grid.addWidget(self.time_label, irow, 0) grid.addWidget(self.time_edit, irow, 1) grid.addWidget(self.time_button, irow, 2) irow += 1 # spacer spacer = QLabel('') grid.addWidget(self.fps_label, irow, 0) grid.addWidget(self.fps_edit, irow, 1) grid.addWidget(self.fps_button, irow, 2) irow += 1 grid.addWidget(self.animation_type, irow, 0) grid.addWidget(self.animation_type_edit, irow, 1) irow += 1 grid.addWidget(spacer, irow, 0) irow += 1 #---------- #Time grid_time = QGridLayout() jrow = 0 self.fringe_label.setAlignment(Qt.AlignCenter) self.displacement_label.setAlignment(Qt.AlignCenter) if not IS_TIME_FRINGE: self.fringe_label.hide() self.icase_fringe_delta_edit.hide() self.icase_fringe_start_edit.hide() self.icase_fringe_end_edit.hide() self.icase_fringe_delta_button.hide() grid_time.addWidget(self.displacement_label, jrow, 1) grid_time.addWidget(self.fringe_label, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_start, jrow, 0) grid_time.addWidget(self.icase_disp_start_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_start_edit, jrow, 2) #grid_time.addWidget(self.icase_disp_start_button, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_end_label, jrow, 0) grid_time.addWidget(self.icase_disp_end_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_end_edit, jrow, 2) #grid_time.addWidget(self.icase_end_button, jrow, 2) jrow += 1 grid_time.addWidget(self.icase_delta_label, jrow, 0) grid_time.addWidget(self.icase_disp_delta_edit, jrow, 1) grid_time.addWidget(self.icase_fringe_delta_edit, jrow, 2) #grid_time.addWidget(self.icase_delta_button, jrow, 2) jrow += 1 hbox_min = QHBoxLayout() hbox_min.addWidget(self.min_value_enable) hbox_min.addWidget(self.min_value_label) grid_time.addLayout(hbox_min, jrow, 0) grid_time.addWidget(self.min_value_edit, jrow, 1) grid_time.addWidget(self.min_value_button, jrow, 2) jrow += 1 hbox_max = QHBoxLayout() hbox_max.addWidget(self.max_value_enable) hbox_max.addWidget(self.max_value_label) grid_time.addLayout(hbox_max, jrow, 0) grid_time.addWidget(self.max_value_edit, jrow, 1) grid_time.addWidget(self.max_value_button, jrow, 2) jrow += 1 grid_time.addWidget(spacer, jrow, 0) jrow += 1 #-------------- grid_scale = QGridLayout() grid_scale.addWidget(self.animation_profile_label, 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 self.box_scale.setLayout(grid_scale) self.box_time.setLayout(grid_time) #---------- grid2 = QGridLayout() irow = 0 #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, irow, 0) irow += 1 grid2.addWidget(self.resolution_label, irow, 0) grid2.addWidget(self.resolution_edit, irow, 1) grid2.addWidget(self.resolution_button, irow, 2) irow += 1 grid2.addWidget(self.browse_folder_label, irow, 0) grid2.addWidget(self.browse_folder_edit, irow, 1) grid2.addWidget(self.browse_folder_button, irow, 2) irow += 1 grid2.addWidget(self.gif_label, irow, 0) grid2.addWidget(self.gif_edit, irow, 1) grid2.addWidget(self.gif_button, irow, 2) irow += 1 grid2.addWidget(self.make_images_checkbox, irow, 0) #grid2.addWidget(self.overwrite_images_checkbox, irow, 0) grid2.addWidget(self.delete_images_checkbox, irow, 1) grid2.addWidget(self.make_gif_checkbox, irow, 2) irow += 1 grid2.addWidget(self.repeat_checkbox, irow, 0) irow += 1 grid2.addWidget(spacer, irow, 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(self.box_scale) vbox.addWidget(self.box_time) #vbox.addLayout(checkboxes) vbox.addLayout(grid_hbox) vbox.addStretch() vbox.addLayout(step_run_box) vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def on_step(self): """click the Step button""" passed, validate_out = self.on_validate() if passed: try: self._make_gif(validate_out, istep=self.istep) self.istep += 1 except IndexError: self._make_gif(validate_out, istep=0) self.istep += 1 self.wipe_button.setEnabled(True) def on_wipe(self): """click the Wipe button""" passed, validate_out = self.on_validate(wipe=True) if passed: self.istep = 0 self._make_gif(validate_out, istep=self.istep) self.wipe_button.setEnabled(False) self.stop_button.setEnabled(False) def on_stop(self): """click the Stop button""" #passed, validate_out = self.on_validate() #if passed: #self._make_gif(validate_out, stop_animation=True) if self.is_gui: self.win_parent.win_parent.stop_animation() self.wipe_button.setEnabled(True) self.stop_button.setEnabled(False) def on_run(self): """click the Run button""" self.istep = 0 self.wipe_button.setEnabled(False) self.stop_button.setEnabled(True) passed, validate_out = self.on_validate() if passed: self._make_gif(validate_out, istep=None) return passed def _make_gif(self, validate_out, istep=None, stop_animation=False): """interface for making the gif""" (icase_fringe, icase_disp, icase_vector, scale, time, fps, animate_in_gui, magnify, output_dir, gifbase, min_value, max_value) = validate_out fps = int(fps) gif_filename = None if not stop_animation and not animate_in_gui and gifbase is not None: if gifbase.lower().endswith('.gif'): gifbase = gifbase[:-4] gif_filename = os.path.join(output_dir, gifbase + '.gif') animate_fringe = self.checkbox_fringe.isChecked() animate_vector = self.checkbox_vector.isChecked() animate_scale = self.animate_scale_radio.isChecked() animate_phase = self.animate_phase_radio.isChecked() animate_time = self.animate_time_radio.isChecked() if not self.checkbox_vector.isEnabled(): icase_vector = None animate_scale = False animate_phase = False animate_time = False if self._animate_type == 'scale': animate_scale = True elif self._animate_type == 'phase': animate_phase = True elif self._animate_type == 'time': animate_time = True else: raise NotImplementedError(self._animate_type) make_images = self.make_images_checkbox.isChecked() delete_images = self.delete_images_checkbox.isChecked() make_gif = self.make_gif_checkbox.isChecked() animation_profile = str(self.animation_profile_edit.currentText()) icase_disp_start = self.icase_disp_start_edit.value() icase_disp_end = self.icase_disp_end_edit.value() icase_disp_delta = self.icase_disp_delta_edit.value() bool_repeat = self.repeat_checkbox.isChecked( ) # TODO: change this to an integer if bool_repeat: nrepeat = 0 else: nrepeat = 1 #self.out_data['is_shown'] = self.show_radio.isChecked() #icase = self._icase if self.is_gui: self.win_parent.win_parent.make_gif( gif_filename, scale, istep=istep, animate_scale=animate_scale, animate_phase=animate_phase, animate_time=animate_time, icase_fringe=icase_fringe, icase_disp=icase_disp, icase_vector=icase_vector, animate_fringe=animate_fringe, animate_vector=animate_vector, icase_start=icase_disp_start, icase_end=icase_disp_end, icase_delta=icase_disp_delta, time=time, animation_profile=animation_profile, nrepeat=nrepeat, fps=fps, magnify=magnify, make_images=make_images, delete_images=delete_images, make_gif=make_gif, stop_animation=stop_animation, animate_in_gui=animate_in_gui, min_value=min_value, max_value=max_value, ) self.out_data['clicked_ok'] = True self.out_data['close'] = True def get_min_max(self, icase): if self.is_gui: (obj, (i, name)) = self.win_parent.win_parent.result_cases[icase] min_value, max_value = obj.get_min_max(i, name) else: return 0., 1.0 return min_value, max_value def on_validate(self, wipe=False): """checks to see if the input is valid""" # requires no special validation icase_fringe, flag0 = check_int(self.icase_fringe_edit) icase_disp, unused_flaga = check_int(self.icase_disp_edit) icase_vector, unused_flagb = check_int(self.icase_vector_edit) #icase_disp = self._icase_disp #icase_vector = self._icase_vector scale, flag1 = check_float(self.scale_edit) time, flag2 = check_float(self.time_edit) fps, flag3 = check_float(self.fps_edit) min_value = max_value = None flag4 = flag5 = True if self.min_value_edit.isEnabled(): min_value, flag4 = check_float(self.min_value_edit) if self.max_value_edit.isEnabled(): max_value, flag5 = check_float(self.max_value_edit) if wipe: animate_in_gui = False scale = 0. flag1 = True else: animate_in_gui = self.animate_in_gui_checkbox.isChecked() if animate_in_gui or wipe: passed = all([flag0, flag1, flag2, flag3, flag4, flag5]) magnify, output_dir, gifbase = None, None, None else: magnify, flag6 = check_int(self.resolution_edit) output_dir, flag7 = self.check_path(self.browse_folder_edit) gifbase, flag8 = self.check_name(self.gif_edit) passed = all([ flag0, flag1, flag2, flag3, flag4, flag5, flag6, flag7, flag8 ]) return passed, (icase_fringe, icase_disp, icase_vector, scale, time, fps, animate_in_gui, magnify, output_dir, gifbase, min_value, max_value) @staticmethod def check_name(cell): """verifies that the data is string-able""" cell_value = cell.text() try: text = str(cell_value).strip() except UnicodeEncodeError: cell.setStyleSheet("QLineEdit{background: red;}") return None, False if len(text): cell.setStyleSheet("QLineEdit{background: white;}") return text, True else: cell.setStyleSheet("QLineEdit{background: red;}") return None, False def check_path(self, cell): """verifies that the path exists""" text, passed = self.check_name(cell) if not passed: return None, False if os.path.exists(text): cell.setStyleSheet("QLineEdit{background: white;}") return text, True else: cell.setStyleSheet("QLineEdit{background: red;}") return None, False #def on_ok(self): #"""click the OK button""" #passed = self.on_apply() #if passed: #self.win_parent._animation_window_shown = False #self.close() ##self.destroy() def on_cancel(self): """click the Cancel button""" self.on_stop() self.out_data['close'] = True self.close()
class ChannelProperty(QWidget): """ For manipulate chanel properties. 1. Apply gaussian blur to channel 2. Fixed range for coloring In future should be extended :param settings: for storing internal state. allow keep state between sessions :param start_name: name used to select proper information from settings object. Introduced for case with multiple image view. """ def __init__(self, settings: ViewSettings, start_name: str): if start_name == "": raise ValueError( "ChannelProperty should have non empty start_name") super().__init__() self.current_name = start_name self.current_channel = 0 self._settings = settings self.widget_dict: typing.Dict[str, ColorComboBoxGroup] = {} self.minimum_value = CustomSpinBox(self) self.minimum_value.setRange(-(10**6), 10**6) self.minimum_value.valueChanged.connect(self.range_changed) self.maximum_value = CustomSpinBox(self) self.maximum_value.setRange(-(10**6), 10**6) self.maximum_value.valueChanged.connect(self.range_changed) self.fixed = QCheckBox("Fix range") self.fixed.stateChanged.connect(self.lock_channel) self.use_filter = QEnumComboBox(enum_class=NoiseFilterType) self.use_filter.setToolTip("Only current channel") self.filter_radius = QDoubleSpinBox() self.filter_radius.setSingleStep(0.1) self.filter_radius.valueChanged.connect(self.gauss_radius_changed) self.use_filter.currentIndexChanged.connect(self.gauss_use_changed) self.gamma_value = QDoubleSpinBox() self.gamma_value.setRange(0.01, 100) self.gamma_value.setSingleStep(0.1) self.gamma_value.valueChanged.connect(self.gamma_value_changed) self.collapse_widget = CollapseCheckbox("Channel property") self.collapse_widget.add_hide_element(self.minimum_value) self.collapse_widget.add_hide_element(self.maximum_value) self.collapse_widget.add_hide_element(self.fixed) self.collapse_widget.add_hide_element(self.use_filter) self.collapse_widget.add_hide_element(self.filter_radius) self.collapse_widget.add_hide_element(self.gamma_value) layout = QGridLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.collapse_widget, 0, 0, 1, 4) label1 = QLabel("Min bright:") layout.addWidget(label1, 1, 0) layout.addWidget(self.minimum_value, 1, 1) label2 = QLabel("Max bright:") layout.addWidget(label2, 2, 0) layout.addWidget(self.maximum_value, 2, 1) layout.addWidget(self.fixed, 1, 2, 1, 2) label3 = QLabel("Filter:") layout.addWidget(label3, 3, 0, 1, 1) layout.addWidget(self.use_filter, 3, 1, 1, 1) layout.addWidget(self.filter_radius, 3, 2, 1, 1) label4 = QLabel("Gamma:") layout.addWidget(label4, 4, 0, 1, 1) layout.addWidget(self.gamma_value, 4, 1, 1, 1) self.setLayout(layout) self.collapse_widget.add_hide_element(label1) self.collapse_widget.add_hide_element(label2) self.collapse_widget.add_hide_element(label3) self.collapse_widget.add_hide_element(label4) def send_info(self): """send info to""" widget = self.widget_dict[self.current_name] widget.parameters_changed(self.current_channel) def register_widget(self, widget: "ColorComboBoxGroup") -> None: """ Register new viewer by its color combo box group :param ColorComboBoxGroup widget: viewer widget for color control """ if widget.viewer_name in self.widget_dict: raise ValueError(f"name {widget.viewer_name} already register") self.widget_dict[widget.viewer_name] = widget self._settings.connect_to_profile(widget.viewer_name, self.refresh_values) self.change_current(widget.viewer_name, 0) def refresh_values(self, path: typing.Optional[str]): if path is None or path.startswith(self.current_name): self.change_current(self.current_name, self.current_channel) def change_current(self, name: str, channel: int) -> None: """ Change to show values connected with channel `channel` from viewer `viewer` :param str name: name of viewer :param int channel: channel to which data should be presented :rtype: None """ if name not in self.widget_dict: raise ValueError(f"name {name} not in register") self.current_name = name self.current_channel = channel block = self.blockSignals(True) self.minimum_value.blockSignals(True) self.minimum_value.setValue( self._settings.get_from_profile( f"{self.current_name}.range_{self.current_channel}", (0, 65000))[0]) self.minimum_value.blockSignals(False) self.maximum_value.setValue( self._settings.get_from_profile( f"{self.current_name}.range_{self.current_channel}", (0, 65000))[1]) self.use_filter.setCurrentEnum( self._settings.get_from_profile( f"{self.current_name}.use_filter_{self.current_channel}", NoiseFilterType.No)) self.filter_radius.setValue( self._settings.get_from_profile( f"{self.current_name}.filter_radius_{self.current_channel}", 1)) self.fixed.setChecked( self._settings.get_from_profile( f"{self.current_name}.lock_{self.current_channel}", False)) self.gamma_value.setValue( self._settings.get_from_profile( f"{self.current_name}.gamma_value_{self.current_channel}", 1)) self.blockSignals(block) def gamma_value_changed(self): self._settings.set_in_profile( f"{self.current_name}.gamma_value_{self.current_channel}", self.gamma_value.value()) self.send_info() def gauss_radius_changed(self): self._settings.set_in_profile( f"{self.current_name}.filter_radius_{self.current_channel}", self.filter_radius.value()) if self.use_filter.currentEnum() != NoiseFilterType.No: self.send_info() def gauss_use_changed(self): self._settings.set_in_profile( f"{self.current_name}.use_filter_{self.current_channel}", self.use_filter.currentEnum()) if self.use_filter.currentEnum() == NoiseFilterType.Median: self.filter_radius.setDecimals(0) self.filter_radius.setSingleStep(1) else: self.filter_radius.setDecimals(2) self.filter_radius.setSingleStep(0.1) self.send_info() def lock_channel(self, value): self._settings.set_in_profile( f"{self.current_name}.lock_{self.current_channel}", value) self.send_info() def range_changed(self): self._settings.set_in_profile( f"{self.current_name}.range_{self.current_channel}", (self.minimum_value.value(), self.maximum_value.value()), ) if self.fixed.isChecked(): self.send_info()
class PlotPreferences(QWidget): def __init__(self, mainWindow): super().__init__() self.mainWindow = mainWindow plotStyleGroup = GroupWidget("Plot style") styles = self.mainWindow.plot.getValidStyles() self.customStyle = StyleDesigner( styleKeys=self.mainWindow.plot.getStyleKeys(), symbolKeys=self.mainWindow.plot.getStyleSymbolKeys(), invalidNames=styles) self.customStyle.setEnabled(False) self.customStyle.saveStyle.connect(self._saveStyle) self.plotStyleList = QComboBox() styles = [s.capitalize() for s in styles] styles += ["Add custom theme..."] self.plotStyleList.addItems(styles) self.plotStyleList.currentTextChanged.connect( self._updateCustomStyleWidget) foregroundColour = self.palette().windowText().color() icon = makeForegroundIcon("edit", foregroundColour) self.editPlotStyleButton = QPushButton(icon, "") self.editPlotStyleButton.setCheckable(True) self.editPlotStyleButton.setToolTip("Edit theme") self.editPlotStyleButton.toggled.connect(self._editStyle) icon = makeForegroundIcon("trash", foregroundColour) self.deletePlotStyleButton = QPushButton(icon, "") self.deletePlotStyleButton.setToolTip("Delete theme") self.deletePlotStyleButton.clicked.connect(self._deleteTheme) self.plotStyleList.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.editPlotStyleButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.deletePlotStyleButton.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) plotStyleBox = QHBoxLayout() plotStyleBox.addWidget(self.plotStyleList) plotStyleBox.addWidget(self.editPlotStyleButton) plotStyleBox.addWidget(self.deletePlotStyleButton) plotStyleGroup.addLayout(plotStyleBox) plotStyleGroup.addWidget(self.customStyle) plotConfigGroup = GroupWidget("Default plot range", layout="vbox") self.plotRangeCombo = QComboBox() ranges = [ "1 month", "3 months", "6 months", "1 year", "Current year", "All" ] self.plotRangeCombo.addItems(ranges) self.customRangeCheckBox = QCheckBox("Custom range") self.customRangeSpinBox = QSpinBox() self.customRangeSpinBox.setSuffix(" months") maxMonths = len(mainWindow.data.splitMonths()) self.customRangeSpinBox.setRange(1, maxMonths) self.customRangeCheckBox.clicked.connect(self.setCustomRange) plotRangeLayout = QHBoxLayout() plotRangeLayout.addWidget(self.plotRangeCombo) customRangeLayout = QHBoxLayout() customRangeLayout.addWidget(self.customRangeCheckBox) customRangeLayout.addWidget(self.customRangeSpinBox) plotConfigGroup.addLayout(plotRangeLayout) plotConfigGroup.addLayout(customRangeLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(plotStyleGroup) mainLayout.addWidget(plotConfigGroup) mainLayout.addStretch(1) self.setLayout(mainLayout) self.setCurrentValues() # apply initial state self.apply() def setCurrentValues(self): self.settings = Settings() self.settings.beginGroup("plot") plotStyle = self.settings.value("style", "dark") self.plotStyleList.setCurrentText(plotStyle.capitalize()) # does setCurrentText not emit currentTextChanged signal? self._enableDisableDeleteButton(plotStyle) self.customStyle.setName(plotStyle) self.customStyle.setStyle(self.mainWindow.plot.getStyle(plotStyle)) customRange = self.settings.value("customRange", False) rng = self.settings.value("range", "All") self.setCustomRange(customRange) if customRange: rng = int(rng) self.customRangeSpinBox.setValue(rng) else: items = [ self.plotRangeCombo.itemText(idx) for idx in range(self.plotRangeCombo.count()) ] idx = items.index(rng) self.plotRangeCombo.setCurrentIndex(idx) self.settings.endGroup() def _saveStyle(self, name, style, setStyle=False): self.mainWindow.plot.addCustomStyle(name, style, setStyle=setStyle) idx = self.plotStyleList.count() - 1 self.plotStyleList.insertItem(idx, name.capitalize()) self.plotStyleList.setCurrentIndex(idx) def _editStyle(self, edit): self.customStyle.setEditMode(edit) self._updateCustomStyleWidget() def apply(self): styleName = self.plotStyleList.currentText().lower() if styleName == "add custom theme...": styleName, styleDct = self.customStyle.getStyle() self._saveStyle(styleName, styleDct, setStyle=True) else: self.mainWindow.plot.setStyle(styleName) customRange = self.customRangeCheckBox.isChecked() if customRange: months = self.customRangeSpinBox.value() else: text = self.plotRangeCombo.currentText() if text == "1 year": text = "12 months" elif text == "Current year": text = f"{date.today().month} months" months = None if text == 'All' else int(text.strip(' months')) self.mainWindow.plot.setXAxisRange(months) self.settings.beginGroup("plot") self.settings.setValue("style", styleName) self.settings.setValue("customRange", customRange) if customRange: self.settings.setValue("range", self.customRangeSpinBox.value()) else: self.settings.setValue("range", self.plotRangeCombo.currentText()) self.settings.endGroup() @Slot(bool) def setCustomRange(self, custom): self.customRangeCheckBox.setChecked(custom) if custom: self.customRangeSpinBox.setEnabled(True) self.plotRangeCombo.setEnabled(False) else: self.customRangeSpinBox.setEnabled(False) self.plotRangeCombo.setEnabled(True) def _updateCustomStyleWidget(self, name=None): if name is None or name == "Add custom theme...": self.customStyle.setEnabled(True) if name is None: name = self.customStyle.name else: name = f"custom-{self.customStyle.name}" self.customStyle.setName(name) else: name = name.lower() style = self.mainWindow.plot.getStyle(name) self.customStyle.setStyle(style, name=name) self.customStyle.setEnabled(False) self._enableDisableDeleteButton(name) def _enableDisableDeleteButton(self, plotStyle): if plotStyle in self.mainWindow.plot.getDefaultStyles(): self.deletePlotStyleButton.setEnabled(False) self.deletePlotStyleButton.setToolTip( "Cannot delete default theme") else: self.deletePlotStyleButton.setEnabled(True) self.deletePlotStyleButton.setToolTip("Delete theme") def _deleteTheme(self): styleName = self.plotStyleList.currentText() items = [ self.plotStyleList.itemText(idx) for idx in range(self.plotStyleList.count()) ] idx = items.index(styleName) self.plotStyleList.removeItem(idx) self.mainWindow.plot.removeCustomStyle(styleName.lower())
class ColorbarWidget(QWidget): colorbarChanged = Signal() # The parent should simply redraw their canvas def __init__(self, parent=None): super(ColorbarWidget, self).__init__(parent) self.setWindowTitle("Colorbar") self.setMaximumWidth(200) self.cmap = QComboBox() self.cmap.addItems(sorted(cm.cmap_d.keys())) self.cmap.currentIndexChanged.connect(self.cmap_index_changed) self.cmin = QLineEdit() self.cmin_value = 0 self.cmin.setMaximumWidth(100) self.cmin.editingFinished.connect(self.clim_changed) self.cmin_layout = QHBoxLayout() self.cmin_layout.addStretch() self.cmin_layout.addWidget(self.cmin) self.cmin_layout.addStretch() self.cmax = QLineEdit() self.cmax_value = 1 self.cmax.setMaximumWidth(100) self.cmax.editingFinished.connect(self.clim_changed) self.cmin.setValidator(QDoubleValidator()) self.cmax.setValidator(QDoubleValidator()) self.cmax_layout = QHBoxLayout() self.cmax_layout.addStretch() self.cmax_layout.addWidget(self.cmax) self.cmax_layout.addStretch() self.norm_layout = QHBoxLayout() self.norm = QComboBox() self.norm.addItems(NORM_OPTS) self.norm.currentIndexChanged.connect(self.norm_changed) self.powerscale = QLineEdit() self.powerscale_value = 2 self.powerscale.setText("2") self.powerscale.setValidator(QDoubleValidator(0.001,100,3)) self.powerscale.setMaximumWidth(50) self.powerscale.editingFinished.connect(self.norm_changed) self.powerscale.hide() self.powerscale_label = QLabel("n=") self.powerscale_label.hide() self.norm_layout.addStretch() self.norm_layout.addWidget(self.norm) self.norm_layout.addStretch() self.norm_layout.addWidget(self.powerscale_label) self.norm_layout.addWidget(self.powerscale) self.autoscale = QCheckBox("Autoscaling") self.autoscale.setChecked(True) self.autoscale.stateChanged.connect(self.update_clim) self.canvas = FigureCanvas(Figure()) if parent: # Set facecolor to match parent self.canvas.figure.set_facecolor(parent.palette().window().color().getRgbF()) self.ax = self.canvas.figure.add_axes([0.4,0.05,0.2,0.9]) # layout self.layout = QVBoxLayout(self) self.layout.addWidget(self.cmap) self.layout.addLayout(self.cmax_layout) self.layout.addWidget(self.canvas, stretch=1) self.layout.addLayout(self.cmin_layout) self.layout.addLayout(self.norm_layout) self.layout.addWidget(self.autoscale) def set_mappable(self, mappable): """ When a new plot is created this method should be called with the new mappable """ self.ax.clear() try: # Use current cmap cmap = self.colorbar.get_cmap() except AttributeError: try: # else use viridis cmap = cm.viridis except AttributeError: # else default cmap = None self.colorbar = Colorbar(ax=self.ax, mappable=mappable) self.cmin_value, self.cmax_value = self.colorbar.get_clim() self.update_clim_text() self.cmap_changed(cmap) self.cmap.setCurrentIndex(sorted(cm.cmap_d.keys()).index(self.colorbar.get_cmap().name)) self.redraw() def cmap_index_changed(self): self.cmap_changed(self.cmap.currentText()) def cmap_changed(self, name): self.colorbar.set_cmap(name) self.colorbar.mappable.set_cmap(name) self.redraw() def norm_changed(self): """ Called when a different normalization is selected """ idx = self.norm.currentIndex() if NORM_OPTS[idx] == 'Power': self.powerscale.show() self.powerscale_label.show() else: self.powerscale.hide() self.powerscale_label.hide() self.colorbar.mappable.set_norm(self.get_norm()) self.set_mappable(self.colorbar.mappable) def get_norm(self): """ This will create a matplotlib.colors.Normalize from selected idx, limits and powerscale """ idx = self.norm.currentIndex() if self.autoscale.isChecked(): cmin = cmax = None else: cmin = self.cmin_value cmax = self.cmax_value if NORM_OPTS[idx] == 'Power': if self.powerscale.hasAcceptableInput(): self.powerscale_value = float(self.powerscale.text()) return PowerNorm(gamma=self.powerscale_value, vmin=cmin, vmax=cmax) elif NORM_OPTS[idx] == "SymmetricLog10": return SymLogNorm(1e-8 if cmin is None else max(1e-8, abs(cmin)*1e-3), vmin=cmin, vmax=cmax) else: return Normalize(vmin=cmin, vmax=cmax) def clim_changed(self): """ Called when either the min or max is changed. Will unset the autoscale. """ self.autoscale.blockSignals(True) self.autoscale.setChecked(False) self.autoscale.blockSignals(False) self.update_clim() def update_clim(self): """ This will update the clim of the plot based on min, max, and autoscale """ if self.autoscale.isChecked(): data = self.colorbar.mappable.get_array() try: try: self.cmin_value = data[~data.mask].min() self.cmax_value = data[~data.mask].max() except AttributeError: self.cmin_value = np.nanmin(data) self.cmax_value = np.nanmax(data) except (ValueError, RuntimeWarning): # all values mask pass self.update_clim_text() else: if self.cmin.hasAcceptableInput(): cmin = float(self.cmin.text()) if cmin < self.cmax_value: self.cmin_value = cmin else: # reset values back self.update_clim_text() if self.cmax.hasAcceptableInput(): cmax = float(self.cmax.text()) if cmax > self.cmin_value: self.cmax_value = cmax else: #reset values back self.update_clim_text() self.colorbar.set_clim(self.cmin_value, self.cmax_value) self.redraw() def update_clim_text(self): """ Update displayed limit values based on stored ones """ self.cmin.setText("{:.4}".format(self.cmin_value)) self.cmax.setText("{:.4}".format(self.cmax_value)) def redraw(self): """ Redraws the colobar and emits signal to cause the parent to redraw """ self.colorbar.update_ticks() self.colorbar.draw_all() self.canvas.draw_idle() self.colorbarChanged.emit()
class ContourOptionsDialog(QDialog): """ Dialog box for selecting contour options """ def __init__(self, contour_settings): super(ContourOptionsDialog, self).__init__(contour_settings.cubeviz_layout) self.setWindowFlags(self.windowFlags() | Qt.Tool) self.setWindowTitle("Contour Options") self.is_preview_active = False # preview mode? self.contour_settings = contour_settings # ref to caller ContourSettings self.image_viewer = self.contour_settings.image_viewer # ref to image viewer self.options = self.contour_settings.options # ref to ContourSettings options self._colormap_members = self.contour_settings.colormap_members # Colormap options self._colormap_index = DEFAULT_GLUE_COLORMAP_INDEX # Currently selected colormap if "cmap" in self.options: if self.options["cmap"] in self._colormap_members: self._colormap_index = self._colormap_members.index(self.options["cmap"]) # Is there a user spacing? if self.contour_settings.spacing is None: self.is_custom_spacing = False else: self.is_custom_spacing = True # Is there a user min? if self.contour_settings.vmin is None: self.is_vmin = False else: self.is_vmin = True # Is there a user max? if self.contour_settings.vmax is None: self.is_vmax = False else: self.is_vmax = True self.add_contour_label = self.contour_settings.add_contour_label # bool self._init_ui() def _init_ui(self): # Line 1: Color map self.colormap_label = QLabel("Color Scheme: ") self.colormap_combo = QColormapCombo() self.colormap_combo.addItem("", userData=UserDataWrapper(cm.viridis)) self.colormap_combo._update_icons() self.colormap_combo.setCurrentIndex(self._colormap_index) self.colormap_combo.setMaximumWidth(150) self.colormap_combo.currentIndexChanged.connect( self._on_colormap_change) # hbl is short for Horizontal Box Layout hbl1 = QHBoxLayout() hbl1.addWidget(self.colormap_label) hbl1.addWidget(self.colormap_combo) # Line 2: Display contour labels self.contour_label_checkBox = QCheckBox("Contour labels (font size):") if self.contour_settings.add_contour_label: self.contour_label_checkBox.setChecked(True) self.contour_label_checkBox.toggled.connect(self.toggle_labels) font_string = str(self.contour_settings.font_size) self.font_size_input = QLineEdit(font_string) self.font_size_input.setFixedWidth(150) self.font_size_input.setDisabled( not self.contour_settings.add_contour_label) hbl2 = QHBoxLayout() hbl2.addWidget(self.contour_label_checkBox) hbl2.addWidget(self.font_size_input) # Line 3: Contour Spacing self.custom_spacing_checkBox = QCheckBox("Contour spacing (interval):") if self.is_custom_spacing: self.custom_spacing_checkBox.setChecked(True) self.custom_spacing_checkBox.toggled.connect(self.custom_spacing) self.spacing_input = QLineEdit() self.spacing_input.setFixedWidth(150) self.spacing_input.setDisabled(not self.is_custom_spacing) spacing = "" if self.is_custom_spacing: spacing = str(self.contour_settings.spacing) elif self.contour_settings.data_spacing is not None: spacing = self.contour_settings.data_spacing spacing = "{0:1.4f}".format(spacing) self.spacing_default_text = spacing self.spacing_input.setText(spacing) hbl3 = QHBoxLayout() hbl3.addWidget(self.custom_spacing_checkBox) hbl3.addWidget(self.spacing_input) # Line 4: Vmax self.vmax_checkBox = QCheckBox("Set max:") self.vmax_input = QLineEdit() self.vmax_input.setFixedWidth(150) self.vmax_input.setDisabled(not self.is_vmax) vmax = "" if self.is_vmax: self.vmax_checkBox.setChecked(True) vmax = str(self.contour_settings.vmax) elif self.contour_settings.data_max is not None: vmax = self.contour_settings.data_max vmax = "{0:1.4f}".format(vmax) self.vmax_input.setText(vmax) self.vmax_default_text = vmax self.vmax_checkBox.toggled.connect(self.toggle_vmax) hbl4 = QHBoxLayout() hbl4.addWidget(self.vmax_checkBox) hbl4.addWidget(self.vmax_input) # Line 5: Vmin self.vmin_checkBox = QCheckBox("Set min:") self.vmin_input = QLineEdit() self.vmin_input.setFixedWidth(150) self.vmin_input.setDisabled(not self.is_vmin) vmin = "" if self.is_vmin: self.vmin_checkBox.setChecked(True) vmin = str(self.contour_settings.vmin) elif self.contour_settings.data_min is not None: vmin = self.contour_settings.data_min vmin = "{0:1.4f}".format(vmin) self.vmin_input.setText(vmin) self.vmin_default_text = vmin self.vmin_checkBox.toggled.connect(self.toggle_vmin) hbl5 = QHBoxLayout() hbl5.addWidget(self.vmin_checkBox) hbl5.addWidget(self.vmin_input) # Line f: self.previewButton = QPushButton("Preview") self.previewButton.clicked.connect(self.preview) self.defaultButton = QPushButton("Reset") self.defaultButton.clicked.connect(self.default) self.okButton = QPushButton("OK") self.okButton.clicked.connect(self.finish) self.okButton.setDefault(True) self.cancelButton = QPushButton("Cancel") self.cancelButton.clicked.connect(self.cancel) hblf = QHBoxLayout() hblf.addStretch(1) hblf.addWidget(self.previewButton) hblf.addWidget(self.defaultButton) hblf.addWidget(self.cancelButton) hblf.addWidget(self.okButton) vbl = QVBoxLayout() vbl.addLayout(hbl1) vbl.addLayout(hbl2) vbl.addLayout(hbl3) vbl.addLayout(hbl4) vbl.addLayout(hbl5) vbl.addLayout(hblf) self.setLayout(vbl) self.show() def update_data_vals(self, vmin="", vmax="", spacing="1"): self.vmin_default_text = vmin if not self.is_vmin: self.vmin_input.setText(vmin) self.vmax_default_text = vmax if not self.is_vmax: self.vmax_input.setText(vmax) self.spacing_default_text = spacing if not self.is_custom_spacing: self.spacing_input.setText(spacing) def _on_colormap_change(self, index): """Combo index changed handler""" self._colormap_index = index def custom_spacing(self): """Checkbox toggled handler""" if self.is_custom_spacing: self.is_custom_spacing = False self.spacing_input.setDisabled(True) spacing = "" if self.contour_settings.data_spacing: spacing = self.contour_settings.data_spacing spacing = "{0:1.4f}".format(spacing) self.spacing_input.setText(spacing) self.spacing_input.setStyleSheet("") else: self.is_custom_spacing = True self.spacing_input.setDisabled(False) def toggle_labels(self): """Checkbox toggled handler""" if self.add_contour_label: self.add_contour_label = False self.font_size_input.setDisabled(True) font_string = str(self.contour_settings.font_size) self.font_size_input.setText(font_string) self.font_size_input.setStyleSheet("") else: self.add_contour_label = True self.font_size_input.setDisabled(False) def toggle_vmax(self): """Checkbox toggled handler""" if self.is_vmax: self.is_vmax = False self.vmax_input.setDisabled(True) vmax = "" if self.contour_settings.data_max: vmax = self.contour_settings.data_max vmax = "{0:1.4f}".format(vmax) self.vmax_input.setText(vmax) self.vmax_input.setStyleSheet("") else: self.is_vmax = True self.vmax_input.setDisabled(False) def toggle_vmin(self): """Checkbox toggled handler""" if self.is_vmin: self.is_vmin = False self.vmin_input.setDisabled(True) vmin = "" if self.contour_settings.data_min: vmin = self.contour_settings.data_min vmin = "{0:1.4f}".format(vmin) self.vmin_input.setText(vmin) self.vmin_input.setStyleSheet("") else: self.is_vmin = True self.vmin_input.setDisabled(False) def input_validation(self): red = "background-color: rgba(255, 0, 0, 128);" def float_check(min_val=None): if user_input.text() == "": user_input.setStyleSheet(red) return False else: try: value = float(user_input.text()) if min_val is not None: if value <= min_val: user_input.setStyleSheet(red) return False else: user_input.setStyleSheet("") except ValueError: user_input.setStyleSheet(red) return False return True def int_check(min_val=None): if user_input.text() == "": user_input.setStyleSheet(red) return False else: try: value = int(user_input.text()) if min_val is not None: if value <= min_val: user_input.setStyleSheet(red) return False else: user_input.setStyleSheet("") except ValueError: user_input.setStyleSheet(red) return False return True success = True # Check 1: spacing_input if self.is_custom_spacing: user_input = self.spacing_input float_check(0) success = success and float_check() # Check 2: font_size_input if self.add_contour_label: user_input = self.font_size_input int_check(0) success = success and int_check() # Check 3: vmax if self.is_vmax: user_input = self.vmax_input float_check() success = success and float_check() # Check 4: vmax if self.is_vmin: user_input = self.vmin_input float_check() success = success and float_check() # Check 5: vmax and vmin if self.is_vmax and self.is_vmin and success: vmax = float(self.vmax_input.text()) vmin = float(self.vmin_input.text()) if vmax <= vmin: self.vmax_input.setStyleSheet(red) self.vmin_input.setStyleSheet(red) success = False return success def finish(self): """ Ok button pressed. Finalize options and send to image viewer """ success = self.input_validation() if not success: return # Change Color Map self._colormap_index = self.colormap_combo.currentIndex() colormap = self._colormap_members[self._colormap_index] self.contour_settings.options["cmap"] = colormap # labels self.contour_settings.add_contour_label = self.add_contour_label # font size if self.add_contour_label: font_size = int(self.font_size_input.text()) self.contour_settings.font_size = font_size else: self.contour_settings.font_size = DEFAULT_CONTOUR_FONT_SIZE # Spacing if self.is_custom_spacing: self.contour_settings.spacing = float(self.spacing_input.text()) else: self.contour_settings.spacing = None # vmax if self.is_vmax: vmax = float(self.vmax_input.text()) self.contour_settings.vmax = vmax self.contour_settings.options["vmax"] = vmax else: self.contour_settings.vmax = None self.contour_settings.options["vmax"] = None # vmin if self.is_vmin: vmin = float(self.vmin_input.text()) self.contour_settings.vmin = vmin self.contour_settings.options["vmin"] = vmin else: self.contour_settings.vmin = None self.contour_settings.options["vmin"] = None # Redraw contour if self.contour_settings.image_viewer.is_contour_active: self.contour_settings.draw_function() self.close() def preview(self): """ Prepare preview contour settings and send to image viewer """ success = self.input_validation() if not success: return image_viewer = self.contour_settings.image_viewer preview_settings = ContourSettings(image_viewer) preview_settings.dialog = self preview_settings.options = self.contour_settings.options.copy() preview_settings.spacing = self.contour_settings.spacing # Change Color Map self._colormap_index = self.colormap_combo.currentIndex() colormap = self._colormap_members[self._colormap_index] preview_settings.options["cmap"] = colormap # labels add_contour_label = self.contour_label_checkBox.isChecked() preview_settings.add_contour_label = add_contour_label # font size if add_contour_label: font_size = int(self.font_size_input.text()) preview_settings.font_size = font_size # Spacing if self.is_custom_spacing: preview_settings.spacing = float(self.spacing_input.text()) else: preview_settings.spacing = None # vmax if self.is_vmax: vmax = float(self.vmax_input.text()) preview_settings.vmax = vmax preview_settings.options["vmax"] = vmax else: preview_settings.vmax = None preview_settings.options["vmax"] = None # vmin if self.is_vmin: vmin = float(self.vmin_input.text()) preview_settings.vmin = vmin preview_settings.options["vmin"] = vmin else: preview_settings.vmin = None preview_settings.options["vmin"] = None # Redraw contour if image_viewer.is_contour_active: self.is_preview_active = True image_viewer.set_contour_preview(preview_settings) else: message = "Contour map is currently switched off. " \ "Please turn on the contour map by selecting " \ "a component from the contour map drop-down menu." info = QMessageBox.critical(self, "Error", message) def default(self): """ Set options back to default and send to image viewer """ self.contour_settings.options = self.contour_settings.default_options() self.contour_settings.spacing = None self.contour_settings.font_size = DEFAULT_CONTOUR_FONT_SIZE self.contour_settings.vmax = None self.contour_settings.vmin = None self.contour_settings.add_contour_label = False if self.contour_settings.image_viewer.is_contour_active: self.contour_settings.draw_function() self.contour_settings.options_dialog() def cancel(self): if self.contour_settings.image_viewer.is_contour_active: self.contour_settings.draw_function() self.close() def closeEvent(self, event): """closeEvent handler""" if self.is_preview_active: self.contour_settings.image_viewer.end_contour_preview() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.cancel()
class KernelConnectionDialog(QDialog): """Dialog to connect to existing kernels (either local or remote).""" 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) 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.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.save_connection_settings) self.accept_btns.accepted.connect(self.accept) self.accept_btns.rejected.connect(self.reject) # Save connection settings checkbox self.save_layout = QCheckBox(self) self.save_layout.setText(_("Save connection settings")) btns_layout = QHBoxLayout() btns_layout.addWidget(self.save_layout) btns_layout.addWidget(self.accept_btns) # 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.addLayout(btns_layout) self.load_connection_settings() def load_connection_settings(self): """Load the user's previously-saved kernel connection settings.""" existing_kernel = CONF.get("existing-kernel", "settings", {}) connection_file_path = existing_kernel.get("json_file_path", "") is_remote = existing_kernel.get("is_remote", False) username = existing_kernel.get("username", "") hostname = existing_kernel.get("hostname", "") port = str(existing_kernel.get("port", 22)) is_ssh_kf = existing_kernel.get("is_ssh_keyfile", False) ssh_kf = existing_kernel.get("ssh_key_file_path", "") if connection_file_path != "": self.cf.setText(connection_file_path) if username != "": self.un.setText(username) if hostname != "": self.hn.setText(hostname) if ssh_kf != "": self.kf.setText(ssh_kf) self.rm_group.setChecked(is_remote) self.pn.setText(port) self.kf_radio.setChecked(is_ssh_kf) self.pw_radio.setChecked(not is_ssh_kf) try: import keyring ssh_passphrase = keyring.get_password("spyder_remote_kernel", "ssh_key_passphrase") ssh_password = keyring.get_password("spyder_remote_kernel", "ssh_password") if ssh_passphrase: self.kfp.setText(ssh_passphrase) if ssh_password: self.pw.setText(ssh_password) except Exception: pass def save_connection_settings(self): """Save user's kernel connection settings.""" if not self.save_layout.isChecked(): return is_ssh_key = bool(self.kf_radio.isChecked()) connection_settings = { "json_file_path": self.cf.text(), "is_remote": self.rm_group.isChecked(), "username": self.un.text(), "hostname": self.hn.text(), "port": self.pn.text(), "is_ssh_keyfile": is_ssh_key, "ssh_key_file_path": self.kf.text() } CONF.set("existing-kernel", "settings", connection_settings) try: import keyring if is_ssh_key: keyring.set_password("spyder_remote_kernel", "ssh_key_passphrase", self.kfp.text()) else: keyring.set_password("spyder_remote_kernel", "ssh_password", self.pw.text()) except Exception: pass def select_connection_file(self): cf = getopenfilename(self, _('Select kernel connection file'), jupyter_runtime_dir(), '*.json;;*.*')[0] self.cf.setText(cf) def select_ssh_key(self): kf = getopenfilename(self, _('Select SSH keyfile'), get_home_dir(), '*.pem;;*')[0] self.kf.setText(kf) @staticmethod def get_connection_parameters(parent=None, dialog=None): if not dialog: dialog = KernelConnectionDialog(parent) result = dialog.exec_() is_remote = bool(dialog.rm_group.isChecked()) accepted = result == QDialog.Accepted if is_remote: def falsy_to_none(arg): return arg if arg else None if dialog.hn.text() and dialog.un.text(): port = dialog.pn.text() if dialog.pn.text() else '22' hostname = "{0}@{1}:{2}".format(dialog.un.text(), dialog.hn.text(), port) else: hostname = None if dialog.pw_radio.isChecked(): password = falsy_to_none(dialog.pw.text()) keyfile = None elif dialog.kf_radio.isChecked(): keyfile = falsy_to_none(dialog.kf.text()) password = falsy_to_none(dialog.kfp.text()) else: # imposible? keyfile = None password = None return (dialog.cf.text(), hostname, keyfile, password, accepted) else: path = dialog.cf.text() _dir, filename = osp.dirname(path), osp.basename(path) if _dir == '' and not filename.endswith('.json'): path = osp.join(jupyter_runtime_dir(), 'kernel-'+path+'.json') return (path, None, None, None, accepted)
class RunConfigOptions(QWidget): """Run configuration options""" 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 select_directory(self): """Select directory""" basedir = to_text_string(self.wd_edit.text()) if not osp.isdir(basedir): basedir = getcwd_or_home() directory = getexistingdirectory(self, _("Select directory"), basedir) if directory: self.wd_edit.setText(directory) self.dir = directory def set(self, options): self.runconf.set(options) self.clo_cb.setChecked(self.runconf.args_enabled) self.clo_edit.setText(self.runconf.args) if self.runconf.current: self.current_radio.setChecked(True) elif self.runconf.systerm: self.systerm_radio.setChecked(True) else: self.dedicated_radio.setChecked(True) self.interact_cb.setChecked(self.runconf.interact) self.post_mortem_cb.setChecked(self.runconf.post_mortem) self.pclo_cb.setChecked(self.runconf.python_args_enabled) self.pclo_edit.setText(self.runconf.python_args) self.clear_var_cb.setChecked(self.runconf.clear_namespace) self.file_dir_radio.setChecked(self.runconf.file_dir) self.cwd_radio.setChecked(self.runconf.cw_dir) self.fixed_dir_radio.setChecked(self.runconf.fixed_dir) self.dir = self.runconf.dir self.wd_edit.setText(self.dir) def get(self): self.runconf.args_enabled = self.clo_cb.isChecked() self.runconf.args = to_text_string(self.clo_edit.text()) self.runconf.current = self.current_radio.isChecked() self.runconf.systerm = self.systerm_radio.isChecked() self.runconf.interact = self.interact_cb.isChecked() self.runconf.post_mortem = self.post_mortem_cb.isChecked() self.runconf.python_args_enabled = self.pclo_cb.isChecked() self.runconf.python_args = to_text_string(self.pclo_edit.text()) self.runconf.clear_namespace = self.clear_var_cb.isChecked() self.runconf.file_dir = self.file_dir_radio.isChecked() self.runconf.cw_dir = self.cwd_radio.isChecked() self.runconf.fixed_dir = self.fixed_dir_radio.isChecked() self.runconf.dir = self.wd_edit.text() return self.runconf.get() def is_valid(self): wdir = to_text_string(self.wd_edit.text()) if not self.fixed_dir_radio.isChecked() or osp.isdir(wdir): return True else: QMessageBox.critical(self, _("Run configuration"), _("The following working directory is " "not valid:<br><b>%s</b>") % wdir) return False def set_firstrun_o(self): CONF.set('run', ALWAYS_OPEN_FIRST_RUN_OPTION, self.firstrun_cb.isChecked())
class FindReplace(QWidget): """Find widget""" STYLE = {False: "background-color:rgb(255, 175, 90);", True: ""} visibility_changed = Signal(bool) 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 create_shortcuts(self, parent): """Create shortcuts for this widget""" # Configurable findnext = config_shortcut(self.find_next, context='_', name='Find next', parent=parent) findprev = config_shortcut(self.find_previous, context='_', name='Find previous', parent=parent) togglefind = config_shortcut(self.show, context='_', name='Find text', parent=parent) togglereplace = config_shortcut(self.toggle_replace_widgets, context='_', name='Replace text', parent=parent) # Fixed fixed_shortcut("Escape", self, self.hide) return [findnext, findprev, togglefind, togglereplace] def get_shortcut_data(self): """ Returns shortcut data, a list of tuples (shortcut, text, default) shortcut (QShortcut or QAction instance) text (string): action/shortcut description default (string): default key sequence """ return [sc.data for sc in self.shortcuts] def update_search_combo(self): self.search_text.lineEdit().returnPressed.emit() def update_replace_combo(self): self.replace_text.lineEdit().returnPressed.emit() def toggle_replace_widgets(self): if self.enable_replace: # Toggle replace widgets if self.replace_widgets[0].isVisible(): self.hide_replace() self.hide() else: self.show_replace() self.replace_text.setFocus() @Slot(bool) def toggle_highlighting(self, state): """Toggle the 'highlight all results' feature""" if self.editor is not None: if state: self.highlight_matches() else: self.clear_matches() def show(self): """Overrides Qt Method""" QWidget.show(self) self.visibility_changed.emit(True) if self.editor is not None: text = self.editor.get_selected_text() # If no text is highlighted for search, use whatever word is under the cursor if not text: cursor = self.editor.textCursor() cursor.select(QTextCursor.WordUnderCursor) text = to_text_string(cursor.selectedText()) # Now that text value is sorted out, use it for the search if text: self.search_text.setEditText(text) self.search_text.lineEdit().selectAll() self.refresh() else: self.search_text.lineEdit().selectAll() self.search_text.setFocus() @Slot() def hide(self): """Overrides Qt Method""" for widget in self.replace_widgets: widget.hide() QWidget.hide(self) self.visibility_changed.emit(False) if self.editor is not None: self.editor.setFocus() self.clear_matches() def show_replace(self): """Show replace widgets""" self.show() for widget in self.replace_widgets: widget.show() def hide_replace(self): """Hide replace widgets""" for widget in self.replace_widgets: widget.hide() def refresh(self): """Refresh widget""" if self.isHidden(): if self.editor is not None: self.clear_matches() return state = self.editor is not None for widget in self.widgets: widget.setEnabled(state) if state: self.find() def set_editor(self, editor, refresh=True): """ Set associated editor/web page: codeeditor.base.TextEditBaseWidget browser.WebView """ self.editor = editor # Note: This is necessary to test widgets/editor.py # in Qt builds that don't have web widgets try: from qtpy.QtWebEngineWidgets import QWebEngineView except ImportError: QWebEngineView = type(None) self.words_button.setVisible(not isinstance(editor, QWebEngineView)) self.re_button.setVisible(not isinstance(editor, QWebEngineView)) from spyderlib.widgets.sourcecode.codeeditor import CodeEditor self.is_code_editor = isinstance(editor, CodeEditor) self.highlight_button.setVisible(self.is_code_editor) if refresh: self.refresh() if self.isHidden() and editor is not None: self.clear_matches() @Slot() def find_next(self): """Find next occurrence""" state = self.find(changed=False, forward=True, rehighlight=False) self.editor.setFocus() self.search_text.add_current_text() return state @Slot() def find_previous(self): """Find previous occurrence""" state = self.find(changed=False, forward=False, rehighlight=False) self.editor.setFocus() return state def text_has_been_edited(self, text): """Find text has been edited (this slot won't be triggered when setting the search pattern combo box text programmatically""" self.find(changed=True, forward=True, start_highlight_timer=True) def highlight_matches(self): """Highlight found results""" if self.is_code_editor and self.highlight_button.isChecked(): text = self.search_text.currentText() words = self.words_button.isChecked() regexp = self.re_button.isChecked() self.editor.highlight_found_results(text, words=words, regexp=regexp) def clear_matches(self): """Clear all highlighted matches""" if self.is_code_editor: self.editor.clear_found_results() def find(self, changed=True, forward=True, rehighlight=True, start_highlight_timer=False): """Call the find function""" text = self.search_text.currentText() if len(text) == 0: self.search_text.lineEdit().setStyleSheet("") return None else: case = self.case_button.isChecked() words = self.words_button.isChecked() regexp = self.re_button.isChecked() found = self.editor.find_text(text, changed, forward, case=case, words=words, regexp=regexp) self.search_text.lineEdit().setStyleSheet(self.STYLE[found]) if self.is_code_editor and found: if rehighlight or not self.editor.found_results: self.highlight_timer.stop() if start_highlight_timer: self.highlight_timer.start() else: self.highlight_matches() else: self.clear_matches() return found @Slot() def replace_find(self): """Replace and find""" if (self.editor is not None): replace_text = to_text_string(self.replace_text.currentText()) search_text = to_text_string(self.search_text.currentText()) pattern = search_text if self.re_button.isChecked() else None case = self.case_button.isChecked() first = True cursor = None while True: if first: # First found seltxt = to_text_string(self.editor.get_selected_text()) cmptxt1 = search_text if case else search_text.lower() cmptxt2 = seltxt if case else seltxt.lower() if self.editor.has_selected_text() and cmptxt1 == cmptxt2: # Text was already found, do nothing pass else: if not self.find(changed=False, forward=True, rehighlight=False): break first = False wrapped = False position = self.editor.get_position('cursor') position0 = position cursor = self.editor.textCursor() cursor.beginEditBlock() else: position1 = self.editor.get_position('cursor') if is_position_inf(position1, position0 + len(replace_text) - len(search_text) + 1): # Identify wrapping even when the replace string # includes part of the search string wrapped = True if wrapped: if position1 == position or \ is_position_sup(position1, position): # Avoid infinite loop: replace string includes # part of the search string break if position1 == position0: # Avoid infinite loop: single found occurrence break position0 = position1 if pattern is None: cursor.removeSelectedText() cursor.insertText(replace_text) else: seltxt = to_text_string(cursor.selectedText()) cursor.removeSelectedText() cursor.insertText(re.sub(pattern, replace_text, seltxt)) if self.find_next(): found_cursor = self.editor.textCursor() cursor.setPosition(found_cursor.selectionStart(), QTextCursor.MoveAnchor) cursor.setPosition(found_cursor.selectionEnd(), QTextCursor.KeepAnchor) else: break if not self.all_check.isChecked(): break self.all_check.setCheckState(Qt.Unchecked) if cursor is not None: cursor.endEditBlock()
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('JSON valid') JSON_INVALID = _('JSON invalid') def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('appearance', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate() @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}") self.host_input.setToolTip('Hostname must be valid') return else: self.host_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.host_input.setToolTip('Hostname is valid') self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Command must be non empty') return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Program was not found ' 'on your system') return else: self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.cmd_input.setToolTip('Program was found on your system') self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except: pass except (ValueError, json.decoder.JSONDecodeError): try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) try: self.validate() except: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, configurations=configurations) return server
class LibraryCatalogDialog(QDialog): def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["libraryCatalog"]) self.setMinimumSize(700, 500) self.setupVariables() self.setupUI() def setupVariables(self): self.isUpdating = False self.catalogEntryId = None self.localCatalog = CatalogUtil.loadLocalCatalog() self.remoteCatalog = gitHubRepoCacheData self.localCatalogData = self.getLocalCatalogItems() self.remoteCatalogData = self.getRemoteCatalogItems() self.location = "local" self.textButtonStyle = "QPushButton {background-color: #333972; color: white;} QPushButton:hover {background-color: #333972;} QPushButton:pressed { background-color: #515790;}" def setupUI(self): mainLayout = QVBoxLayout() filterLayout = QHBoxLayout() filterLayout.addWidget(QLabel(config.thisTranslation["menu5_search"])) self.filterEntry = QLineEdit() self.filterEntry.setClearButtonEnabled(True) self.filterEntry.textChanged.connect(self.resetItems) filterLayout.addWidget(self.filterEntry) mainLayout.addLayout(filterLayout) self.searchTypeBox = QGroupBox("") locationLayout = QHBoxLayout() self.localRadioButton = QRadioButton("Local") self.localRadioButton.setChecked(True) self.localRadioButton.toggled.connect( lambda: self.setLocation("local")) locationLayout.addWidget(self.localRadioButton) self.remoteRadioButton = QRadioButton("Remote") self.remoteRadioButton.toggled.connect( lambda: self.setLocation("remote")) locationLayout.addWidget(self.remoteRadioButton) self.searchTypeBox.setLayout(locationLayout) mainLayout.addWidget(self.searchTypeBox) typesLayout = QHBoxLayout() button = QPushButton("All") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(True)) typesLayout.addWidget(button) button = QPushButton("None") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(False)) typesLayout.addWidget(button) self.bookCheckbox = QCheckBox("BOOK") self.bookCheckbox.setChecked(True) self.bookCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.bookCheckbox) self.pdfCheckbox = QCheckBox("PDF") self.pdfCheckbox.setChecked(True) self.pdfCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.pdfCheckbox) self.docxCheckbox = QCheckBox("DOCX") self.docxCheckbox.setChecked(True) self.docxCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.docxCheckbox) self.devotionalCheckbox = QCheckBox("DEVOTIONAL") self.devotionalCheckbox.setChecked(True) self.devotionalCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.devotionalCheckbox) self.commCheckbox = QCheckBox("COMM") self.commCheckbox.setChecked(True) self.commCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.commCheckbox) self.mp3Checkbox = QCheckBox("MP3") self.mp3Checkbox.setChecked(True) self.mp3Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp3Checkbox) self.mp4Checkbox = QCheckBox("MP4") self.mp4Checkbox.setChecked(True) self.mp4Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp4Checkbox) mainLayout.addLayout(typesLayout) self.dataView = QTableView() self.dataView.clicked.connect(self.itemClicked) self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() self.openButton = QPushButton(config.thisTranslation["open"]) self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.openButton.clicked.connect(self.open) buttonLayout.addWidget(self.openButton) self.downloadButton = QPushButton(config.thisTranslation["download"]) self.downloadButton.setEnabled(False) self.downloadButton.clicked.connect(self.download) buttonLayout.addWidget(self.downloadButton) button = QPushButton(config.thisTranslation["close"]) button.setStyleSheet(self.textButtonStyle) button.clicked.connect(self.close) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def setLocation(self, location): self.location = location self.resetItems() if location == "local": self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.downloadButton.setEnabled(False) else: self.openButton.setEnabled(False) self.openButton.setStyleSheet("") self.downloadButton.setEnabled(True) def selectAllTypes(self, value): self.pdfCheckbox.setChecked(value) self.mp3Checkbox.setChecked(value) self.mp4Checkbox.setChecked(value) self.bookCheckbox.setChecked(value) self.docxCheckbox.setChecked(value) self.devotionalCheckbox.setChecked(value) self.commCheckbox.setChecked(value) def getLocalCatalogItems(self): return self.getCatalogItems(self.localCatalog) def getRemoteCatalogItems(self): return self.getCatalogItems(self.remoteCatalog) def getCatalogItems(self, catalog): data = {} pdfCount = 0 mp3Count = 0 mp4Count = 0 bookCount = 0 docxCount = 0 commCount = 0 lexCount = 0 devotionalCount = 0 for filename, type, directory, file, description, repo, installDirectory, sha in catalog: id = "UNKNOWN" if type == "PDF": pdfCount += 1 id = "{0}-{1}".format(type, pdfCount) elif type == "MP3": mp3Count += 1 id = "{0}-{1}".format(type, mp3Count) elif type == "MP4": mp4Count += 1 id = "{0}-{1}".format(type, mp4Count) elif type == "BOOK": bookCount += 1 id = "{0}-{1}".format(type, bookCount) elif type == "DOCX": docxCount += 1 id = "{0}-{1}".format(type, docxCount) elif type == "COMM": commCount += 1 id = "{0}-{1}".format(type, commCount) elif type == "LEX": lexCount += 1 id = "{0}-{1}".format(type, lexCount) elif type == "DEVOTIONAL": devotionalCount += 1 id = "{0}-{1}".format(type, devotionalCount) data[id] = [ id, filename, type, directory, file, description, repo, installDirectory, sha ] return data def resetItems(self): self.isUpdating = True self.dataViewModel.clear() filterEntry = self.filterEntry.text().lower() rowCount = 0 colCount = 0 catalogData = self.localCatalogData if self.location == "remote": catalogData = self.remoteCatalogData for id, value in catalogData.items(): id2, filename, type, directory, file, description, repo, installDirectory, sha = value if (filterEntry == "" or filterEntry in filename.lower() or filterEntry in description.lower()): if (not self.pdfCheckbox.isChecked() and type == "PDF") or \ (not self.mp3Checkbox.isChecked() and type == "MP3") or \ (not self.mp4Checkbox.isChecked() and type == "MP4") or \ (not self.bookCheckbox.isChecked() and type == "BOOK") or \ (not self.docxCheckbox.isChecked() and type == "DOCX") or \ (not self.devotionalCheckbox.isChecked() and type == "DEVOTIONAL") or \ (not self.commCheckbox.isChecked() and type == "COMM"): continue enable = True if self.location == "remote": installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): enable = False item = QStandardItem(id) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(file) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(directory) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 # item = QStandardItem(description) # self.dataViewModel.setItem(rowCount, colCount, item) # colCount += 1 # add row count rowCount += 1 colCount = 0 self.dataViewModel.setHorizontalHeaderLabels([ "#", config.thisTranslation["file"], config.thisTranslation["directory"], # config.thisTranslation["description"] ]) self.dataView.resizeColumnsToContents() self.isUpdating = False def itemClicked(self, index): selectedRow = index.row() self.catalogEntryId = self.dataViewModel.item(selectedRow, 0).text() if self.location == "remote": item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") else: self.downloadButton.setEnabled(True) self.downloadButton.setStyleSheet(self.textButtonStyle) def displayMessage(self, message="", title="UniqueBible"): QMessageBox.information(self, title, message) def saveRemoteCatalogToCache(self): data = CatalogUtil.loadRemoteCatalog() with open("util/GitHubRepoCache.py", "w", encoding="utf-8") as fileObj: fileObj.write("gitHubRepoCacheData = {0}\n".format( pprint.pformat(data))) def fixDirectory(self, directory, type): if type == "PDF": directory = directory.replace(config.marvelData, "") directory = directory.replace("/pdf", "") if len(directory) > 0 and not directory.endswith("/"): directory += "/" if len(directory) > 0 and directory.startswith("/"): directory = directory[1:] return directory def open(self): item = self.localCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item directory = self.fixDirectory(directory, type) command = "" if type == "PDF": command = "PDF:::{0}{1}".format(directory, file) elif type == "MP3": command = "VLC:::{0}{1}".format(directory, file) elif type == "MP4": command = "VLC:::{0}{1}".format(directory, file) elif type == "BOOK": if file.endswith(".book"): file = file.replace(".book", "") config.booksFolder = directory command = "BOOK:::{0}".format(file) elif type == "COMM": file = file.replace(".commentary", "") file = file[1:] config.commentariesFolder = directory command = "COMMENTARY:::{0}:::{1} {2}".format( file, BibleBooks.eng[str(config.mainB)][0], config.mainC) elif type == "DOCX": command = "DOCX:::{0}".format(file) elif type == "DEVOTIONAL": file = file.replace(".devotional", "") command = "DEVOTIONAL:::{0}".format(file) self.parent.runTextCommand(command) def download(self): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item github = GithubUtil(repo) installDirectory = os.path.join(config.marvelData, installDirectory) file = os.path.join(installDirectory, filename + ".zip") github.downloadFile(file, sha) with zipfile.ZipFile(file, 'r') as zipped: zipped.extractall(installDirectory) os.remove(file) self.displayMessage(filename + " " + config.thisTranslation["message_installed"]) self.localCatalog = CatalogUtil.reloadLocalCatalog() self.localCatalogData = self.getLocalCatalogItems() self.resetItems()
class SnippetEditor(QDialog): SNIPPET_VALID = _('Valid snippet') SNIPPET_INVALID = _('Invalid snippet') INVALID_CB_CSS = "QComboBox {border: 1px solid red;}" VALID_CB_CSS = "QComboBox {border: 1px solid green;}" INVALID_LINE_CSS = "QLineEdit {border: 1px solid red;}" VALID_LINE_CSS = "QLineEdit {border: 1px solid green;}" MIN_SIZE = QSize(850, 600) def __init__(self, parent, language=None, trigger_text='', description='', snippet_text='', remove_trigger=False, trigger_texts=[], descriptions=[], get_option=None, set_option=None): super(SnippetEditor, self).__init__(parent) snippet_description = _( "To add a new text snippet, you need to define the text " "that triggers it, a short description (two words maximum) " "of the snippet and if it should delete the trigger text when " "inserted. Finally, you need to define the snippet body to insert." ) self.parent = parent self.trigger_text = trigger_text self.description = description self.remove_trigger = remove_trigger self.snippet_text = snippet_text self.descriptions = descriptions self.base_snippet = Snippet(language=language, trigger_text=trigger_text, snippet_text=snippet_text, description=description, remove_trigger=remove_trigger, get_option=get_option, set_option=set_option) # Widgets self.snippet_settings_description = QLabel(snippet_description) self.snippet_settings_description.setFixedWidth(450) # Trigger text self.trigger_text_label = QLabel(_('Trigger text:')) self.trigger_text_cb = QComboBox(self) self.trigger_text_cb.setEditable(True) # Description self.description_label = QLabel(_('Description:')) self.description_input = QLineEdit(self) # Remove trigger self.remove_trigger_cb = QCheckBox( _('Remove trigger text on insertion'), self) self.remove_trigger_cb.setToolTip( _('Check if the text that triggers ' 'this snippet should be removed ' 'when inserting it')) self.remove_trigger_cb.setChecked(self.remove_trigger) # Snippet body input self.snippet_label = QLabel(_('<b>Snippet text:</b>')) self.snippet_valid_label = QLabel(self.SNIPPET_INVALID, self) self.snippet_input = SimpleCodeEditor(None) # Dialog buttons 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.setWindowTitle(_('Snippet editor')) self.snippet_settings_description.setWordWrap(True) self.trigger_text_cb.setToolTip( _('Trigger text for the current snippet')) self.trigger_text_cb.addItems(trigger_texts) if self.trigger_text != '': idx = trigger_texts.index(self.trigger_text) self.trigger_text_cb.setCurrentIndex(idx) self.description_input.setText(self.description) self.description_input.textChanged.connect(lambda _x: self.validate()) text_inputs = (self.trigger_text, self.description, self.snippet_text) non_empty_text = all([x != '' for x in text_inputs]) if non_empty_text: self.button_ok.setEnabled(True) self.snippet_input.setup_editor(language=language, color_scheme=get_option( 'selected', section='appearance'), wrap=False, highlight_current_line=True, font=get_font()) self.snippet_input.set_language(language) self.snippet_input.setToolTip(_('Snippet text completion to insert')) self.snippet_input.set_text(snippet_text) # Layout setup general_layout = QVBoxLayout() general_layout.addWidget(self.snippet_settings_description) snippet_settings_group = QGroupBox(_('Trigger information')) settings_layout = QGridLayout() settings_layout.addWidget(self.trigger_text_label, 0, 0) settings_layout.addWidget(self.trigger_text_cb, 0, 1) settings_layout.addWidget(self.description_label, 1, 0) settings_layout.addWidget(self.description_input, 1, 1) all_settings_layout = QVBoxLayout() all_settings_layout.addLayout(settings_layout) all_settings_layout.addWidget(self.remove_trigger_cb) snippet_settings_group.setLayout(all_settings_layout) general_layout.addWidget(snippet_settings_group) text_layout = QVBoxLayout() text_layout.addWidget(self.snippet_label) text_layout.addWidget(self.snippet_input) text_layout.addWidget(self.snippet_valid_label) general_layout.addLayout(text_layout) general_layout.addWidget(self.bbox) self.setLayout(general_layout) # Signals self.trigger_text_cb.editTextChanged.connect(self.validate) self.description_input.textChanged.connect(self.validate) self.snippet_input.textChanged.connect(self.validate) self.bbox.accepted.connect(self.accept) self.bbox.rejected.connect(self.reject) # Final setup if trigger_text != '' or snippet_text != '': self.validate() @Slot() def validate(self): trigger_text = self.trigger_text_cb.currentText() description_text = self.description_input.text() snippet_text = self.snippet_input.toPlainText() invalid = False try: build_snippet_ast(snippet_text) self.snippet_valid_label.setText(self.SNIPPET_VALID) except SyntaxError: invalid = True self.snippet_valid_label.setText(self.SNIPPET_INVALID) if trigger_text == '': invalid = True self.trigger_text_cb.setStyleSheet(self.INVALID_CB_CSS) else: self.trigger_text_cb.setStyleSheet(self.VALID_CB_CSS) if trigger_text in self.descriptions: if self.trigger_text != trigger_text: if description_text in self.descriptions[trigger_text]: invalid = True self.description_input.setStyleSheet(self.INVALID_LINE_CSS) else: self.description_input.setStyleSheet(self.VALID_LINE_CSS) else: if description_text != self.description: if description_text in self.descriptions[trigger_text]: invalid = True self.description_input.setStyleSheet( self.INVALID_LINE_CSS) else: self.description_input.setStyleSheet( self.VALID_LINE_CSS) else: self.description_input.setStyleSheet(self.VALID_LINE_CSS) self.button_ok.setEnabled(not invalid) def get_options(self): trigger_text = self.trigger_text_cb.currentText() description_text = self.description_input.text() snippet_text = self.snippet_input.toPlainText() remove_trigger = self.remove_trigger_cb.isChecked() self.base_snippet.update(trigger_text, description_text, snippet_text, remove_trigger) return self.base_snippet
class ProcessImage(QWidget): def __init__(self, parent=None, place='LI-Energy', prefix=_VACA_PREFIX): super().__init__(parent) self._place = place or 'LI-Energy' self._prefix = prefix self._select_experimental_setup() self.cen_x = None self.cen_y = None self.sigma_x = None self.sigma_y = None self.bg_ready = False self.bg = None self.nbg = 0 self._setupUi() def _select_experimental_setup(self): pref = self._prefix if self._place.lower().startswith('li-ene'): prof = pref + ('-' if pref else '') + 'LA-BI:PRF4' self.conv_coefx = PV(prof + ':X:Gauss:Coef') self.conv_coefy = PV(prof + ':Y:Gauss:Coef') self.image_channel = prof + ':RAW:ArrayData' self.width_channel = prof + ':ROI:MaxSizeX_RBV' self.trig_name = 'LI-Fam:TI-Scrn' elif self._place.lower().startswith('li-emit'): prof = pref + ('-' if pref else '') + 'LA-BI:PRF5' self.conv_coefx = PV(prof + ':X:Gauss:Coef') self.conv_coefy = PV(prof + ':Y:Gauss:Coef') self.image_channel = prof + ':RAW:ArrayData' self.width_channel = prof + ':ROI:MaxSizeX_RBV' self.trig_name = 'LI-Fam:TI-Scrn' elif self._place.lower().startswith('tb-emit'): prof = _PVName('TB-02:DI-ScrnCam-2').substitute(prefix=pref) self.conv_coefx = PV(prof.substitute(propty='ImgScaleFactorX-RB')) self.conv_coefy = PV(prof.substitute(propty='ImgScaleFactorY-RB')) prof = _PVName('TB-02:DI-Scrn-2').substitute(prefix=pref) self.image_channel = prof.substitute(propty='ImgData-Mon') self.width_channel = prof.substitute(propty='ImgROIWidth-RB') self.trig_name = 'TB-Fam:TI-Scrn' else: raise Exception('Wrong value for "place".') def _setupUi(self): vl = QVBoxLayout(self) self.image_view = ImageView( self.process_image, parent=self, image_channel=self.image_channel, width_channel=self.width_channel) self.image_view.maxRedrawRate = 5 self.image_view.readingOrder = self.image_view.Clike self.plt_roi = PlotCurveItem([0, 0, 400, 400, 0], [0, 400, 400, 0, 0]) pen = mkPen() pen.setColor(QColor('red')) pen.setWidth(1) self.plt_roi.setPen(pen) self.image_view.addItem(self.plt_roi) self.plt_fit_x = PlotCurveItem([0, 0], [0, 400]) self.plt_fit_y = PlotCurveItem([0, 0], [0, 400]) self.plt_his_x = PlotCurveItem([0, 0], [0, 400]) self.plt_his_y = PlotCurveItem([0, 0], [0, 400]) pen = mkPen() pen.setColor(QColor('yellow')) self.plt_his_x.setPen(pen) self.plt_his_y.setPen(pen) self.image_view.addItem(self.plt_fit_x) self.image_view.addItem(self.plt_fit_y) self.image_view.addItem(self.plt_his_x) self.image_view.addItem(self.plt_his_y) vl.addWidget(self.image_view) gb_trig = QGroupBox('Trigger', self) vl.addWidget(gb_trig) gb_trig.setLayout(QVBoxLayout()) gb_trig.layout().addWidget(HLTriggerSimple( gb_trig, device=self.trig_name, prefix=self._prefix)) gb_pos = QGroupBox('Position [mm]', self) vl.addWidget(gb_pos) hl = QHBoxLayout(gb_pos) fl = QFormLayout() hl.addLayout(fl) self.cbox_method = QComboBox(gb_pos) self.cbox_method.addItem('Gauss Fit') self.cbox_method.addItem('Moments') fl.addRow(QLabel('Method', gb_pos), self.cbox_method) self.spbox_roi_size_x = QSpinBoxPlus(gb_pos) self.spbox_roi_size_y = QSpinBoxPlus(gb_pos) self.spbox_roi_center_x = QSpinBoxPlus(gb_pos) self.spbox_roi_center_y = QSpinBoxPlus(gb_pos) self.spbox_roi_size_x.setKeyboardTracking(False) self.spbox_roi_size_y.setKeyboardTracking(False) self.spbox_roi_center_x.setKeyboardTracking(False) self.spbox_roi_center_y.setKeyboardTracking(False) self.spbox_roi_size_x.setMaximum(2448) self.spbox_roi_size_y.setMaximum(2050) self.spbox_roi_center_x.setMaximum(2448) self.spbox_roi_center_y.setMaximum(2050) self.spbox_roi_size_x.setValue(300) self.spbox_roi_size_y.setValue(400) self.spbox_roi_center_x.setValue(500) self.spbox_roi_center_y.setValue(500) fl.addRow(QLabel('ROI Size X', gb_pos), self.spbox_roi_size_x) fl.addRow(QLabel('ROI Size Y', gb_pos), self.spbox_roi_size_y) self.cbbox_auto_center = QCheckBox('Automatic Centering', gb_pos) self.cbbox_auto_center.clicked.connect(self.cbbox_auto_center_clicked) self.cbbox_auto_center.setChecked(True) fl.addRow(self.cbbox_auto_center) fl.addRow(QLabel('ROI Center X', gb_pos), self.spbox_roi_center_x) fl.addRow(QLabel('ROI Center Y', gb_pos), self.spbox_roi_center_y) self.spbox_img_max = QSpinBoxPlus(gb_pos) self.spbox_img_max.setKeyboardTracking(False) self.spbox_img_max.setMinimum(0) self.spbox_img_max.setMaximum(2448) self.spbox_img_max.setValue(0) fl.addRow(QLabel('Max. Pixel Val.', gb_pos), self.spbox_img_max) self.cbbox_acq_bg = QCheckBox('Acquire Background', gb_pos) self.cbbox_acq_bg.clicked.connect(self.cbbox_acq_bg_checked) fl.addRow(self.cbbox_acq_bg) self.pb_reset_bg = QPushButton('Reset BG', gb_pos) self.pb_reset_bg.clicked.connect(self.pb_reset_bg_clicked) fl.addRow(self.pb_reset_bg) fl = QFormLayout() hl.addLayout(fl) self.lb_xave = QLabel('0', gb_pos) self.lb_yave = QLabel('0', gb_pos) self.lb_xstd = QLabel('0', gb_pos) self.lb_ystd = QLabel('0', gb_pos) fl.addRow(QLabel('Average Position', gb_pos)) fl.addRow(QLabel('x = ', gb_pos), self.lb_xave) fl.addRow(QLabel('y = ', gb_pos), self.lb_yave) fl.addRow(QLabel('Beam Size', gb_pos)) fl.addRow(QLabel('x = ', gb_pos), self.lb_xstd) fl.addRow(QLabel('y = ', gb_pos), self.lb_ystd) hl.setSpacing(12) hl.setStretch(0, 1) hl.setStretch(1, 1) def cbbox_auto_center_clicked(self, clicked): self.spbox_roi_center_x.setEnabled(not clicked) self.spbox_roi_center_y.setEnabled(not clicked) def pb_reset_bg_clicked(self, clicked=False): self.bg_ready = False self.bg = None self.nbg = 0 def cbbox_acq_bg_checked(self, check): if check: self.pb_reset_bg_clicked() else: if self.bg is not None: self.bg /= self.nbg self.bg_ready = True def calc_roi(self, image): proj_x = image.sum(axis=0) proj_y = image.sum(axis=1) axis_x = np.arange(image.shape[1]) axis_y = np.arange(image.shape[0]) if self.cbbox_auto_center.isChecked(): cen_x, _ = _calc_moments(axis_x, proj_x) cen_y, _ = _calc_moments(axis_y, proj_y) else: cen_x = self.spbox_roi_center_x.value() cen_y = self.spbox_roi_center_y.value() roi_size_x = self.spbox_roi_size_x.value() roi_size_y = self.spbox_roi_size_y.value() strt_x, end_x = np.array([-1, 1])*roi_size_x + int(cen_x) strt_y, end_y = np.array([-1, 1])*roi_size_y + int(cen_y) strt_x = max(strt_x, 0) strt_y = max(strt_y, 0) end_x = min(end_x, image.shape[1]) end_y = min(end_y, image.shape[0]) self.plt_roi.setData( np.array([strt_x, strt_x, end_x, end_x, strt_x]), np.array([strt_y, end_y, end_y, strt_y, strt_y])) image = image[strt_y:end_y, strt_x:end_x] proj_x = image.sum(axis=0) proj_y = image.sum(axis=1) axis_x = axis_x[strt_x:end_x] axis_y = axis_y[strt_y:end_y] return proj_x, proj_y, axis_x, axis_y def process_image(self, image, wid): if wid <= 0: return image try: image = image.reshape((-1, wid)) except (TypeError, ValueError, AttributeError): return image if self.cbbox_acq_bg.isChecked(): if self.bg is None: self.bg = np.array(image, dtype=float) else: self.bg += np.array(image, dtype=float) self.nbg += 1 return image if self.bg_ready: image -= np.array(self.bg, dtype=image.dtype) b = np.where(image < 0) image[b] = 0 maxi = self.spbox_img_max.value() if maxi > 0: b = np.where(image > maxi) self.image_view.colorMapMax = maxi image[b] = maxi proj_x, proj_y, axis_x, axis_y = self.calc_roi(image) x_max = max(proj_x) y_max = max(proj_y) if self.cbox_method.currentIndex(): cen_x, std_x = _calc_moments(axis_x, proj_x) cen_y, std_y = _calc_moments(axis_y, proj_y) amp_x = x_max amp_y = y_max off_x = 0 off_y = 0 else: amp_x, cen_x, std_x, off_x = _fit_gaussian(axis_x, proj_x) amp_y, cen_y, std_y, off_y = _fit_gaussian(axis_y, proj_y) std_x = abs(std_x) std_y = abs(std_y) yd = _gaussian(axis_x, amp_x, cen_x, std_x, off_x)/x_max*400 self.plt_fit_x.setData(axis_x, yd + axis_y[0]) self.plt_his_x.setData(axis_x, proj_x/x_max*400 + axis_y[0]) yd = _gaussian(axis_y, amp_y, cen_y, std_y, off_y)/y_max*400 self.plt_fit_y.setData(yd + axis_x[0], axis_y) self.plt_his_y.setData(proj_y/y_max*400 + axis_x[0], axis_y) offset_x = image.shape[1]/2 offset_y = image.shape[0]/2 self.lb_xave.setText('{0:4d}'.format(int(cen_x or 0))) self.lb_yave.setText('{0:4d}'.format(int(cen_y or 0))) self.lb_xstd.setText('{0:4d}'.format(int(std_x or 0))) self.lb_ystd.setText('{0:4d}'.format(int(std_y or 0))) coefx = self.conv_coefx.value coefy = self.conv_coefy.value if coefx is None or coefy is None: return cen_x -= offset_x cen_y -= offset_y self.cen_x = cen_x * coefx*1e-3 # transform to meter self.cen_y = cen_y * coefy*1e-3 self.sigma_x = std_x * coefx*1e-3 self.sigma_y = std_y * coefy*1e-3 return image def get_params(self): return self.cen_x, self.sigma_x, self.cen_y, self.sigma_y
class QtTracksControls(QtLayerControls): """Qt view and controls for the Tracks layer. Parameters ---------- layer : napari.layers.Tracks An instance of a Tracks layer. Attributes ---------- grid_layout : qtpy.QtWidgets.QGridLayout Layout of Qt widget controls for the layer. layer : layers.Tracks An instance of a Tracks layer. """ layer: 'napari.layers.Tracks' def __init__(self, layer): super().__init__(layer) # NOTE(arl): there are no events fired for changing checkboxes self.layer.events.tail_width.connect(self._on_tail_width_change) self.layer.events.tail_length.connect(self._on_tail_length_change) self.layer.events.head_length.connect(self._on_head_length_change) self.layer.events.properties.connect(self._on_properties_change) self.layer.events.colormap.connect(self._on_colormap_change) self.layer.events.color_by.connect(self._on_color_by_change) # combo box for track coloring, we can get these from the properties # keys self.color_by_combobox = QComboBox() self.color_by_combobox.addItems(self.layer.properties_to_color_by) self.colormap_combobox = QComboBox() for name, colormap in AVAILABLE_COLORMAPS.items(): display_name = colormap._display_name self.colormap_combobox.addItem(display_name, name) # slider for track head length self.head_length_slider = QSlider(Qt.Horizontal) self.head_length_slider.setFocusPolicy(Qt.NoFocus) self.head_length_slider.setMinimum(0) self.head_length_slider.setMaximum(self.layer._max_length) self.head_length_slider.setSingleStep(1) # slider for track tail length self.tail_length_slider = QSlider(Qt.Horizontal) self.tail_length_slider.setFocusPolicy(Qt.NoFocus) self.tail_length_slider.setMinimum(1) self.tail_length_slider.setMaximum(self.layer._max_length) self.tail_length_slider.setSingleStep(1) # slider for track edge width self.tail_width_slider = QSlider(Qt.Horizontal) self.tail_width_slider.setFocusPolicy(Qt.NoFocus) self.tail_width_slider.setMinimum(1) self.tail_width_slider.setMaximum(int(2 * self.layer._max_width)) self.tail_width_slider.setSingleStep(1) # checkboxes for display self.id_checkbox = QCheckBox() self.tail_checkbox = QCheckBox() self.tail_checkbox.setChecked(True) self.graph_checkbox = QCheckBox() self.graph_checkbox.setChecked(True) self.tail_width_slider.valueChanged.connect(self.change_tail_width) self.tail_length_slider.valueChanged.connect(self.change_tail_length) self.head_length_slider.valueChanged.connect(self.change_head_length) self.tail_checkbox.stateChanged.connect(self.change_display_tail) self.id_checkbox.stateChanged.connect(self.change_display_id) self.graph_checkbox.stateChanged.connect(self.change_display_graph) self.color_by_combobox.currentTextChanged.connect(self.change_color_by) self.colormap_combobox.currentTextChanged.connect(self.change_colormap) self.layout().addRow(trans._('color by:'), self.color_by_combobox) self.layout().addRow(trans._('colormap:'), self.colormap_combobox) self.layout().addRow(trans._('blending:'), self.blendComboBox) self.layout().addRow(trans._('opacity:'), self.opacitySlider) self.layout().addRow(trans._('tail width:'), self.tail_width_slider) self.layout().addRow(trans._('tail length:'), self.tail_length_slider) self.layout().addRow(trans._('head length:'), self.head_length_slider) self.layout().addRow(trans._('tail:'), self.tail_checkbox) self.layout().addRow(trans._('show ID:'), self.id_checkbox) self.layout().addRow(trans._('graph:'), self.graph_checkbox) self._on_tail_length_change() self._on_tail_width_change() self._on_colormap_change() self._on_color_by_change() def _on_tail_width_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.tail_width.blocker(): value = int(2 * self.layer.tail_width) self.tail_width_slider.setValue(value) def _on_tail_length_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.tail_length.blocker(): value = self.layer.tail_length self.tail_length_slider.setValue(value) def _on_head_length_change(self): """Receive layer model track line width change event and update slider.""" with self.layer.events.head_length.blocker(): value = self.layer.head_length self.head_length_slider.setValue(value) def _on_properties_change(self): """Change the properties that can be used to color the tracks.""" with self.layer.events.properties.blocker(): with qt_signals_blocked(self.color_by_combobox): self.color_by_combobox.clear() self.color_by_combobox.addItems(self.layer.properties_to_color_by) def _on_colormap_change(self): """Receive layer model colormap change event and update combobox.""" with self.layer.events.colormap.blocker(): self.colormap_combobox.setCurrentIndex( self.colormap_combobox.findData(self.layer.colormap)) def _on_color_by_change(self): """Receive layer model color_by change event and update combobox.""" with self.layer.events.color_by.blocker(): color_by = self.layer.color_by idx = self.color_by_combobox.findText(color_by, Qt.MatchFixedString) self.color_by_combobox.setCurrentIndex(idx) def change_tail_width(self, value): """Change track line width of shapes on the layer model. Parameters ---------- value : float Line width of track tails. """ self.layer.tail_width = float(value) / 2.0 def change_tail_length(self, value): """Change edge line backward length of shapes on the layer model. Parameters ---------- value : int Line length of track tails. """ self.layer.tail_length = value def change_head_length(self, value): """Change edge line forward length of shapes on the layer model. Parameters ---------- value : int Line length of track tails. """ self.layer.head_length = value def change_display_tail(self, state): self.layer.display_tail = self.tail_checkbox.isChecked() def change_display_id(self, state): self.layer.display_id = self.id_checkbox.isChecked() def change_display_graph(self, state): self.layer.display_graph = self.graph_checkbox.isChecked() def change_color_by(self, value: str): self.layer.color_by = value def change_colormap(self, colormap: str): self.layer.colormap = self.colormap_combobox.currentData()
class MaskWidget(QWidget): values_changed = Signal() def __init__(self, settings: ImageSettings, parent=None): super().__init__(parent) self.setContentsMargins(0, 0, 0, 0) self.settings = settings self.dilate_radius = QSpinBox() self.dilate_radius.setRange(-100, 100) # self.dilate_radius.setButtonSymbols(QAbstractSpinBox.NoButtons) self.dilate_radius.setSingleStep(1) self.dilate_radius.setDisabled(True) self.dilate_dim = QEnumComboBox(enum_class=RadiusType) self.dilate_dim.setToolTip("With minus radius mask will be eroded") # noinspection PyUnresolvedReferences self.dilate_dim.currentIndexChanged.connect( partial(off_widget, self.dilate_radius, self.dilate_dim)) self.radius_information = QLabel() # noinspection PyUnresolvedReferences self.dilate_radius.valueChanged.connect(self.dilate_change) # noinspection PyUnresolvedReferences self.dilate_dim.currentIndexChanged.connect(self.dilate_change) self.fill_holes = QEnumComboBox(enum_class=RadiusType) self.max_hole_size = QSpinBox() self.max_hole_size.setRange(-1, 10000) self.max_hole_size.setValue(-1) self.max_hole_size.setSingleStep(100) self.max_hole_size.setDisabled(True) self.max_hole_size.setToolTip( "Maximum size of holes to be closed. -1 means that all holes will be closed" ) # noinspection PyUnresolvedReferences self.fill_holes.currentIndexChanged.connect( partial(off_widget, self.max_hole_size, self.fill_holes)) self.save_components = QCheckBox() self.clip_to_mask = QCheckBox() self.reversed_check = QCheckBox() # noinspection PyUnresolvedReferences self.dilate_radius.valueChanged.connect(self.values_changed.emit) # noinspection PyUnresolvedReferences self.dilate_dim.currentIndexChanged.connect(self.values_changed.emit) # noinspection PyUnresolvedReferences self.fill_holes.currentIndexChanged.connect(self.values_changed.emit) # noinspection PyUnresolvedReferences self.max_hole_size.valueChanged.connect(self.values_changed.emit) self.save_components.stateChanged.connect(self.values_changed.emit) self.clip_to_mask.stateChanged.connect(self.values_changed.emit) self.reversed_check.stateChanged.connect(self.values_changed.emit) layout = QVBoxLayout() layout1 = QHBoxLayout() layout1.addWidget(QLabel("Dilate mask:")) layout1.addWidget(self.dilate_dim) layout1.addWidget(QLabel("radius (in pix):")) layout1.addWidget(self.dilate_radius) layout.addLayout(layout1) layout2 = QHBoxLayout() layout2.addWidget(QLabel("Fill holes:")) layout2.addWidget(self.fill_holes) layout2.addWidget(QLabel("Max size:")) layout2.addWidget(self.max_hole_size) layout.addLayout(layout2) layout3 = QHBoxLayout() comp_lab = QLabel("Save components:") comp_lab.setToolTip( "save components information in mask. Dilation, " "holes filing will be done separately for each component") self.save_components.setToolTip(comp_lab.toolTip()) layout3.addWidget(comp_lab) layout3.addWidget(self.save_components) layout3.addStretch() clip_mask = QLabel("Clip to previous mask:") clip_mask.setToolTip("Useful dilated new mask") layout3.addWidget(clip_mask) layout3.addWidget(self.clip_to_mask) layout.addLayout(layout3) layout4 = QHBoxLayout() layout4.addWidget(QLabel("Reversed mask:")) layout4.addWidget(self.reversed_check) layout4.addStretch(1) layout.addLayout(layout4) self.setLayout(layout) self.dilate_change() def get_dilate_radius(self): radius = calculate_operation_radius(self.dilate_radius.value(), self.settings.image_spacing, self.dilate_dim.currentEnum()) if isinstance(radius, (list, tuple)): return [int(x + 0.5) for x in radius] return int(radius) def dilate_change(self): if self.dilate_radius.value() == 0 or self.dilate_dim.currentEnum( ) == RadiusType.NO: self.radius_information.setText("Dilation radius: 0") else: dilate_radius = self.get_dilate_radius() if isinstance(dilate_radius, list): self.radius_information.setText( f"Dilation radius: {dilate_radius[::-1]}") else: self.radius_information.setText( f"Dilation radius: {dilate_radius}") def get_mask_property(self): return MaskProperty( dilate=self.dilate_dim.currentEnum() if self.dilate_radius.value() != 0 else RadiusType.NO, dilate_radius=self.dilate_radius.value() if self.dilate_dim.currentEnum() != RadiusType.NO else 0, fill_holes=self.fill_holes.currentEnum() if self.max_hole_size.value() != 0 else RadiusType.NO, max_holes_size=self.max_hole_size.value() if self.fill_holes.currentEnum() != RadiusType.NO else 0, save_components=self.save_components.isChecked(), clip_to_mask=self.clip_to_mask.isChecked(), reversed_mask=self.reversed_check.isChecked(), ) def set_mask_property(self, prop: MaskProperty): self.dilate_dim.setCurrentEnum(prop.dilate) self.dilate_radius.setValue(prop.dilate_radius) self.fill_holes.setCurrentEnum(prop.fill_holes) self.max_hole_size.setValue(prop.max_holes_size) self.save_components.setChecked(prop.save_components) self.clip_to_mask.setChecked(prop.clip_to_mask) self.reversed_check.setChecked(prop.reversed_mask)
class PreferencesWindow(PyDialog): """ +-------------+ | Preferences | +---------------------------------+ | Text Size ______ Default | | Annotation Color ______ | | Annotation Size ______ | | Picker Size ______ | | Back Color ______ | | Text Color ______ | | | | Apply OK Cancel | +---------------------------------+ """ def __init__(self, data, win_parent=None): """ Saves the data members from data and performs type checks """ PyDialog.__init__(self, data, win_parent) self._updated_preference = False self._default_font_size = data['font_size'] self._default_text_size = 14 self._default_annotation_size = 18 self._default_coord_scale = 0.05 * 100. self._default_coord_text_scale = 0.5 * 100. self._default_clipping_min = data['min_clip'] self._default_clipping_max = data['max_clip'] #self._default_annotation_size = data['annotation_size'] # int #self.default_magnify = data['magnify'] self.dim_max = data['dim_max'] self._use_gradient_background = data['use_gradient_background'] # bool self._show_corner_coord = data['show_corner_coord'] self._annotation_size = data['annotation_size'] # int #self.out_data = data self._picker_size = data['picker_size'] * 100. self._coord_scale = data['coord_scale'] * 100. self._coord_text_scale = data['coord_text_scale'] * 100. self._magnify = data['magnify'] self._text_size = data['text_size'] self._highlight_opacity = data['highlight_opacity'] self.annotation_color_float, self.annotation_color_int = _check_color( data['annotation_color']) self.background_color_float, self.background_color_int = _check_color( data['background_color']) self.background_color2_float, self.background_color2_int = _check_color( data['background_color2']) self.text_color_float, self.text_color_int = _check_color( data['text_color']) self.highlight_color_float, self.highlight_color_int = _check_color( data['highlight_color']) self._nastran_is_element_quality = data['nastran_is_element_quality'] self._nastran_is_properties = data['nastran_is_properties'] self._nastran_is_3d_bars = data['nastran_is_3d_bars'] self._nastran_is_3d_bars_update = data['nastran_is_3d_bars_update'] self._nastran_is_bar_axes = data['nastran_is_bar_axes'] self._nastran_create_coords = data['nastran_create_coords'] self.setWindowTitle('Preferences') self.create_widgets() self.create_layout() self.set_connections() self.on_font(self._default_font_size) self.on_gradient_scale() #self.show() def create_widgets(self): """creates the display window""" # Text Size self.font_size_label = QLabel("Font Size:") self.font_size_edit = QSpinBox(self) self.font_size_edit.setValue(self._default_font_size) self.font_size_edit.setRange(7, 20) self.font_size_button = QPushButton("Default") #----------------------------------------------------------------------- # Annotation Color self.annotation_color_label = QLabel("Annotation Color:") self.annotation_color_edit = QPushButtonColor( self.annotation_color_int) self.annotation_color_label.hide() self.annotation_color_edit.hide() #----------------------------------------------------------------------- # Text Color self.text_size_label = QLabel("Text Size:") self.text_size_edit = QSpinBox(self) self.text_size_edit.setValue(self._default_text_size) self.text_size_edit.setRange(7, 30) self.text_size_button = QPushButton("Default") # Text Color self.text_color_label = QLabel("Text Color:") self.text_color_edit = QPushButtonColor(self.text_color_int) #----------------------------------------------------------------------- # Highlight Color self.highlight_opacity_label = QLabel("Highlight Opacity:") self.highlight_opacity_edit = QDoubleSpinBox(self) self.highlight_opacity_edit.setValue(self._highlight_opacity) self.highlight_opacity_edit.setRange(0.1, 1.0) self.highlight_opacity_edit.setDecimals(1) self.highlight_opacity_edit.setSingleStep(0.1) self.highlight_opacity_button = QPushButton("Default") # Text Color self.highlight_color_label = QLabel("Highlight Color:") self.highlight_color_edit = QPushButtonColor(self.highlight_color_int) #----------------------------------------------------------------------- # Background Color self.background_color_label = QLabel("Btm Background Color:") self.background_color_edit = QPushButtonColor( self.background_color_int) # Background Color2 self.gradient_scale_label = QLabel("Gradient Background:") self.gradient_scale_checkbox = QCheckBox() self.gradient_scale_checkbox.setChecked(self._use_gradient_background) self.background_color2_label = QLabel("Top Background Color:") self.background_color2_edit = QPushButtonColor( self.background_color2_int) #----------------------------------------------------------------------- # Annotation Size self.annotation_size_label = QLabel("Annotation Size:") self.annotation_size_edit = QSpinBox(self) self.annotation_size_edit.setRange(1, 500) self.annotation_size_edit.setValue(self._annotation_size) self.annotation_size_button = QPushButton("Default") #----------------------------------------------------------------------- # Picker Size self.picker_size_label = QLabel("Picker Size (% of Screen):") self.picker_size_edit = QDoubleSpinBox(self) self.picker_size_edit.setRange(0., 10.) log_dim = log10(self.dim_max) decimals = int(ceil(abs(log_dim))) decimals = max(6, decimals) self.picker_size_edit.setDecimals(decimals) self.picker_size_edit.setSingleStep(10. / 5000.) self.picker_size_edit.setValue(self._picker_size) #----------------------------------------------------------------------- # Clipping Min self.clipping_min_label = QLabel("Clipping Min:") self.clipping_min_edit = QLineEdit(str(self._default_clipping_min)) self.clipping_min_button = QPushButton("Default") # Clipping Max self.clipping_max_label = QLabel("Clipping Max:") self.clipping_max_edit = QLineEdit(str(self._default_clipping_max)) self.clipping_max_button = QPushButton("Default") #----------------------------------------------------------------------- self.coord_scale_label = QLabel('Coordinate System Scale:') self.coord_scale_button = QPushButton("Default") self.coord_scale_edit = QDoubleSpinBox(self) self.coord_scale_edit.setRange(0.1, 1000.) self.coord_scale_edit.setDecimals(3) self.coord_scale_edit.setSingleStep(2.5) self.coord_scale_edit.setValue(self._coord_scale) self.coord_text_scale_label = QLabel('Coordinate System Text Scale:') self.coord_text_scale_button = QPushButton("Default") self.coord_text_scale_edit = QDoubleSpinBox(self) self.coord_text_scale_edit.setRange(0.1, 2000.) self.coord_text_scale_edit.setDecimals(3) self.coord_text_scale_edit.setSingleStep(2.5) self.coord_text_scale_edit.setValue(self._coord_text_scale) # Show corner coord self.corner_coord_label = QLabel("Show Corner Coordinate System:") self.corner_coord_checkbox = QCheckBox() self.corner_coord_checkbox.setChecked(self._show_corner_coord) #----------------------------------------------------------------------- self.magnify_label = QLabel('Screenshot Magnify:') self.magnify_edit = QSpinBox(self) self.magnify_edit.setMinimum(1) self.magnify_edit.setMaximum(10) self.magnify_edit.setValue(self._magnify) #----------------------------------------------------------------------- self.nastran_is_element_quality_checkbox = QCheckBox('Element Quality') self.nastran_is_properties_checkbox = QCheckBox('Properties') self.nastran_is_3d_bars_checkbox = QCheckBox('3D Bars') self.nastran_is_3d_bars_update_checkbox = QCheckBox('Update 3D Bars') self.nastran_create_coords_checkbox = QCheckBox('Coords') self.nastran_is_bar_axes_checkbox = QCheckBox('Bar Axes') self.nastran_is_3d_bars_checkbox.setDisabled(True) self.nastran_is_3d_bars_update_checkbox.setDisabled(True) #self.nastran_is_bar_axes_checkbox.setDisabled(True) self.nastran_is_element_quality_checkbox.setChecked( self._nastran_is_element_quality) self.nastran_is_properties_checkbox.setChecked( self._nastran_is_properties) self.nastran_is_3d_bars_checkbox.setChecked(self._nastran_is_3d_bars) #self.nastran_is_3d_bars_update_checkbox.setChecked(self._nastran_is_3d_bars_update) self.nastran_is_3d_bars_update_checkbox.setChecked(False) self.nastran_is_bar_axes_checkbox.setChecked(self._nastran_is_bar_axes) self.nastran_create_coords_checkbox.setChecked( self._nastran_create_coords) #----------------------------------------------------------------------- # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel") #def create_legend_widgets(self): #""" #Creates the widgets for the legend control #Name Itailic Bold Font #==== ======= ===== ======== #Title check check pulldown #Label check check pulldown #""" #self.name_label = QLabel("Name:") #self.italic_label = QLabel("Italic:") #self.bold_label = QLabel("Bold:") #self.font_label = QLabel("Font:") #self.legend_label = QLabel("Legend:") #self.legend_title_name = QLabel("Title") #self.legend_title_italic_check = QCheckBox() #self.legend_title_bold_check = QCheckBox() #self.legend_title_font_edit = QComboBox() #self.legend_title_font_edit.addItems(['cat', 'dog', 'frog']) #self.legend_label_italic_name = QLabel("Label") #self.legend_label_italic_check = QCheckBox() #self.legend_label_bold_check = QCheckBox() #self.legend_label_font_edit = QComboBox() #self.legend_label_font_edit.addItems(['cat2', 'dog2', 'frog2']) #def create_legend_layout(self): #""" #Creates the layout for the legend control #Name Itailic Bold Font #==== ======= ===== ======== #Title check check pulldown #Label check check pulldown #""" #grid2 = QGridLayout() #grid2.addWidget(self.legend_label, 0, 0) #grid2.addWidget(self.name_label, 1, 0) #grid2.addWidget(self.italic_label, 1, 1) #grid2.addWidget(self.bold_label, 1, 2) #grid2.addWidget(self.font_label, 1, 3) #grid2.addWidget(self.legend_title_name, 2, 0) #grid2.addWidget(self.legend_title_italic_check, 2, 1) #grid2.addWidget(self.legend_title_bold_check, 2, 2) #grid2.addWidget(self.legend_title_font_edit, 2, 3) #grid2.addWidget(self.legend_label_italic_name, 3, 0) #grid2.addWidget(self.legend_label_italic_check, 3, 1) #grid2.addWidget(self.legend_label_bold_check, 3, 2) #grid2.addWidget(self.legend_label_font_edit, 3, 3) #return grid2 def create_layout(self): grid = QGridLayout() irow = 0 grid.addWidget(self.font_size_label, irow, 0) grid.addWidget(self.font_size_edit, irow, 1) grid.addWidget(self.font_size_button, irow, 2) irow += 1 grid.addWidget(self.gradient_scale_label, irow, 0) grid.addWidget(self.gradient_scale_checkbox, irow, 1) irow += 1 grid.addWidget(self.background_color2_label, irow, 0) grid.addWidget(self.background_color2_edit, irow, 1) irow += 1 grid.addWidget(self.background_color_label, irow, 0) grid.addWidget(self.background_color_edit, irow, 1) irow += 1 grid.addWidget(self.highlight_color_label, irow, 0) grid.addWidget(self.highlight_color_edit, irow, 1) irow += 1 grid.addWidget(self.highlight_opacity_label, irow, 0) grid.addWidget(self.highlight_opacity_edit, irow, 1) irow += 1 grid.addWidget(self.text_color_label, irow, 0) grid.addWidget(self.text_color_edit, irow, 1) irow += 1 grid.addWidget(self.text_size_label, irow, 0) grid.addWidget(self.text_size_edit, irow, 1) grid.addWidget(self.text_size_button, irow, 2) irow += 1 grid.addWidget(self.annotation_color_label, irow, 0) grid.addWidget(self.annotation_color_edit, irow, 1) irow += 1 grid.addWidget(self.annotation_size_label, irow, 0) grid.addWidget(self.annotation_size_edit, irow, 1) grid.addWidget(self.annotation_size_button, irow, 2) irow += 1 #grid.addWidget(self.clipping_min_label, irow, 0) #grid.addWidget(self.clipping_min_edit, irow, 1) #grid.addWidget(self.clipping_min_button, irow, 2) #irow += 1 #grid.addWidget(self.clipping_max_label, irow, 0) #grid.addWidget(self.clipping_max_edit, irow, 1) #grid.addWidget(self.clipping_max_button, irow, 2) #irow += 1 grid.addWidget(self.corner_coord_label, irow, 0) grid.addWidget(self.corner_coord_checkbox, irow, 1) irow += 1 grid.addWidget(self.coord_scale_label, irow, 0) grid.addWidget(self.coord_scale_edit, irow, 1) grid.addWidget(self.coord_scale_button, irow, 2) irow += 1 grid.addWidget(self.coord_text_scale_label, irow, 0) grid.addWidget(self.coord_text_scale_edit, irow, 1) grid.addWidget(self.coord_text_scale_button, irow, 2) irow += 1 #----------------------------------------------- grid.addWidget(self.magnify_label, irow, 0) grid.addWidget(self.magnify_edit, irow, 1) irow += 1 grid.addWidget(self.picker_size_label, irow, 0) grid.addWidget(self.picker_size_edit, irow, 1) irow += 1 #-------------------------------------------------- grid_nastran = QGridLayout() irow = 0 grid_nastran.addWidget(self.nastran_create_coords_checkbox, irow, 0) irow += 1 grid_nastran.addWidget(self.nastran_is_element_quality_checkbox, irow, 0) grid_nastran.addWidget(self.nastran_is_properties_checkbox, irow, 1) irow += 1 grid_nastran.addWidget(self.nastran_is_bar_axes_checkbox, irow, 0) irow += 1 grid_nastran.addWidget(self.nastran_is_3d_bars_checkbox, irow, 0) grid_nastran.addWidget(self.nastran_is_3d_bars_update_checkbox, irow, 1) irow += 1 #bold_font = make_font(self._default_font_size, is_bold=True) vbox_nastran = QVBoxLayout() self.nastran_label = QLabel('Nastran:') vbox_nastran.addWidget(self.nastran_label) vbox_nastran.addLayout(grid_nastran) #self.create_legend_widgets() #grid2 = self.create_legend_layout() 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.addLayout(vbox_nastran) #vbox.addStretch() #vbox.addLayout(grid2) vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): """creates the actions for the menu""" self.font_size_button.clicked.connect(self.on_default_font_size) self.font_size_edit.valueChanged.connect(self.on_font) self.annotation_size_edit.editingFinished.connect( self.on_annotation_size) self.annotation_size_edit.valueChanged.connect(self.on_annotation_size) self.annotation_color_edit.clicked.connect(self.on_annotation_color) self.annotation_size_button.clicked.connect( self.on_default_annotation_size) self.background_color_edit.clicked.connect(self.on_background_color) self.background_color2_edit.clicked.connect(self.on_background_color2) self.gradient_scale_checkbox.clicked.connect(self.on_gradient_scale) self.highlight_color_edit.clicked.connect(self.on_highlight_color) self.highlight_opacity_edit.valueChanged.connect( self.on_highlight_opacity) self.text_color_edit.clicked.connect(self.on_text_color) self.text_size_edit.valueChanged.connect(self.on_text_size) self.text_size_button.clicked.connect(self.on_default_text_size) self.picker_size_edit.valueChanged.connect(self.on_picker_size) self.picker_size_edit.editingFinished.connect(self.on_picker_size) self.coord_scale_edit.valueChanged.connect(self.on_coord_scale) self.coord_scale_edit.editingFinished.connect(self.on_coord_scale) self.coord_scale_button.clicked.connect(self.on_default_coord_scale) self.corner_coord_checkbox.clicked.connect(self.on_corner_coord) self.coord_text_scale_edit.valueChanged.connect( self.on_coord_text_scale) self.coord_text_scale_edit.editingFinished.connect( self.on_coord_text_scale) self.coord_text_scale_button.clicked.connect( self.on_default_coord_text_scale) self.magnify_edit.valueChanged.connect(self.on_magnify) self.magnify_edit.editingFinished.connect(self.on_magnify) self.clipping_min_button.clicked.connect(self.on_default_clipping_min) self.clipping_max_button.clicked.connect(self.on_default_clipping_max) #------------------------------------ # format-specific self.nastran_is_element_quality_checkbox.clicked.connect( self.on_nastran_is_element_quality) self.nastran_is_properties_checkbox.clicked.connect( self.on_nastran_is_properties) self.nastran_is_3d_bars_checkbox.clicked.connect( self.on_nastran_is_3d_bars) self.nastran_is_3d_bars_update_checkbox.clicked.connect( self.on_nastran_is_3d_bars) self.nastran_is_bar_axes_checkbox.clicked.connect( self.on_nastran_is_bar_axes) self.nastran_create_coords_checkbox.clicked.connect( self.on_nastran_create_coords) #------------------------------------ self.apply_button.clicked.connect(self.on_apply) self.ok_button.clicked.connect(self.on_ok) self.cancel_button.clicked.connect(self.on_cancel) # closeEvent def on_nastran_is_element_quality(self): """set the nastran element quality preferences""" is_checked = self.nastran_is_element_quality_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_is_element_quality = is_checked def on_nastran_is_properties(self): """set the nastran properties preferences""" is_checked = self.nastran_is_properties_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_is_properties = is_checked def on_nastran_is_3d_bars(self): """set the nastran properties preferences""" is_checked = self.nastran_is_3d_bars_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_is_3d_bars = is_checked def on_nastran_is_3d_bars_update(self): """set the nastran properties preferences""" is_checked = self.nastran_is_3d_bars_update_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_is_3d_bars_update = is_checked def on_nastran_is_bar_axes(self): """set the nastran properties preferences""" is_checked = self.nastran_is_bar_axes_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_is_bar_axes = is_checked def on_nastran_create_coords(self): """set the nastran properties preferences""" is_checked = self.nastran_create_coords_checkbox.isChecked() if self.win_parent is not None: self.win_parent.settings.nastran_create_coords = is_checked def on_font(self, value=None): """update the font for the current window""" if value is None: value = self.font_size_edit.value() font = QtGui.QFont() font.setPointSize(value) self.setFont(font) bold_font = make_font(value, is_bold=True) self.nastran_label.setFont(bold_font) def on_annotation_size(self, value=None): """update the annotation size""" if value is None: value = int(self.annotation_size_edit.text()) self._annotation_size = value #self.on_apply(force=True) #self.min_edit.setText(str(self._default_min)) #self.min_edit.setStyleSheet("QLineEdit{background: white;}") self.update_annotation_size_color() def update_annotation_size_color(self): if self.win_parent is not None: self.win_parent.settings.set_annotation_size_color( size=self._annotation_size) def on_gradient_scale(self): is_checked = self.gradient_scale_checkbox.isChecked() self.background_color2_label.setEnabled(is_checked) self.background_color2_edit.setEnabled(is_checked) if self.win_parent is not None: self.win_parent.settings.set_gradient_background( use_gradient_background=is_checked) def on_corner_coord(self): is_checked = self.corner_coord_checkbox.isChecked() if self.win_parent is not None: self.win_parent.set_corner_axis_visiblity(is_checked, render=True) def on_annotation_color(self): rgb_color_ints = self.annotation_color_int title = "Choose an annotation color" passed, rgb_color_ints, rgb_color_floats = self.on_color( self.annotation_color_edit, rgb_color_ints, title) if passed: self.annotation_color_int = rgb_color_ints self.annotation_color_float = rgb_color_floats self.update_annotation_size_color() def on_background_color(self): """ Choose a background color""" title = "Choose a primary background color" rgb_color_ints = self.background_color_int color_edit = self.background_color_edit func_name = 'set_background_color' passed, rgb_color_ints, rgb_color_floats = self._background_color( title, color_edit, rgb_color_ints, func_name) if passed: self.background_color_int = rgb_color_ints self.background_color_float = rgb_color_floats def on_background_color2(self): """ Choose a background color""" title = "Choose a secondary background color" rgb_color_ints = self.background_color2_int color_edit = self.background_color2_edit func_name = 'set_background_color2' passed, rgb_color_ints, rgb_color_floats = self._background_color( title, color_edit, rgb_color_ints, func_name) if passed: self.background_color2_int = rgb_color_ints self.background_color2_float = rgb_color_floats def on_highlight_color(self): """ Choose a highlight color""" title = "Choose a highlight color" rgb_color_ints = self.highlight_color_int color_edit = self.highlight_color_edit func_name = 'set_highlight_color' passed, rgb_color_ints, rgb_color_floats = self._background_color( title, color_edit, rgb_color_ints, func_name) if passed: self.highlight_color_int = rgb_color_ints self.highlight_color_float = rgb_color_floats def on_highlight_opacity(self, value=None): if value is None: value = self.highlight_opacity_edit.value() self._highlight_opacity = value if self.win_parent is not None: self.win_parent.settings.set_highlight_opacity(value) def _background_color(self, title, color_edit, rgb_color_ints, func_name): """helper method for ``on_background_color`` and ``on_background_color2``""" passed, rgb_color_ints, rgb_color_floats = self.on_color( color_edit, rgb_color_ints, title) if passed: if self.win_parent is not None: settings = self.win_parent.settings func_background_color = getattr(settings, func_name) func_background_color(rgb_color_floats) return passed, rgb_color_ints, rgb_color_floats def on_text_color(self): """ Choose a text color """ rgb_color_ints = self.text_color_int title = "Choose a text color" passed, rgb_color_ints, rgb_color_floats = self.on_color( self.text_color_edit, rgb_color_ints, title) if passed: self.text_color_int = rgb_color_ints self.text_color_float = rgb_color_floats if self.win_parent is not None: self.win_parent.settings.set_text_color(rgb_color_floats) def on_default_text_size(self): self.text_size_edit.setValue(self._default_text_size) self.on_text_size(self._default_text_size) def on_text_size(self, value=None): if value is None: value = self.text_size_edit.value() self._text_size = value if self.win_parent is not None: self.win_parent.settings.set_text_size(value) def on_color(self, color_edit, rgb_color_ints, title): """pops a color dialog""" col = QColorDialog.getColor(QtGui.QColor(*rgb_color_ints), self, title) if not col.isValid(): return False, rgb_color_ints, None color_float = col.getRgbF()[:3] # floats color_int = [int(colori * 255) for colori in color_float] assert isinstance(color_float[0], float), color_float assert isinstance(color_int[0], int), color_int color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color_int) + #"border:1px solid rgb(255, 170, 255); " "}") return True, color_int, color_float def on_picker_size(self): self._picker_size = float(self.picker_size_edit.text()) if self.win_parent is not None: self.win_parent.element_picker_size = self._picker_size / 100. #self.on_apply(force=True) def on_magnify(self, value=None): if value is None: value = self.magnify_edit.value() self._magnify = value if self.win_parent is not None: self.win_parent.settings.set_magnify(value) #--------------------------------------------------------------------------- def on_coord_scale(self, value=None): if value is None: value = self.coord_scale_edit.value() self._coord_scale = value if self.win_parent is not None: self.win_parent.settings.set_coord_scale(value / 100.) def on_default_coord_scale(self): self.coord_scale_edit.setValue(self._default_coord_scale) self.on_coord_scale(self._default_coord_scale) def on_coord_text_scale(self, value=None): if value is None: value = self.coord_text_scale_edit.value() self._coord_text_scale = value if self.win_parent is not None: self.win_parent.settings.set_coord_text_scale(value / 100.) def on_default_coord_text_scale(self): self.coord_text_scale_edit.setValue(self._default_coord_text_scale) self.on_coord_text_scale(self._default_coord_text_scale) #--------------------------------------------------------------------------- def on_default_font_size(self): self.font_size_edit.setValue(self._default_font_size) self.on_font(self._default_font_size) def on_default_annotation_size(self): self.annotation_size_edit.setValue(self._default_annotation_size) self.on_annotation_size(self._default_annotation_size) def on_default_clipping_min(self): self.clipping_min_edit.setText(str(self._default_clipping_min)) self.clipping_min_edit.setStyleSheet("QLineEdit{background: white;}") def on_default_clipping_max(self): self.clipping_max_edit.setText(str(self._default_clipping_max)) self.clipping_max_edit.setStyleSheet("QLineEdit{background: white;}") def on_validate(self): font_size_value, flag0 = check_float(self.font_size_edit) annotation_size_value, flag1 = check_float(self.annotation_size_edit) assert isinstance(self.annotation_color_float[0], float), self.annotation_color_float assert isinstance(self.annotation_color_int[0], int), self.annotation_color_int picker_size_value, flag2 = check_float(self.picker_size_edit) clipping_min_value, flag3 = check_label_float(self.clipping_min_edit) clipping_max_value, flag4 = check_label_float(self.clipping_max_edit) if all([flag0, flag1, flag2, flag3, flag4]): self._annotation_size = annotation_size_value self._picker_size = picker_size_value self.out_data['font_size'] = int(font_size_value) self.out_data['min_clip'] = min(clipping_min_value, clipping_max_value) self.out_data['max_clip'] = max(clipping_min_value, clipping_max_value) self.out_data['clicked_ok'] = True return True return False def on_apply(self, force=False): passed = self.on_validate() if (passed or force) and self.win_parent is not None: self.win_parent.settings.on_set_font_size( self.out_data['font_size']) #self.win_parent.settings.set_annotation_size_color(self._annotation_size) #self.win_parent.element_picker_size = self._picker_size / 100. if passed and self.win_parent is not None: self.win_parent.clipping_obj.apply_clipping(self.out_data) return passed def on_ok(self): passed = self.on_apply() if passed: self.close() #self.destroy() def on_cancel(self): self.out_data['close'] = True self.close()
class ColorbarWidget(QWidget): colorbarChanged = Signal() # The parent should simply redraw their canvas def __init__(self, parent=None): super(ColorbarWidget, self).__init__(parent) self.setWindowTitle("Colorbar") self.setMaximumWidth(200) self.dval = QDoubleValidator() self.cmin = QLineEdit() self.cmin_value = 0 self.cmin.setMaximumWidth(100) self.cmin.editingFinished.connect(self.clim_changed) self.cmin_layout = QHBoxLayout() self.cmin_layout.addStretch() self.cmin_layout.addWidget(self.cmin) self.cmin_layout.addStretch() self.cmax = QLineEdit() self.cmax_value = 1 self.cmax.setMaximumWidth(100) self.cmax.editingFinished.connect(self.clim_changed) self.cmin.setValidator(self.dval) self.cmax.setValidator(self.dval) self.cmax_layout = QHBoxLayout() self.cmax_layout.addStretch() self.cmax_layout.addWidget(self.cmax) self.cmax_layout.addStretch() self.norm_layout = QHBoxLayout() self.norm = QComboBox() self.norm.addItems(NORM_OPTS) self.norm.currentIndexChanged.connect(self.norm_changed) self.powerscale = QLineEdit() self.powerscale_value = 2 self.powerscale.setText("2") self.powerscale.setValidator(QDoubleValidator(0.001,100,3)) self.powerscale.setMaximumWidth(50) self.powerscale.editingFinished.connect(self.norm_changed) self.powerscale.hide() self.powerscale_label = QLabel("n=") self.powerscale_label.hide() self.norm_layout.addStretch() self.norm_layout.addWidget(self.norm) self.norm_layout.addStretch() self.norm_layout.addWidget(self.powerscale_label) self.norm_layout.addWidget(self.powerscale) self.autoscale = QCheckBox("Autoscaling") self.autoscale.setChecked(True) self.autoscale.stateChanged.connect(self.update_clim) self.canvas = FigureCanvas(Figure()) if parent: # Set facecolor to match parent self.canvas.figure.set_facecolor(parent.palette().window().color().getRgbF()) self.ax = self.canvas.figure.add_axes([0.4,0.05,0.2,0.9]) # layout self.layout = QVBoxLayout(self) self.layout.addLayout(self.cmax_layout) self.layout.addWidget(self.canvas, stretch=1) self.layout.addLayout(self.cmin_layout) self.layout.addLayout(self.norm_layout) self.layout.addWidget(self.autoscale) def set_mappable(self, mappable): """ When a new plot is created this method should be called with the new mappable """ self.ax.clear() self.colorbar = Colorbar(ax=self.ax, mappable=mappable) self.cmin_value, self.cmax_value = self.colorbar.get_clim() self.update_clim_text() self.redraw() def norm_changed(self): """ Called when a different normalization is selected """ idx = self.norm.currentIndex() if NORM_OPTS[idx] == 'Power': self.powerscale.show() self.powerscale_label.show() else: self.powerscale.hide() self.powerscale_label.hide() self.colorbar.mappable.set_norm(self.get_norm()) self.colorbarChanged.emit() def get_norm(self): """ This will create a matplotlib.colors.Normalize from selected idx, limits and powerscale """ idx = self.norm.currentIndex() if self.autoscale.isChecked(): cmin = cmax = None else: cmin = self.cmin_value cmax = self.cmax_value if NORM_OPTS[idx] == 'Power': if self.powerscale.hasAcceptableInput(): self.powerscale_value = float(self.powerscale.text()) return PowerNorm(gamma=self.powerscale_value, vmin=cmin, vmax=cmax) elif NORM_OPTS[idx] == "SymmetricLog10": return SymLogNorm(1e-8 if cmin is None else max(1e-8, abs(cmin)*1e-3), vmin=cmin, vmax=cmax) else: return Normalize(vmin=cmin, vmax=cmax) def clim_changed(self): """ Called when either the min or max is changed. Will unset the autoscale. """ self.autoscale.blockSignals(True) self.autoscale.setChecked(False) self.autoscale.blockSignals(False) self.update_clim() def update_clim(self): """ This will update the clim of the plot based on min, max, and autoscale """ if self.autoscale.isChecked(): data = self.colorbar.mappable.get_array() try: try: self.cmin_value = data[~data.mask].min() self.cmax_value = data[~data.mask].max() except AttributeError: self.cmin_value = np.nanmin(data) self.cmax_value = np.nanmax(data) except (ValueError, RuntimeWarning): # all values mask pass self.update_clim_text() else: if self.cmin.hasAcceptableInput(): self.cmin_value = float(self.cmin.text()) if self.cmax.hasAcceptableInput(): self.cmax_value = float(self.cmax.text()) self.colorbar.set_clim(self.cmin_value, self.cmax_value) self.redraw() def update_clim_text(self): """ Update displayed limit values based on stored ones """ self.cmin.setText("{:.4}".format(self.cmin_value)) self.cmax.setText("{:.4}".format(self.cmax_value)) def redraw(self): """ Redraws the colobar and emits signal to cause the parent to redraw """ self.colorbar.update_ticks() self.colorbar.draw_all() self.canvas.draw_idle() self.colorbarChanged.emit()
class ColorComboBox(QComboBox): """ Combobox showing colormap instead of text :param id_num: id which be emit in signals. Designed to inform which channel information is changed :param colors: list of colors which should be able to chose. All needs to be keys in `color_dict` :param color_dict: dict from name to colormap definition :param colormap: initial colormap :param base_height: initial height of widgethow information that :param lock: show lock padlock to inform that fixed range is used :param blur: show info about blur selected """ triangle_width = 20 clicked = Signal(int) """Information about mouse click event on widget""" channel_visible_changed = Signal(int, bool) """Signal with information about change of channel visibility (ch_num, visible)""" channel_colormap_changed = Signal(int, str) """Signal with information about colormap change. (ch_num, name_of_colorma)""" def __init__( self, id_num: int, colors: typing.List[str], color_dict: ColorMapDict, colormap: str = "", base_height=50, lock=False, blur=NoiseFilterType.No, gamma=1, ): super().__init__() self.id = id_num self.check_box = QCheckBox() # ColorCheckBox(parent=self) self.check_box.setChecked(True) self.lock = LockedInfoWidget(base_height - 10) self.lock.setVisible(lock) self.blur = BlurInfoWidget(base_height - 10) self.blur.setVisible(blur != NoiseFilterType.No) self.gamma = GammaInfoWidget() self.gamma.setVisible(gamma != 1) self.color_dict = color_dict self.colors = colors self.addItems(self.colors) self.color = colormap or self.itemText(0) self.setCurrentText(self.color) self.currentTextChanged.connect(self._update_image) self.base_height = base_height self.show_arrow = False self.show_frame = False view = QListView() view.setMinimumWidth(300) view.setItemDelegate(ColorStyledDelegate(self.base_height, color_dict)) self.setView(view) self.image = None # only for moment, to reduce code repetition layout = QHBoxLayout() layout.setContentsMargins(7, 0, 0, 0) layout.addWidget(self.check_box) layout.addWidget(self.lock) layout.addWidget(self.blur) layout.addWidget(self.gamma) layout.addStretch(1) self.setLayout(layout) self.check_box.stateChanged.connect( partial(self.channel_visible_changed.emit, self.id)) self.currentTextChanged.connect( partial(self.channel_colormap_changed.emit, self.id)) self._update_image() def change_colors(self, colors: typing.List[str]): """change list of colormaps to choose""" self.colors = colors current_color = self.currentText() prev = self.blockSignals(True) try: index = colors.index(current_color) except ValueError: index = -1 self.clear() self.addItems(colors) if index != -1: self.setCurrentIndex(index) self.blockSignals(prev) else: self.blockSignals(prev) self._update_image() self.repaint() def _update_image(self): self.color = self.currentText() if self.color not in image_dict: image_dict[self.color] = create_colormap_image( self.color, self.color_dict) self.image = image_dict[self.color] self.show_arrow = False def enterEvent(self, event: QEvent): self.show_arrow = True self.repaint() def mouseMoveEvent(self, _): self.show_arrow = True def leaveEvent(self, event: QEvent): self.show_arrow = False self.repaint() def showEvent(self, _event: QShowEvent): self.show_arrow = False def paintEvent(self, event: QPaintEvent): painter = QPainter(self) painter.drawImage(self.rect(), self.image) if self.show_frame: painter.save() pen = QPen() pen.setWidth(2) pen.setColor(QColor("black")) painter.setPen(pen) rect = QRect(1, 1, self.width() - 2, self.height() - 2) painter.drawRect(rect) pen.setColor(QColor("white")) painter.setPen(pen) rect = QRect(3, 3, self.width() - 6, self.height() - 6) painter.drawRect(rect) painter.restore() if self.show_arrow: painter.save() triangle = QPolygonF() dist = 4 point1 = QPoint(self.width() - self.triangle_width, 0) size = QSize(20, self.height() // 2) rect = QRect(point1, size) painter.fillRect(rect, QColor("white")) triangle.append(point1 + QPoint(dist, dist)) triangle.append(point1 + QPoint(size.width() - dist, dist)) triangle.append(point1 + QPoint(size.width() // 2, size.height() - dist)) painter.setBrush(Qt.black) painter.drawPolygon(triangle, Qt.WindingFill) painter.restore() def mousePressEvent(self, event: QMouseEvent): if event.x() > self.width() - self.triangle_width: super().mousePressEvent(event) self.clicked.emit(self.id) def minimumSizeHint(self): size: QSize = super().minimumSizeHint() return QSize(size.width(), max(size.height(), self.base_height)) def is_checked(self): """check if checkbox on widget is checked""" return self.check_box.isChecked() @property def is_lock(self): """check if lock property is set""" return self.lock.isVisible @property def is_blur(self): """check if blur property is set""" return self.blur.isVisible @property def set_lock(self) -> typing.Callable[[bool], None]: """set lock property""" return self.lock.setVisible @property def set_gamma(self) -> typing.Callable[[bool], None]: """set lock property""" return self.gamma.setVisible @property def set_blur(self) -> typing.Callable[[bool], None]: """set blur property""" return self.blur.setVisible @property def colormap_changed(self): """alias for signal, return color name""" return self.currentTextChanged def set_selection(self, val: bool): """set element selected (add frame)""" self.show_frame = val self.repaint() def set_color(self, val: str): """set current colormap""" self.setCurrentText(val)
class LSPServerEditor(QDialog): DEFAULT_HOST = '127.0.0.1' DEFAULT_PORT = 2084 DEFAULT_CMD = '' DEFAULT_ARGS = '' DEFAULT_CONFIGURATION = '{}' DEFAULT_EXTERNAL = False HOST_REGEX = re.compile(r'^\w+([.]\w+)*$') NON_EMPTY_REGEX = re.compile(r'^\S+$') JSON_VALID = _('JSON valid') JSON_INVALID = _('JSON invalid') def __init__(self, parent, language=None, cmd='', host='127.0.0.1', port=2084, args='', external=False, configurations={}, **kwargs): super(LSPServerEditor, self).__init__(parent) self.parent = parent self.external = external bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_ok = bbox.button(QDialogButtonBox.Ok) self.button_cancel = bbox.button(QDialogButtonBox.Cancel) self.button_ok.setEnabled(False) description = _('To create a new configuration, ' 'you need to select a programming ' 'language, along with a executable ' 'name for the server to execute ' '(If the instance is local), ' 'and the host and port. Finally, ' 'you need to provide the ' 'arguments that the server accepts. ' 'The placeholders <tt>%(host)s</tt> and ' '<tt>%(port)s</tt> refer to the host ' 'and the port, respectively.') server_settings_description = QLabel(description) server_settings_description.setWordWrap(True) lang_label = QLabel(_('Language:')) self.lang_cb = QComboBox(self) self.lang_cb.setToolTip(_('Programming language provided ' 'by the LSP server')) self.lang_cb.addItem(_('Select a language')) self.lang_cb.addItems(LSP_LANGUAGES) if language is not None: idx = LSP_LANGUAGES.index(language) self.lang_cb.setCurrentIndex(idx + 1) self.button_ok.setEnabled(True) host_label = QLabel(_('Host:')) self.host_input = QLineEdit(self) self.host_input.setToolTip(_('Name of the host that will provide ' 'access to the server')) self.host_input.setText(host) self.host_input.textChanged.connect(lambda x: self.validate()) port_label = QLabel(_('Port:')) self.port_spinner = QSpinBox(self) self.port_spinner.setToolTip(_('TCP port number of the server')) self.port_spinner.setMinimum(1) self.port_spinner.setMaximum(60000) self.port_spinner.setValue(port) cmd_label = QLabel(_('Command to execute:')) self.cmd_input = QLineEdit(self) self.cmd_input.setToolTip(_('Command used to start the ' 'LSP server locally')) self.cmd_input.setText(cmd) if not external: self.cmd_input.textChanged.connect(lambda x: self.validate()) args_label = QLabel(_('Server arguments:')) self.args_input = QLineEdit(self) self.args_input.setToolTip(_('Additional arguments required to ' 'start the server')) self.args_input.setText(args) conf_label = QLabel(_('LSP Server Configurations:')) self.conf_input = CodeEditor(None) self.conf_input.textChanged.connect(self.validate) color_scheme = CONF.get('color_schemes', 'selected') self.conf_input.setup_editor( language='JSON', color_scheme=color_scheme, wrap=False, edge_line=True, highlight_current_line=True, highlight_current_cell=True, occurrence_highlighting=True, auto_unindent=True, font=get_font(), filename='config.json') self.conf_input.setToolTip(_('Additional LSP server configurations ' 'set at runtime. JSON required')) conf_text = '{}' try: conf_text = json.dumps(configurations, indent=4, sort_keys=True) except Exception: pass self.conf_input.set_text(conf_text) self.json_label = QLabel(self.JSON_VALID, self) self.external_cb = QCheckBox(_('External server'), self) self.external_cb.setToolTip(_('Check if the server runs ' 'on a remote location')) self.external_cb.setChecked(external) self.external_cb.stateChanged.connect(self.set_local_options) hlayout = QHBoxLayout() general_vlayout = QVBoxLayout() general_vlayout.addWidget(server_settings_description) vlayout = QVBoxLayout() lang_layout = QVBoxLayout() lang_layout.addWidget(lang_label) lang_layout.addWidget(self.lang_cb) # layout2 = QHBoxLayout() # layout2.addLayout(lang_layout) lang_layout.addWidget(self.external_cb) vlayout.addLayout(lang_layout) host_layout = QVBoxLayout() host_layout.addWidget(host_label) host_layout.addWidget(self.host_input) port_layout = QVBoxLayout() port_layout.addWidget(port_label) port_layout.addWidget(self.port_spinner) conn_info_layout = QHBoxLayout() conn_info_layout.addLayout(host_layout) conn_info_layout.addLayout(port_layout) vlayout.addLayout(conn_info_layout) cmd_layout = QVBoxLayout() cmd_layout.addWidget(cmd_label) cmd_layout.addWidget(self.cmd_input) vlayout.addLayout(cmd_layout) args_layout = QVBoxLayout() args_layout.addWidget(args_label) args_layout.addWidget(self.args_input) vlayout.addLayout(args_layout) conf_layout = QVBoxLayout() conf_layout.addWidget(conf_label) conf_layout.addWidget(self.conf_input) conf_layout.addWidget(self.json_label) hlayout.addLayout(vlayout) hlayout.addLayout(conf_layout) general_vlayout.addLayout(hlayout) general_vlayout.addWidget(bbox) self.setLayout(general_vlayout) bbox.accepted.connect(self.accept) bbox.rejected.connect(self.reject) self.lang_cb.currentIndexChanged.connect( self.lang_selection_changed) self.form_status(False) if language is not None: self.form_status(True) self.validate() @Slot() def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}") self.host_input.setToolTip('Hostname must be valid') return else: self.host_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.host_input.setToolTip('Hostname is valid') self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Command must be non empty') return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Program was not found ' 'on your system') return else: self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.cmd_input.setToolTip('Program was found on your system') self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except: pass except (ValueError, json.decoder.JSONDecodeError): try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except: pass def form_status(self, status): self.host_input.setEnabled(status) self.port_spinner.setEnabled(status) self.external_cb.setEnabled(status) self.cmd_input.setEnabled(status) self.args_input.setEnabled(status) self.conf_input.setEnabled(status) self.json_label.setVisible(status) @Slot() def lang_selection_changed(self): idx = self.lang_cb.currentIndex() if idx == 0: self.set_defaults() self.form_status(False) self.button_ok.setEnabled(False) else: server = self.parent.get_server_by_lang(LSP_LANGUAGES[idx - 1]) self.form_status(True) if server is not None: self.host_input.setText(server.host) self.port_spinner.setValue(server.port) self.external_cb.setChecked(server.external) self.cmd_input.setText(server.cmd) self.args_input.setText(server.args) self.conf_input.set_text(json.dumps(server.configurations)) self.json_label.setText(self.JSON_VALID) self.button_ok.setEnabled(True) else: self.set_defaults() def set_defaults(self): self.cmd_input.setStyleSheet('') self.host_input.setStyleSheet('') self.host_input.setText(self.DEFAULT_HOST) self.port_spinner.setValue(self.DEFAULT_PORT) self.external_cb.setChecked(self.DEFAULT_EXTERNAL) self.cmd_input.setText(self.DEFAULT_CMD) self.args_input.setText(self.DEFAULT_ARGS) self.conf_input.set_text(self.DEFAULT_CONFIGURATION) self.json_label.setText(self.JSON_VALID) @Slot(bool) @Slot(int) def set_local_options(self, enabled): self.external = enabled self.cmd_input.setEnabled(True) self.args_input.setEnabled(True) if enabled: self.cmd_input.setEnabled(False) self.cmd_input.setStyleSheet('') self.args_input.setEnabled(False) try: self.validate() except: pass def get_options(self): language_idx = self.lang_cb.currentIndex() language = LSP_LANGUAGES[language_idx - 1] host = self.host_input.text() port = int(self.port_spinner.value()) external = self.external_cb.isChecked() args = self.args_input.text() cmd = self.cmd_input.text() configurations = json.loads(self.conf_input.toPlainText()) server = LSPServer(language=language.lower(), cmd=cmd, args=args, host=host, port=port, external=external, configurations=configurations) return server
class QtReaderDialog(QDialog): """Dialog for user to select a reader plugin for a given file extension or folder""" def __init__( self, pth: str = '', parent: QWidget = None, extension: str = '', readers: Dict[str, str] = {}, error_message: str = '', ): super().__init__(parent) self.setObjectName('Choose reader') self.setWindowTitle('Choose reader') self._current_file = pth self._extension = extension self._reader_buttons = [] self.setup_ui(error_message, readers) def setup_ui(self, error_message, readers): """Build UI using given error_messsage and readers dict""" # add instruction label layout = QVBoxLayout() label = QLabel( f"{error_message}Choose reader for {self._current_file}:" ) layout.addWidget(label) # add radio button for each reader plugin self.reader_btn_group = QButtonGroup(self) self.add_reader_buttons(layout, readers) if self.reader_btn_group.buttons(): self.reader_btn_group.buttons()[0].toggle() # OK & cancel buttons for the dialog btns = QDialogButtonBox.Ok | QDialogButtonBox.Cancel self.btn_box = QDialogButtonBox(btns) self.btn_box.accepted.connect(self.accept) self.btn_box.rejected.connect(self.reject) # checkbox to remember the choice (doesn't pop up for folders) extension = os.path.splitext(self._current_file)[1] if extension: self.persist_checkbox = QCheckBox( f'Remember this choice for files with a {extension} extension' ) self.persist_checkbox.toggle() layout.addWidget(self.persist_checkbox) layout.addWidget(self.btn_box) self.setLayout(layout) def add_reader_buttons(self, layout, readers): """Add radio button to layout for each reader in readers""" for display_name in sorted(readers): button = QRadioButton(f"{display_name}") self.reader_btn_group.addButton(button) layout.addWidget(button) def _get_plugin_choice(self): """Get user's plugin choice based on the checked button""" checked_btn = self.reader_btn_group.checkedButton() if checked_btn: return checked_btn.text() def _get_persist_choice(self): """Get persistence checkbox choice""" return ( hasattr(self, 'persist_checkbox') and self.persist_checkbox.isChecked() ) def get_user_choices(self) -> Optional[Tuple[str, bool]]: """Execute dialog and get user choices""" dialog_result = self.exec_() # user pressed cancel if not dialog_result: return None # grab the selected radio button text display_name = self._get_plugin_choice() # grab the persistence checkbox choice persist_choice = self._get_persist_choice() return display_name, persist_choice
class RunConfigOptions(QWidget): """Run configuration options""" 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.console_ns_cb = QCheckBox(CONSOLE_NAMESPACE) common_layout.addWidget(self.console_ns_cb, 1, 0) self.post_mortem_cb = QCheckBox(POST_MORTEM) common_layout.addWidget(self.post_mortem_cb, 2, 0) self.clo_cb = QCheckBox(_("Command line options:")) common_layout.addWidget(self.clo_cb, 3, 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, 3, 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 select_directory(self): """Select directory""" basedir = to_text_string(self.wd_edit.text()) if not osp.isdir(basedir): basedir = getcwd_or_home() directory = getexistingdirectory(self, _("Select directory"), basedir) if directory: self.wd_edit.setText(directory) self.dir = directory def set(self, options): self.runconf.set(options) self.clo_cb.setChecked(self.runconf.args_enabled) self.clo_edit.setText(self.runconf.args) if self.runconf.current: self.current_radio.setChecked(True) elif self.runconf.systerm: self.systerm_radio.setChecked(True) else: self.dedicated_radio.setChecked(True) self.interact_cb.setChecked(self.runconf.interact) self.post_mortem_cb.setChecked(self.runconf.post_mortem) self.pclo_cb.setChecked(self.runconf.python_args_enabled) self.pclo_edit.setText(self.runconf.python_args) self.clear_var_cb.setChecked(self.runconf.clear_namespace) self.console_ns_cb.setChecked(self.runconf.console_namespace) self.file_dir_radio.setChecked(self.runconf.file_dir) self.cwd_radio.setChecked(self.runconf.cw_dir) self.fixed_dir_radio.setChecked(self.runconf.fixed_dir) self.dir = self.runconf.dir self.wd_edit.setText(self.dir) def get(self): self.runconf.args_enabled = self.clo_cb.isChecked() self.runconf.args = to_text_string(self.clo_edit.text()) self.runconf.current = self.current_radio.isChecked() self.runconf.systerm = self.systerm_radio.isChecked() self.runconf.interact = self.interact_cb.isChecked() self.runconf.post_mortem = self.post_mortem_cb.isChecked() self.runconf.python_args_enabled = self.pclo_cb.isChecked() self.runconf.python_args = to_text_string(self.pclo_edit.text()) self.runconf.clear_namespace = self.clear_var_cb.isChecked() self.runconf.console_namespace = self.console_ns_cb.isChecked() self.runconf.file_dir = self.file_dir_radio.isChecked() self.runconf.cw_dir = self.cwd_radio.isChecked() self.runconf.fixed_dir = self.fixed_dir_radio.isChecked() self.runconf.dir = self.wd_edit.text() return self.runconf.get() def is_valid(self): wdir = to_text_string(self.wd_edit.text()) if not self.fixed_dir_radio.isChecked() or osp.isdir(wdir): return True else: QMessageBox.critical(self, _("Run configuration"), _("The following working directory is " "not valid:<br><b>%s</b>") % wdir) return False def set_firstrun_o(self): CONF.set('run', ALWAYS_OPEN_FIRST_RUN_OPTION, self.firstrun_cb.isChecked())
class ShearMomentTorqueWindow(PyDialog): """ +-------------------------+ | ShearMomentTorqueWindow | +-------------------------+ | Origin cid x y z | | P2 cid x y z | | z-axis cid x y z | | tol cid x y z | | | | Apply OK Cancel | +-------------------------+ """ def __init__(self, data, win_parent=None): """ Saves the data members from data and performs type checks """ PyDialog.__init__(self, data, win_parent) self._updated_preference = False self._default_font_size = data['font_size'] #self.dim_max = data['dim_max'] self.model_name = data['model_name'] self.cids = data['cids'] self.gpforce = data['gpforce'] #self._origin = data['origin'] #self._p1 = data['origin'] #self._p2 = data['origin'] #self.out_data = data self.plane_color_float, self.plane_color_int = check_color( data['plane_color']) self.plane_opacity = data['plane_opacity'] self.methods = ['Z-Axis Projection', 'CORD2R'] self.zaxis_methods = ['Global Z', 'Camera Normal', 'Manual'] self._zaxis_method = 0 # Global Z self.setWindowTitle('Shear, Moment, Torque') self.create_widgets() self.create_layout() self.set_connections() self.on_font(self._default_font_size) #self.on_gradient_scale() #self.show() def on_font(self, value=None): """update the font for the current window""" if value is None: value = self.font_size_edit.value() font = QtGui.QFont() font.setPointSize(value) self.setFont(font) def set_font_size(self, font_size): """ Updates the font size of all objects in the PyDialog Parameters ---------- font_size : int the font size """ if self.font_size == font_size: return self.font_size = font_size font = make_font(font_size, is_bold=False) self.setFont(font) self.set_bold_font(font_size) def set_bold_font(self, font_size): """ Updates the font size of all bolded objects in the dialog Parameters ---------- font_size : int the font size """ bold_font = make_font(font_size, is_bold=True) self.additional_params_label.setFont(bold_font) self.case_info_label.setFont(bold_font) self.plane_label.setFont(bold_font) self.location_label.setFont(bold_font) self.cid_label.setFont(bold_font) self.x_label.setFont(bold_font) self.y_label.setFont(bold_font) self.z_label.setFont(bold_font) def create_widgets(self): """creates the display window""" # CORD2R #self.origin_label = QLabel("Origin:") #self.zaxis_label = QLabel("Z Axis:") #self.xz_plane_label = QLabel("XZ Plane:") # Z-Axis Projection self.p1_label = QLabel("Origin:") self.p3_label = QLabel("End:") self.p2_label = QLabel("XZ Plane:") self.p1_label.setToolTip( 'Defines the starting point for the shear, moment, torque plot') self.p3_label.setToolTip( 'Defines the end point for the shear, moment, torque plot') self.p2_label.setToolTip('Defines the XZ plane for the shears/moments') self.zaxis_label = QLabel("Z Axis:") self.method_pulldown = QComboBox() for method in self.methods: self.method_pulldown.addItem(method) self.zaxis_method_pulldown = QComboBox() for method in self.zaxis_methods: self.zaxis_method_pulldown.addItem(method) self.cid_label = QLabel("Coordinate System:") self.p1_cid_pulldown = QComboBox() self.p2_cid_pulldown = QComboBox() self.p3_cid_pulldown = QComboBox() self.zaxis_cid_pulldown = QComboBox() cid_global_str = '0/Global' for cid in sorted(self.cids): if cid == 0: cid_str = cid_global_str else: cid_str = str(cid) #print('cid_str = %r' % cid_str) self.p1_cid_pulldown.addItem(cid_str) self.p2_cid_pulldown.addItem(cid_str) self.p3_cid_pulldown.addItem(cid_str) self.zaxis_cid_pulldown.addItem(cid_str) self.p1_cid_pulldown.setCurrentIndex(0) self.p2_cid_pulldown.setCurrentIndex(0) self.p3_cid_pulldown.setCurrentIndex(0) self.zaxis_cid_pulldown.setCurrentIndex(0) if len(self.cids) == 1: self.p1_cid_pulldown.setEnabled(False) self.p2_cid_pulldown.setEnabled(False) self.p3_cid_pulldown.setEnabled(False) self.zaxis_cid_pulldown.setEnabled(False) #self.p1_cid_pulldown.setItemText(0, cid_str) #self.p2_cid_pulldown.setItemText(0, cid_str) #self.zaxis_cid_pulldown.setItemText(0, cid_str) self.p1_cid_pulldown.setToolTip( 'Defines the coordinate system for Point P1') self.p2_cid_pulldown.setToolTip( 'Defines the coordinate system for Point P2') self.p3_cid_pulldown.setToolTip( 'Defines the coordinate system for Point P3') self.zaxis_cid_pulldown.setToolTip( 'Defines the coordinate system for the Z Axis') self.p1_x_edit = QFloatEdit('') self.p1_y_edit = QFloatEdit('') self.p1_z_edit = QFloatEdit('') self.p2_x_edit = QFloatEdit('') self.p2_y_edit = QFloatEdit('') self.p2_z_edit = QFloatEdit('') self.p3_x_edit = QFloatEdit('') self.p3_y_edit = QFloatEdit('') self.p3_z_edit = QFloatEdit('') self.zaxis_x_edit = QFloatEdit('') self.zaxis_y_edit = QFloatEdit('') self.zaxis_z_edit = QFloatEdit('') self.additional_params_label = QLabel('Plane Parameters:') self.case_info_label = QLabel('Case Info:') self.p2_label = QLabel("XZ Plane:") # Plane Color self.plane_color_label = QLabel("Plane Color:") self.plane_color_edit = QPushButtonColor(self.plane_color_int) self.plane_opacity_label = QLabel("Plane Opacity:") self.plane_opacity_edit = QDoubleSpinBox() self.plane_opacity_edit.setRange(0.1, 1.0) self.plane_opacity_edit.setDecimals(1) self.plane_opacity_edit.setSingleStep(0.1) self.plane_opacity_edit.setValue(self.plane_opacity) self.flip_coord_label = QLabel("Flip Coordinate System:") self.flip_coord_checkbox = QCheckBox() #----------------------------------------------------------------------- self.time_label = QLabel('Time:') if self.gpforce is None: times = ['0.', '0.5', '1.', '1.5', '2.'] time = '0.' else: times = [func_str(time) for time in self.gpforce._times] time = times[0] self.times_pulldown = make_combo_box(times, time) self.time_label.setEnabled(False) self.times_pulldown.setEnabled(False) #self.node_label = QLabel('Nodes:') #self.node_edit = QNodeEdit(self.win_parent, self.model_name, parent=self.gui, #pick_style='area', tab_to_next=False) #self.element_label = QLabel('Elements:') #self.element_edit = QElementEdit(self.win_parent, self.model_name, parent=self.gui, #pick_style='area', tab_to_next=False) #self.node_element_label = QLabel('Nodes/Elements:') #self.node_element_edit = QLineEdit() #self.node_element_edit.setReadOnly(True) self.nplanes_label = QLabel('Num Planes:') self.nplanes_spinner = QSpinBox() self.nplanes_spinner.setMinimum(2) self.nplanes_spinner.setMaximum(500) self.nplanes_spinner.setValue(20) #----------------------------------------------------------------------- self.method_label = QLabel('Method:') self.plane_label = QLabel('Plane:') self.location_label = QLabel('Location:') self.zaxis_method_label = QLabel('Z-Axis Method:') self.cid_label = QLabel('Coordinate System:') self.x_label = QLabel('X') self.y_label = QLabel('Y') self.z_label = QLabel('Z') #self.location_label.setAlignment(Qt.AlignCenter) self.cid_label.setAlignment(Qt.AlignCenter) self.x_label.setAlignment(Qt.AlignCenter) self.y_label.setAlignment(Qt.AlignCenter) self.z_label.setAlignment(Qt.AlignCenter) self.export_checkbox = QCheckBox() self.csv_label = QLabel('CSV Filename:') self.csv_edit = QLineEdit() self.csv_button = QPushButton('Browse...') self.csv_label.setEnabled(False) self.csv_edit.setEnabled(False) self.csv_button.setEnabled(False) #----------------------------------------------------------------------- # nodes self.add_button = QPushButton('Add') self.remove_button = QPushButton('Remove') # elements self.add2_button = QPushButton('Add') self.remove2_button = QPushButton('Remove') #----------------------------------------------------------------------- # closing self.apply_button = QPushButton('Apply') self.cancel_button = QPushButton('Cancel') self.set_bold_font(self._default_font_size) @property def gui(self): if self.win_parent is None: return None return self.win_parent.parent.gui def create_layout(self): """sets up the window""" grid = self._make_grid_layout() #hbox_csv = QHBoxLayout() grid2 = QGridLayout() #irow = 0 #grid2.addWidget(self.node_label, irow, 0) #grid2.addWidget(self.node_edit, irow, 1) #grid2.addWidget(self.add_button, irow, 2) #grid2.addWidget(self.remove_button, irow, 3) #irow += 1 #grid2.addWidget(self.element_label, irow, 0) #grid2.addWidget(self.element_edit, irow, 1) #grid2.addWidget(self.add2_button, irow, 2) #grid2.addWidget(self.remove2_button, irow, 3) #irow += 1 #grid2.addWidget(self.node_element_label, irow, 0) #grid2.addWidget(self.node_element_edit, irow, 1) #irow += 1 hbox_csv = QHBoxLayout() hbox_csv.addWidget(self.export_checkbox) hbox_csv.addWidget(self.csv_label) hbox_csv.addWidget(self.csv_edit) hbox_csv.addWidget(self.csv_button) #---------------------------------------------- ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.cancel_button) vbox = QVBoxLayout() vbox.addLayout(grid) vbox.addLayout(grid2) #vbox.addStretch() vbox.addLayout(hbox_csv) vbox.addStretch() #----------------------- #vbox.addLayout(add_remove_box) vbox.addLayout(ok_cancel_box) self.on_method(0) self.on_zaxis_method(0) self.setLayout(vbox) def on_export_checkbox(self): """this is called when the checkbox is clicked""" is_checked = self.export_checkbox.isChecked() self.csv_label.setEnabled(is_checked) self.csv_edit.setEnabled(is_checked) self.csv_button.setEnabled(is_checked) def on_browse_csv(self): """opens a file dialog""" default_dirname = os.getcwd() csv_filename, wildcard = save_file_dialog( self, 'Select the file name for export', default_dirname, wildcard_csv) if not csv_filename: return self.csv_edit.setText(csv_filename) 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 set_connections(self): """creates the actions for the menu""" self.method_pulldown.currentIndexChanged.connect(self.on_method) self.zaxis_method_pulldown.currentIndexChanged.connect( self.on_zaxis_method) self.plane_color_edit.clicked.connect(self.on_plane_color) self.export_checkbox.clicked.connect(self.on_export_checkbox) self.csv_button.clicked.connect(self.on_browse_csv) self.apply_button.clicked.connect(self.on_apply) self.cancel_button.clicked.connect(self.on_cancel) def on_method(self, method_int=None): method = get_pulldown_text(method_int, self.methods, self.method_pulldown) if method == 'Z-Axis Projection': is_cord2r = False elif method == 'CORD2R': is_cord2r = True else: raise NotImplementedError(method) if is_cord2r: self._zaxis_method = self.zaxis_method_pulldown.currentIndex() # set to manual #self.on_zaxis_method(method_int=2) # manual self.zaxis_method_pulldown.setCurrentIndex(2) self.on_zaxis_method() # update else: self.zaxis_method_pulldown.setCurrentIndex(self._zaxis_method) self.on_zaxis_method() # update # works self.zaxis_method_pulldown.setEnabled(not is_cord2r) self.zaxis_method_pulldown.setVisible(not is_cord2r) self.zaxis_method_label.setEnabled(not is_cord2r) def on_zaxis_method(self, method_int=None): method = get_pulldown_text(method_int, self.zaxis_methods, self.zaxis_method_pulldown) if method == 'Global Z': is_visible = False elif method == 'Camera Normal': is_visible = False elif method == 'Manual': is_visible = True else: raise NotImplementedError(method) self.zaxis_cid_pulldown.setVisible(is_visible) self.zaxis_x_edit.setVisible(is_visible) self.zaxis_y_edit.setVisible(is_visible) self.zaxis_z_edit.setVisible(is_visible) def on_plane_color(self): """ Choose a plane color""" title = "Choose a cutting plane color" rgb_color_ints = self.plane_color_int color_edit = self.plane_color_edit func_name = 'set_plane_color' passed, rgb_color_ints, rgb_color_floats = self._background_color( title, color_edit, rgb_color_ints, func_name) if passed: self.plane_color_int = rgb_color_ints self.plane_color_float = rgb_color_floats def _background_color(self, title, color_edit, rgb_color_ints, func_name): """helper method for ``on_background_color`` and ``on_background_color2``""" passed, rgb_color_ints, rgb_color_floats = self.on_color( color_edit, rgb_color_ints, title) if passed and 0: if self.win_parent is not None: settings = self.win_parent.settings func_background_color = getattr(settings, func_name) func_background_color(rgb_color_floats) return passed, rgb_color_ints, rgb_color_floats def on_color(self, color_edit, rgb_color_ints, title): """pops a color dialog""" col = QColorDialog.getColor(QtGui.QColor(*rgb_color_ints), self, title) if not col.isValid(): return False, rgb_color_ints, None color_float = col.getRgbF()[:3] # floats color_int = [int(colori * 255) for colori in color_float] assert isinstance(color_float[0], float), color_float assert isinstance(color_int[0], int), color_int color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color_int) + #"border:1px solid rgb(255, 170, 255); " "}") return True, color_int, color_float #--------------------------------------------------------------------------- def on_validate(self): p1_cidi = self.p1_cid_pulldown.currentText() p2_cidi = self.p2_cid_pulldown.currentText() p3_cidi = self.p3_cid_pulldown.currentText() zaxis_cidi = self.zaxis_cid_pulldown.currentText() p1_cid = int(p1_cidi) if 'Global' not in p1_cidi else 0 p2_cid = int(p2_cidi) if 'Global' not in p2_cidi else 0 p3_cid = int(p3_cidi) if 'Global' not in p3_cidi else 0 zaxis_cid = int(zaxis_cidi) if 'Global' not in zaxis_cidi else 0 #print('p1_cidi=%r p2_cidi=%r p3_cidi=%r' % (p1_cidi, p2_cidi, zaxis_cidi)) #print('p2_cid=%r p2_cid=%r p3_cidi=%r' % (p2_cid, p2_cid, zaxis_cid)) p1_x, flag1 = check_float(self.p1_x_edit) p1_y, flag2 = check_float(self.p1_y_edit) p1_z, flag3 = check_float(self.p1_z_edit) p2_x, flag4 = check_float(self.p2_x_edit) p2_y, flag5 = check_float(self.p2_y_edit) p2_z, flag6 = check_float(self.p2_z_edit) p3_x, flag7 = check_float(self.p3_x_edit) p3_y, flag8 = check_float(self.p3_y_edit) p3_z, flag9 = check_float(self.p3_z_edit) p1 = [p1_x, p1_y, p1_z] p2 = [p2_x, p2_y, p2_z] p3 = [p3_x, p3_y, p3_z] flag10, flag11, flag12, zaxis_cid, zaxis = get_zaxis( self.win_parent, # for camera self.zaxis_method_pulldown, self.zaxis_x_edit, self.zaxis_y_edit, self.zaxis_z_edit) method = self.method_pulldown.currentText() assert method in self.methods, 'method=%r' % method flag13 = True plane_opacity = self.plane_opacity_edit.value() nplanes = self.nplanes_spinner.value() csv_filename = None flag14 = True if self.export_checkbox.isChecked(): csv_filename, flag14 = check_save_path(self.csv_edit) flags = [ flag1, flag2, flag3, flag4, flag5, flag6, flag7, flag8, flag9, flag10, flag11, flag12, flag13, flag14 ] if all(flags): self.out_data['method'] = method self.out_data['p1'] = [p1_cid, p1] self.out_data['p2'] = [p2_cid, p2] self.out_data['p3'] = [p3_cid, p3] self.out_data['zaxis'] = [zaxis_cid, zaxis] self.out_data['plane_color'] = self.plane_color_float self.out_data['plane_opacity'] = plane_opacity self.out_data['nplanes'] = nplanes self.out_data['csv_filename'] = csv_filename self.out_data['clicked_ok'] = True return True return False def on_apply(self): passed = self.on_validate() if passed and self.win_parent is not None: self.win_parent.shear_moment_torque_obj.make_smt_from_data( self.out_data, show=True) #self.win_parent.make_smt_from_data(self.out_data) return passed def on_cancel(self): self.out_data['close'] = True self.close()
class FindReplaceDialog(QDialog): def __init__(self, parent): super(FindReplaceDialog, self).__init__(parent) self.parent = parent self.setWindowTitle("Find Replace") self.setFixedSize(400, 200) main_layout = QVBoxLayout() find_layout = QHBoxLayout() replace_layout = QHBoxLayout() options_layout = QHBoxLayout() buttons_layout = QHBoxLayout() find_label = QLabel() find_label.setText("Find:") self.find_input = QLineEdit() find_layout.addWidget(find_label) find_layout.addWidget(self.find_input) replace_label = QLabel() replace_label.setText("Replace:") self.replace_input = QLineEdit() replace_layout.addWidget(replace_label) replace_layout.addWidget(self.replace_input) self.close_button = QPushButton() self.close_button.setText("Close") self.find_button = QPushButton() self.find_button.setText("Find") self.replace_button = QPushButton() self.replace_button.setText("Replace") self.all_button = QPushButton() self.all_button.setText("Replace All") buttons_layout.addWidget(self.close_button) buttons_layout.addWidget(self.find_button) buttons_layout.addWidget(self.replace_button) buttons_layout.addWidget(self.all_button) self.highlight_result = QCheckBox() self.highlight_result.setText("highlight results") options_layout.addWidget(self.highlight_result) main_layout.addLayout(find_layout) main_layout.addLayout(replace_layout) main_layout.addLayout(options_layout) main_layout.addLayout(buttons_layout) self.setLayout(main_layout) self.find_button.clicked.connect(self.find_text) self.replace_button.clicked.connect(self.replace_text) self.all_button.clicked.connect(self.replace_all_text) self.close_button.clicked.connect(self.hide_dialog) def find_text(self): find_text = self.find_input.text() highlight = self.highlight_result.isChecked() self.parent.search_text(find_text, highlight) def replace_text(self): find_text = self.find_input.text() replace_text = self.replace_input.text() self.parent.replace_text(find_text, replace_text) def replace_all_text(self): find_text = self.find_input.text() replace_text = self.replace_input.text() if find_text == "": return self.parent.replace_all_text(find_text, replace_text) def hide_dialog(self): self.hide()
class EpochDialog(QDialog): def __init__(self, parent, events): super().__init__(parent) self.setWindowTitle("Create Epochs") grid = QGridLayout(self) label = QLabel("Events:") label.setAlignment(Qt.AlignTop) grid.addWidget(label, 0, 0, 1, 1) self.events = QListWidget() self.events.insertItems(0, unique(events[:, 2]).astype(str)) self.events.setSelectionMode(QListWidget.ExtendedSelection) grid.addWidget(self.events, 0, 1, 1, 2) grid.addWidget(QLabel("Interval around events:"), 1, 0, 1, 1) self.tmin = QDoubleSpinBox() self.tmin.setMinimum(-10000) self.tmin.setValue(-0.2) self.tmin.setSingleStep(0.1) self.tmin.setAlignment(Qt.AlignRight) self.tmax = QDoubleSpinBox() self.tmax.setMinimum(-10000) self.tmax.setValue(0.5) self.tmax.setSingleStep(0.1) self.tmax.setAlignment(Qt.AlignRight) grid.addWidget(self.tmin, 1, 1, 1, 1) grid.addWidget(self.tmax, 1, 2, 1, 1) self.baseline = QCheckBox("Baseline Correction:") self.baseline.setChecked(True) self.baseline.stateChanged.connect(self.toggle_baseline) grid.addWidget(self.baseline, 2, 0, 1, 1) self.a = QDoubleSpinBox() self.a.setMinimum(-10000) self.a.setValue(-0.2) self.a.setSingleStep(0.1) self.a.setAlignment(Qt.AlignRight) self.b = QDoubleSpinBox() self.b.setMinimum(-10000) self.b.setValue(0) self.b.setSingleStep(0.1) self.b.setAlignment(Qt.AlignRight) grid.addWidget(self.a, 2, 1, 1, 1) grid.addWidget(self.b, 2, 2, 1, 1) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) grid.addWidget(self.buttonbox, 3, 0, 1, -1) self.events.itemSelectionChanged.connect(self.toggle_ok) self.toggle_ok() grid.setSizeConstraint(QGridLayout.SetFixedSize) @Slot() def toggle_ok(self): if self.events.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) @Slot() def toggle_baseline(self): if self.baseline.isChecked(): self.a.setEnabled(True) self.b.setEnabled(True) else: self.a.setEnabled(False) self.b.setEnabled(False)
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent = None, name = '', isMD=False, noExp = 0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(Qt.Window) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.clicked.connect(self.presenter.clicked) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu self.addWidget(self.table) frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp-1) self.experimentInfo.valueChanged.connect(self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) self.full_time = QCheckBox("Relative Time") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Mean:', self.stats_widgets["mean"]) layout_stats.addRow('Median:', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) frame_right.setLayout(layout_right) self.addWidget(frame_right) self.setStretchFactor(0,1) self.resize(1200,800) self.show() def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) self.fig.canvas.draw() def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, marker='.', FullTime=not self.full_time.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [row.row() for row in self.table.selectionModel().selectedRows()] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = {"minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit()} for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format(getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()