def create_error_dlg(self, message, info=None, detail=None, ignore_buttons=True): dlg = QMessageBox() dlg.setText(message) if info: dlg.setInformativeText(info) if detail: dlg.setDetailedText(detail) dlg.setWindowTitle(self.name + self.tr(" - Error")) dlg.setIcon(QMessageBox.Critical) if ignore_buttons: dlg.setStandardButtons(QMessageBox.Abort | QMessageBox.Ignore) dlg.btn_ignoreall = dlg.addButton(self.tr("Ignore A&ll"), QMessageBox.ActionRole) dlg.setDefaultButton(QMessageBox.Ignore) else: dlg.setDefaultButton(QMessageBox.NoButton) return dlg
def handle_timeout(self): """ Handles the timeout event for the timer. Decreases the time remaining counter until 0 and when it is time, cleans up the event filter and closes the window. """ if is_qt_designer(): return # Decrease remaining time self._time_rem_ms -= self._timer.interval() # Update screen label with new remaining time self._update_label() if self._time_rem_ms > 0: self.start() return QApplication.instance().removeEventFilter(self) if self._window: logger.debug('Time to close the window') self._window.close() msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setText( "Your window was closed due to inactivity for {}.".format( self._get_time_text(self.timeout))) msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.exec_()
def validate_password(self): """ If the widget is ```passwordProtected```, this method will propmt the user for the correct password. Returns ------- bool True in case the password was correct of if the widget is not password protected. """ if not self._password_protected: return True pwd, ok = QInputDialog().getText(None, "Authentication", "Please enter your password:"******"") pwd = str(pwd) if not ok or pwd == "": return False sha = hashlib.sha256() sha.update(pwd.encode()) pwd_encrypted = sha.hexdigest() if pwd_encrypted != self._protected_password: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Invalid password.") msg.setWindowTitle("Error") msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.setEscapeButton(QMessageBox.Ok) msg.exec_() return False return True
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
def closeEvent(self, event): mb_close = QMessageBox( QMessageBox.Question, "Exit", "Are you sure you want to EXIT the program?", QMessageBox.Yes | QMessageBox.No, parent=self, ) mb_close.setDefaultButton(QMessageBox.No) if mb_close.exec() == QMessageBox.Yes: event.accept() self.wnd_manage_emission_lines.close() self.wnd_compute_roi_maps.close() self.wnd_image_wizard.close() self.wnd_load_quantitative_calibration.close() self.wnd_general_fitting_settings.close() self.wnd_fitting_parameters_shared.close() self.wnd_fitting_parameters_lines.close() # This flag is used for CI tests self._is_closed = True else: event.ignore()
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
def delete_patches(self): if self.mainwin.selection.hasSelection(): rows = sorted( [r.row() for r in self.mainwin.selection.selectedRows()]) patches = tuple(self.patches.get_row(r).displayname for r in rows) msg_box = QMessageBox() if len(rows) == 1: msg_box.setText( self.tr("Delete patch '{}'?").format(patches[0])) else: msg_box.setText( self.tr("Delete {} patches?").format(len(rows))) msg_box.setDetailedText('\n'.join(patches)) msg_box.setInformativeText( self.tr("Patches can only be restored by re-importing them.")) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) msg_box.setDefaultButton(QMessageBox.Cancel) msg_box.setIcon(QMessageBox.Warning) if msg_box.exec_() == QMessageBox.Yes: with self.session.begin(): for n, row in enumerate(rows): self.patches.removeRows(row - n)
def confirm_restore_defaults(self): m = QMessageBox(QMessageBox.Question, "Restore to defaults", "Restore settings to the default state?\n" "You'll need to restart the program.", QMessageBox.Yes | QMessageBox.No, self) m.setDefaultButton(QMessageBox.No) yesButton = m.button(QMessageBox.Yes) yesButton.clicked.connect(self.restore_defaults) m.show()
def display_message_box(self, title, message, details): msg = QMessageBox(self) msg.setIcon(QMessageBox.Warning) msg.setText(message) msg.setWindowTitle(title) msg.setDetailedText(details) msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.setEscapeButton(QMessageBox.Ok) msg.exec_()
def _on_reset(self): """ Callback for reset button. Prompts user for confirmation, then proceeds to reset settings to default values if confirmed, before updating controls and applying any changes (emits change signal if any changes). """ mb = QMessageBox( QMessageBox.Warning, tr("Reset all settings"), tr("This will reset all settings to their default " + "values. Are you sure you want to continue?"), QMessageBox.Yes | QMessageBox.No) mb.setDefaultButton(QMessageBox.No) dr = mb.exec_() if dr == QMessageBox.Yes: # This clears all settings, and recreates only those values # initialized with set_default this session. Settings.restore_from_defaults() # Now we update controls: s = QSettings(self.ui) keys = list( self._initial_values.keys()) # Use copy, as we may modify for k in keys: # Check if setting is still present if s.contains(k): # Present, update to new value (triggers _on_change) v = s.value(k) w = self._lut[k] if isinstance(w, QLineEdit): w.setText(v) elif isinstance(w, QCheckBox): w.setChecked(v.lower() == "true") elif isinstance(w, (QSpinBox, QDoubleSpinBox)): w.setValue(v) else: # Setting was removed, remove editor w = self._lut[k] layout = w.parent().layout() label = layout.labelForField(w) layout.removeWidget(w) w.close() if label is not None: layout.removeWidget(label) label.close() del self._lut[k] del self._initial_values[k] self._changes[k] = None # Check whether all editors for tab was removed if layout.count() == 0: wrap = w.parent() self.tabs.removeTab(self.tabs.indexOf(wrap)) # Finally apply changes (update _initial_values, and emit signal) self.apply_changes()
def showYesNoDialog(parent: QWidget = None, *, title: str, text: str) -> QMessageBox.StandardButton: box = QMessageBox(parent) box.setWindowTitle(title) box.setText(text) box.setWindowModality(Qt.ApplicationModal) box.setIcon(QMessageBox.Question) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.No) return box.exec_()
def _on_reset(self): """ Callback for reset button. Prompts user for confirmation, then proceeds to reset settings to default values if confirmed, before updating controls and applying any changes (emits change signal if any changes). """ mb = QMessageBox(QMessageBox.Warning, tr("Reset all settings"), tr("This will reset all settings to their default " + "values. Are you sure you want to continue?"), QMessageBox.Yes | QMessageBox.No) mb.setDefaultButton(QMessageBox.No) dr = mb.exec_() if dr == QMessageBox.Yes: # This clears all settings, and recreates only those values # initialized with set_default this session. Settings.restore_from_defaults() # Now we update controls: s = QSettings(self.ui) keys = list(self._initial_values.keys()) # Use copy, as we may modify for k in keys: # Check if setting is still present if s.contains(k): # Present, update to new value (triggers _on_change) v = s.value(k) w = self._lut[k] if isinstance(w, QLineEdit): w.setText(v) elif isinstance(w, QCheckBox): w.setChecked(v.lower() == "true") elif isinstance(w, (QSpinBox, QDoubleSpinBox)): w.setValue(v) else: # Setting was removed, remove editor w = self._lut[k] layout = w.parent().layout() label = layout.labelForField(w) layout.removeWidget(w) w.close() if label is not None: layout.removeWidget(label) label.close() del self._lut[k] del self._initial_values[k] self._changes[k] = None # Check whether all editors for tab was removed if layout.count() == 0: wrap = w.parent() self.tabs.removeTab(self.tabs.indexOf(wrap)) # Finally apply changes (update _initial_values, and emit signal) self.apply_changes()
def permission_box_to_prepend_import(): msg_box = QMessageBox() msg_box.setWindowTitle("Mantid Workbench") msg_box.setWindowIcon(QIcon(':/images/MantidIcon.ico')) msg_box.setText("It looks like this python file uses a Mantid " "algorithm but does not import the Mantid API.") msg_box.setInformativeText("Would you like to add a line to import " "the Mantid API?") msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box.setDefaultButton(QMessageBox.Yes) permission = msg_box.exec_() if permission == QMessageBox.Yes: return True return False
def exception(parent, ex, buttons=QMessageBox.Ok, defaultButton=QMessageBox.NoButton): title = type(ex).__name__ message = str(ex) tb = StringIO() if hasattr(ex, '__traceback__'): exc_traceback = ex.__traceback__ else: exc_traceback = sys.exc_info()[2] traceback.print_tb(exc_traceback, file=tb) msgbox = QMessageBox(QMessageBox.Critical, title, message, buttons, parent) msgbox.setDefaultButton(defaultButton) msgbox.setDetailedText(tb.getvalue()) msgbox.exec_()
def _ok_to_close(self, entity): if entity.widget in self.modified_widgets: mb = QMessageBox() mb.setText("The tab contents may have been modified.") mb.setInformativeText("Do you want to save your changes?") mb.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) mb.setDefaultButton(QMessageBox.Save) ans = mb.exec() if ans == QMessageBox.Cancel: return False elif ans == QMessageBox.Save: saved = self.save() if not saved: return False return True
def _dialog_create(self, title, text, info_text, callback, *, icon='Warning', buttons=[], modal=True, window=None): window = self._window if window is None else window widget = QMessageBox(window) widget.setWindowTitle(title) widget.setText(text) # icon is one of _QtDialog.supported_icon_names icon_id = getattr(QMessageBox, icon) widget.setIcon(icon_id) widget.setInformativeText(info_text) if not buttons: buttons = ["Ok"] button_ids = list() for button in buttons: # button is one of _QtDialog.supported_button_names button_id = getattr(QMessageBox, button) button_ids.append(button_id) standard_buttons = default_button = button_ids[0] for button_id in button_ids[1:]: standard_buttons |= button_id widget.setStandardButtons(standard_buttons) widget.setDefaultButton(default_button) @safe_event def func(button): button_id = widget.standardButton(button) for button_name in _QtDialog.supported_button_names: if button_id == getattr(QMessageBox, button_name): widget.setCursor(QCursor(Qt.WaitCursor)) try: callback(button_name) finally: widget.unsetCursor() break widget.buttonClicked.connect(func) return _QtDialogWidget(widget, modal)
def exceptions(parent, exs, buttons=QMessageBox.Ok, defaultButton=QMessageBox.NoButton): title = 'Exception(s)' message = '\n'.join(map(str, exs)) tracebacks = [] for ex in exs: tb = StringIO() if not hasattr(ex, '__traceback__'): continue exc_traceback = ex.__traceback__ traceback.print_tb(exc_traceback, file=tb) tracebacks.append(tb.getvalue()) msgbox = QMessageBox(QMessageBox.Critical, title, message, buttons, parent) msgbox.setDefaultButton(defaultButton) msgbox.setDetailedText('\n'.join(tracebacks)) msgbox.exec_()
def _ok_to_quit(self): if not hasattr(self, "_main_window"): return True if self.modified: mb = QMessageBox() mb.setText("The project may have been modified.") mb.setInformativeText("Do you want to save your changes?") mb.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) mb.setDefaultButton(QMessageBox.Save) ans = mb.exec() if ans == QMessageBox.Cancel: return False elif ans == QMessageBox.Save: saved = self.save() if not saved: return False return True
def confirm_dialog(self, is_release=False): """ Show the confirmation dialog with the proper message in case ```showConfirmMessage``` is True. Returns ------- bool True if the message was confirmed or if ```showCofirmMessage``` is False. """ if self._show_confirm_dialog: if self._confirm_message == "": self._confirm_message = PyDMPushButton.DEFAULT_CONFIRM_MESSAGE msg = QMessageBox() msg.setIcon(QMessageBox.Question) relative = "Yes" if self._relative else "No" val = self._pressValue op = "Press" if is_release: val = self._releaseValue op = "Release" message = os.linesep.join([ self._confirm_message, "Value: {}".format(val), "Relative Change: {}".format(relative) ]) msg.setText(message) msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg.setDefaultButton(QMessageBox.No) ret = msg.exec_() if ret == QMessageBox.No: return False return True
def __init__(self, parent, init_val): super(EditVal_Dialog, self).__init__(parent) # shortcut save_shortcut = QShortcut(QKeySequence.Save, self) save_shortcut.activated.connect(self.save_triggered) main_layout = QVBoxLayout() self.val_text_edit = QPlainTextEdit() val_str = '' try: val_str = str(init_val) except Exception as e: msg_box = QMessageBox(QMessageBox.Warning, 'Value parsing failed', 'Couldn\'t stringify value', QMessageBox.Ok, self) msg_box.setDefaultButton(QMessageBox.Ok) msg_box.exec_() self.reject() self.val_text_edit.setPlainText(val_str) main_layout.addWidget(self.val_text_edit) button_box = QDialogButtonBox() button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) button_box.rejected.connect(self.reject) main_layout.addWidget(button_box) self.setLayout(main_layout) self.resize(450, 300) self.setWindowTitle('edit val')
def confirm_dialog(self): """ Show the confirmation dialog with the proper message in case ```showConfirmMessage``` is True. Returns ------- bool True if the message was confirmed or if ```showCofirmMessage``` is False. """ if self._show_confirm_dialog: if self._confirm_message == "": self._confirm_message = PyDMPushButton.DEFAULT_CONFIRM_MESSAGE msg = QMessageBox() msg.setIcon(QMessageBox.Question) msg.setText(self._confirm_message) msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg.setDefaultButton(QMessageBox.No) ret = msg.exec_() if ret == QMessageBox.No: return False return True
class PosAngCorr(SiriusMainWindow): """Main Class.""" def __init__(self, parent=None, prefix='', tl=None): """Class construc.""" super(PosAngCorr, self).__init__(parent) if not prefix: self._prefix = _VACA_PREFIX else: self._prefix = prefix self._tl = tl.upper() base_name = _PVName('TL-Glob:AP-PosAng') self.posang_prefix = base_name.substitute( prefix=self._prefix, sec=self._tl) self.setObjectName(self._tl+'App') self.setWindowTitle(self._tl + ' Position and Angle Correction Window') if self._tl == 'TS': self._is_chsept = False ch3_pv = _PV(self.posang_prefix.substitute(propty='CH3-Cte'), connection_timeout=1) if not ch3_pv.wait_for_connection(): self._is_chsept = True if tl == 'ts': corr_h = (Const.TS_CORRH_POSANG_CHSEPT if self._is_chsept else Const.TS_CORRH_POSANG_SEPTSEPT) corr_v = Const.TS_CORRV_POSANG elif tl == 'tb': corr_h = Const.TB_CORRH_POSANG corr_v = Const.TB_CORRV_POSANG self.corrs = dict() self.corrs['CH1'] = _PVName(corr_h[0]) self.corrs['CH2'] = _PVName(corr_h[1]) if len(corr_h) == 3: self.corrs['CH3'] = _PVName(corr_h[2]) self.corrs['CV1'] = _PVName(corr_v[0]) self.corrs['CV2'] = _PVName(corr_v[1]) if len(corr_v) == 4: self.corrs['CV3'] = _PVName(corr_v[2]) self.corrs['CV4'] = _PVName(corr_v[3]) self._just_need_update = False self._update_ref_action = False self._my_input_widgets = list() self._setupUi() self.setFocus(True) self.setFocusPolicy(Qt.StrongFocus) self._ask_message = QMessageBox(self) self._ask_message.setWindowTitle('Message') self._ask_message.setText( 'The '+self._tl+' PosAng IOC indicates reference needs to ' 'be updated! Do you want to update the reference?') self._ask_message.setStandardButtons(QMessageBox.No | QMessageBox.Yes) self._ask_message.setDefaultButton(QMessageBox.No) self.app = QApplication.instance() self.app.focusChanged.connect(self._spinbox_onfocus) def _setupUi(self): cwt = QWidget(self) self.setCentralWidget(cwt) # label lab = QLabel( '<h3>'+self._tl+' Position and Angle Correction</h3>', cwt) lab.setStyleSheet(""" min-height:1.55em; max-height: 1.55em; qproperty-alignment: 'AlignVCenter | AlignRight'; background-color: qlineargradient(spread:pad, x1:1, y1:0.0227273, x2:0, y2:0, stop:0 rgba(173, 190, 207, 255), stop:1 rgba(213, 213, 213, 255));""") # apply button self.pb_updateref = PyDMPushButton( self, 'Update Reference', pressValue=1, init_channel=self.posang_prefix.substitute( propty='SetNewRefKick-Cmd')) self.pb_updateref.setStyleSheet( 'min-height: 2.4em; max-height: 2.4em;') self.led_needrefupdt = SiriusLedAlert( self, self.posang_prefix.substitute(propty='NeedRefUpdate-Mon')) self.ch_needrefupdt = SiriusConnectionSignal( self.posang_prefix.substitute(propty='NeedRefUpdate-Mon')) self.ch_needrefupdt.new_value_signal[int].connect( self._handle_need_update_ref_led) self.led_needrefupdt.setStyleSheet( 'QLed{min-width: 1.29em; max-width: 1.29em;}') box_ref = QHBoxLayout() box_ref.setContentsMargins(0, 0, 0, 0) box_ref.addWidget(self.pb_updateref) box_ref.addWidget(self.led_needrefupdt) # delta setters self.hgbox = QGroupBox('Horizontal', self) self.hgbox.setLayout(self._setupDeltaControlLayout('x')) self.vgbox = QGroupBox('Vertical', self) self.vgbox.setLayout(self._setupDeltaControlLayout('y')) # correctors self.corrgbox = QGroupBox('Correctors', self) self.corrgbox.setLayout(self._setupCorrectorsLayout()) # status self.statgbox = QGroupBox('Correction Status', self) self.statgbox.setLayout(self._setupStatusLayout()) glay = QGridLayout(cwt) glay.setHorizontalSpacing(12) glay.setVerticalSpacing(12) glay.addWidget(lab, 0, 0, 1, 2) glay.addLayout(box_ref, 1, 0, 1, 2) glay.addWidget(self.hgbox, 2, 0) glay.addWidget(self.vgbox, 2, 1) glay.addWidget(self.corrgbox, 3, 0, 1, 2) glay.addWidget(self.statgbox, 4, 0, 1, 2) # menu act_settings = self.menuBar().addAction('Settings') _hlautil.connect_window(act_settings, CorrParamsDetailWindow, parent=self, tl=self._tl, prefix=self._prefix) # stlesheet self.setStyleSheet(""" PyDMSpinbox{ min-width: 5em; max-width: 5em; } PyDMLabel, PyDMSpinboxScrollbar{ min-width: 6em; max-width: 6em; } QPushButton{ min-width: 8em; } QLabel{ min-height: 1.35em; qproperty-alignment: AlignCenter; } """) def _setupDeltaControlLayout(self, axis=''): # pos label_pos = QLabel("<h4>Δ"+axis+"</h4>", self) sb_deltapos = PyDMSpinbox(self, self.posang_prefix.substitute( propty='DeltaPos'+axis.upper()+'-SP')) sb_deltapos.step_exponent = -2 sb_deltapos.update_step_size() sb_deltapos.showStepExponent = False lb_deltapos = PyDMLabel(self, self.posang_prefix.substitute( propty='DeltaPos'+axis.upper()+'-RB')) lb_deltapos.showUnits = True self._my_input_widgets.append(sb_deltapos) # ang label_ang = QLabel("<h4>Δ"+axis+"'</h4>", self) sb_deltaang = PyDMSpinbox(self, self.posang_prefix.substitute( propty='DeltaAng'+axis.upper()+'-SP')) sb_deltaang.step_exponent = -2 sb_deltaang.update_step_size() sb_deltaang.showStepExponent = False lb_deltaang = PyDMLabel(self, self.posang_prefix.substitute( propty='DeltaAng'+axis.upper()+'-RB')) lb_deltaang.showUnits = True self._my_input_widgets.append(sb_deltaang) lay = QGridLayout() lay.setVerticalSpacing(12) lay.setHorizontalSpacing(12) lay.addItem( QSpacerItem(10, 0, QSzPlcy.Expanding, QSzPlcy.Ignored), 0, 0) lay.addWidget(label_pos, 0, 1) lay.addWidget(sb_deltapos, 0, 2) lay.addWidget(lb_deltapos, 0, 3) lay.addWidget(label_ang, 1, 1) lay.addWidget(sb_deltaang, 1, 2) lay.addWidget(lb_deltaang, 1, 3) lay.addItem( QSpacerItem(10, 0, QSzPlcy.Expanding, QSzPlcy.Ignored), 0, 4) return lay def _setupCorrectorsLayout(self): lay = QGridLayout() lay.setVerticalSpacing(9) lay.setHorizontalSpacing(9) label_kicksp = QLabel('<h4>Kick-SP</h4>', self) label_kickrb = QLabel('<h4>Kick-RB</h4>', self) label_kickref = QLabel('<h4>RefKick-Mon</h4>', self) lay.addWidget(label_kicksp, 0, 2) lay.addWidget(label_kickrb, 0, 3) lay.addWidget(label_kickref, 0, 4) idx = 1 for corrid, corr in self.corrs.items(): pbt = QPushButton(qta.icon('fa5s.list-ul'), '', self) pbt.setObjectName('pbt') pbt.setStyleSheet(""" #pbt{ min-width:25px; max-width:25px; min-height:25px; max-height:25px; icon-size:20px;} """) if corr.dis == 'PU': _hlautil.connect_window( pbt, _PUDetailWindow, self, devname=corr) else: _hlautil.connect_window( pbt, _PSDetailWindow, self, psname=corr) lb_name = QLabel(corr, self) le_sp = PyDMSpinboxScrollbar( self, corr.substitute(prefix=self._prefix, propty='Kick-SP')) le_sp.spinbox.setAlignment(Qt.AlignCenter) le_sp.scrollbar.limitsFromPV = True lb_rb = PyDMLabel(self, corr.substitute( prefix=self._prefix, propty='Kick-RB')) lb_ref = PyDMLabel(self, self.posang_prefix.substitute( propty='RefKick'+corrid+'-Mon')) lay.addWidget(pbt, idx, 0, alignment=Qt.AlignTop) lay.addWidget( lb_name, idx, 1, alignment=Qt.AlignLeft | Qt.AlignTop) lay.addWidget(le_sp, idx, 2, alignment=Qt.AlignTop) lay.addWidget(lb_rb, idx, 3, alignment=Qt.AlignTop) lay.addWidget(lb_ref, idx, 4, alignment=Qt.AlignTop) idx += 1 if self._tl == 'TB': pref = self._prefix + ('-' if self._prefix else '') lay.addItem(QSpacerItem(0, 8, QSzPlcy.Ignored, QSzPlcy.Fixed)) label_voltsp = QLabel('<h4>Amplitude-SP</h4>', self) label_voltrb = QLabel('<h4>Amplitude-RB</h4>', self) lay.addWidget(label_voltsp, idx+2, 2) lay.addWidget(label_voltrb, idx+2, 3) lb_kly2_name = QLabel('Klystron 2', self) le_kly2_sp = PyDMSpinboxScrollbar( self, pref+'LA-RF:LLRF:KLY2:SET_AMP') le_kly2_sp.spinbox.precisionFromPV = False le_kly2_sp.spinbox.precision = 2 le_kly2_sp.spinbox.setAlignment(Qt.AlignCenter) le_kly2_sp.scrollbar.limitsFromPV = True lb_kly2_rb = PyDMLabel(self, pref+'LA-RF:LLRF:KLY2:GET_AMP') lb_kly2_rb.precisionFromPV = False lb_kly2_rb.precision = 2 lay.addWidget(lb_kly2_name, idx+3, 1, alignment=Qt.AlignLeft | Qt.AlignTop) lay.addWidget(le_kly2_sp, idx+3, 2, alignment=Qt.AlignTop) lay.addWidget(lb_kly2_rb, idx+3, 3, alignment=Qt.AlignTop) self._kckr_name = _PVName('BO-01D:PU-InjKckr') else: self._kckr_name = _PVName('SI-01SA:PU-InjNLKckr') label_voltsp = QLabel('<h4>Voltage-SP</h4>', self) label_voltrb = QLabel('<h4>Voltage-RB</h4>', self) lay.addWidget(label_voltsp, idx+4, 2) lay.addWidget(label_voltrb, idx+4, 3) lay.addItem(QSpacerItem(0, 8, QSzPlcy.Ignored, QSzPlcy.Fixed)) pb_kckr = QPushButton(qta.icon('fa5s.list-ul'), '', self) pb_kckr.setObjectName('pb') pb_kckr.setStyleSheet(""" #pb{ min-width:25px; max-width:25px; min-height:25px; max-height:25px; icon-size:20px;} """) lb_kckr_name = QLabel(self._kckr_name, self) _hlautil.connect_window( pb_kckr, _PUDetailWindow, self, devname=self._kckr_name) lb_kckr_sp = PyDMSpinboxScrollbar( self, self._kckr_name.substitute( prefix=self._prefix, propty='Voltage-SP')) lb_kckr_sp.scrollbar.limitsFromPV = True lb_kckr_rb = PyDMLabel(self, self._kckr_name.substitute( prefix=self._prefix, propty='Voltage-RB')) lay.addWidget(pb_kckr, idx+5, 0, alignment=Qt.AlignTop) lay.addWidget( lb_kckr_name, idx+5, 1, alignment=Qt.AlignLeft | Qt.AlignTop) lay.addWidget(lb_kckr_sp, idx+5, 2, alignment=Qt.AlignTop) lay.addWidget(lb_kckr_rb, idx+5, 3, alignment=Qt.AlignTop) return lay def _setupStatusLayout(self): self.log = PyDMLogLabel( self, self.posang_prefix.substitute(propty='Log-Mon')) self.lb_sts0 = QLabel(Const.STATUSLABELS[0], self) self.led_sts0 = SiriusLedAlert( self, self.posang_prefix.substitute(propty='Status-Mon'), bit=0) self.lb_sts1 = QLabel(Const.STATUSLABELS[1], self) self.led_sts1 = SiriusLedAlert( self, self.posang_prefix.substitute(propty='Status-Mon'), bit=1) self.lb_sts2 = QLabel(Const.STATUSLABELS[2], self) self.led_sts2 = SiriusLedAlert( self, self.posang_prefix.substitute(propty='Status-Mon'), bit=2) self.lb_sts3 = QLabel(Const.STATUSLABELS[3], self) self.led_sts3 = SiriusLedAlert( self, self.posang_prefix.substitute(propty='Status-Mon'), bit=3) self.pb_config = PyDMPushButton( self, label='Config Correctors', pressValue=1, init_channel=self.posang_prefix.substitute(propty='ConfigPS-Cmd')) lay = QGridLayout() lay.setVerticalSpacing(12) lay.setHorizontalSpacing(12) lay.addWidget(self.log, 0, 0, 6, 1) lay.addWidget(self.lb_sts0, 1, 2) lay.addWidget(self.led_sts0, 1, 1) lay.addWidget(self.lb_sts1, 2, 2) lay.addWidget(self.led_sts1, 2, 1) lay.addWidget(self.lb_sts2, 3, 2) lay.addWidget(self.led_sts2, 3, 1) lay.addWidget(self.lb_sts3, 4, 2) lay.addWidget(self.led_sts3, 4, 1) lay.addWidget(self.pb_config, 5, 1, 1, 2) if self._tl == 'TS': self.led_corrtype = PyDMLedMultiChannel( self, {self.posang_prefix.substitute( propty='CH1-Cte'): self.corrs['CH1']}) self.lb_corrtype = QLabel( 'Control ' + ('CH-Sept' if self._is_chsept else 'Sept-Sept')) lay.addWidget(self.led_corrtype, 0, 1) lay.addWidget(self.lb_corrtype, 0, 2) return lay def _set_correctors_channels(self, corrs): self.centralwidget.pushButton_CH1.setText(corrs[0]) _hlautil.connect_window( self.centralwidget.pushButton_CH1, _PSDetailWindow, self, psname=corrs[0]) self.centralwidget.PyDMLabel_KickRBCH1.channel = ( corrs[0].substitute(prefix=self._prefix, propty='Kick-RB')) self.centralwidget.pushButton_CH2.setText(corrs[1]) if corrs[1].dis == 'PU': _hlautil.connect_window( self.centralwidget.pushButton_CH2, _PUDetailWindow, self, devname=corrs[1]) else: _hlautil.connect_window( self.centralwidget.pushButton_CH2, _PSDetailWindow, self, psname=corrs[1]) self.centralwidget.PyDMLabel_KickRBCH2.channel = ( corrs[1].substitute(prefix=self._prefix, propty='Kick-RB')) self.centralwidget.pushButton_CV1.setText(corrs[2]) _hlautil.connect_window( self.centralwidget.pushButton_CV1, _PSDetailWindow, self, psname=corrs[2]) self.centralwidget.PyDMLabel_KickRBCV1.channel = ( corrs[2].substitute(prefix=self._prefix, propty='Kick-RB')) self.centralwidget.pushButton_CV2.setText(corrs[3]) _hlautil.connect_window( self.centralwidget.pushButton_CV2, _PSDetailWindow, self, psname=corrs[3]) self.centralwidget.PyDMLabel_KickRBCV2.channel = ( corrs[3].substitute(prefix=self._prefix, propty='Kick-RB')) def _handle_need_update_ref_led(self, value): self._just_need_update = bool(value) def _spinbox_onfocus(self, old_focus, new_focus): if not self._update_ref_action and not self._just_need_update: return if self.led_needrefupdt.value != 0: if new_focus in self._my_input_widgets and self._just_need_update: ans = self._ask_message.exec_() if ans == QMessageBox.No: self._update_ref_action = False else: self._update_ref_action = True self.pb_updateref.sendValue() self._just_need_update = False
class ExportDialog(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.ui = Ui_Dialog_Export() self.ui.setupUi(self) self._fig = None # setup file types for frmt in IMAGE_FORMATS: self.ui.comboBox_fileType.addItem("{0[1]} (.{0[0]})".format(frmt)) self._file_name_changed() # select the default format # setup directory and dir dialog self._dir_dialog = QFileDialog(self) # self._dir_dialog.setDirectory(os.path.expanduser("~")) self._dir_dialog.setFileMode(QFileDialog.DirectoryOnly) self._dir_dialog.setWindowTitle("Save to ...") self.ui.comboBox_directory.addItem(os.path.expanduser("~")) self.ui.pushButton_browse.clicked.connect(self._browse) self._dir_validator = DirectoryValidator() self.ui.comboBox_directory.lineEdit().setValidator(self._dir_validator) # file name self.ui.comboBox_fileName.currentIndexChanged.connect( self._file_name_changed) # file type self.ui.comboBox_fileType.currentIndexChanged.connect( self._file_type_changed) # cancel button self.ui.pushButton_cancel.clicked.connect(self._cancel_button_clicked) # export button self.ui.pushButton_export.clicked.connect(self._export_button_clicked) # preview button self.ui.pushButton_preview.clicked.connect( self._preview_button_clicked) # dpi self.ui.spinBox_dpi.valueChanged.connect(self._dpi_changed) # inches self.ui.doubleSpinBox_width_inches.valueChanged.connect( self._width_inches_changed) self.ui.doubleSpinBox_height_inches.valueChanged.connect( self._height_inches_changed) # pixels self.ui.spinBox_width_pixels.valueChanged.connect( self._width_pixels_changed) self.ui.spinBox_height_pixels.valueChanged.connect( self._height_pixels_changed) # message box for when the file already exist self._msg_box = QMessageBox(self) self._msg_box.setWindowTitle("Export...") self._msg_box_button_overwrite = self._msg_box.addButton( self.tr("Overwrite"), QMessageBox.AcceptRole) self._msg_box_button_save_as = self._msg_box.addButton( self.tr("Save As"), QMessageBox.ActionRole) self._msg_box_button_cancel = self._msg_box.addButton( QMessageBox.Cancel) self._msg_box.setDefaultButton(self._msg_box_button_save_as) _orientation = ['portrait', 'landscape'] _no_alpha_channel_formats = ('jpg', 'jpeg') # ("png", "gif", "psd") def _dpi_changed(self, dpi): self.ui.doubleSpinBox_height_inches.blockSignals(True) self.ui.doubleSpinBox_width_inches.blockSignals(True) self.ui.spinBox_height_pixels.setValue( self.ui.doubleSpinBox_height_inches.value() * dpi) self.ui.spinBox_width_pixels.setValue( self.ui.doubleSpinBox_width_inches.value() * dpi) self.ui.doubleSpinBox_height_inches.blockSignals(False) self.ui.doubleSpinBox_width_inches.blockSignals(False) def _width_pixels_changed(self, width): self.ui.doubleSpinBox_width_inches.blockSignals(True) new_width_inches = width / float(self.ui.spinBox_dpi.value()) self.ui.doubleSpinBox_width_inches.setValue(new_width_inches) self.ui.doubleSpinBox_width_inches.blockSignals(False) def _height_pixels_changed(self, height): self.ui.doubleSpinBox_height_inches.blockSignals(True) new_height_inches = height / float(self.ui.spinBox_dpi.value()) self.ui.doubleSpinBox_height_inches.setValue(new_height_inches) self.ui.doubleSpinBox_height_inches.blockSignals(False) def _width_inches_changed(self, width): self.ui.spinBox_width_pixels.blockSignals(True) self.ui.spinBox_width_pixels.setValue(width * self.ui.spinBox_dpi.value()) self.ui.spinBox_width_pixels.blockSignals(False) def _height_inches_changed(self, height): self.ui.spinBox_height_pixels.blockSignals(True) self.ui.spinBox_height_pixels.setValue(height * self.ui.spinBox_dpi.value()) self.ui.spinBox_height_pixels.blockSignals(False) def _cancel_button_clicked(self, b): self.reject() def _export_button_clicked(self, b): self.accept() def _preview_button_clicked(self): if self._fig: # set figures self._fig.set_size_inches(self.get_size_inches_width(), self.get_size_inches_height()) params = self.get_savefig_params() self._fig.set_dpi(params['dpi']) self._fig.set_tight_layout(True if params['bbox_inches'] == 'tight' else False) canvas = FigureCanvas(self._fig) canvas.show() # dialog preview_dialog = PreviewDialog(self, self._fig) preview_dialog.exec_() def _file_type_changed(self, *args, **kwargs): index = self.ui.comboBox_fileType.currentIndex() ext, _ = IMAGE_FORMATS[index] filename = str(self.ui.comboBox_fileName.currentText()) filename, _ = os.path.splitext(filename) if ext in self._no_alpha_channel_formats: # enable transparent if the format supports self.ui.checkBox_transparent.setEnabled(False) else: self.ui.checkBox_transparent.setEnabled(True) # update file name filename = "%s.%s" % (filename, ext) index = self.ui.comboBox_fileName.findText(filename) if index == -1: self.ui.comboBox_fileName.addItem(filename) self.ui.comboBox_fileName.setCurrentIndex( index if index >= 0 else self.ui.comboBox_fileName. findText(filename)) def _file_name_changed(self, *args, **kwargs): filename = str(self.ui.comboBox_fileName.currentText()) filename, extension = os.path.splitext(filename) extension = extension[1:] # get ride of . # check if the extension is supported index = [ i for i, (ext, dsc) in enumerate(IMAGE_FORMATS) if ext == extension ] if index: self.ui.comboBox_fileType.setCurrentIndex(index[0]) elif extension: # no extension pass else: # extension not supported: pass def _browse(self, *args, **kwargs): if self._dir_dialog.exec_() == QDialog.Accepted: dirs = self._dir_dialog.selectedFiles( ) # behave differently in pyqt4 and pyqt5 directory = str(dirs[0] if dirs else self._dir_dialog.directory(). absolutePath()) # this makes the behave the same # update directory index = self.ui.comboBox_directory.findText(directory) if index == -1: self.ui.comboBox_directory.addItem(directory) self.ui.comboBox_directory.setCurrentIndex( index if index >= 0 else self.ui.comboBox_directory. findText(directory)) def export_to_file(self, fig): self._fig = fig respawn = True while respawn: respawn = False self.ui.spinBox_dpi.setValue(fig.get_dpi()) self.ui.doubleSpinBox_width_inches.setValue(fig.get_figwidth()) self.ui.doubleSpinBox_height_inches.setValue(fig.get_figheight()) response = self.exec_() if response == QDialog.Accepted: # saving files fname = self.get_save_file_name() if os.path.exists(fname): new_name = generate_unique_file_name(fname) self._show_file_exist_message(fname, new_name) if self._msg_box.clickedButton( ) == self._msg_box_button_cancel: respawn = True continue elif self._msg_box.clickedButton( ) == self._msg_box_button_save_as: fname = new_name # save_as else: pass # use the original name to overwrite params = self.get_savefig_params() # change size fig.set_size_inches(self.get_size_inches_width(), self.get_size_inches_height()) try: fig.savefig(fname, **params) except IOError as err: if 'RGBA' in err.message: # if the problem is RGBA as the alpha channel is not supported in the selected format # save to png then save as basename = os.path.basename(fname) tmp_dir = tempfile.gettempdir() filename, ext = os.path.splitext(basename) png_file = filename + ".png" final_format = params['format'] params['format'] = 'png' new_fname = os.path.join(tmp_dir, png_file) fig.savefig(new_fname, **params) with Image.open(new_fname) as im: rgb_im = im.convert('RGB') # make sure the fname is ended with the right extension fname, _ = os.path.splitext(fname) fname += "." + final_format rgb_im.save(fname) else: raise err if self.ui.checkBox_open_after_export.isChecked(): # open with the system default application, this should work on all platforms webbrowser.open(fname) return fname # elif response == QtGu def get_size_inches_width(self): return self.ui.doubleSpinBox_width_inches.value() def get_size_inches_height(self): return self.ui.doubleSpinBox_height_inches.value() def _show_file_exist_message(self, fname, new_name): self._msg_box.setText( "<p>File \"{0}\" already exists. Do you want to overwrite the existing, or save to \"{1}\" instead?<\p>" .format(fname, new_name)) self._msg_box.exec_() def get_save_file_name(self): name = os.path.join(str(self.ui.comboBox_directory.currentText()), str(self.ui.comboBox_fileName.currentText())) return os.path.normpath(name) def get_savefig_params(self): params = { 'dpi': self.ui.spinBox_dpi.value(), 'orientation': self.get_orientation(), 'format': self.get_file_format()[0], 'transparent': self.get_transparent(), 'bbox_inches': self.get_bbox_inches() } return params def get_transparent(self): return self.ui.checkBox_transparent.isEnabled( ) and self.ui.checkBox_transparent.isChecked() def get_file_format(self): return IMAGE_FORMATS[self.ui.comboBox_fileType.currentIndex()] def get_bbox_inches(self): return 'tight' if self.ui.checkBox_tightBbox.isChecked() else None def get_orientation(self): return self._orientation[self.ui.comboBox_orientation.currentIndex()] def keyPressEvent(self, event): """Capture and ignore all key press events. This is used so that return key event does not trigger any button from the dialog. We need to allow the return key to be used in filters in the widget.""" if event.key() == QtCore.Qt.Key_Escape: # call reject if Escape is pressed. self.reject() pass