class SpinBoxEvent(qt_import.QObject): returnPressedSignal = qt_import.pyqtSignal() contextMenuSignal = qt_import.pyqtSignal() def eventFilter(self, obj, event): if event.type() == qt_import.QEvent.KeyPress: if event.key() in [qt_import.Qt.Key_Enter, qt_import.Qt.Key_Return]: self.returnPressedSignal.emit() elif event.type() == qt_import.QEvent.MouseButtonRelease: self.returnPressedSignal.emit() elif event.type() == qt_import.QEvent.ContextMenu: self.contextMenuSignal.emit() return False
def disconnect_hwobj(self, sender, signal, slot): signal = str(signal) if signal[0].isdigit(): pysignal = signal[0] == "9" signal = signal[1:] else: pysignal = True if isinstance(sender, HardwareObject): sender.disconnect(sender, signal, slot) return # workaround for PyQt lapse if hasattr(sender, "disconnectNotify"): sender.disconnect_notify(signal) if not isinstance(sender, qt_import.QObject): sender = emitter(sender) try: uid = ( sender, pysignal and qt_import.pyqtSignal(signal) or qt_import.pyqtSignal(signal), hash(slot), ) signal_slot_filter = self._signal_slot_filters[uid] except KeyError: getattr(sender, signal).disconnect(slot) # qt_import.QObject.disconnect(sender, # pysignal and # qt_import.QtCore.SIGNAL(signal) or # qt_import.QtCore.SIGNAL(signal), # slot) else: getattr(sender, signal).disconnect(signal_slot_filter) # qt_import.QObject.disconnect(sender, # pysignal and # qt_import.SIGNAL(signal) or # qt_import.SIGNAL(signal), # signal_slot_filter) del self._signal_slot_filters[uid] else: getattr(sender, signal).disconnect(signal_slot_filter)
def connect_hwobj(self, sender, signal, slot, instance_filter=False, should_cache=True): if sys.version_info > (3, 0): signal = str( signal.decode("utf8") if isinstance(signal, bytes) else signal) else: signal = str(signal) if signal[0].isdigit(): pysignal = signal[0] == "9" signal = signal[1:] else: pysignal = True if not isinstance(sender, qt_import.QObject): if isinstance(sender, HardwareObject): sender.connect(signal, slot) return else: _sender = emitter(sender) else: _sender = sender if instance_filter: self.connect_signal_slot_filter( _sender, pysignal and qt_import.pyqtSignal(signal) or qt_import.pyqtSignal(signal), slot, should_cache, ) else: # Porting to Qt5 getattr(_sender, signal).connect(slot)
class DuoStateButton(qt_import.QToolButton): commandExecuteSignal = qt_import.pyqtSignal(bool) def __init__(self, parent, caption): qt_import.QToolButton.__init__(self, parent) self.setToolButtonStyle(qt_import.Qt.ToolButtonTextUnderIcon) self.executing = False self.run_icon = None self.stop_icon = None self.standard_color = self.palette().color(qt_import.QPalette.Window) self.setText(caption) self.setSizePolicy(qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Fixed) self.clicked.connect(self.button_clicked) def set_icons(self, icon_run, icon_stop): self.run_icon = icons.load_icon(icon_run) self.stop_icon = icons.load_icon(icon_stop) if self.executing: self.setIcon(self.stop_icon) else: self.setIcon(self.run_icon) def button_clicked(self): self.commandExecuteSignal.emit(not self.executing) # if not self.executing: # self.setEnabled(False) def command_started(self): colors.set_widget_color(self, colors.LIGHT_YELLOW, qt_import.QPalette.Button) if self.stop_icon is not None: self.setIcon(self.stop_icon) self.executing = True self.setEnabled(True) def is_executing(self): return self.executing def command_done(self): self.executing = False colors.set_widget_color(self, self.standard_color, qt_import.QPalette.Button) if self.run_icon is not None: self.setIcon(self.run_icon) self.setEnabled(True) def command_failed(self): self.command_done()
class LogViewBrick(BaseWidget): """Views the log messages, either in a list or in separated tabs. In tab mode it is possible to send a feedback mail. The debug messages might me switched off (in either modes). """ TOOLTIPS = { "info": "Displays the progress of the requested operations", "feedback": "Submits a feedback email about this software", "details": "Detailed messages, including warnings and errors", "debug": "Debug messages; please disregard them", } incUnreadMessagesSignal = qt_import.pyqtSignal(int, bool) resetUnreadMessagesSignal = qt_import.pyqtSignal(bool) def __init__(self, *args): BaseWidget.__init__(self, *args) # Hardware objects ---------------------------------------------------- # Internal values ----------------------------------------------------- # Properties ---------------------------------------------------------- self.add_property("level", "combo", ("NOT SET", "INFO", "WARNING", "ERROR"), "NOT SET") self.add_property("showDebug", "boolean", True) self.add_property("appearance", "combo", ("list", "tabs"), "tabs") self.add_property("enableFeedback", "boolean", True) self.add_property("emailAddresses", "string", "") self.add_property("fromEmailAddress", "string", "") self.add_property("maxLogLines", "integer", -1) self.add_property("autoSwitchTabs", "boolean", False) self.add_property("myTabLabel", "string", "") # Signals ------------------------------------------------------------- self.define_signal("incUnreadMessagesSignal", ()) self.define_signal("resetUnreadMessagesSignal", ()) # Slots --------------------------------------------------------------- self.define_slot("clearLog", ()) self.define_slot("tabSelected", ()) # Graphic elements ---------------------------------------------------- self.tab_widget = qt_import.QTabWidget(self) self.details_log = CustomTreeWidget(self.tab_widget, "Errors and warnings") self.info_log = CustomTreeWidget(self.tab_widget, "Information") self.debug_log = CustomTreeWidget(self.tab_widget, "Debug") self.feedback_log = Submitfeedback(self.tab_widget, self["emailAddresses"], "Submit feedback") self.tab_widget.addTab(self.details_log, icons.load_icon("Caution"), "Errors and warnings") self.tab_widget.addTab(self.info_log, icons.load_icon("Inform"), "Information") self.tab_widget.addTab(self.debug_log, icons.load_icon("Hammer"), "Debug") self.tab_widget.addTab(self.feedback_log, icons.load_icon("Envelope"), "Submit feedback") # Layout -------------------------------------------------------------- _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.tab_widget) _main_vlayout.setSpacing(0) _main_vlayout.setContentsMargins(2, 2, 2, 2) # SizePolicies -------------------------------------------------------- self.setSizePolicy(qt_import.QSizePolicy.Minimum, qt_import.QSizePolicy.Expanding) # Qt signal/slot connections ------------------------------------------ # Other --------------------------------------------------------------- self.tab_levels = { logging.NOTSET: self.info_log, logging.DEBUG: self.info_log, logging.INFO: self.info_log, logging.WARNING: self.info_log, logging.ERROR: self.info_log, logging.CRITICAL: self.info_log, } self.filter_level = logging.NOTSET # Register to GUI log handler gui_log_handler.GUILogHandler().register(self) def run(self): # Register to GUI log handler self.tab_widget.currentChanged.connect(self.resetUnreadMessages) def clearLog(self): self.details_log.clear() self.info_log.clear() self.debug_log.clear() self.details_log.unread_messages = 0 self.info_log.unread_messages = 0 self.debug_log.unread_messages = 0 def tabSelected(self, tab_name): if self["appearance"] == "list": if tab_name == self["myTabLabel"]: self.resetUnreadMessagesSignal.emit(True) def append_log_record(self, record): rec_level = record.getLevel() if rec_level == logging.DEBUG and not self["showDebug"]: return else: if rec_level < self.filter_level: return tab = self.tab_levels[rec_level] tab.add_log_line(record) if self["appearance"] == "tabs": if self.tab_widget.currentWidget() != tab: if self["autoSwitchTabs"]: self.tab_widget.setCurrentWidget(tab) else: tab.unread_messages += 1 tab_label = "%s (%d)" % (tab.tab_label, tab.unread_messages) self.tab_widget.setTabText(self.tab_widget.indexOf(tab), tab_label) elif self["appearance"] == "list": self.incUnreadMessagesSignal.emit(1, True) def resetUnreadMessages(self, tab_index): selected_tab = self.tab_widget.widget(tab_index) selected_tab.unread_messages = 0 self.tab_widget.setTabText(tab_index, selected_tab.tab_label) def customEvent(self, event): if self.is_running(): self.append_log_record(event.record) def blockSignals(self, block): pass def property_changed(self, property_name, old_value, new_value): if property_name == "level": self.filter_level = logging.NOTSET if new_value == "INFO": self.filter_level = logging.INFO elif new_value == "WARNING": self.filter_level = logging.WARNING elif new_value == "ERROR": self.filter_level = logging.ERROR elif property_name == "showDebug": if self["appearance"] == "tabs": if not new_value: self.tab_widget.removeTab( self.tab_widget.indexOf(self.debug_log)) elif property_name == "emailAddresses": self.feedback_log.email_addresses = new_value elif property_name == "fromEmailAddress": self.feedback_log.from_email_address = new_value elif property_name == "enableFeedback": if self["appearance"] == "tabs": if not new_value: self.tab_widget.removeTab( self.tab_widget.indexOf(self.feedback_log)) elif property_name == "appearance": if new_value == "list": self.tab_levels = { logging.NOTSET: self.info_log, logging.DEBUG: self.info_log, logging.INFO: self.info_log, logging.WARNING: self.info_log, logging.ERROR: self.info_log, logging.CRITICAL: self.info_log, } self.tab_widget.removeTab( self.tab_widget.indexOf(self.details_log)) self.tab_widget.removeTab( self.tab_widget.indexOf(self.debug_log)) elif new_value == "tabs": self.tab_levels = { logging.NOTSET: self.details_log, logging.DEBUG: self.debug_log, logging.INFO: self.info_log, logging.WARNING: self.details_log, logging.ERROR: self.details_log, logging.CRITICAL: self.details_log, } elif property_name == "maxLogLines": self.details_log.set_max_log_lines(new_value) self.info_log.set_max_log_lines(new_value) self.debug_log.set_max_log_lines(new_value) else: BaseWidget.property_changed(self, property_name, old_value, new_value)
class ProposalBrick(BaseWidget): sessionSelected = qt_import.pyqtSignal(int, str, str, int, str, str, bool) setWindowTitle = qt_import.pyqtSignal(str) loggedIn = qt_import.pyqtSignal(bool) userGroupSaved = qt_import.pyqtSignal(str) NOBODY_STR = "<nobr><b>Login is required for collecting data!</b>" def __init__(self, *args): """ Proposal brick is used to authentificate current user. Brick can be used in two modes defined by ispyb hwobj - loginAsUser = True, Brick displays combobox with all proposals from ISPyB that are associated to the current user - loginAsUser = False. Brick displays combobox to choose proposal type and linedits to enter proposal number and password. In this case user is authentificated with LDAP and proposal from ISPyB is retrieved. """ BaseWidget.__init__(self, *args) # Hardware objects ---------------------------------------------------- self.local_login_hwobj = None # Internal values ----------------------------------------------------- self.login_as_user = None self.proposal = None self.person = None self.laboratory = None # self.sessionId=None self.inhouseProposal = None self.instance_server_hwobj = None self.secondary_proposals = [] # Properties ---------------------------------------------------------- self.add_property("titlePrefix", "string", "") self.add_property("autoSessionUsers", "string", "") self.add_property("codes", "string", "fx ifx ih im ix ls mx opid") self.add_property("secondaryProposals", "string", "") self.add_property("icons", "string", "") self.add_property("serverStartDelay", "integer", 500) # Signals ------------------------------------------------------------ self.define_signal("sessionSelected", ()) self.define_signal("setWindowTitle", ()) self.define_signal("loggedIn", ()) self.define_signal("userGroupSaved", ()) # Slots --------------------------------------------------------------- self.define_slot("setButtonEnabled", ()) self.define_slot("impersonateProposal", ()) # Graphic elements ---------------------------------------------------- self.main_gbox = qt_import.QGroupBox("ISPyB proposal", self) self.login_as_proposal_widget = qt_import.QWidget(self.main_gbox) code_label = qt_import.QLabel(" Code: ", self.login_as_proposal_widget) self.proposal_type_combox = qt_import.QComboBox(self.login_as_proposal_widget) self.proposal_type_combox.setEditable(True) self.proposal_type_combox.setFixedWidth(60) dash_label = qt_import.QLabel(" - ", self.login_as_proposal_widget) self.proposal_number_ledit = qt_import.QLineEdit(self.login_as_proposal_widget) self.proposal_number_ledit.setFixedWidth(60) password_label = qt_import.QLabel(" Password: "******"Login", self.login_as_proposal_widget) self.login_button.setFixedWidth(70) self.logout_button = qt_import.QPushButton("Logout", self.main_gbox) self.logout_button.hide() self.logout_button.setFixedWidth(70) self.login_as_proposal_widget.hide() self.login_as_user_widget = qt_import.QWidget(self.main_gbox) self.proposal_combo = qt_import.QComboBox(self.login_as_user_widget) self.user_group_widget = qt_import.QWidget(self.main_gbox) # self.title_label = QtGui.QLabel(self.user_group_widget) # self.title_label.setAlignment(QtCore.Qt.AlignCenter) self.user_group_label = qt_import.QLabel(" Group: ", self.user_group_widget) self.user_group_ledit = qt_import.QLineEdit(self.user_group_widget) self.user_group_ledit.setFixedSize(100, 27) self.user_group_save_button = qt_import.QToolButton(self.user_group_widget) self.user_group_save_button.setText("Set") self.user_group_save_button.setFixedHeight(27) self.saved_group = True # Layout -------------------------------------------------------------- _user_group_widget_hlayout = qt_import.QHBoxLayout(self.user_group_widget) _user_group_widget_hlayout.setSpacing(2) # _user_group_widget_hlayout.addWidget(self.title_label) _user_group_widget_hlayout.addWidget(self.user_group_label) _user_group_widget_hlayout.addWidget(self.user_group_ledit) _user_group_widget_hlayout.addWidget(self.user_group_save_button) _user_group_widget_hlayout.setContentsMargins(0, 0, 0, 0) self.user_group_widget.hide() _login_as_proposal_widget_layout = qt_import.QHBoxLayout( self.login_as_proposal_widget ) _login_as_proposal_widget_layout.addWidget(code_label) _login_as_proposal_widget_layout.addWidget(self.proposal_type_combox) _login_as_proposal_widget_layout.addWidget(dash_label) _login_as_proposal_widget_layout.addWidget(self.proposal_number_ledit) _login_as_proposal_widget_layout.addWidget(password_label) _login_as_proposal_widget_layout.addWidget(self.proposal_password_ledit) _login_as_proposal_widget_layout.addWidget(self.login_button) _login_as_proposal_widget_layout.setSpacing(2) _login_as_proposal_widget_layout.setContentsMargins(0, 0, 0, 0) _login_as_user_widget_layout = qt_import.QHBoxLayout(self.login_as_user_widget) _login_as_user_widget_layout.addWidget(self.proposal_combo) _login_as_user_widget_layout.setSpacing(2) _login_as_user_widget_layout.setContentsMargins(0, 0, 0, 0) _main_gboxlayout = qt_import.QHBoxLayout(self.main_gbox) _main_gboxlayout.addWidget(self.login_as_proposal_widget) _main_gboxlayout.addWidget(self.logout_button) _main_gboxlayout.addWidget(self.login_as_user_widget) _main_gboxlayout.addStretch() _main_gboxlayout.addWidget(self.user_group_widget) _main_gboxlayout.setSpacing(2) _main_gboxlayout.setContentsMargins(0, 0, 0, 0) _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.main_gbox) _main_vlayout.setSpacing(2) _main_vlayout.setContentsMargins(2, 2, 2, 2) # SizePolicies -------------------------------------------------------- # self.user_group_ledit # Qt signal/slot connections ------------------------------------------ self.proposal_password_ledit.returnPressed.connect(self.login) self.login_button.clicked.connect(self.login) self.logout_button.clicked.connect(self.logout_clicked) self.proposal_combo.activated.connect(self.proposal_combo_activated) self.user_group_save_button.clicked.connect(self.save_group) self.user_group_ledit.returnPressed.connect(self.save_group) self.user_group_ledit.textChanged.connect(self.user_group_changed) # Other --------------------------------------------------------------- colors.set_widget_color( self.proposal_number_ledit, colors.LIGHT_RED, qt_import.QPalette.Base ) colors.set_widget_color( self.proposal_password_ledit, colors.LIGHT_RED, qt_import.QPalette.Base ) def save_group(self): user_group = str(self.user_group_ledit.text()) pattern = r"^[a-zA-Z0-9_-]*$" valid = re.match(pattern, user_group, flags=0).group() == user_group if valid: self.saved_group = True colors.set_widget_color( self.user_group_ledit, colors.LIGHT_GREEN, qt_import.QPalette.Base ) msg = "User group set to: %s" % str(self.user_group_ledit.text()) logging.getLogger("GUI").info(msg) self.userGroupSaved.emit(self.user_group_ledit.text()) else: msg = "User group not valid, please enter a valid user group" logging.getLogger("GUI").info(msg) colors.set_widget_color( self.user_group_ledit, colors.LIGHT_RED, qt_import.QPalette.Base ) def user_group_changed(self, value): if self.saved_group: msg = "User group changed, press set to apply change" logging.getLogger("GUI").warning(msg) colors.set_widget_color( self.user_group_ledit, colors.LINE_EDIT_CHANGED, qt_import.QPalette.Base ) self.saved_group = False def customEvent(self, event): if self.is_running(): if event.type() == PROPOSAL_GUI_EVENT: try: method = event.method arguments = event.arguments except (Exception, diag): logging.getLogger().exception( "ProposalBrick2: problem in event! (%s)" % str(diag) ) except BaseException: logging.getLogger().exception("ProposalBrick2: problem in event!") else: # logging.getLogger().debug("ProposalBrick2: custom event method is %s" % method) if callable(method): try: method(*arguments) except Exception as diag: logging.getLogger().exception( "ProposalBrick2: uncaught exception! (%s)" % str(diag) ) except BaseException: logging.getLogger().exception( "ProposalBrick2: uncaught exception!" ) else: # logging.getLogger().debug("ProposalBrick2: custom event finished") pass else: logging.getLogger().warning( "ProposalBrick2: uncallable custom event!" ) # Enabled/disabled the login/logout button def setButtonEnabled(self, state): self.login_button.setEnabled(state) self.logout_button.setEnabled(state) def impersonateProposal(self, proposal_code, proposal_number): if BaseWidget.is_instance_user_id_inhouse(): self._do_login_as_proposal( proposal_code, proposal_number, None, HWR.beamline.lims.beamline_name, impersonate=True, ) else: logging.getLogger().debug( "ProposalBrick2: cannot impersonate unless logged as the inhouse user!" ) # Opens the logout dialog (modal); if the answer is OK then logout the user def logout_clicked(self): if ( qt_import.QMessageBox.question( self, "Confirm logout", "Press OK to logout.", qt_import.QMessageBox.Ok, qt_import.QMessageBox.Cancel, ) == qt_import.QMessageBox.Ok ): self.log_out() # Logout the user; reset the brick; changes from logout mode to login mode def log_out(self): # Reset brick info self.proposal_number_ledit.setText("") self.proposal = None # self.sessionId=None self.person = None self.laboratory = None # Change mode from logout to login if not self.login_as_user: self.login_as_proposal_widget.setEnabled(True) self.login_button.show() self.logout_button.hide() # self.title_label.hide() self.user_group_widget.hide() # resets active proposal self.reset_proposal() # self.proposalLabel.setText(ProposalBrick2.NOBODY_STR) # QToolTip.add(self.proposalLabel,"") # Emit signals clearing the proposal and session self.setWindowTitle.emit(self["titlePrefix"]) # self.sessionSelected.emit(None, None, None, None, None, None, None) self.loggedIn.emit(False) def reset_proposal(self): HWR.beamline.session.proposal_code = None HWR.beamline.session.session_id = None HWR.beamline.session.proposal_id = None HWR.beamline.session.proposal_number = None # Sets the current session; changes from login mode to logout mode def set_proposal(self, proposal, session): HWR.beamline.lims.enable() HWR.beamline.session.proposal_code = proposal["code"] HWR.beamline.session.session_id = session["sessionId"] HWR.beamline.session.proposal_id = proposal["proposalId"] HWR.beamline.session.proposal_number = proposal["number"] # Change mode if not self.login_as_user: self.login_button.hide() self.login_as_proposal_widget.setDisabled(True) self.logout_button.show() # Store info in the brick self.proposal = proposal code = proposal["code"].lower() if code == "": logging.getLogger().warning( "Using local login: the data collected won't be stored in the database" ) HWR.beamline.lims.disable() self.loggedIn.emit(False) else: msg = "Results in ISPyB will be stored under proposal %s%s - '%s'" % ( proposal["code"], str(proposal["number"]), proposal["title"], ) logging.getLogger("GUI").debug(msg) self.loggedIn.emit(True) def set_codes(self, codes): codes_list = codes.split() self.proposal_type_combox.clear() for cd in codes_list: self.proposal_type_combox.addItem(cd) def run(self): self.setEnabled(HWR.beamline.session is not None) self.login_as_user = HWR.beamline.lims.get_login_type() == "user" if self.login_as_user: self.login_as_user_widget.show() self.login_as_proposal_widget.hide() else: self.login_as_user_widget.hide() self.login_as_proposal_widget.show() # find if we are using dbconnection, etc. or not if not HWR.beamline.lims: self.login_as_proposal_widget.hide() self.login_button.hide() # self.title_label.setText("<nobr><b>%s</b></nobr>" % os.environ["USER"]) # self.title_label.show() self.user_group_widget.show() HWR.beamline.session.proposal_code = "" HWR.beamline.session.session_id = 1 HWR.beamline.session.proposal_id = "" HWR.beamline.session.proposal_number = "" self.setWindowTitle.emit(self["titlePrefix"]) # self.loggedIn.emit(False) # self.sessionSelected.emit(None, None, None, None, None, None, None) self.loggedIn.emit(True) self.sessionSelected.emit( HWR.beamline.session.session_id, str(os.environ["USER"]), 0, "", "", HWR.beamline.session.session_id, False, ) else: self.setWindowTitle.emit(self["titlePrefix"]) # self.sessionSelected.emit(None, None, None, None, None, None, None) self.loggedIn.emit(False) if self.login_as_user: if os.getenv("SUDO_USER"): user_name = os.getenv("SUDO_USER") else: user_name = os.getenv("USER") self._do_login_as_user(user_name) start_server_event = ProposalGUIEvent(self.start_servers, ()) qt_import.QApplication.postEvent(self, start_server_event) def start_servers(self): if self.instance_server_hwobj is not None: self.instance_server_hwobj.initialize_instance() def refuse_login(self, stat, message=None): if message is not None: if stat is False: icon = qt_import.QMessageBox.Critical elif stat is None: icon = qt_import.QMessageBox.Warning elif stat is True: icon = qt_import.QMessageBox.Information msg_dialog = qt_import.QMessageBox( "Register user", message, icon, qt_import.QMessageBox.Ok, qt_import.QMessageBox.NoButton, qt_import.QMessageBox.NoButton, self, ) s = self.font().pointSize() f = msg_dialog.font() f.setPointSize(s) msg_dialog.setFont(f) msg_dialog.updateGeometry() msg_dialog.show() self.setEnabled(True) def accept_login(self, proposal_dict, session_dict): self.set_proposal(proposal_dict, session_dict) self.setEnabled(True) def set_ispyb_down(self): msg_dialog = qt_import.QMessageBox( "Register user", "Couldn't contact " + "the ISPyB database server: you've been logged as the local user.\n" + "Your experiments' information will not be stored in ISPyB!", qt_import.QMessageBox.Warning, qt_import.QMessageBox.Ok, qt_import.QMessageBox.NoButton, qt_import.QMessageBox.NoButton, self, ) s = self.font().pointSize() f = msg_dialog.font() f.setPointSize(s) msg_dialog.setFont(f) msg_dialog.updateGeometry() msg_dialog.show() now = time.strftime("%Y-%m-%d %H:%M:S") prop_dict = {"code": "", "number": "", "title": "", "proposalId": ""} ses_dict = {"sessionId": "", "startDate": now, "endDate": now, "comments": ""} self.accept_login(prop_dict, ses_dict) # Handler for the Login button (check the password in LDAP) def login(self): self.saved_group = False colors.set_widget_color(self.user_group_ledit, colors.WHITE) self.user_group_ledit.setText("") self.setEnabled(False) if not self.login_as_user: prop_type = str(self.proposal_type_combox.currentText()) prop_number = str(self.proposal_number_ledit.text()) prop_password = str(self.proposal_password_ledit.text()) self.proposal_password_ledit.setText("") if prop_type == "" and prop_number == "": if self.local_login_hwobj is None: return self.refuse_login(False, "Local login not configured.") try: locallogin_password = self.local_login_hwobj.password except AttributeError: return self.refuse_login(False, "Local login not configured.") if prop_password != locallogin_password: return self.refuse_login(None, "Invalid local login password.") now = time.strftime("%Y-%m-%d %H:%M:S") prop_dict = {"code": "", "number": "", "title": "", "proposalId": ""} ses_dict = { "sessionId": "", "startDate": now, "endDate": now, "comments": "", } return self.accept_login(prop_dict, ses_dict) if HWR.beamline.lims is None: return self.refuse_login( False, "Not connected to the ISPyB database, unable to get proposal.", ) self._do_login_as_proposal( prop_type, prop_number, prop_password, HWR.beamline.lims.beamline_name ) def pass_control(self, has_control_id): pass def have_control(self, have_control): pass def property_changed(self, property_name, old_value, new_value): if property_name == "codes": self.set_codes(new_value) elif property_name == "localLogin": self.local_login_hwobj = self.get_hardware_object(new_value, optional=True) elif property_name == "instanceServer": if self.instance_server_hwobj is not None: self.disconnect( self.instance_server_hwobj, "passControl", self.pass_control ) self.disconnect( self.instance_server_hwobj, "haveControl", self.have_control ) self.instance_server_hwobj = self.get_hardware_object( new_value, optional=True ) if self.instance_server_hwobj is not None: self.connect( self.instance_server_hwobj, "passControl", self.pass_control ) self.connect( self.instance_server_hwobj, "haveControl", self.have_control ) elif property_name == "icons": icons_list = new_value.split() try: self.login_button.setIcon(icons.load_icon(icons_list[0])) except IndexError: pass try: self.logout_button.setIcon(icons.load_icon(icons_list[1])) except IndexError: pass elif property_name == "secondaryProposals": self.secondary_proposals = new_value.split() else: BaseWidget.property_changed(self, property_name, old_value, new_value) def _do_login_as_proposal( self, proposal_code, proposal_number, proposal_password, beamline_name, impersonate=False, ): # Get proposal and sessions logging.getLogger().debug("ProposalBrick: querying ISPyB database...") prop = HWR.beamline.lims.getProposal(proposal_code, proposal_number) # Check if everything went ok prop_ok = True try: prop_ok = prop["status"]["code"] == "ok" except KeyError: prop_ok = False if not prop_ok: self.set_ispyb_down() BaseWidget.set_status_info("ispyb", "error") else: self.select_proposal(prop) BaseWidget.set_status_info( "user", "%s%s@%s" % (proposal_code, str(proposal_number), beamline_name) ) BaseWidget.set_status_info("ispyb", "ready") def proposal_combo_activated(self, item_index): self.select_proposal(self.proposals[item_index]) def select_proposal(self, selected_proposal): beamline_name = HWR.beamline.lims.beamline_name proposal = selected_proposal["Proposal"] # person = selected_proposal['Person'] # laboratory = selected_proposal['Laboratory'] sessions = selected_proposal["Session"] # Check if there are sessions in the proposal todays_session = None if sessions is None or len(sessions) == 0: pass else: # Check for today's session for session in sessions: beamline = session["beamlineName"] start_date = "%s 00:00:00" % session["startDate"].split()[0] end_date = "%s 23:59:59" % session["endDate"].split()[0] try: start_struct = time.strptime(start_date, "%Y-%m-%d %H:%M:%S") except ValueError: pass else: try: end_struct = time.strptime(end_date, "%Y-%m-%d %H:%M:%S") except ValueError: pass else: start_time = time.mktime(start_struct) end_time = time.mktime(end_struct) current_time = time.time() # Check beamline name if beamline == beamline_name: # Check date if current_time >= start_time and current_time <= end_time: todays_session = session break if todays_session is None: is_inhouse = HWR.beamline.session.is_inhouse( proposal["code"], proposal["number"] ) if not is_inhouse: if BaseWidget.is_instance_role_client(): self.refuse_login( None, "You don't have a session scheduled for today!" ) return current_time = time.localtime() start_time = time.strftime("%Y-%m-%d 00:00:00", current_time) end_time = time.mktime(current_time) + 60 * 60 * 24 tomorrow = time.localtime(end_time) end_time = time.strftime("%Y-%m-%d 07:59:59", tomorrow) # Create a session new_session_dict = {} new_session_dict["proposalId"] = selected_proposal["Proposal"]["proposalId"] new_session_dict["startDate"] = start_time new_session_dict["endDate"] = end_time new_session_dict["beamlineName"] = beamline_name new_session_dict["scheduled"] = 0 new_session_dict["nbShifts"] = 3 new_session_dict["comments"] = "Session created by MXCuBE" session_id = HWR.beamline.lims.create_session(new_session_dict) new_session_dict["sessionId"] = session_id todays_session = new_session_dict localcontact = None else: session_id = todays_session["sessionId"] logging.getLogger().debug( "ProposalBrick: getting local contact for %s" % session_id ) localcontact = HWR.beamline.lims.get_session_local_contact(session_id) self.accept_login(selected_proposal["Proposal"], todays_session) def _do_login_as_user(self, user_name): logging.getLogger().debug("ProposalBrick: querying ISPyB database...") self.proposals = HWR.beamline.lims.get_proposals_by_user(user_name) if len(self.proposals) == 0: logging.getLogger("GUI").error( "No proposals for user %s found in ISPyB" % user_name ) self.set_ispyb_down() BaseWidget.set_status_info("ispyb", "error") else: self.proposal_combo.clear() proposal_tooltip = "Available proposals:" for proposal in self.proposals: proposal_info = "%s%s - %s" % ( proposal["Proposal"]["code"], proposal["Proposal"]["number"], proposal["Proposal"]["title"], ) self.proposal_combo.addItem(proposal_info) proposal_tooltip += "\n %s" % proposal_info if len(self.proposals) > 1: proposal_index = self.select_todays_proposal(self.proposals) self.proposal_combo.setEnabled(True) else: proposal_tooltip = "" proposal_index = 0 self.proposal_combo.setEnabled(False) self.select_proposal(self.proposals[proposal_index]) self.proposal_combo.setCurrentIndex(proposal_index) proposal_info = "%s%s - %s" % ( self.proposals[proposal_index]["Proposal"]["code"], self.proposals[proposal_index]["Proposal"]["number"], self.proposals[proposal_index]["Proposal"]["title"], ) proposal_tooltip += "\nSelected proposal:\n %s" % proposal_info self.proposal_combo.setToolTip(proposal_tooltip) logging.getLogger("GUI").info("ISPyB proposal: %s" % proposal_info) BaseWidget.set_status_info( "user", "%s@%s" % (user_name, HWR.beamline.lims.beamline_name) ) BaseWidget.set_status_info("ispyb", "ready") def select_todays_proposal(self, proposal_list): """Selects a proposal that is assigned for current day If no session found then returns first proposal """ for prop_index, proposal in enumerate(proposal_list): sessions = proposal["Session"] proposal_code_number = ( proposal["Proposal"]["code"] + proposal["Proposal"]["number"] ) if ( len(sessions) > 0 and not proposal_code_number in self.secondary_proposals ): # Check for today's session for session in sessions: beamline = session["beamlineName"] start_date = "%s 00:00:00" % session["startDate"].split()[0] end_date = "%s 23:59:59" % session["endDate"].split()[0] try: start_struct = time.strptime(start_date, "%Y-%m-%d %H:%M:%S") except ValueError: pass else: try: end_struct = time.strptime(end_date, "%Y-%m-%d %H:%M:%S") except ValueError: pass else: start_time = time.mktime(start_struct) end_time = time.mktime(end_struct) current_time = time.time() # Check beamline name if beamline == HWR.beamline.lims.beamline_name: # Check date if ( current_time >= start_time and current_time <= end_time ): return prop_index # If no proposal with valid session found then the last # proposal from the list is selected return len(proposal_list) - 1
class StepEditor(qt_import.QFrame): """ Brick to handle the step a motor position changes. Combines the +/- Step button and the widget to change the step value. Long description for Step Editor. """ (LEFT_LAYOUT, RIGHT_LAYOUT) = (0, 1) value_changed_signal = qt_import.pyqtSignal('double') clicked_signal = qt_import.pyqtSignal('double') def __init__(self, layout, initial_value, parent=None, title="", prefix=""): """" Constructor of StepEditor :param layout: select step value edit widget position :param initial_value: set motor initial position :param parent: parent widget :param title: step value edit widget's title :param prefix: + / - symbol fordward/backward step buttons """ super(StepEditor, self).__init__(parent) self.prefix = prefix self.value = initial_value # Graphic elements----------------------------------------------------- self.title_label = qt_import.QLabel(title) self.selection_box = qt_import.QFrame() self.edition_box = qt_import.QFrame() self.txt_new_value = qt_import.QLineEdit() self.cmd_ok = qt_import.QPushButton() self.cmd_ok.setIcon(icons.load_icon("button_ok_small")) self.cmd_ok.setFixedWidth(20) self.cmd_cancel = qt_import.QPushButton() self.cmd_cancel.setIcon(icons.load_icon("button_cancel_small")) self.cmd_cancel.setFixedWidth(20) self.edition_box.hide() self.title_label.hide() self.edition_box.setSizePolicy(qt_import.QSizePolicy.Fixed, qt_import.QSizePolicy.Fixed) self.double_validator = qt_import.QDoubleValidator(self.txt_new_value) self.double_validator.setNotation( qt_import.QDoubleValidator.StandardNotation) self.txt_new_value.setValidator(self.double_validator) # Layout -------------------------------------------------------------- self.main_vertical_layout = qt_import.QVBoxLayout() self.selection_box_layout = qt_import.QHBoxLayout() self.edition_box_layout = qt_import.QHBoxLayout() # widget's addition order defines their position self.edition_box_layout.addWidget(self.txt_new_value) self.edition_box_layout.addWidget(self.cmd_ok) self.edition_box_layout.addWidget(self.cmd_cancel) self.edition_box.setLayout(self.edition_box_layout) self.selection_box_layout.addWidget(self.edition_box) if layout == StepEditor.RIGHT_LAYOUT: self.cmd_select_value = qt_import.QPushButton(prefix + str(initial_value)) self.cmd_edit_value = qt_import.QPushButton("...") self.selection_box_layout.addWidget(self.cmd_select_value) self.selection_box_layout.addWidget(self.cmd_edit_value) else: self.cmd_edit_value = qt_import.QPushButton("...") self.cmd_select_value = qt_import.QPushButton(prefix + str(initial_value)) self.selection_box_layout.addWidget(self.cmd_edit_value) self.selection_box_layout.addWidget(self.cmd_select_value) self.selection_box.setLayout(self.selection_box_layout) self.main_vertical_layout.addWidget(self.title_label) self.main_vertical_layout.addWidget(self.selection_box) self.setLayout(self.main_vertical_layout) self.cmd_select_value.setAutoDefault(False) # Qt signal/slot connections ----------------------------------------- self.cmd_select_value.clicked.connect(self.cmd_select_value_clicked) self.cmd_edit_value.clicked.connect(self.cmd_edit_values_clicked) self.txt_new_value.returnPressed.connect(self.validate_new_value) self.cmd_ok.clicked.connect(self.validate_new_value) self.cmd_cancel.clicked.connect(self.end_edit) def set_title(self, title): """Set step value edit widget's title.""" self.title_label.setText(title) def set_prefix(self, prefix): """Set prefix to step forward/backward buttons.""" self.prefix = prefix self.cmd_select_value.setText(self.prefix + str(self.value)) def set_value(self, value): """Set value step forward/backward buttons.""" self.value = value self.cmd_select_value.setText(self.prefix + str(value)) def allow_change_value(self, allow): """Show/hide step value edit widget.""" if allow: self.cmd_edit_value.show() else: self.cmd_edit_value.hide() def cmd_select_value_clicked(self): """Emit step value edited signal.""" self.clicked_signal.emit(self.value) def cmd_edit_values_clicked(self): """Open step value edit widget.""" self.cmd_edit_value.hide() self.cmd_select_value.hide() self.edition_box.show() self.title_label.show() self.txt_new_value.setText(str(self.value)) self.txt_new_value.selectAll() self.txt_new_value.setFocus() def end_edit(self): """Close step value edit widget.""" self.cmd_edit_value.show() self.cmd_select_value.show() self.title_label.hide() self.edition_box.hide() self.value_changed_signal.emit(self.value) def validate_new_value(self): """Validate entered new value.""" try: self.value = float(str(self.txt_new_value.text())) except BaseException: logging.getLogger().error("%s is not a valid float value", self.txtNewValue.text()) else: self.cmd_select_value.setText(self.prefix + str(self.value)) self.end_edit()
class PlotWidget(qt_import.QWidget): mouseMovedSignal = qt_import.pyqtSignal(float, float) mouseClickedSignal = qt_import.pyqtSignal(float, float) mouseDoubleClickedSignal = qt_import.pyqtSignal(float, float) mouseLeftSignal = qt_import.pyqtSignal() def __init__(self, parent=None): qt_import.QWidget.__init__(self, parent) self.im = None self.mpl_canvas = MplCanvas(self) self.colorbar = None # self.ntb = NavigationToolbar(self.mpl_canvas, self) self.selection_xrange = None self.selection_span = None self.mouse_clicked = None _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.mpl_canvas) # _main_vlayout.addWidget(self.ntb) _main_vlayout.setSpacing(2) _main_vlayout.setContentsMargins(0, 0, 0, 0) self.setSizePolicy(qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Expanding) # self.mpl_canvas.axes.grid(True) self.mpl_canvas.axes.grid(color="r") self.mpl_canvas.fig.canvas.mpl_connect("button_press_event", self.button_pressed) self.mpl_canvas.fig.canvas.mpl_connect("button_release_event", self.mouse_released) self.mpl_canvas.fig.canvas.mpl_connect("motion_notify_event", self.motion_notify_event) # self.setFixedSize(1000, 700) def button_pressed(self, mouse_event): dbl_click = False if hasattr(mouse_event, "dblclick"): dbl_click = mouse_event.dblclick #if mouse_event.xdata and mouse_event.ydata: if True: if dbl_click: self.mouseDoubleClickedSignal.emit(mouse_event.xdata, mouse_event.ydata) else: self.mouse_clicked = True self.mouseClickedSignal.emit(mouse_event.xdata, mouse_event.ydata) def mouse_released(self, mouse_event): self.mouse_clicked = False def motion_notify_event(self, mouse_event): if mouse_event.xdata and mouse_event.ydata: self.mouseMovedSignal.emit(mouse_event.xdata, mouse_event.ydata) else: self.mouseLeftSignal.emit() if self.selection_xrange and mouse_event.xdata: do_update = False (x_start, x_end) = self.mpl_canvas.axes.get_xlim() delta = abs((x_end - x_start) / 50.0) if abs(self.selection_xrange[0] - mouse_event.xdata) < delta: qt_import.QApplication.setOverrideCursor( qt_import.QCursor(qt_import.Qt.SizeHorCursor)) if self.mouse_clicked: self.selection_xrange[0] = mouse_event.xdata do_update = True elif abs(self.selection_xrange[1] - mouse_event.xdata) < delta: qt_import.QApplication.setOverrideCursor( qt_import.QCursor(qt_import.Qt.SizeHorCursor)) if self.mouse_clicked: self.selection_xrange[1] = mouse_event.xdata do_update = True if do_update: self.selection_span.set_xy([ [self.selection_xrange[0], 0], [self.selection_xrange[0], 1], [self.selection_xrange[1], 1], [self.selection_xrange[1], 0], [self.selection_xrange[0], 0], ]) self.mpl_canvas.fig.canvas.draw() self.mpl_canvas.fig.canvas.flush_events() def plot_result(self, result, aspect=None): if not aspect: aspect = "auto" if self.im is None: self.im = self.mpl_canvas.axes.imshow( result, interpolation="none", aspect="auto", extent=[0, result.shape[1], 0, result.shape[0]], ) self.im.set_cmap("hot") else: self.im.set_data(result) self.im.autoscale() self.mpl_canvas.fig.canvas.draw() self.mpl_canvas.fig.canvas.flush_events() if result.max() > 0 and self.colorbar is None: self.add_colorbar() def get_current_coord(self): return self.mpl_canvas.get_mouse_coord() def set_x_axis_limits(self, limits): self.mpl_canvas.axes.set_xlim(limits) def set_y_axis_limits(self, limits): self.mpl_canvas.axes.set_ylim(limits) def set_ytick_labels(self, labels): self.mpl_canvas.axes.set_yticklabels(labels) self.mpl_canvas.fig.canvas.draw() self.mpl_canvas.fig.canvas.flush_events() def set_yticks(self, ticks): self.mpl_canvas.axes.set_yticks(ticks) self.mpl_canvas.fig.canvas.draw() self.mpl_canvas.fig.canvas.flush_events() def add_colorbar(self): if self.colorbar: self.colorbar.remove() divider = make_axes_locatable(self.mpl_canvas.axes) cax = divider.append_axes("right", size=0.3, pad=0.05) cax.tick_params(axis="x", labelsize=8) cax.tick_params(axis="y", labelsize=8) self.colorbar = plt.colorbar(self.im, cax=cax) def clear(self): self.im = None self.selection_xrange = None self.selection_span = None self.mpl_canvas.clear() if self.colorbar: self.colorbar.remove() self.colorbar = None def hide_all_curves(self): self.mpl_canvas.hide_curves() def show_curve(self, name): self.mpl_canvas.show_curve(name) self.mpl_canvas.refresh() def adjust_axes(self, name): self.mpl_canvas.adjust_axes(name) self.mpl_canvas.refresh() def add_curve( self, name, y_axis_array, x_axis_array=None, label=None, linestyle="-", color="blue", marker=None, ytick_labels=None, ): self.mpl_canvas.plot_curve( name, y_axis_array, x_axis_array=None, label=None, linestyle=linestyle, color=color, marker=marker, ) if x_axis_array: self.set_x_axis_limits((x_axis_array.min(), x_axis_array.max())) if ytick_labels: self.set_ytick_labels(ytick_labels) def update_curves(self, data): self.mpl_canvas.update_curves(data) def enable_selection_range(self): (x_start, x_end) = self.mpl_canvas.axes.get_xlim() offset = abs((x_end - x_start) / 10.0) self.selection_xrange = [x_start + offset, x_end - offset] self.selection_span = self.mpl_canvas.axes.axvspan( self.selection_xrange[0], self.selection_xrange[1], facecolor="g", alpha=0.3) self.mpl_canvas.refresh() def enable_legend(self): self.mpl_canvas.axes.legend() self.mpl_canvas.refresh()
class PeriodicTableWidget(qt_import.QWidget): elementEdgeSelectedSignal = qt_import.pyqtSignal(str, str) def __init__(self, parent=None, name=None, fl=0): qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) self.selected_element = None self.selected_edge = "L3" # Signals ------------------------------------------------------------ # Slots --------------------------------------------------------------- # Graphic elements ---------------------------------------------------- if PYMCA_IMPORTED: self.periodic_table = CustomPeriodicTable(self) else: self.periodic_elements_combo = qt_import.QComboBox(self) self.periodic_elements_combo.setFixedWidth(100) self.edge_widget = qt_import.QWidget(self) edge_label = qt_import.QLabel("Edge:", self.edge_widget) self.edge_combo = qt_import.QComboBox(self.edge_widget) # Layout -------------------------------------------------------------- _edge_hlayout = qt_import.QHBoxLayout(self.edge_widget) if not PYMCA_IMPORTED: _edge_hlayout.addWidget(self.periodic_elements_combo) _edge_hlayout.addWidget(edge_label) _edge_hlayout.addWidget(self.edge_combo) _edge_hlayout.addStretch(0) _edge_hlayout.setSpacing(2) _edge_hlayout.setContentsMargins(0, 0, 0, 0) _main_vlayout = qt_import.QVBoxLayout(self) if PYMCA_IMPORTED: _main_vlayout.addWidget(self.periodic_table, qt_import.Qt.AlignHCenter) _main_vlayout.addWidget(self.edge_widget) _main_vlayout.addStretch(0) _main_vlayout.setSpacing(2) _main_vlayout.setContentsMargins(2, 2, 2, 2) # SizePolicies -------------------------------------------------------- # Qt signal/slot connections ------------------------------------------ if PYMCA_IMPORTED: self.periodic_table.edgeSelectedSignal.connect(self.edge_selected) else: self.periodic_elements_combo.activated.connect(self.element_combo_activated) self.edge_combo.addItem("K") self.edge_combo.activated.connect(self.edge_combo_activated) self.edge_combo.setEnabled(False) # Other --------------------------------------------------------------- def element_combo_activated(self, element): self.selected_element = str(self.periodic_elements_combo.currentText()) self.selected_edge = str(self.edge_combo.currentText()) self.elementEdgeSelectedSignal.emit(self.selected_element, self.selected_edge) def edge_selected(self, element, edge): self.selected_element = str(element) self.selected_edge = str(edge) self.edge_widget.setEnabled(self.selected_edge != "K") self.edge_combo.clear() if edge == "K": edge_list = "K" else: edge_list = ("L1", "L2", "L3") for item in edge_list: self.edge_combo.addItem(item) self.edge_combo.setCurrentIndex(edge_list.index(item)) self.edge_combo.setEnabled(self.edge_combo.count() > 1) self.elementEdgeSelectedSignal.emit(self.selected_element, self.selected_edge) def set_current_element_edge(self, element, edge): if PYMCA_IMPORTED: self.periodic_table.table_element_clicked(element, edge) def get_selected_element_edge(self): return self.selected_element, self.selected_edge def edge_combo_activated(self, item_index): self.selected_edge = str(self.edge_combo.currentText()) self.elementEdgeSelectedSignal.emit(self.selected_element, self.selected_edge) def set_elements(self, elements): if PYMCA_IMPORTED: self.periodic_table.setElements(elements) else: for element in elements: self.periodic_elements_combo.addItem(element["symbol"])
class GphlDataDialog(qt_import.QDialog): continueClickedSignal = qt_import.pyqtSignal() def __init__(self, parent=None, name=None, fl=0): qt_import.QDialog.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) # Internal variables -------------------------------------------------- # AsyncResult to return values self._async_result = None # Layout qt_import.QVBoxLayout(self) main_layout = self.layout() main_layout.setSpacing(10) main_layout.setMargin(6) self.setSizePolicy(qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Expanding) self.setWindowTitle("GPhL Workflow parameters") # Info box self.info_gbox = qt_import.QGroupBox("Info", self) qt_import.QVBoxLayout(self.info_gbox) main_layout.addWidget(self.info_gbox) self.info_text = qt_import.QTextEdit(self.info_gbox) self.info_text.setFont(qt_import.QFont("Courier")) self.info_text.setReadOnly(True) self.info_gbox.layout().addWidget(self.info_text) # Special parameter box self.cplx_gbox = qt_import.QGroupBox("Indexing solution", self) qt_import.QVBoxLayout(self.cplx_gbox) main_layout.addWidget(self.cplx_gbox) self.cplx_gbox.setSizePolicy(qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Expanding) self.cplx_widget = None # Parameter box self.parameter_gbox = qt_import.QGroupBox("Parameters", self) main_layout.addWidget(self.parameter_gbox) self.parameter_gbox.setSizePolicy(qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Expanding) self.params_widget = None # Button bar button_layout = qt_import.QHBoxLayout(None) hspacer = qt_import.QSpacerItem(1, 20, qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Minimum) button_layout.addItem(hspacer) self.continue_button = qt_import.QPushButton("Continue", self) button_layout.addWidget(self.continue_button) self.cancel_button = qt_import.QPushButton("Abort", self) button_layout.addWidget(self.cancel_button) main_layout.addLayout(button_layout) self.continue_button.clicked.connect(self.continue_button_click) self.cancel_button.clicked.connect(self.cancel_button_click) self.resize( qt_import.QSize(1018, 472).expandedTo(self.minimumSizeHint())) # self.clearWState(qt_import.WState_Polished) def continue_button_click(self): result = {} if self.parameter_gbox.isVisible(): result.update(self.params_widget.get_parameters_map()) if self.cplx_gbox.isVisible(): result["_cplx"] = self.cplx_widget.get_value() self.accept() self._async_result.set(result) self._async_result = None def cancel_button_click(self): self.reject() HWR.beamline.gphl_workflow.abort("Manual abort") def open_dialog(self, field_list, async_result): self._async_result = async_result # get special parameters parameters = [] info = None cplx = None for dd0 in field_list: if info is None and dd0.get("variableName") == "_info": # Info text - goes to info_gbox info = dd0 elif cplx is None and dd0.get("variableName") == "_cplx": # Complex parameter - goes to cplx_gbox cplx = dd0 else: parameters.append(dd0) # Info box if info is None: self.info_text.setText("") self.info_gbox.setTitle("Info") self.info_gbox.hide() else: self.info_text.setText(info.get("defaultValue")) self.info_gbox.setTitle(info.get("uiLabel")) self.info_gbox.show() # Complex box if self.cplx_widget: self.cplx_widget.close() if cplx is None: self.cplx_gbox.hide() else: if cplx.get("type") == "selection_table": self.cplx_widget = SelectionTable(self.cplx_gbox, "cplx_widget", cplx["header"]) self.cplx_gbox.layout().addWidget(self.cplx_widget) self.cplx_gbox.setTitle(cplx.get("uiLabel")) for ii, values in enumerate(cplx["defaultValue"]): self.cplx_widget.populateColumn( ii, values, colours=cplx.get("colours")) self.cplx_gbox.show() else: raise NotImplementedError( "GPhL complex widget type %s not recognised for parameter _cplx" % repr(cplx.get("type"))) # parameters widget if self.params_widget is not None: self.params_widget.close() self.params_widget = None if parameters: self.params_widget = FieldsWidget(fields=parameters, parent=self.parameter_gbox) values = {} for dd0 in field_list: name = dd0["variableName"] value = dd0.get("defaultValue") if value is not None: dd0[name] = value self.params_widget.set_values(values) self.parameter_gbox.show() else: self.parameter_gbox.hide() self.show() self.setEnabled(True)
class _Bar(qt_import.QWidget): clickedValue = qt_import.pyqtSignal(int) def __init__(self, steps, *args, **kwargs): super(_Bar, self).__init__(*args, **kwargs) self.setSizePolicy(qt_import.QSizePolicy.MinimumExpanding, qt_import.QSizePolicy.MinimumExpanding) if isinstance(steps, list): # list of colours. self.n_steps = len(steps) self.steps = steps elif isinstance(steps, int): # int number of bars, defaults to red. self.n_steps = steps self.steps = ['red'] * steps else: raise TypeError('steps must be a list or int') self._bar_solid_percent = 0.8 self._background_color = qt_import.QColor('black') self._padding = 4.0 # n-pixel gap around edge. def paintEvent(self, e): painter = qt_import.QPainter(self) brush = qt_import.QBrush() brush.setColor(self._background_color) brush.setStyle(qt_import.Qt.SolidPattern) rect = qt_import.QRect(0, 0, painter.device().width(), painter.device().height()) painter.fillRect(rect, brush) # Get current state. parent = self.parent() vmin, vmax = parent.minimum(), parent.maximum() value = parent.value() # Define our canvas. d_height = painter.device().height() - (self._padding * 2) d_width = painter.device().width() - (self._padding * 2) # Draw the bars. step_size = d_height / self.n_steps bar_height = step_size * self._bar_solid_percent bar_spacer = step_size * (1 - self._bar_solid_percent) / 2 # Calculate the y-stop position, from the value in range. pc = (value - vmin) / (vmax - vmin) n_steps_to_draw = int(pc * self.n_steps) for n in range(n_steps_to_draw): brush.setColor(qt_import.QColor(self.steps[n])) rect = qt_import.QRect( self._padding, self._padding + d_height - ((1 + n) * step_size) + bar_spacer, d_width, bar_height) painter.fillRect(rect, brush) painter.end() def sizeHint(self): return qt_import.QSize(40, 120) def _trigger_refresh(self): self.update() def _calculate_clicked_value(self, e): parent = self.parent() vmin, vmax = parent.minimum(), parent.maximum() d_height = self.size().height() + (self._padding * 2) step_size = d_height / self.n_steps click_y = e.y() - self._padding - step_size / 2 pc = (d_height - click_y) / d_height value = vmin + pc * (vmax - vmin) self.clickedValue.emit(value) def mouseMoveEvent(self, e): self._calculate_clicked_value(e) def mousePressEvent(self, e): self._calculate_clicked_value(e)
class DataPathWidget(qt_import.QWidget): pathTemplateChangedSignal = qt_import.pyqtSignal() def __init__(self, parent=None, name="", fl=0, data_model=None, layout=None): qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) self.parent = parent # Hardware objects ---------------------------------------------------- # Internal variables -------------------------------------------------- self._base_image_dir = "" self._base_process_dir = "" self.path_conflict_state = False self.enable_macros = False if data_model is None: self._data_model = queue_model_objects.PathTemplate() else: self._data_model = data_model self._data_model_pm = DataModelInputBinder(self._data_model) # Graphic elements ---------------------------------------------------- if layout == "vertical": self.data_path_layout = qt_import.load_ui_file( "data_path_widget_vertical_layout.ui" ) else: self.data_path_layout = qt_import.load_ui_file( "data_path_widget_horizontal_layout.ui" ) # Layout -------------------------------------------------------------- _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.data_path_layout) _main_vlayout.setSpacing(0) _main_vlayout.setContentsMargins(0, 0, 0, 0) # Qt signal/slot connections ------------------------------------------ self.data_path_layout.prefix_ledit.textChanged.connect( self._prefix_ledit_change ) self.data_path_layout.run_number_ledit.textChanged.connect( self._run_number_ledit_change ) self.data_path_layout.browse_button.clicked.connect(self._browse_clicked) self.data_path_layout.folder_ledit.textChanged.connect( self._folder_ledit_change ) self.data_path_layout.compression_cbox.clicked.connect( self._compression_toggled ) # Other --------------------------------------------------------------- self._data_model_pm.bind_value_update( "base_prefix", self.data_path_layout.prefix_ledit, str, None ) self._data_model_pm.bind_value_update( "run_number", self.data_path_layout.run_number_ledit, int, qt_import.QIntValidator(0, 1000, self), ) self._data_model_pm.bind_value_update( "compression", self.data_path_layout.compression_cbox, bool, None ) def set_base_image_directory(self, base_image_dir): self._base_image_dir = base_image_dir def set_base_process_directory(self, base_process_dir): self._base_process_dir = base_process_dir def _browse_clicked(self): file_dialog = qt_import.QFileDialog(self) file_dialog.setNameFilter("%s*" % self._base_image_dir) selected_dir = str( file_dialog.getExistingDirectory( self, "Select a directory", str(self._base_image_dir), qt_import.QFileDialog.ShowDirsOnly ) ) if selected_dir is not None and len(selected_dir) > 0 and selected_dir.startswith(self._base_image_dir): self.set_directory(selected_dir) else: msg = "Selected directory do not start " +\ "with the base directory %s" % self._base_image_dir logging.getLogger("GUI").error(msg) def _prefix_ledit_change(self, new_value): cursor_pos = self.data_path_layout.prefix_ledit.cursorPosition() if len(new_value) > 0: available_chars = ( string.ascii_lowercase + string.ascii_uppercase + string.digits + "-_" ) if self.enable_macros: available_chars += "%" new_value = "".join(i for i in str(new_value) if i in available_chars) new_value = new_value.replace("\\", "") if len(new_value) > 50: logging.getLogger("GUI").error( "Current prefix is to long (max 50 characters are allowed)" ) new_value = new_value[:-1] self.data_path_layout.prefix_ledit.setText(new_value) self.data_path_layout.prefix_ledit.setCursorPosition(cursor_pos) self._data_model.base_prefix = str(new_value) self.update_file_name() self.pathTemplateChangedSignal.emit() def _run_number_ledit_change(self, new_value): if str(new_value).isdigit(): self._data_model.run_number = int(new_value) self.data_path_layout.run_number_ledit.setText(str(new_value)) self.update_file_name() self.pathTemplateChangedSignal.emit() else: # self.data_path_layout.run_number_ledit.setText(str(self._data_model.run_number)) colors.set_widget_color( self.data_path_layout.folder_ledit, colors.LIGHT_YELLOW ) def _folder_ledit_change(self, new_value): base_image_dir = self._base_image_dir base_proc_dir = self._base_process_dir new_sub_dir = str(new_value).strip(" ") cursor_pos = self.data_path_layout.folder_ledit.cursorPosition() if len(new_value) > 0: available_chars = ( string.ascii_lowercase + string.ascii_uppercase + string.digits + "-_/" ) if self.enable_macros: available_chars += "%" new_value = "".join(i for i in str(new_value) if i in available_chars) # new_value = new_value.replace("\\", "") new_sub_dir = str(new_value).strip(" ") self.data_path_layout.folder_ledit.setText(new_value) self.data_path_layout.folder_ledit.setCursorPosition(cursor_pos) if len(new_sub_dir) > 0: if new_sub_dir[0] == os.path.sep: new_sub_dir = new_sub_dir[1:] new_image_directory = os.path.join(base_image_dir, str(new_sub_dir)) new_proc_dir = os.path.join(base_proc_dir, str(new_sub_dir)) else: new_image_directory = base_image_dir new_proc_dir = base_proc_dir self._data_model.directory = new_image_directory self._data_model.process_directory = new_proc_dir colors.set_widget_color(self.data_path_layout.folder_ledit, colors.WHITE) self.pathTemplateChangedSignal.emit() def _compression_toggled(self, state): if hasattr(self.parent, "_tree_brick"): if self.parent._tree_brick: self.parent._tree_brick.compression_state = state queue_model_objects.Characterisation.set_char_compression(state) self._data_model.compression = state self.update_file_name() self.pathTemplateChangedSignal.emit() def update_file_name(self): """ updates filename if prefix or run number changed at start values are initalized before precision is set. so a check for isdigit is done to be on the safe side """ if str(self._data_model.precision).isdigit(): file_name = self._data_model.get_image_file_name() file_name = file_name.replace( "%" + str(self._data_model.precision) + "d", int(self._data_model.precision) * "#", ) file_name = file_name.strip(" ") self.data_path_layout.file_name_value_label.setText(file_name) def set_data_path(self, path): (dir_name, file_name) = os.path.split(path) if self._data_model.precision: precision = self._data_model.precision else: precision = "5" self.set_directory(dir_name) file_name = file_name.replace( "%" + str(self._data_model.precision) + "d", int(precision) * "#", ) self.data_path_layout.file_name_value_label.setText(file_name) def set_directory(self, directory): self._data_model.directory = str(directory) if len(directory.split("/")) != len(self._base_image_dir.split("/")): dir_parts = directory.split("/") sub_dir = os.path.join(*dir_parts[len(self._base_image_dir.split("/")) :]) self.data_path_layout.folder_ledit.setText(sub_dir) else: self.data_path_layout.folder_ledit.setText("") self._data_model.directory = self._base_image_dir self.data_path_layout.base_path_ledit.setText(self._base_image_dir) # def set_run_number(self, run_number): # """ # Descript. : # """ # self._data_model.run_number = int(run_number) # self.data_path_layout.run_number_ledit.setText(str(run_number)) def set_prefix(self, base_prefix): self._data_model.base_prefix = str(base_prefix) self.data_path_layout.prefix_ledit.setText(str(base_prefix)) file_name = self._data_model.get_image_file_name() file_name = file_name.replace( "%" + str(self._data_model.precision) + "d", int(self._data_model.precision) * "#", ) self.data_path_layout.file_name_value_label.setText(file_name) def update_data_model(self, data_model): self._data_model = data_model self.set_data_path(data_model.get_image_path()) self._data_model_pm.set_model(data_model) self.data_path_layout.browse_button.setEnabled(os.path.exists(self._base_image_dir)) def indicate_path_conflict(self, conflict): if conflict: colors.set_widget_color( self.data_path_layout.prefix_ledit, colors.LIGHT_RED, qt_import.QPalette.Base, ) colors.set_widget_color( self.data_path_layout.run_number_ledit, colors.LIGHT_RED, qt_import.QPalette.Base, ) colors.set_widget_color( self.data_path_layout.folder_ledit, colors.LIGHT_RED, qt_import.QPalette.Base, ) logging.getLogger("GUI").error( "The current path settings will overwrite data " + "from another task. Correct the problem before " + "adding to queue" ) else: # We had a conflict previous, but its corrected now ! if self.path_conflict_state: logging.getLogger("GUI").info("Path valid") colors.set_widget_color( self.data_path_layout.prefix_ledit, colors.WHITE, qt_import.QPalette.Base, ) colors.set_widget_color( self.data_path_layout.run_number_ledit, colors.WHITE, qt_import.QPalette.Base, ) colors.set_widget_color( self.data_path_layout.folder_ledit, colors.WHITE, qt_import.QPalette.Base, ) self.path_conflict_state = conflict
class BaseWidget(connectable.Connectable, qt_import.QFrame): """Base class for MXCuBE bricks""" ( INSTANCE_ROLE_UNKNOWN, INSTANCE_ROLE_SERVER, INSTANCE_ROLE_SERVERSTARTING, INSTANCE_ROLE_CLIENT, INSTANCE_ROLE_CLIENTCONNECTING, ) = (0, 1, 2, 3, 4) (INSTANCE_MODE_UNKNOWN, INSTANCE_MODE_MASTER, INSTANCE_MODE_SLAVE) = (0, 1, 2) ( INSTANCE_LOCATION_UNKNOWN, INSTANCE_LOCATION_LOCAL, INSTANCE_LOCATION_INHOUSE, INSTANCE_LOCATION_INSITE, INSTANCE_LOCATION_EXTERNAL, ) = (0, 1, 2, 3, 4) ( INSTANCE_USERID_UNKNOWN, INSTANCE_USERID_LOGGED, INSTANCE_USERID_INHOUSE, INSTANCE_USERID_IMPERSONATE, ) = (0, 1, 2, 3) (INSTANCE_MIRROR_UNKNOWN, INSTANCE_MIRROR_ALLOW, INSTANCE_MIRROR_PREVENT) = ( 0, 1, 2, ) _run_mode = False _instance_role = INSTANCE_ROLE_UNKNOWN _instance_mode = INSTANCE_MODE_UNKNOWN _instance_location = INSTANCE_LOCATION_UNKNOWN _instance_user_id = INSTANCE_USERID_UNKNOWN _instance_mirror = INSTANCE_MIRROR_UNKNOWN _filter_installed = False _events_cache = {} _menu_background_color = None _menubar = None _toolbar = None _statusbar = None _warning_box = None _progressbar = None _progress_dialog = None _application_event_filter = InstanceEventFilter(None) widgetSynchronizeSignal = qt_import.pyqtSignal([]) @staticmethod def set_run_mode(mode): if mode: BaseWidget._run_mode = True for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): widget.__run() try: widget.set_expert_mode(False) except BaseException: logging.getLogger().exception( "Could not set %s to user mode", widget.name()) else: BaseWidget._run_mode = False for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): widget.__stop() try: widget.set_expert_mode(True) except Exception as ex: logging.getLogger().exception( "Could not set %s to expert mode: %s" % (str(widget), str(ex))) @staticmethod def is_running(): return BaseWidget._run_mode @staticmethod def update_menu_bar_color(enable_checkbox=None): """Not a direct way how to change menubar color It is now done by changing stylesheet """ color = None if BaseWidget._menubar is not None: BaseWidget._menubar.parent.update_instance_caption("") if BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER: if (BaseWidget._instance_user_id == BaseWidget.INSTANCE_USERID_IMPERSONATE): color = "lightBlue" else: color = "rgb(204,255,204)" elif BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_SLAVE: BaseWidget._menubar.parent.update_instance_caption( " : slave instance (all controls are disabled)") if (BaseWidget._instance_role == BaseWidget.INSTANCE_ROLE_CLIENTCONNECTING): color = "rgb(255,204,204)" elif BaseWidget._instance_user_id == BaseWidget.INSTANCE_USERID_UNKNOWN: color = "rgb(255, 165, 0)" else: color = "yellow" if color is not None: BaseWidget._menubar.set_color(color) @staticmethod def set_instance_mode(mode): BaseWidget._instance_mode = mode for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): widget._instance_mode_changed(mode) if widget["instanceAllowAlways"]: widget.setEnabled(True) else: widget.setEnabled(mode == BaseWidget.INSTANCE_MODE_MASTER) if BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER: if BaseWidget._filter_installed: qt_import.QApplication.instance().removeEventFilter( BaseWidget._application_event_filter) BaseWidget._filter_installed = False BaseWidget.synchronize_with_cache() # why? else: if not BaseWidget._filter_installed: qt_import.QApplication.instance().installEventFilter( BaseWidget._application_event_filter) BaseWidget._filter_installed = True BaseWidget.update_menu_bar_color( BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER) @staticmethod def set_status_info(info_type, info_message, info_status=""): """Updates status bar""" if BaseWidget._statusbar: BaseWidget._statusbar.parent().update_status_info( info_type, info_message, info_status) @staticmethod def set_warning_box(warning_msg): """Updates status bar""" if BaseWidget._warning_box: BaseWidget._warning_box.parent().show_warning_box(warning_msg) @staticmethod def init_progress_bar(progress_type, number_of_steps): """Updates status bar""" if BaseWidget._statusbar: BaseWidget._statusbar.parent().init_progress_bar( progress_type, number_of_steps) @staticmethod def set_progress_bar_step(step): """Updates status bar""" if BaseWidget._statusbar: BaseWidget._statusbar.parent().set_progress_bar_step(step) @staticmethod def stop_progress_bar(): """Updates status bar""" if BaseWidget._statusbar: BaseWidget._statusbar.parent().stop_progress_bar() @staticmethod def open_progress_dialog(msg, max_steps): if BaseWidget._progress_dialog: BaseWidget._progress_dialog.parent().open_progress_dialog( msg, max_steps) @staticmethod def set_progress_dialog_step(step, msg): if BaseWidget._progress_dialog: BaseWidget._progress_dialog.parent().set_progress_dialog_step( step, msg) @staticmethod def close_progress_dialog(): if BaseWidget._progress_dialog: BaseWidget._progress_dialog.parent().close_progress_dialog() @staticmethod def set_user_file_directory(user_file_directory): BaseWidget.user_file_directory = user_file_directory def should_filter_event(self): if BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER: return False try: allow_always = self["instanceAllowAlways"] except KeyError: return False if not allow_always: try: allow_connected = self["instanceAllowConnected"] except KeyError: return False connected = BaseWidget._instance_role in ( BaseWidget.INSTANCE_ROLE_SERVER, BaseWidget.INSTANCE_ROLE_CLIENT, ) if allow_connected and connected: return False return True return False def connect_group_box(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.toggled.connect(lambda s: BaseWidget.widget_groupbox_toggled( brick_name, widget_name, master_sync, s)) def connect_combobox(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.activated.connect( lambda i: BaseWidget.widget_combobox_activated( brick_name, widget_name, widget, master_sync, i)) def connect_line_edit(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.textChanged.connect( lambda t: BaseWidget.widget_line_edit_text_changed( brick_name, widget_name, master_sync, t)) def connect_spinbox(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.valueChanged.connect( lambda t: BaseWidget.widget_spinbox_value_changed( brick_name, widget_name, master_sync, t)) def connect_double_spinbox(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.valueChanged.connect( lambda t: BaseWidget.widget_double_spinbox_value_changed( brick_name, widget_name, master_sync, t)) def connect_generic_widget(self, widget, widget_name, master_sync): brick_name = self.objectName() widget.widgetSynchronize.connect( lambda state: BaseWidget.widget_generic_changed( brick_name, widget_name, master_sync, state)) def _instance_mode_changed(self, mode): for widget, widget_name, master_sync in self._widget_events: if isinstance(widget, qt_import.QGroupBox): self.connect_group_box(widget, widget_name, master_sync) elif isinstance(widget, qt_import.QComboBox): self.connect_combobox(widget, widget_name, master_sync) elif isinstance(widget, qt_import.QLineEdit): self.connect_line_edit(widget, widget_name, master_sync) elif isinstance(widget, qt_import.QSpinBox): self.connect_spinbox(widget, widget_name, master_sync) elif isinstance(widget, qt_import.QDoubleSpinBox): self.connect_double_spinbox(widget, widget_name, master_sync) else: # verify if widget has the widgetSynchronize method!!! self.connect_generic_widget(widget, widget_name, master_sync) self._widget_events = [] if self.should_filter_event(): self.setCursor(qt_import.QCursor(qt_import.Qt.ForbiddenCursor)) else: self.setCursor(qt_import.QCursor(qt_import.Qt.ArrowCursor)) @staticmethod def is_instance_mode_master(): return BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER @staticmethod def is_instance_mode_slave(): return BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_SLAVE @staticmethod def is_istance_role_unknown(): return BaseWidget._instance_role == BaseWidget.INSTANCE_ROLE_UNKNOWN @staticmethod def is_instance_role_client(): return BaseWidget._instance_role == BaseWidget.INSTANCE_ROLE_CLIENT @staticmethod def is_instance_role_server(): return BaseWidget._instance_role == BaseWidget.INSTANCE_ROLE_SERVER @staticmethod def is_instance_user_id_unknown(): return BaseWidget._instance_user_id == BaseWidget.INSTANCE_USERID_UNKNOWN @staticmethod def is_instance_user_id_logged(): return BaseWidget._instance_user_id == BaseWidget.INSTANCE_USERID_LOGGED @staticmethod def is_instance_user_id_inhouse(): return BaseWidget._instance_user_id == BaseWidget.INSTANCE_USERID_INHOUSE @staticmethod def set_instance_role(role): if role == BaseWidget._instance_role: return BaseWidget._instance_role = role for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): # try: widget.instance_role_changed(role) # except: # pass @staticmethod def set_instance_location(location): if location == BaseWidget._instance_location: return BaseWidget._instance_location = location for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): # try: widget.instance_location_changed(location) # except: # pass @staticmethod def set_instance_user_id(user_id): if user_id == BaseWidget._instance_user_id: return BaseWidget._instance_user_id = user_id for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): # try: widget.instance_user_id_changed(user_id) # except: # pass BaseWidget.update_menu_bar_color() @staticmethod def set_instance_mirror(mirror): if mirror == BaseWidget._instance_mirror: return BaseWidget._instance_mirror = mirror if mirror == BaseWidget.INSTANCE_MIRROR_ALLOW: BaseWidget.synchronize_with_cache() for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): widget.instance_mirror_changed(mirror) def instance_mirror_changed(self, mirror): pass def instance_location_changed(self, location): pass @staticmethod def is_instance_location_unknown(): return BaseWidget._instance_location == BaseWidget.INSTANCE_LOCATION_UNKNOWN @staticmethod def is_instance_location_local(): return BaseWidget._instance_location == BaseWidget.INSTANCE_LOCATION_LOCAL @staticmethod def is_instance_mirror_allow(): return BaseWidget._instance_mirror == BaseWidget.INSTANCE_MIRROR_ALLOW def instance_user_id_changed(self, user_id): pass def instance_role_changed(self, role): pass @staticmethod def update_whats_this(): for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): msg = "%s (%s)\n%s" % ( widget.objectName(), widget.__class__.__name__, widget.get_hardware_objects_info(), ) widget.setWhatsThis(msg) qt_import.QWhatsThis.enterWhatsThisMode() @staticmethod def update_widget(brick_name, widget_name, method_name, method_args, master_sync): for widget in qt_import.QApplication.allWidgets(): try: if hasattr(widget, "configuration"): top_level_widget = widget break except NameError: continue if (not master_sync or BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER): top_level_widget.brickChangedSignal.emit(brick_name, widget_name, method_name, method_args, master_sync) @staticmethod def update_tab_widget(tab_name, tab_index): if BaseWidget._instance_mode == BaseWidget.INSTANCE_MODE_MASTER: for widget in qt_import.QApplication.allWidgets(): if hasattr(widget, "configuration"): widget.tabChangedSignal.emit(tab_name, tab_index) @staticmethod def widget_groupbox_toggled(brick_name, widget_name, master_sync, state): BaseWidget.update_widget(brick_name, widget_name, "setChecked", (state, ), master_sync) @staticmethod def widget_combobox_activated(brick_name, widget_name, widget, master_sync, item_index): lines = [] if widget.isEditable(): for index in range(widget.count()): lines.append(str(widget.itemText(index))) BaseWidget.update_widget(brick_name, widget_name, "setCurrentIndex", (item_index, ), master_sync) @staticmethod def widget_line_edit_text_changed(brick_name, widget_name, master_sync, text): BaseWidget.update_widget(brick_name, widget_name, "setText", (text, ), master_sync) @staticmethod def widget_spinbox_value_changed(brick_name, widget_name, master_sync, text): BaseWidget.update_widget(brick_name, widget_name, "setValue", (int(text), ), master_sync) @staticmethod def widget_double_spinbox_value_changed(brick_name, widget_name, master_sync, text): BaseWidget.update_widget(brick_name, widget_name, "setValue", (float(text), ), master_sync) @staticmethod def widget_generic_changed(brick_name, widget_name, master_sync, state): BaseWidget.update_widget(brick_name, widget_name, "widget_synchronize", (state, ), master_sync) def instance_forward_events(self, widget_name, master_sync): if widget_name == "": widget = self else: widget = getattr(self, widget_name) self._widget_events.append((widget, widget_name, master_sync)) def instance_synchronize(self, *args, **kwargs): for widget_name in args: self.instance_forward_events(widget_name, kwargs.get("master_sync", True)) @staticmethod def should_run_event(): return BaseWidget._instance_mirror == BaseWidget.INSTANCE_MIRROR_ALLOW @staticmethod def add_event_to_cache(timestamp, method, *args): try: method_to_add = WeakMethod(method) except TypeError: method_to_add = method BaseWidget._events_cache[method_to_add] = (timestamp, method_to_add, args) @staticmethod def synchronize_with_cache(): events = list(BaseWidget._events_cache.values()) ordered_events = sorted(events, key=operator.itemgetter(0)) for event_timestamp, event_method, event_args in ordered_events: try: method = event_method() if method is not None: method(*event_args) except BaseException: pass BaseWidget._events_cache = {} @staticmethod def set_gui_enabled(enabled): for widget in qt_import.QApplication.allWidgets(): if isinstance(widget, BaseWidget): widget.setEnabled(enabled) def __init__(self, parent=None, widget_name=""): connectable.Connectable.__init__(self) qt_import.QFrame.__init__(self, parent) self.setObjectName(widget_name) self.property_bag = property_bag.PropertyBag() self.__enabled_state = True self.__loaded_hardware_objects = [] self.__failed_to_load_hwobj = False self.__use_progress_dialog = False self._signal_slot_filters = {} self._widget_events = [] self.setWhatsThis("%s (%s)\n" % (widget_name, self.__class__.__name__)) self.add_property("fontSize", "string", str(self.font().pointSize())) self.add_property("frame", "boolean", False, comment="Draw a frame around the widget") self.add_property( "instanceAllowAlways", "boolean", False, comment="Allow to control brick in all modes", ) self.add_property( "instanceAllowConnected", "boolean", False, comment="Allow to control brick in slave mode", ) self.add_property("fixedWidth", "integer", "-1", comment="Set fixed width in pixels") self.add_property("fixedHeight", "integer", "-1", comment="Set fixed height in pixels") self.add_property("hide", "boolean", False, comment="Hide widget") dispatcher.connect( self.__hardware_object_discarded, "hardwareObjectDiscarded", HWR.get_hardware_repository(), ) self.define_slot("enable_widget", ()) self.define_slot("disable_widget", ()) # If PySide used then connect method was not overriden # This solution of redirecting methods works... self.connect = self.connect_hwobj self.disconnect = self.disconnect_hwobj # self.run_mode = QPushButton("Run mode", self) def __run(self): self.setAcceptDrops(False) self.blockSignals(False) self.setEnabled(self.__enabled_state) # self.run_mode_pushbutton = QPushButton("Simulation", self) try: self.run() except BaseException: logging.getLogger().exception("Could not set %s to run mode", self.objectName()) def __stop(self): self.blockSignals(True) try: self.stop() except BaseException: logging.getLogger().exception("Could not stop %s", self.objectName()) # self.setAcceptDrops(True) self.__enabled_state = self.isEnabled() qt_import.QWidget.setEnabled(self, True) def __repr__(self): return repr("<%s: %s>" % (self.__class__, self.objectName())) def connect_signal_slot_filter(self, sender, signal, slot, should_cache): uid = (sender, signal, hash(slot)) signal_slot_filter = SignalSlotFilter(signal, slot, should_cache) self._signal_slot_filters[uid] = signal_slot_filter signal.connect(signal_slot_filter) # qt_import.QObject.connect(sender, signal, signal_slot_filter) def connect_hwobj(self, sender, signal, slot, instance_filter=False, should_cache=True): if sys.version_info > (3, 0): signal = str( signal.decode("utf8") if isinstance(signal, bytes) else signal) else: signal = str(signal) if signal[0].isdigit(): pysignal = signal[0] == "9" signal = signal[1:] else: pysignal = True if not isinstance(sender, qt_import.QObject): if isinstance(sender, HardwareObject): sender.connect(signal, slot) return else: _sender = emitter(sender) else: _sender = sender if instance_filter: self.connect_signal_slot_filter( _sender, pysignal and qt_import.pyqtSignal(signal) or qt_import.pyqtSignal(signal), slot, should_cache, ) else: # Porting to Qt5 getattr(_sender, signal).connect(slot) # QtCore.QObject.connect(_sender, # pysignal and \ # QtCore.SIGNAL(signal) or \ # QtCore.SIGNAL(signal), # slot) # workaround for PyQt lapse # if hasattr(sender, "connectNotify"): # sender.connect_notify(QtCore.pyqtSignal(signal)) def disconnect_hwobj(self, sender, signal, slot): signal = str(signal) if signal[0].isdigit(): pysignal = signal[0] == "9" signal = signal[1:] else: pysignal = True if isinstance(sender, HardwareObject): sender.disconnect(sender, signal, slot) return # workaround for PyQt lapse if hasattr(sender, "disconnectNotify"): sender.disconnect_notify(signal) if not isinstance(sender, qt_import.QObject): sender = emitter(sender) try: uid = ( sender, pysignal and qt_import.pyqtSignal(signal) or qt_import.pyqtSignal(signal), hash(slot), ) signal_slot_filter = self._signal_slot_filters[uid] except KeyError: getattr(sender, signal).disconnect(slot) # qt_import.QObject.disconnect(sender, # pysignal and # qt_import.QtCore.SIGNAL(signal) or # qt_import.QtCore.SIGNAL(signal), # slot) else: getattr(sender, signal).disconnect(signal_slot_filter) # qt_import.QObject.disconnect(sender, # pysignal and # qt_import.SIGNAL(signal) or # qt_import.SIGNAL(signal), # signal_slot_filter) del self._signal_slot_filters[uid] else: getattr(sender, signal).disconnect(signal_slot_filter) # qt_import.QObject.disconnect(sender, # pysignal and # qt_import.SIGNAL(signal) or # qt_import.SIGNAL(signal), # signal_slot_filter) def reparent(self, widget_to): saved_enabled_state = self.isEnabled() if self.parent() is not None: self.parent().layout().removeWidget(self) if widget_to is not None: widget_to.layout().addWidget(self) self.setEnabled(saved_enabled_state) def blockSignals(self, block): for child in self.children(): child.blockSignals(block) def run(self): pass def stop(self): pass def restart(self): self.stop() self.run() def set_persistent_property_bag(self, persistent_property_bag): if id(persistent_property_bag) != id(self.property_bag): for prop in persistent_property_bag: if hasattr(prop, "get_name"): if prop.get_name() in self.property_bag.properties: self.property_bag.get_property( prop.get_name()).set_value(prop.get_user_value()) elif prop.hidden: self.property_bag[prop.get_name()] = prop else: if prop["name"] in self.property_bag.properties: self.property_bag.get_property(prop["name"]).set_value( prop["value"]) elif prop["hidden"]: self.property_bag[prop["name"]] = prop self.read_properties() def read_properties(self): for prop in self.property_bag: self._property_changed(prop.get_name(), None, prop.get_user_value()) def add_property(self, *args, **kwargs): self.property_bag.add_property(*args, **kwargs) def get_property(self, property_name): return self.property_bag.get_property(property_name) def show_property(self, property_name): return self.property_bag.show_property(property_name) def hide_property(self, property_name): return self.property_bag.hide_property(property_name) def del_property(self, property_name): return self.property_bag.del_property(property_name) def get_hardware_object(self, hardware_object_name, optional=False): splash_screen = mxcubeqt.get_splash() if splash_screen: splash_screen.set_message( "Loading hardware object defined in %s.xml" % hardware_object_name) if not hardware_object_name in self.__loaded_hardware_objects: if splash_screen: splash_screen.inc_progress_value() self.__loaded_hardware_objects.append(hardware_object_name) hwobj = HWR.get_hardware_repository().get_hardware_object( hardware_object_name) if hwobj is not None: self.connect(hwobj, "progressInit", self.progress_init) self.connect(hwobj, "progressStep", self.progress_step) self.connect(hwobj, "progressStop", self.progress_stop) self.connect(hwobj, "statusMessage", self.status_message_changed) self.connect(hwobj, "showWarning", self.show_warning) if hwobj is None and not optional: logging.getLogger("GUI").error( "%s: Unable to initialize hardware object defined in %s.xml" % (self.objectName(), hardware_object_name[1:])) self.set_background_color(colors.LIGHT_RED) self.__failed_to_load_hwobj = True self.setDisabled(True) return hwobj def progress_init(self, progress_type, number_of_steps, use_dialog=False): self.__use_progress_dialog = use_dialog if self.__use_progress_dialog: BaseWidget.open_progress_dialog(progress_type, number_of_steps) def progress_step(self, step, msg=None): if self.__use_progress_dialog: BaseWidget.set_progress_dialog_step(step, msg) def progress_stop(self): if self.__use_progress_dialog: BaseWidget.close_progress_dialog() def status_message_changed(self, info_type, message, state): BaseWidget.set_status_info(info_type, message, state) def show_warning(self, warning_msg): BaseWidget.set_warning_box(warning_msg) def __hardware_object_discarded(self, hardware_object_name): if hardware_object_name in self.__loaded_hardware_objects: # there is a high probability we need to reload this hardware object... self.readProperties() # force to read properties def get_hardware_objects_info(self): info_dict = {} for ho_name in self.__loaded_hardware_objects: info = HWR.get_hardware_repository().get_info(ho_name) if len(info) > 0: info_dict[ho_name] = info if len(info_dict): return "Hardware Objects:\n\n%s" % pprint.pformat(info_dict) else: return "" def __getitem__(self, property_name): return self.property_bag[property_name] def __setitem__(self, property_name, value): property_bag = self.property_bag.get_property(property_name) old_value = property_bag.get_value() property_bag.set_value(value) self._property_changed(property_name, old_value, property_bag.get_user_value()) def _property_changed(self, property_name, old_value, new_value): if property_name == "fontSize": try: font_size = int(new_value) except BaseException: self.get_property("fontSize").set_value( self.font().pointSize()) else: font = self.font() font.setPointSize(font_size) self.setFont(font) for brick in self.children(): if isinstance(brick, BaseWidget): brick["fontSize"] = font_size self.update() elif property_name == "frame": try: if new_value: self.setFrameStyle(qt_import.QFrame.StyledPanel) else: self.setFrameStyle(qt_import.QFrame.NoFrame) except BaseException: pass self.update() elif property_name == "fixedWidth": if new_value > -1: self.setFixedWidth(new_value) elif property_name == "fixedHeight": if new_value > -1: self.setFixedHeight(new_value) elif property_name == "hide": if new_value: self.setHidden(True) #else: # self.setVisible(True) else: try: self.property_changed(property_name, old_value, new_value) except BaseException: logging.getLogger().exception( "Error while setting property %s " % property_name + "for %s (details in log file)." % str(self.objectName())) def property_changed(self, property_name, old_value, new_value): pass def set_expert_mode(self, expert): pass def enable_widget(self, state): if self.__failed_to_load_hwobj: state = False if state: self.setEnabled(True) else: self.setDisabled(True) def disable_widget(self, state): if self.__failed_to_load_hwobj: state = True if state: self.setDisabled(True) else: self.setEnabled(True) def get_window_display_widget(self): for widget in qt_import.QApplication.allWidgets(): if hasattr(widget, "configuration"): return widget def set_background_color(self, color): colors.set_widget_color(self, color, qt_import.QPalette.Background)
class EsrfConfigurationBrick(BaseWidget): operation_modes_edited = qt_import.pyqtSignal(list) operation_modes_saved = qt_import.pyqtSignal(list) data_policy_changed = qt_import.pyqtSignal(dict) def __init__(self, *args): BaseWidget.__init__(self, *args) # variables ----------------------------------------------------------- self.list_of_operational_modes = [] self.default_session = None self.multipos_file_xml_path = None self.bliss_session_list = None # Hardware objects ---------------------------------------------------- self.multipos_hwobj = None # Internal values ----------------------------------------------------- self.table_created = False # Properties ---------------------------------------------------------- self.add_property("mnemonic", "string", "/multiple-positions") # Signals ------------------------------------------------------------ self.define_signal("operation_modes_edited", ()) self.define_signal("operation_modes_saved", ()) self.define_signal("data_policy_changed", ()) # Slots --------------------------------------------------------------- # Graphic elements ---------------------------------------------------- self.main_groupbox = qt_import.QGroupBox("Beam Configuration", self) self.ui_widgets_manager = qt_import.load_ui_file( "esrf_id13_configuration_widget.ui") # Size policy -------------------------------- self.ui_widgets_manager.configuration_table.setSizePolicy( qt_import.QSizePolicy.Minimum, qt_import.QSizePolicy.Minimum, ) # Layout -------------------------------------------------------------- _groupbox_vlayout = qt_import.QVBoxLayout(self) _groupbox_vlayout.addWidget(self.ui_widgets_manager) _groupbox_vlayout.setSpacing(0) _groupbox_vlayout.setContentsMargins(0, 0, 0, 0) self.main_groupbox.setLayout(_groupbox_vlayout) _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.main_groupbox) _main_vlayout.setSpacing(0) _main_vlayout.setContentsMargins(0, 0, 0, 0) self.setLayout(_main_vlayout) # Qt signal/slot connections ------------------------------------------ self.ui_widgets_manager.save_table_changes.clicked.connect( self.save_table_changes) self.ui_widgets_manager.cancel_table_changes.clicked.connect( self.cancel_table_changes) self.ui_widgets_manager.bliss_session_combo_box.currentIndexChanged.connect( self.display_data_policy) self.ui_widgets_manager.configuration_table.itemChanged.connect( self.configuration_table_item_changed) self.ui_widgets_manager.add_label_button.clicked.connect( self.add_op_mode_to_list) self.ui_widgets_manager.delete_label_button.clicked.connect( self.delete_op_mode_from_list) self.ui_widgets_manager.save_labels_button.clicked.connect( self.save_op_mode_list) self.ui_widgets_manager.label_list.itemSelectionChanged.connect( self.label_list_selection_changed) self.ui_widgets_manager.reload_data_policy_button.clicked.connect( self.reload_data_policy) def configuration_table_item_changed(self, item): validated_value = self.validate_cell_value(item.text()) item.setText(str(validated_value)) item.setBackground(qt_import.QColor(qt_import.Qt.yellow)) # create new dict from new data table = self.ui_widgets_manager.configuration_table item_row = item.row() item_col = item.column() if item_col in (1, 2): who_changed = 0 elif item_col in (3, 4): who_changed = 1 else: who_changed = 2 edited_key = table.item(item_row, 0).text() dict_elem = {} dict_elem["zoom_tag"] = edited_key dict_elem["beam_pos_x"] = int( self.validate_cell_value(table.item(item_row, 1).text())) dict_elem["beam_pos_y"] = int( self.validate_cell_value(table.item(item_row, 2).text())) resox = self.validate_cell_value(table.item(item_row, 3).text()) dict_elem["cal_x"] = float(resox) resoy = self.validate_cell_value(table.item(item_row, 4).text()) dict_elem["cal_y"] = float(resoy) dict_elem["light"] = int( self.validate_cell_value(table.item(item_row, 5).text())) dict_elem["zoom"] = int( self.validate_cell_value(table.item(item_row, 6).text())) self.multipos_hwobj.edit_data(dict_elem, edited_key, who_changed) def load_default_session(self): """ Parse xml file and search for 'default_session' tag """ xml_file_tree = cElementTree.parse(self.multipos_file_xml_path) xml_tree = xml_file_tree.getroot() if xml_tree.find("default_session") is not None: self.default_session = xml_tree.find("default_session").text def load_list_of_operational_modes(self): """ Parse xml file and load list of operational modes : 'tag0', 'tag1', ... """ xml_file_tree = cElementTree.parse(self.multipos_file_xml_path) xml_tree = xml_file_tree.getroot() mode_list = [] if xml_tree.find("operational_modes") is not None: mode_list = xml_tree.find("operational_modes").text self.list_of_operational_modes = eval(mode_list) else: #if no operational_mode, hide all related controls self.ui_widgets_manager.add_label_button.hide() self.ui_widgets_manager.delete_label_button.hide() self.ui_widgets_manager.label_list.hide() self.ui_widgets_manager.label_3.hide() self.ui_widgets_manager.new_label_edit.hide() self.ui_widgets_manager.save_labels_button.hide() def property_changed(self, property_name, old_value, new_value): if property_name == "mnemonic": if self.multipos_hwobj is not None: # disconnect signal/slots self.disconnect(self.multipos_hwobj, "beam_pos_cal_data_changed", self.beam_pos_cal_data_changed) self.disconnect(self.multipos_hwobj, "beam_pos_cal_data_saved", self.beam_cal_pos_data_saved) self.disconnect(self.multipos_hwobj, "beam_pos_cal_data_cancelled", self.beam_cal_pos_data_cancelled) pass if new_value is not None: self.multipos_hwobj = self.get_hardware_object(new_value) #search xml file so it handles the 'tags' # TODO : create a separate xml file for tags!! if new_value.startswith("/"): new_value = new_value[1:] self.multipos_file_xml_path = os.path.join( HWR.getHardwareRepositoryConfigPath(), new_value + ".xml") if self.multipos_hwobj is not None: self.connect(self.multipos_hwobj, "beam_pos_cal_data_changed", self.beam_pos_cal_data_changed) self.connect(self.multipos_hwobj, "beam_pos_cal_data_saved", self.beam_cal_pos_data_saved) self.connect(self.multipos_hwobj, "beam_pos_cal_data_cancelled", self.beam_cal_pos_data_cancelled) # self.load_zoom_positions_dict() self.load_list_of_operational_modes() self.load_default_session() self.init_interface() else: BaseWidget.property_changed(self, property_name, old_value, new_value) def beam_pos_cal_data_changed(self, who_changed, new_data_dict): self.fill_config_table() if new_data_dict: current_pos_name = new_data_dict["zoom_tag"] else: current_pos_name = self.multipos_hwobj.get_value() table = self.ui_widgets_manager.configuration_table self.ui_widgets_manager.configuration_table.itemChanged.disconnect( self.configuration_table_item_changed) for row_index in range(table.rowCount()): if table.item(row_index, 0).text() == current_pos_name: if who_changed == 0: table.item(row_index, 1).setBackground( qt_import.QColor(qt_import.Qt.yellow)) table.item(row_index, 2).setBackground( qt_import.QColor(qt_import.Qt.yellow)) elif who_changed == 1: table.item(row_index, 3).setBackground( qt_import.QColor(qt_import.Qt.yellow)) table.item(row_index, 4).setBackground( qt_import.QColor(qt_import.Qt.yellow)) self.ui_widgets_manager.configuration_table.itemChanged.connect( self.configuration_table_item_changed) def fill_op_modes_list(self): if self.list_of_operational_modes is not None: self.ui_widgets_manager.label_list.clear() for tag_text in self.list_of_operational_modes: self.ui_widgets_manager.label_list.addItem(tag_text) def fill_config_table(self): tmp_dict = self.multipos_hwobj.get_positions() if tmp_dict is not None: self.ui_widgets_manager.configuration_table.itemChanged.disconnect( self.configuration_table_item_changed) if not self.table_created: # create table items for first and only time self.ui_widgets_manager.configuration_table.setRowCount( len(tmp_dict)) for row in range(len(tmp_dict)): for col in range(7): tmp_item = qt_import.QTableWidgetItem() if col == 0: #zoom position name not editable tmp_item.setFlags(tmp_item.flags() ^ qt_import.Qt.ItemIsEditable) self.ui_widgets_manager.configuration_table.setItem( row, col, tmp_item) self.table_created = True table = self.ui_widgets_manager.configuration_table for i, (position, position_dict_elem) in enumerate(tmp_dict.items()): table.item(i, 0).setText(str(position)) table.item(i, 1).setText(str(position_dict_elem["beam_pos_x"])) table.item(i, 2).setText(str(position_dict_elem["beam_pos_y"])) if position_dict_elem["cal_x"] == 1: y_calib = "Not defined" else: y_calib = str(abs(int(position_dict_elem["cal_x"]))) if position_dict_elem["cal_y"] == 1: z_calib = "Not defined" else: z_calib = str(abs(int(position_dict_elem["cal_y"]))) table.item(i, 3).setText(y_calib) table.item(i, 4).setText(z_calib) table.item(i, 5).setText(str(position_dict_elem['light'])) table.item(i, 6).setText(str(position_dict_elem['zoom'])) self.ui_widgets_manager.configuration_table.itemChanged.connect( self.configuration_table_item_changed) self.ui_widgets_manager.configuration_table.horizontalHeader( ).setSectionResizeMode(qt_import.QHeaderView.ResizeToContents) def beam_cal_pos_data_saved(self): """ data saved: clean cell background """ self.clean_cells_background() def beam_cal_pos_data_cancelled(self): """ data cancelled: clean cell background reload data from hardware object """ self.fill_config_table() self.clean_cells_background() def init_interface(self): """ Fill table and combobox and make them functional """ if self.multipos_hwobj is not None: self.fill_config_table() self.fill_op_modes_list() self.load_sessions() self.reload_data_policy() def load_sessions(self): """ Load list of sessions and populate combobox """ self.bliss_session_list = get_sessions_list() self.ui_widgets_manager.bliss_session_combo_box.clear() self.ui_widgets_manager.bliss_session_combo_box.currentIndexChanged.disconnect( self.display_data_policy) for session in self.bliss_session_list: self.ui_widgets_manager.bliss_session_combo_box.addItem(session) if self.default_session in self.bliss_session_list: index = self.ui_widgets_manager.bliss_session_combo_box.findText( self.default_session) if index != -1: self.ui_widgets_manager.bliss_session_combo_box.setCurrentIndex( index) else: self.ui_widgets_manager.bliss_session_combo_box.setCurrentIndex(-1) self.ui_widgets_manager.bliss_session_combo_box.currentIndexChanged.connect( self.display_data_policy) def reload_data_policy(self): self.display_data_policy( self.ui_widgets_manager.bliss_session_combo_box.currentIndex()) def display_data_policy(self, index): """ Display data policy of selected session in combobox """ if index > -1: new_session = self.bliss_session_list[index] scan_savings = ESRFScanSaving(new_session) session_info_string = '' session_info_dict = {} session_info_dict['session'] = new_session session_info_dict['base_path'] = scan_savings.base_path session_info_dict['data_filename'] = scan_savings.data_filename session_info_dict['data_path'] = scan_savings.data_path session_info_dict['dataset'] = scan_savings.dataset session_info_dict['date'] = scan_savings.date session_info_dict['sample'] = scan_savings.sample session_info_dict['proposal'] = scan_savings.proposal session_info_dict['template'] = scan_savings.template session_info_dict['beamline'] = scan_savings.beamline # waiting to https://gitlab.esrf.fr/bliss/bliss/-/merge_requests/2948 # be part of current BLISS version #session_info_dict['filename'] = scan_savings.filename #session_info_dict['data_fullpath'] = scan_savings.data_fullpath for key, val in session_info_dict.items(): info_str = ' ' + key + ' : ' + val session_info_string += info_str + ' \n' self.data_policy_changed.emit(session_info_dict) self.ui_widgets_manager.data_policy_label.setText( session_info_string) def save_op_mode_list(self): """ Save data to xml file Clean cell background """ xml_file_tree = cElementTree.parse(self.multipos_file_xml_path) xml_tree = xml_file_tree.getroot() xml_tree.find("operational_modes").text = str( self.list_of_operational_modes) xml_file_tree.write(self.multipos_file_xml_path) self.operation_modes_saved.emit(self.list_of_operational_modes) def add_op_mode_to_list(self): """ add lable list to list and to self.list_of_operational_modes Data not saved yet """ new_label_list_full = self.ui_widgets_manager.new_label_edit.text( ).strip() new_label_list = new_label_list_full.split() if not new_label_list: return # check if label already exist for new_label in new_label_list: if new_label not in self.list_of_operational_modes: self.list_of_operational_modes.append(new_label) self.ui_widgets_manager.label_list.addItem(new_label) #select newly added item self.ui_widgets_manager.label_list.setCurrentRow( self.ui_widgets_manager.label_list.count() - 1) self.operation_modes_edited.emit(self.list_of_operational_modes) def delete_op_mode_from_list(self): """ delete lable from list detele from self.list_of_operational_modes changes not saved yet """ label_to_delete_list_full = self.ui_widgets_manager.new_label_edit.text( ).strip() label_to_delete_list = label_to_delete_list_full.split() if not label_to_delete_list: return for label_to_delete in label_to_delete_list: if label_to_delete not in self.list_of_operational_modes: continue index = self.list_of_operational_modes.index(label_to_delete) self.ui_widgets_manager.label_list.takeItem(index) self.list_of_operational_modes.remove(label_to_delete) #select first item if self.list_of_operational_modes: self.ui_widgets_manager.label_list.setCurrentRow(0) self.operation_modes_edited.emit(self.list_of_operational_modes) def label_list_selection_changed(self): selected_label_list = self.ui_widgets_manager.label_list.selectedItems( ) label_text_list = [] for label in selected_label_list: label_text_list.append(label.text()) self.ui_widgets_manager.new_label_edit.setText( ' '.join(label_text_list)) def clean_cells_background(self): """ clean cells background color """ table = self.ui_widgets_manager.configuration_table table.itemChanged.disconnect(self.configuration_table_item_changed) for row in range(table.rowCount()): for col in range(table.columnCount()): table.item(row, col).setData(qt_import.Qt.BackgroundRole, None) table.itemChanged.connect(self.configuration_table_item_changed) def save_table_changes(self): """ send signal to self.multipos_hwobj to save data to file clean cells background color """ if self.multipos_hwobj is not None: self.multipos_hwobj.save_data_to_file(self.multipos_file_xml_path) def validate_cell_value(self, input_val): """ return value adapted according to input """ try: output = int(input_val) except ValueError: output = 1 return output def cancel_table_changes(self): """ cancel any change in config table. reload data from last saved version of xml file: recover data from multipos_hwobj and display it """ self.multipos_hwobj.cancel_edited_data() self.fill_config_table() self.clean_cells_background() def clear_table(self): """ clean table of contents. keep headers """ self.ui_widgets_manager.configuration_table.clearContents() def from_text_to_int(self, input_str, factor=1): if input_str is None: return 0 return abs(int(float(input_str) * factor)) def from_text_to_float(self, input_str, factor=1): if input_str is None: return 0 return abs((float(input_str) * factor))
class AcquisitionWidget(qt_import.QWidget): acqParametersChangedSignal = qt_import.pyqtSignal(list) madEnergySelectedSignal = qt_import.pyqtSignal(str, float, bool) def __init__( self, parent=None, name=None, fl=0, acq_params=None, path_template=None, layout="horizontal", ): qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) # Hardware objects ---------------------------------------------------- # Internal variables -------------------------------------------------- self.previous_energy = 0 # If the acq. widget is used with grids then total osc range is not # equal to num_images * osc_range, but num_images_per_line * osc_range # For grids the osc total range is updated when a grid is selected self.grid_mode = False # Properties ---------------------------------------------------------- # Signals ------------------------------------------------------------- # Slots --------------------------------------------------------------- # Graphic elements ---------------------------------------------------- if acq_params is None: self._acquisition_parameters = queue_model_objects.AcquisitionParameters() else: self._acquisition_parameters = acq_params if path_template is None: self._path_template = queue_model_objects.PathTemplate() else: self._path_template = path_template self._acquisition_mib = DataModelInputBinder(self._acquisition_parameters) if layout == "horizontal": self.acq_widget_layout = qt_import.load_ui_file( "acquisition_widget_horizontal_layout.ui" ) self.use_osc_start(False) else: self.acq_widget_layout = qt_import.load_ui_file( "acquisition_widget_vertical_layout.ui" ) # Layout -------------------------------------------------------------- __main_vlayout = qt_import.QVBoxLayout(self) __main_vlayout.addWidget(self.acq_widget_layout) __main_vlayout.setSpacing(0) __main_vlayout.setContentsMargins(0, 0, 0, 0) # SizePolicies -------------------------------------------------------- # Qt signal/slot connections ------------------------------------------ self.acq_widget_layout.osc_start_cbox.stateChanged.connect(self.fix_osc_start) self.acq_widget_layout.exp_time_ledit.textChanged.connect( self.exposure_time_ledit_changed ) self.acq_widget_layout.exp_time_total_ledit.textEdited.connect( self.exp_time_total_ledit_changed ) self.acq_widget_layout.first_image_ledit.textChanged.connect( self.first_image_ledit_change ) self.acq_widget_layout.num_images_ledit.textChanged.connect( self.num_images_ledit_change ) self.acq_widget_layout.detector_roi_mode_combo.activated.connect( self.detector_roi_mode_changed ) self.acq_widget_layout.energies_combo.activated.connect(self.energy_selected) self.acq_widget_layout.mad_cbox.toggled.connect(self.use_mad) self.acq_widget_layout.osc_start_ledit.textEdited.connect( self.osc_start_ledit_changed ) self.acq_widget_layout.osc_range_ledit.textEdited.connect( self.osc_range_per_frame_ledit_changed ) self.acq_widget_layout.osc_total_range_ledit.textEdited.connect( self.osc_total_range_ledit_changed ) self.acq_widget_layout.energy_ledit.textEdited.connect( self.energy_ledit_changed ) self.acq_widget_layout.transmission_ledit.textEdited.connect( self.transmission_ledit_changed ) self.acq_widget_layout.resolution_ledit.textEdited.connect( self.resolution_ledit_changed ) self.acq_widget_layout.kappa_ledit.textEdited.connect(self.kappa_ledit_changed) self.acq_widget_layout.kappa_phi_ledit.textEdited.connect( self.kappa_phi_ledit_changed ) if self.acq_widget_layout.findChild(qt_import.QLineEdit, "overlap_ledit"): self.acq_widget_layout.overlap_ledit.textChanged.connect( self.overlap_changed ) if self.acq_widget_layout.findChild(qt_import.QCheckBox, "max_osc_range_cbx"): self.acq_widget_layout.max_osc_range_cbx.toggled.connect( self.max_osc_range_toggled ) # Other --------------------------------------------------------------- self.value_changed_list = [] self.acq_widget_layout.energies_combo.setDisabled(True) self.acq_widget_layout.energies_combo.addItems( ["ip: -", "pk: -", "rm1: -", "rm2: -"] ) self.osc_start_validator = qt_import.QDoubleValidator( -10000, 10000, 4, self.acq_widget_layout.osc_start_ledit ) self.osc_range_per_frame_validator = qt_import.QDoubleValidator( 0, 10000, 4, self.acq_widget_layout.osc_range_ledit ) self.osc_total_range_validator = qt_import.QDoubleValidator( 0, 10000, 4, self.acq_widget_layout.osc_total_range_ledit ) self.kappa_validator = qt_import.QDoubleValidator( -0.01, 360, 4, self.acq_widget_layout.kappa_ledit ) self.kappa_phi_validator = qt_import.QDoubleValidator( -360, 360, 4, self.acq_widget_layout.kappa_phi_ledit ) self.energy_validator = qt_import.QDoubleValidator( 4, 25, 5, self.acq_widget_layout.energy_ledit ) self.resolution_validator = qt_import.QDoubleValidator( 0, 15, 3, self.acq_widget_layout.resolution_ledit ) self.transmission_validator = qt_import.QDoubleValidator( 0, 100, 3, self.acq_widget_layout.transmission_ledit ) self.exp_time_validator = qt_import.QDoubleValidator( 0.0001, 10000, 7, self.acq_widget_layout.exp_time_ledit ) self.first_img_validator = qt_import.QIntValidator( 0, 99999, self.acq_widget_layout.first_image_ledit ) self.num_img_validator = qt_import.QIntValidator( 1, 9999999, self.acq_widget_layout.num_images_ledit ) self.acq_widget_layout.detector_roi_mode_label.setEnabled(False) self.acq_widget_layout.detector_roi_mode_combo.setEnabled(False) self.init_limits() def use_max_osc_range(self, state): self.acq_widget_layout.max_osc_range_cbx.setEnabled(state) def fix_osc_start(self, state): """ Fix osc start, so the lineEdit do not change when osc is changed """ self.acq_widget_layout.osc_start_ledit.setEnabled(state) def update_osc_start(self, new_value): """ Updates osc line edit """ if ( "osc_start" not in self.value_changed_list and not self.acq_widget_layout.osc_start_ledit.hasFocus() and not self.acq_widget_layout.osc_start_cbox.isChecked() ): osc_start_value = 0 try: osc_start_value = round(float(new_value), 2) except TypeError: pass self.acq_widget_layout.osc_start_ledit.setText(str(osc_start_value)) self._acquisition_parameters.osc_start = osc_start_value # self.update_osc_total_range_limits() # self.update_num_images_limits() def osc_start_ledit_changed(self, osc_start): """Fixes osc start edit""" if "osc_start" not in self.value_changed_list: self.value_changed_list.append("osc_start") self.update_osc_total_range_limits() self.update_num_images_limits() self.emit_acq_parameters_changed() def update_osc_start_limits(self): """In the plate mode sets osc start limits""" self.update_osc_total_range_limits() self.update_num_images_limits() def osc_range_per_frame_ledit_changed(self, new_value): self.update_osc_total_range() self.update_num_images_limits() self.update_exp_time_limits() self.emit_acq_parameters_changed() def update_osc_range_per_frame_limits(self): try: max_osc_speed = HWR.beamline.diffractometer.get_osc_max_speed() top_limit = max_osc_speed * float( self.acq_widget_layout.exp_time_ledit.text() ) self.osc_range_per_frame_validator.setTop(top_limit) tool_tip = ( "Oscillation range per frame limits 0 : " + "%0.4f\n4 digits precision." % top_limit ) self.acq_widget_layout.osc_range_ledit.setToolTip(tool_tip) self._acquisition_mib.validate_all() except BaseException: pass def update_osc_total_range(self): self.acq_widget_layout.osc_total_range_ledit.blockSignals(True) if not self.grid_mode: try: self.acq_widget_layout.osc_total_range_ledit.setText( "%0.2f" % ( float(self.acq_widget_layout.osc_range_ledit.text()) * int(self.acq_widget_layout.num_images_ledit.text()) ) ) except BaseException: pass self.acq_widget_layout.osc_total_range_ledit.blockSignals(False) def update_total_exp_time(self): try: self.acq_widget_layout.exp_time_total_ledit.setText( "%.2f" % ( float(self.acq_widget_layout.exp_time_ledit.text()) * float(self.acq_widget_layout.num_images_ledit.text()) ) ) except BaseException: pass def osc_total_range_ledit_changed(self, new_value): if not self.grid_mode: try: num_images = int( float(new_value) / float(self.acq_widget_layout.osc_range_ledit.text()) ) self.acq_widget_layout.num_images_ledit.blockSignals(True) self.acq_widget_layout.num_images_ledit.setText("%d" % num_images) self._acquisition_parameters.num_images = num_images self.acq_widget_layout.num_images_ledit.blockSignals(False) except BaseException: pass self.emit_acq_parameters_changed() def exp_time_total_ledit_changed(self, new_value): try: exp_time = float(new_value) / float(self.acq_widget_layout.num_images_ledit.text()) self.acq_widget_layout.exp_time_ledit.blockSignals(True) self.acq_widget_layout.exp_time_ledit.setText("%.4f" % exp_time) self._acquisition_parameters.exp_time = exp_time self.acq_widget_layout.exp_time_ledit.blockSignals(False) except BaseException: pass self.emit_acq_parameters_changed() def update_osc_total_range_limits(self, num_images=None): """Updates osc totol range. Limits are changed if a plate is used. - For simple oscillation osc_range is defined by osc_start and osc_start top limit. - For mesh osc_range is defined by number of images per line and osc in the middle of mesh """ if HWR.beamline.diffractometer.in_plate_mode(): if hasattr(self.parent(), "set_osc_total_range"): self.parent().set_osc_total_range(num_images) self._acquisition_mib.validate_all() def update_exp_time_limits(self): try: exp_time_limits = ( HWR.beamline.detector.get_exposure_time_limits() ) max_osc_speed = HWR.beamline.diffractometer.get_osc_max_speed() top_limit = ( float(self.acq_widget_layout.osc_range_ledit.text()) / max_osc_speed ) limits = (max(exp_time_limits[0], top_limit), exp_time_limits[1]) self.update_detector_exp_time_limits(limits) except BaseException: pass def update_kappa(self, new_value): if not self.acq_widget_layout.kappa_ledit.hasFocus() and new_value is not None: self.acq_widget_layout.kappa_ledit.setText(str(new_value)) self.emit_acq_parameters_changed() def update_kappa_phi(self, new_value): if not self.acq_widget_layout.kappa_phi_ledit.hasFocus() and new_value: self.acq_widget_layout.kappa_phi_ledit.setText(str(new_value)) self.emit_acq_parameters_changed() def use_osc_start(self, state): self.acq_widget_layout.osc_start_cbox.setVisible(state) self.acq_widget_layout.osc_start_label.setVisible(not state) self.acq_widget_layout.osc_start_ledit.setEnabled(not state) def use_kappa(self, state): if HWR.beamline.diffractometer is not None: if HWR.beamline.diffractometer.in_plate_mode(): state = False self.acq_widget_layout.kappa_label.setEnabled(state) self.acq_widget_layout.kappa_ledit.setEnabled(state) self.acq_widget_layout.kappa_phi_label.setEnabled(state) self.acq_widget_layout.kappa_phi_ledit.setEnabled(state) def init_limits(self): limits_dict = HWR.beamline.acquisition_limit_values tpl = limits_dict.get("osc_range") if tpl: self.osc_start_validator.setRange(tpl[0], tpl[1], 4) self.osc_range_per_frame_validator.setRange(tpl[0], tpl[1], 4) self.osc_total_range_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "osc_start", self.acq_widget_layout.osc_start_ledit, float, self.osc_start_validator, ) self._acquisition_mib.bind_value_update( "osc_range", self.acq_widget_layout.osc_range_ledit, float, self.osc_range_per_frame_validator, ) self._acquisition_mib.bind_value_update( "osc_total_range", self.acq_widget_layout.osc_total_range_ledit, float, self.osc_total_range_validator, ) tpl = limits_dict.get("kappa") if tpl: self.kappa_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "kappa", self.acq_widget_layout.kappa_ledit, float, self.kappa_validator ) tpl = limits_dict.get("kappa_phi") if tpl: self.kappa_phi_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "kappa_phi", self.acq_widget_layout.kappa_phi_ledit, float, self.kappa_phi_validator, ) tpl = limits_dict.get("exposure_time") if tpl: self.exp_time_validator.setRange(tpl[0], tpl[1], 6) self._acquisition_mib.bind_value_update( "exp_time", self.acq_widget_layout.exp_time_ledit, float, self.exp_time_validator, ) tpl = limits_dict.get("number_of_images") if tpl: self.num_img_validator.setRange(tpl[0], tpl[1]) self.first_img_validator.setRange(tpl[0], tpl[1]) self._acquisition_mib.bind_value_update( "first_image", self.acq_widget_layout.first_image_ledit, int, self.first_img_validator, ) self._acquisition_mib.bind_value_update( "num_images", self.acq_widget_layout.num_images_ledit, int, self.num_img_validator, ) num_passes = self.acq_widget_layout.findChild( qt_import.QLineEdit, "num_passes_ledit" ) if num_passes: self._acquisition_mib.bind_value_update( "num_passes", num_passes, int, qt_import.QIntValidator(1, 1000, self) ) overlap_ledit = self.acq_widget_layout.findChild( qt_import.QLineEdit, "overlap_ledit" ) if overlap_ledit: self._acquisition_mib.bind_value_update( "overlap", overlap_ledit, float, qt_import.QDoubleValidator(-1000, 1000, 2, self), ) self._acquisition_mib.bind_value_update( "energy", self.acq_widget_layout.energy_ledit, float, self.energy_validator ) self.update_energy_limits( (self.energy_validator.bottom(), self.energy_validator.top()) ) self._acquisition_mib.bind_value_update( "transmission", self.acq_widget_layout.transmission_ledit, float, self.transmission_validator, ) self.update_transmission_limits( (self.transmission_validator.bottom(), self.transmission_validator.top()) ) self._acquisition_mib.bind_value_update( "resolution", self.acq_widget_layout.resolution_ledit, float, self.resolution_validator, ) # self.update_resolution_limits((self.resolution_validator.bottom(), # self.resolution_validator.top())) self._acquisition_mib.bind_value_update( "shutterless", self.acq_widget_layout.shutterless_cbx, bool, None ) self.set_tunable_energy(HWR.beamline.tunable_wavelength) has_shutter_less = HWR.beamline.detector.has_shutterless() self.acq_widget_layout.shutterless_cbx.setEnabled(has_shutter_less) self.acq_widget_layout.shutterless_cbx.setChecked(has_shutter_less) if HWR.beamline.disable_num_passes: num_passes = self.acq_widget_layout.findChild( qt_import.QLineEdit, "num_passes_ledit" ) if num_passes: num_passes.setDisabled(True) self.init_detector_roi_modes() def first_image_ledit_change(self, new_value): self.emit_acq_parameters_changed() def exposure_time_ledit_changed(self, new_value): """If the exposure time changes we have to check the osc speed and if necessary update osc range per frame """ self.update_osc_range_per_frame_limits() self.update_osc_total_range_limits() self.update_total_exp_time() self.emit_acq_parameters_changed() def num_images_ledit_change(self, new_value): if str(new_value).isdigit(): # self._path_template.num_files = int(new_value) self.update_osc_range_per_frame_limits() self.update_osc_total_range_limits() self.update_osc_total_range() self.update_total_exp_time() self.emit_acq_parameters_changed() def overlap_changed(self, new_value): if HWR.beamline.detector.has_shutterless(): try: new_value = float(new_value) except ValueError: pass if new_value != 0: self.acq_widget_layout.shutterless_cbx.setEnabled(False) self.acq_widget_layout.shutterless_cbx.setChecked(False) self._acquisition_parameters.shutterless = False else: self.acq_widget_layout.shutterless_cbx.setEnabled(True) self.acq_widget_layout.shutterless_cbx.setChecked(True) self._acquisition_parameters.shutterless = True def use_mad(self, state): self.acq_widget_layout.energies_combo.setEnabled(state) if state: (name, energy) = self.get_mad_energy() if energy != 0: self.update_energy(energy) self.madEnergySelectedSignal.emit(name, energy, state) else: self.update_energy(self.previous_energy) # energy = HWR.beamline.energy.get_current_energy() self.madEnergySelectedSignal.emit("", self.previous_energy, state) def max_osc_range_toggled(self, state): self.update_osc_total_range_limits() def get_mad_energy(self): energy_str = str(self.acq_widget_layout.energies_combo.currentText()) (name, value) = energy_str.split(":") name = name.strip() value = value.strip() value = 0 if (value == "-") else float(value) return (name, value) def set_energies(self, energy_scan_result): self.acq_widget_layout.energies_combo.clear() inflection = ( ("ip: %.4f" % energy_scan_result.inflection) if energy_scan_result.inflection else "ip: -" ) peak = ( ("pk: %.4f" % energy_scan_result.peak) if energy_scan_result.peak else "pk: -" ) first_remote = ( ("rm1: %.4f" % energy_scan_result.first_remote) if energy_scan_result.first_remote else "rm1: -" ) second_remote = ( ("rm2: %.4f" % energy_scan_result.second_remote) if energy_scan_result.second_remote else "rm2: -" ) self.acq_widget_layout.energies_combo.addItems( [inflection, peak, first_remote, second_remote] ) def energy_selected(self, index): if self.acq_widget_layout.mad_cbox.isChecked(): (name, energy) = self.get_mad_energy() if energy != 0: self.update_energy(energy) self.madEnergySelectedSignal.emit(name, energy, True) def energy_ledit_changed(self, new_value): if "energy" not in self.value_changed_list: self.value_changed_list.append("energy") self.emit_acq_parameters_changed() def update_energy(self, energy): if ( "energy" not in self.value_changed_list and not self.acq_widget_layout.energy_ledit.hasFocus() ): self.acq_widget_layout.energy_ledit.setText(str(energy)) self.emit_acq_parameters_changed() def transmission_ledit_changed(self, transmission): if "transmission" not in self.value_changed_list: self.value_changed_list.append("transmission") self.emit_acq_parameters_changed() def update_transmission(self, transmission): if "transmission" not in self.value_changed_list: self.acq_widget_layout.transmission_ledit.setText(str(transmission)) self.emit_acq_parameters_changed() def resolution_ledit_changed(self, resolution): if "resolution" not in self.value_changed_list: self.value_changed_list.append("resolution") self.emit_acq_parameters_changed() def update_resolution(self, resolution): if ( "resolution" not in self.value_changed_list and not self.acq_widget_layout.resolution_ledit.hasFocus() ): self.acq_widget_layout.resolution_ledit.setText(str(resolution)) self.emit_acq_parameters_changed() def update_energy_limits(self, limits): if limits: self.energy_validator.setBottom(limits[0]) self.energy_validator.setTop(limits[1]) self.acq_widget_layout.energy_ledit.setToolTip( "Energy limits %0.4f : %0.4f keV\n" % (limits[0], limits[1]) + "4 digits precision." ) self._acquisition_mib.validate_all() def update_transmission_limits(self, limits): if limits: self.transmission_validator.setBottom(limits[0]) self.transmission_validator.setTop(limits[1]) self.acq_widget_layout.transmission_ledit.setToolTip( "Transmission limits %0.2f : %0.2f %%\n" % (limits[0], limits[1]) + "2 digits precision." ) self._acquisition_mib.validate_all() def update_resolution_limits(self, limits): if limits: self.resolution_validator.setBottom(limits[0]) self.resolution_validator.setTop(limits[1]) self.acq_widget_layout.resolution_ledit.setToolTip( "Resolution limits %0.4f : %0.4f %s\n" % (limits[0], limits[1], chr(197)) + "4 digits precision." ) self._acquisition_mib.validate_all() def update_detector_exp_time_limits(self, limits): if limits: self.exp_time_validator.setRange(limits[0], limits[1], 6) self.acq_widget_layout.exp_time_ledit.setToolTip( "Exposure time limits %0.6f s : %0.1f s\n" % (limits[0], limits[1]) + "6 digits precision." ) self._acquisition_mib.validate_all() def update_num_images_limits(self, num_images_limits=None): """Updates number of images limit. Method used if plate mode. """ self._acquisition_mib.validate_all() return if HWR.beamline.diffractometer.in_plate_mode(): if num_images_limits is None: try: osc_start = float(self.acq_widget_layout.osc_start_ledit.text()) osc_range = float(self.acq_widget_layout.osc_range_ledit.text()) except ValueError: return if osc_range == 0: return num_images_limits = int( (self.osc_start_validator.top() - osc_start) / osc_range ) self.num_img_validator.setTop(num_images_limits) self.acq_widget_layout.num_images_ledit.setToolTip( "Number of images limits : %d" % num_images_limits ) self._acquisition_mib.validate_all() def init_detector_roi_modes(self): roi_modes = HWR.beamline.detector.get_roi_modes() if ( len(roi_modes) > 0 and self.acq_widget_layout.detector_roi_mode_combo.count() == 0 ): for roi_mode in roi_modes: self.acq_widget_layout.detector_roi_mode_combo.addItem(roi_mode) self.acq_widget_layout.detector_roi_mode_label.setEnabled(True) self.acq_widget_layout.detector_roi_mode_combo.setEnabled(True) def update_detector_roi_mode(self, roi_mode_index): if ( roi_mode_index is not None and self.acq_widget_layout.detector_roi_mode_combo.count() > 0 ): self.acq_widget_layout.detector_roi_mode_combo.setCurrentIndex( roi_mode_index ) def detector_roi_mode_changed(self, roi_mode_index): HWR.beamline.detector.set_roi_mode(roi_mode_index) def kappa_ledit_changed(self, new_value): if "kappa" not in self.value_changed_list: self.value_changed_list.append("kappa") self.emit_acq_parameters_changed() def kappa_phi_ledit_changed(self, new_value): if "kappa_phi" not in self.value_changed_list: self.value_changed_list.append("kappa_phi") self.emit_acq_parameters_changed() def update_data_model(self, acquisition_parameters, path_template): self._acquisition_parameters = acquisition_parameters self._path_template = path_template self._acquisition_mib.set_model(acquisition_parameters) # Update mad widgets mad = True if self._path_template.mad_prefix != "" else False if mad: mad_prefix = str(self._path_template.mad_prefix) index = MAD_ENERGY_COMBO_NAMES[str(mad_prefix)] self.acq_widget_layout.energies_combo.setCurrentIndex(index) self.acq_widget_layout.mad_cbox.setChecked(True) self.acq_widget_layout.energies_combo.setEnabled(True) else: self.acq_widget_layout.mad_cbox.setChecked(False) self.acq_widget_layout.energies_combo.setEnabled(False) self.acq_widget_layout.energies_combo.setCurrentIndex(0) self.update_osc_total_range() self.update_total_exp_time() self.emit_acq_parameters_changed() def set_tunable_energy(self, state): self.acq_widget_layout.energy_ledit.setEnabled(state) self.acq_widget_layout.mad_cbox.setEnabled(state) self.acq_widget_layout.energies_combo.setEnabled(state) def check_parameter_conflict(self): return self._acquisition_mib.validate_all() def emit_acq_parameters_changed(self): self.acqParametersChangedSignal.emit(self._acquisition_mib.validate_all())
class ConfirmDialog(qt_import.QDialog): continueClickedSignal = qt_import.pyqtSignal(list, list) def __init__(self, parent=None, name=None, flags=0): qt_import.QDialog.__init__( self, parent, qt_import.Qt.WindowFlags(flags | qt_import.Qt.WindowStaysOnTopHint), ) if name is not None: self.setObjectName(name) # Internal variables -------------------------------------------------- self.checked_items = [] self.sample_items = [] # Graphic elements ---------------------------------------------------- self.conf_dialog_layout = qt_import.load_ui_file( "confirmation_dialog_layout.ui") continue_shortcut = qt_import.QShortcut( qt_import.QKeySequence("C"), self.conf_dialog_layout.continue_button) continue_shortcut.activated.connect(self.continue_shortcut_pressed) # Layout -------------------------------------------------------------- _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.conf_dialog_layout) _main_vlayout.setContentsMargins(0, 0, 0, 0) _main_vlayout.setSpacing(0) # Qt signal/slot connections ------------------------------------------ self.conf_dialog_layout.continue_button.clicked.connect( self.continue_button_click) self.conf_dialog_layout.cancel_button.clicked.connect( self.cancel_button_click) # SizePolicies -------------------------------------------------------- self.setMinimumWidth(1200) # Other --------------------------------------------------------------- self.setWindowTitle("Confirm collection") def set_plate_mode(self, plate_mode): """Sets plate mode""" if plate_mode: snapshot_count = [0, 1] self.conf_dialog_layout.take_video_cbx.setEnabled(False) self.conf_dialog_layout.take_video_cbx.setChecked(False) else: snapshot_count = [0, 1, 2, 4] self.conf_dialog_layout.take_snapshots_combo.clear() for item in snapshot_count: self.conf_dialog_layout.take_snapshots_combo.addItem(str(item)) self.conf_dialog_layout.take_snapshots_combo.setCurrentIndex(1) def disable_dark_current_cbx(self): self.conf_dialog_layout.force_dark_cbx.setEnabled(False) self.conf_dialog_layout.force_dark_cbx.setChecked(False) def enable_dark_current_cbx(self): self.conf_dialog_layout.force_dark_cbx.setEnabled(True) self.conf_dialog_layout.force_dark_cbx.setChecked(True) def set_items(self, checked_items): """Populates information about items to be collected""" self.sample_items = [] self.checked_items = checked_items collection_items = [] current_sample_item = None sample_treewidget_item = None collection_group_treewidget_item = None num_images = 0 file_exists = False interleave_items = 0 self.conf_dialog_layout.summary_treewidget.clear() self.conf_dialog_layout.file_treewidget.clear() self.conf_dialog_layout.interleave_cbx.setChecked(False) self.conf_dialog_layout.interleave_images_num_ledit.setText("") self.conf_dialog_layout.inverse_cbx.setChecked(False) self.conf_dialog_layout.inverse_beam_num_images_ledit.setText("") for item in checked_items: # item_type_name = "" info_str_list = [] acq_parameters = None path_template = None item_model = item.get_model() item_type_name = item_model.get_display_name() if isinstance(item, queue_item.SampleQueueItem): self.sample_items.append(item) current_sample_item = item info_str_list.append(item_model.get_name()) if item.mounted_style: info_str_list.append("Already mounted") else: info_str_list.append("Sample mounting") sample_treewidget_item = qt_import.QTreeWidgetItem( self.conf_dialog_layout.summary_treewidget, info_str_list) for col in range(13): sample_treewidget_item.setBackground( col, qt_import.QBrush(colors.TREE_ITEM_SAMPLE)) sample_treewidget_item.setExpanded(True) elif isinstance(item, queue_item.DataCollectionGroupQueueItem): info_str_list.append(item_type_name) collection_group_treewidget_item = qt_import.QTreeWidgetItem( sample_treewidget_item, info_str_list) collection_group_treewidget_item.setExpanded(True) elif isinstance(item, queue_item.SampleCentringQueueItem): info_str_list.append(item_type_name) qt_import.QTreeWidgetItem(collection_group_treewidget_item, info_str_list) elif isinstance(item, queue_item.DataCollectionQueueItem): acq_parameters = item_model.acquisitions[ 0].acquisition_parameters if not item_model.is_helical() and not item_model.is_mesh(): interleave_items += 1 elif isinstance(item, queue_item.CharacterisationQueueItem): acq_parameters = item_model.reference_image_collection.acquisitions[ 0].acquisition_parameters self.conf_dialog_layout.take_snapshots_combo.setCurrentIndex( self.conf_dialog_layout.take_snapshots_combo.count() - 1) elif isinstance(item, queue_item.XrayCenteringQueueItem): acq_parameters = item_model.mesh_dc.acquisitions[ 0].acquisition_parameters elif isinstance(item, queue_item.XrayImagingQueueItem): acq_parameters = item_model.acquisitions[ 0].acquisition_parameters path_template = item_model.get_path_template() if acq_parameters and path_template: info_str_list.append(item_type_name) info_str_list.append("") info_str_list.append(path_template.directory) # This part is also in data_path_widget. Mote to PathTemplate file_name = path_template.get_image_file_name() file_name = file_name.replace( "%" + path_template.precision + "d", int(path_template.precision) * "#", ) file_name = file_name.strip(" ") info_str_list.append(file_name) info_str_list.append("%.3f keV" % acq_parameters.energy) info_str_list.append("%.2f A" % acq_parameters.resolution) info_str_list.append("%.2f %%" % acq_parameters.transmission) info_str_list.append("%.1f" % acq_parameters.osc_start) info_str_list.append(str(acq_parameters.osc_range)) info_str_list.append(str(acq_parameters.num_images)) info_str_list.append("%s s" % str(acq_parameters.exp_time)) info_str_list.append( str(acq_parameters.num_images * acq_parameters.osc_range)) info_str_list.append( "%s s" % str(acq_parameters.num_images * acq_parameters.exp_time)) collection_treewidget_item = qt_import.QTreeWidgetItem( collection_group_treewidget_item, info_str_list) for col in range(13): collection_treewidget_item.setBackground( col, qt_import.QBrush(colors.TREE_ITEM_COLLECTION)) collection_items.append(item) file_paths = path_template.get_files_to_be_written() num_images += acq_parameters.num_images if len(file_paths) > 20: file_paths = file_paths[:20] for file_path in file_paths: if os.path.exists(file_path): (dir_name, file_name) = os.path.split(file_path) sample_name = current_sample_item.get_model( ).get_display_name() if sample_name is "": sample_name = current_sample_item.get_model( ).loc_str file_str_list = [] file_str_list.append(sample_name) file_str_list.append(dir_name) file_str_list.append(file_name) file_treewidgee_item = qt_import.QTreeWidgetItem( self.conf_dialog_layout.file_treewidget, file_str_list) if hasattr(file_treewidgee_item, "setTextcolor"): file_treewidgee_item.setTextcolor( 1, qt_import.Qt.red) file_treewidgee_item.setTextcolor( 2, qt_import.Qt.red) else: file_treewidgee_item.setForeground( 1, qt_import.QBrush(qt_import.Qt.red)) file_treewidgee_item.setForeground( 2, qt_import.QBrush(qt_import.Qt.red)) file_exists = True self.conf_dialog_layout.file_gbox.setEnabled(file_exists) self.conf_dialog_layout.interleave_cbx.setEnabled(interleave_items > 1) self.conf_dialog_layout.inverse_cbx.setEnabled(interleave_items == 1) num_samples = len(self.sample_items) num_collections = len(collection_items) for col_index in range( self.conf_dialog_layout.summary_treewidget.columnCount()): if col_index != 2: self.conf_dialog_layout.summary_treewidget.resizeColumnToContents( col_index) self.conf_dialog_layout.summary_label.setText( "Collecting " + str(num_collections) + " collection(s) on " + str(num_samples) + " sample(s) resulting in " + str(num_images) + " image(s).") def continue_shortcut_pressed(self): self.continue_button_click() def continue_button_click(self): for item in self.checked_items: item_model = item.get_model() acq_parameters = None if isinstance(item_model, queue_model_objects.DataCollection): acq_parameters = item_model.acquisitions[ 0].acquisition_parameters elif isinstance(item_model, queue_model_objects.Characterisation): acq_parameters = item_model.reference_image_collection.acquisitions[ 0].acquisition_parameters elif isinstance(item_model, queue_model_objects.XrayCentering): acq_parameters = item_model.mesh_dc.acquisitions[ 0].acquisition_parameters elif isinstance(item_model, queue_model_objects.TaskGroup): try: item_model.interleave_num_images = int( self.conf_dialog_layout.interleave_images_num_ledit. text()) except BaseException: pass try: item_model.inverse_beam_num_images = int( self.conf_dialog_layout.inverse_beam_num_images_ledit. text()) except BaseException: pass if acq_parameters: acq_parameters.take_snapshots = int( self.conf_dialog_layout.take_snapshots_combo.currentText()) acq_parameters.take_video = ( self.conf_dialog_layout.take_video_cbx.isChecked()) acq_parameters.take_dark_current = ( self.conf_dialog_layout.force_dark_cbx.isChecked()) acq_parameters.skip_existing_images = ( self.conf_dialog_layout.skip_existing_images_cbx.isChecked( )) self.continueClickedSignal.emit(self.sample_items, self.checked_items) self.accept() def cancel_button_click(self): self.reject()
class WrapperHO(qt_import.QObject): DEVICE_MAP = { "Device": "Procedure", "SOLEILGuillotine": "Shutter", "SoleilSafetyShutter": "Shutter", "TangoShutter": "Shutter", "ShutterEpics": "Shutter", "MD2v4_FastShutter": "Shutter", "TempShutter": "Shutter", "EMBLSafetyShutter": "Shutter", "MDFastShutter": "Shutter", "WagoPneu": "WagoPneu", "Shutter": "WagoPneu", "SpecMotorWSpecPositions": "WagoPneu", "Procedure": "WagoPneu", } WAGO_STATE = {"in": "in", "out": "out", "unknown": "unknown"} SHUTTER_STATE = { "fault": "error", "opened": "in", "noperm": "noperm", "closed": "out", "unknown": "unknown", "moving": "moving", "automatic": "automatic", "disabled": "disabled", "error": "error", } DOOR_INTERLOCK_STATE = { "locked": "out", "unlocked": "disabled", "locked_active": "out", "locked_inactive": "disabled", "error": "error", } MOTOR_WPOS = ("out", "in") MOTOR_WSTATE = ("disabled", "error", None, "moving", "moving", "moving") STATES = ( "unknown", "disabled", "closed", "error", "out", "moving", "in", "automatic", "noperm", ) duoStateChangedSignal = qt_import.pyqtSignal(str, str) def __init__(self, hardware_obj): qt_import.QObject.__init__(self) # self.setIn = new.instancemethod(lambda self: None, self) self.setIn = lambda self: None self.setOut = self.setIn # self.get-State = new.instancemethod(lambda self: "unknown", self) self.get_state = lambda self: "unknown" self.dev = hardware_obj try: sClass = str(self.dev.__class__) i, j = re.search("'.*'", sClass).span() except BaseException: dev_class = sClass else: dev_class = sClass[i + 1 : j - 1] self.devClass = dev_class.split(".")[-1] self.devClass = WrapperHO.DEVICE_MAP.get(self.devClass, "Shutter") initFunc = getattr(self, "init%s" % self.devClass) initFunc() self.setIn = getattr(self, "setIn%s" % self.devClass) self.setOut = getattr(self, "setOut%s" % self.devClass) self.get_state = getattr(self, "getState%s" % self.devClass) def __getstate__(self): dict = self.__dict__.copy() del dict["setIn"] del dict["setOut"] del dict["getState"] return dict def __setstate__(self, dict): self.__dict__ = dict.copy() try: # Python2 import new self.setIn = new.instancemethod(lambda self: None, self) self.setOut = self.setIn self.get_state = new.instancemethod(lambda self: "unknown", self) except ImportError: import types self.setIn = types.MethodType(lambda self: None, self) self.setOut = self.setIn self.get_state = types.MethodType(lambda self: "unknown", self) def userName(self): return self.dev.username # WagoPneu HO methods def initWagoPneu(self): self.dev.connect(self.dev, "wagoStateChanged", self.stateChangedWagoPneu) def setInWagoPneu(self): self.duoStateChangedSignal.emit("moving") self.dev.wagoIn() def setOutWagoPneu(self): self.duoStateChangedSignal.emit("moving") self.dev.wagoOut() def stateChangedWagoPneu(self, state): try: state = WrapperHO.WAGO_STATE[state] except KeyError: state = "error" self.duoStateChangedSignal.emit(state) def getStateWagoPneu(self): state = self.dev.getWagoState() try: state = WrapperHO.WAGO_STATE[state] except KeyError: state = "error" return state # Shutter HO methods def initShutter(self): self.dev.connect(self.dev, "shutterStateChanged", self.stateChangedShutter) def setInShutter(self): self.dev.openShutter() def setOutShutter(self): self.dev.closeShutter() def stateChangedShutter(self, state, state_label=None): state = WrapperHO.SHUTTER_STATE.get(state, "unknown") if not state_label: state_label = "" self.duoStateChangedSignal.emit(state, state_label) def getStateShutter(self): state = self.dev.getShutterState() try: state = WrapperHO.SHUTTER_STATE[state] except KeyError: state = "error" return state # SpecMotorWSpecPositions HO methods def initSpecMotorWSpecPositions(self): self.positions = None self.dev.connect( self.dev, "predefinedPositionChanged", self.position_changed_spec_motor_wspec_positions, ) self.dev.connect( self.dev, "stateChanged", self.stateChangedSpecMotorWSpecPositions ) self.dev.connect( self.dev, "newPredefinedPositions", self.new_predefined_spec_motor_wspec_positions, ) def setInSpecMotorWSpecPositions(self): if self.positions is not None: self.dev.moveToPosition(self.positions[1]) def setOutSpecMotorWSpecPositions(self): if self.positions is not None: self.dev.moveToPosition(self.positions[0]) def stateChangedSpecMotorWSpecPositions(self, state): # logging.info("stateChangedSpecMotorWSpecPositions %s" % state) try: state = WrapperHO.MOTOR_WSTATE[state] except IndexError: state = "error" if state is not None: self.duoStateChangedSignal.emit(state) def position_changed_spec_motor_wspec_positions(self, pos_name, pos): if self.dev.get_state() != self.dev.READY: return state = "error" if self.positions is not None: for i in range(len(self.positions)): if pos_name == self.positions[i]: state = WrapperHO.MOTOR_WPOS[i] self.duoStateChangedSignal.emit(state) def get_state_spec_motor_wspec_positions(self): if self.positions is None: return "error" curr_pos = self.dev.get_current_position_name() if curr_pos is None: state = self.dev.get_state() try: state = WrapperHO.MOTOR_WSTATE[state] except IndexError: state = "error" return state else: for i in range(len(self.positions)): if curr_pos == self.positions[i]: return WrapperHO.MOTOR_WPOS[i] return "error" def new_predefined_spec_motor_wspec_positions(self): self.positions = self.dev.get_predefined_positions_list() self.position_changed_spec_motor_wspec_positions( self.dev.get_current_position_name(), self.dev.get_value() ) # Procedure HO methods def init_procedure(self): cmds = self.dev.get_commands() self.set_in_cmd = None self.set_out_cmd = None try: channel = self.dev.get_channel_object("dev_state") except KeyError: channel = None self.stateChannel = channel if self.stateChannel is not None: self.state_dict = { "OPEN": "in", "CLOSED": "out", "ERROR": "error", "1": "in", "0": "out", } self.stateChannel.connect_signal("update", self.channel_update) else: self.state_dict = {} for cmd in cmds: if cmd.name() == "set in": self.set_in_cmd = cmd if self.stateChannel is not None: self.set_in_cmd.connect_signal( "commandReplyArrived", self.procedureSetInEnded ) self.set_in_cmd.connect_signal( "commandBeginWaitReply", self.procedure_started ) self.set_in_cmd.connect_signal( "commandFailed", self.procedure_aborted ) self.set_in_cmd.connect_signal( "commandAborted", self.procedure_aborted ) elif cmd.name() == "set out": self.set_out_cmd = cmd if self.stateChannel is not None: self.set_out_cmd.connect_signal( "commandReplyArrived", self.procedure_set_out_ended ) self.set_out_cmd.connect_signal( "commandBeginWaitReply", self.procedure_started ) self.set_out_cmd.connect_signal( "commandFailed", self.procedure_aborted ) self.set_out_cmd.connect_signal( "commandAborted", self.procedure_aborted ) def channel_update(self, value): try: key = self.dev.statekey except AttributeError: pass else: try: state = value[key] except TypeError: state = "error" try: state = self.state_dict[state] except KeyError: pass self.duoStateChangedSignal.emit(state) def set_in_procedure(self): if self.set_in_cmd is not None: self.set_in_cmd() def set_out_procedure(self): if self.set_out_cmd is not None: self.set_out_cmd() """ def stateChangedProcedure(self,state): pass """ def get_state_procedure(self): if self.stateChannel is not None: try: state = self.stateChannel.get_value() except BaseException: state = "error" else: try: key = self.dev.statekey except AttributeError: pass else: try: state = state[key] except TypeError: state = "error" try: state = self.state_dict[state] except KeyError: pass return state return "unknown" def procedureSetInEnded(self, *args): self.duoStateChangedSignal.emit("in") def procedure_set_out_ended(self, *args): self.duoStateChangedSignal.emit("out") def procedure_started(self, *args): self.duoStateChangedSignal.emit("moving") def procedure_aborted(self, *args): self.duoStateChangedSignal.emit("error")
class AcquisitionWidgetSimple(qt_import.QWidget): acqParametersChangedSignal = qt_import.pyqtSignal(list) madEnergySelectedSignal = qt_import.pyqtSignal(str, float, bool) def __init__( self, parent=None, name=None, fl=0, acq_params=None, path_template=None, layout=None, ): qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) # Internal variables -------------------------------------------------- self.value_changed_list = [] # Properties ---------------------------------------------------------- # Signals ------------------------------------------------------------- # Slots --------------------------------------------------------------- # Graphic elements ---------------------------------------------------- if acq_params is None: self._acquisition_parameters = queue_model_objects.AcquisitionParameters( ) else: self._acquisition_parameters = acq_params if path_template is None: self._path_template = queue_model_objects.PathTemplate() else: self._path_template = path_template self._acquisition_mib = DataModelInputBinder( self._acquisition_parameters) self.acq_widget_layout = qt_import.load_ui_file( "acquisition_widget_vertical_simple_layout.ui") # Layout -------------------------------------------------------------- main_layout = qt_import.QVBoxLayout(self) main_layout.addWidget(self.acq_widget_layout) main_layout.setSpacing(0) main_layout.setContentsMargins(0, 0, 0, 0) # SizePolicies -------------------------------------------------------- # Qt signal/slot connections ------------------------------------------ self.acq_widget_layout.osc_start_cbox.stateChanged.connect( self.use_osc_start) self.acq_widget_layout.num_images_cbox.activated.connect( self.update_num_images) self.acq_widget_layout.detector_roi_mode_combo.activated.connect( self.detector_roi_mode_changed) # Other --------------------------------------------------------------- self.osc_start_validator = qt_import.QDoubleValidator( -10000, 10000, 4, self.acq_widget_layout.osc_start_ledit) self.osc_range_validator = qt_import.QDoubleValidator( -10000, 10000, 4, self.acq_widget_layout.osc_range_ledit) self.kappa_validator = qt_import.QDoubleValidator( 0, 360, 4, self.acq_widget_layout.kappa_ledit) self.kappa_phi_validator = qt_import.QDoubleValidator( 0, 360, 4, self.acq_widget_layout.kappa_phi_ledit) self.energy_validator = qt_import.QDoubleValidator( 0, 25, 5, self.acq_widget_layout.energy_ledit) self.resolution_validator = qt_import.QDoubleValidator( 0, 15, 3, self.acq_widget_layout.resolution_ledit) self.transmission_validator = qt_import.QDoubleValidator( 0, 100, 3, self.acq_widget_layout.transmission_ledit) self.exp_time_validator = qt_import.QDoubleValidator( 0, 10000, 6, self.acq_widget_layout.exp_time_ledit) self.acq_widget_layout.num_images_cbox.setCurrentIndex(1) self.acq_widget_layout.detector_roi_mode_label.setEnabled(False) self.acq_widget_layout.detector_roi_mode_combo.setEnabled(False) self.set_tunable_energy(HWR.beamline.tunable_wavelength) if HWR.beamline.diffractometer.in_plate_mode(): self.acq_widget_layout.num_images_cbox.clear() self.acq_widget_layout.num_images_cbox.addItem("1") self.acq_widget_layout.num_images_cbox.setCurrentIndex(0) self.init_detector_roi_modes() self.init_limits() def update_osc_total_range(self): pass def set_osc_start_limits(self, limits): if not None in limits: self.osc_start_validator.setRange(limits[0], limits[1], 4) def update_osc_start(self, new_value): if not self.acq_widget_layout.osc_start_cbox.hasFocus(): self.acq_widget_layout.osc_start_ledit.setText(str(new_value)) def update_kappa(self, new_value): if not self.acq_widget_layout.kappa_ledit.hasFocus(): self.acq_widget_layout.kappa_ledit.setText(str(new_value)) def update_kappa_phi(self, new_value): if not self.acq_widget_layout.kappa_phi_ledit.hasFocus(): self.acq_widget_layout.kappa_phi_ledit.setText(str(new_value)) def use_kappa(self, state): if HWR.beamline.diffractometer.in_plate_mode(): state = False self.acq_widget_layout.kappa_label.setEnabled(state) self.acq_widget_layout.kappa_ledit.setEnabled(state) self.acq_widget_layout.kappa_phi_label.setEnabled(state) self.acq_widget_layout.kappa_phi_ledit.setEnabled(state) def use_max_osc_range(self, state): pass def update_num_images(self, index=None, num_images=None): if index is not None: if index is 0: self._acquisition_parameters.num_images = 1 self._path_template.num_files = 1 elif index is 1: self._acquisition_parameters.num_images = 2 self._path_template.num_files = 2 elif index is 2: self._acquisition_parameters.num_images = 4 self._path_template.num_files = 4 if num_images: if self.acq_widget_layout.num_images_cbox.count() > 3: self.acq_widget_layout.num_images_cbox.removeItem(4) if num_images is 1: self.acq_widget_layout.num_images_cbox.setCurrentIndex(0) elif num_images is 2: self.acq_widget_layout.num_images_cbox.setCurrentIndex(1) elif num_images is 4: self.acq_widget_layout.num_images_cbox.setCurrentIndex(2) else: self.acq_widget_layout.num_images_cbox.addItem(str(num_images)) self.acq_widget_layout.num_images_cbox.setCurrenIndex(3) self._path_template.num_files = num_images def use_mad(self, state): pass def get_mad_energy(self): pass def set_energies(self, energy_scan_result): pass def energy_selected(self, index): pass def init_limits(self): limits_dict = HWR.beamline.acquisition_limit_values tpl = limits_dict.get("osc_range") if tpl: self.osc_start_validator.setRange(tpl[0], tpl[1], 4) self.osc_range_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "osc_start", self.acq_widget_layout.osc_start_ledit, float, self.osc_start_validator, ) self._acquisition_mib.bind_value_update( "osc_range", self.acq_widget_layout.osc_range_ledit, float, self.osc_range_validator, ) tpl = limits_dict.get("kappa") if tpl: self.kappa_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "kappa", self.acq_widget_layout.kappa_ledit, float, self.kappa_validator) tpl = limits_dict.get("kappa_phi") if tpl: self.kappa_phi_validator.setRange(tpl[0], tpl[1], 4) self._acquisition_mib.bind_value_update( "kappa_phi", self.acq_widget_layout.kappa_phi_ledit, float, self.kappa_phi_validator, ) tpl = limits_dict.get("exposure_time") if tpl: self.exp_time_validator.setRange(tpl[0], tpl[1], 6) self._acquisition_mib.bind_value_update( "exp_time", self.acq_widget_layout.exp_time_ledit, float, self.exp_time_validator, ) self._acquisition_mib.bind_value_update( "energy", self.acq_widget_layout.energy_ledit, float, self.energy_validator) self.acq_widget_layout.energy_ledit.setToolTip( "Energy limits %0.3f : %0.3f" % (self.energy_validator.bottom(), self.energy_validator.top())) self._acquisition_mib.bind_value_update( "transmission", self.acq_widget_layout.transmission_ledit, float, self.transmission_validator, ) self._acquisition_mib.bind_value_update( "resolution", self.acq_widget_layout.resolution_ledit, float, self.resolution_validator, ) if HWR.beamline.diffractometer.in_plate_mode(): self.acq_widget_layout.num_images_cbox.clear() self.acq_widget_layout.num_images_cbox.addItem("1") self.acq_widget_layout.num_images_cbox.setCurrentIndex(0) def set_energy(self, energy, wav): if not self.acq_widget_layout.energy_ledit.hasFocus(): self.acq_widget_layout.energy_ledit.setText(str(energy)) def update_transmission(self, transmission): if self.acq_widget_layout.transmission_ledit.hasFocus(): self.acq_widget_layout.transmission_ledit.setText( str(transmission)) def update_resolution(self, resolution): if not self.acq_widget_layout.resolution_ledit.hasFocus(): self.acq_widget_layout.resolution_ledit.setText(str(resolution)) def update_energy_limits(self, limits): if limits: self.energy_validator.setBottom(limits[0]) self.energy_validator.setTop(limits[1]) self.acq_widget_layout.energy_ledit.setToolTip( "Energy limits %0.3f : %0.3f" % (limits[0], limits[1])) self._acquisition_mib.validate_all() def update_transmission_limits(self, limits): if limits: self.transmission_validator.setBottom(limits[0]) self.transmission_validator.setTop(limits[1]) self.acq_widget_layout.transmission_ledit.setToolTip( "Transmission limits %0.3f : %0.3f" % (limits[0], limits[1])) self._acquisition_mib.validate_all() def update_resolution_limits(self, limits): if limits: self.resolution_validator.setBottom(limits[0]) self.resolution_validator.setTop(limits[1]) self.acq_widget_layout.resolution_ledit.setToolTip( "Resolution limits %0.3f : %0.3f" % (limits[0], limits[1])) self._acquisition_mib.validate_all() def update_detector_exp_time_limits(self, limits): if limits: self.exp_time_validator.setBottom(limits[0]) self.exp_time_validator.setTop(limits[1]) self.acq_widget_layout.exp_time_ledit.setToolTip( "Exposure time limits %0.3f : %0.3f" % (limits[0], limits[1])) self._acquisition_mib.validate_all() def update_energy(self, energy): if ("energy" not in self.value_changed_list and not self.acq_widget_layout.energy_ledit.hasFocus()): self.acq_widget_layout.energy_ledit.setText("%.4f" % float(energy)) def init_detector_roi_modes(self): roi_modes = HWR.beamline.detector.get_roi_modes() if (len(roi_modes) > 0 and self.acq_widget_layout.detector_roi_mode_combo.count() == 0): for roi_mode in roi_modes: self.acq_widget_layout.detector_roi_mode_combo.addItem( roi_mode) self.acq_widget_layout.detector_roi_mode_label.setEnabled(True) self.acq_widget_layout.detector_roi_mode_combo.setEnabled(True) def update_exp_time_limits(self): try: exp_time_limits = ( HWR.beamline.detector.get_exposure_time_limits()) max_osc_speed = HWR.beamline.diffractometer.get_osc_max_speed() top_limit = (float(self.acq_widget_layout.osc_range_ledit.text()) / max_osc_speed) limits = (max(exp_time_limits[0], top_limit), exp_time_limits[1]) self.update_detector_exp_time_limits(limits) except BaseException: pass def update_detector_roi_mode(self, roi_mode_index): if (roi_mode_index is not None and self.acq_widget_layout.detector_roi_mode_combo.count() > 0): self.acq_widget_layout.detector_roi_mode_combo.setCurrentIndex( roi_mode_index) def update_osc_range_limits(self, limits=None): pass def detector_roi_mode_changed(self, roi_mode_index): HWR.beamline.detector.set_roi_mode(roi_mode_index) def update_data_model(self, acquisition_parameters, path_template): self._acquisition_parameters = acquisition_parameters self._acquisition_mib.set_model(acquisition_parameters) self._path_template = path_template self.update_num_images(None, acquisition_parameters.num_images) def set_tunable_energy(self, state): self.acq_widget_layout.energy_ledit.setEnabled(state) def use_osc_start(self, state): self.acq_widget_layout.osc_start_ledit.setEnabled(state) def check_parameter_conflict(self): return self._acquisition_mib.validate_all()
class PowerBar(qt_import.QWidget): """ Custom Qt Widget to show a power bar and dial. Demonstrating compound and custom-drawn widget. Left-clicking the button shows the color-chooser, while right-clicking resets the color to None (no-color). """ value_changed = qt_import.pyqtSignal(int) def __init__(self, steps=5, *args, **kwargs): super(PowerBar, self).__init__(*args, **kwargs) layout = qt_import.QVBoxLayout() self._bar = _Bar(steps) layout.addWidget(self._bar) # Create the QDial widget and set up defaults. # - we provide accessors on this class to override. self._dial = qt_import.QDial() self._dial.setNotchesVisible(True) self._dial.setWrapping(False) self._dial.valueChanged.connect(self._bar._trigger_refresh) self._dial.valueChanged.connect(self.slot_value_changed) # Take feedback from click events on the meter. self._bar.clickedValue.connect(self._dial.setValue) layout.addWidget(self._dial) self.setLayout(layout) # now, _dial and _bar have a parent() a PowerBar object def __getattr__(self, name): if name in self.__dict__: return self[name] return getattr(self._dial, name) def setValue(self, new_value): self._dial.setValue(new_value) def setColor(self, color): self._bar.steps = [color] * self._bar.n_steps self._bar.update() def setColors(self, colors): self._bar.n_steps = len(colors) self._bar.steps = colors self._bar.update() def setBarPadding(self, i): self._bar._padding = int(i) self._bar.update() def setBarSolidPercent(self, f): self._bar._bar_solid_percent = float(f) self._bar.update() def setBackgroundColor(self, color): self._bar._background_color = qt_import.QColor(color) self._bar.update() def slot_value_changed(self, new_value): self.value_changed.emit(new_value) def setDialVisible(self, visible): self._dial.setVisible(visible) def setBarVisible(self, visible): self._bar.setVisible(visible) def dialAndBarInvible(self): return (not self._bar.isVisible() and not self._dial.isVisible())
class TaskToolboxBrick(BaseWidget): request_tree_brick = qt_import.pyqtSignal() def __init__(self, *args): BaseWidget.__init__(self, *args) # Internal values ----------------------------------------------------- self.ispyb_logged_in = False self.tree_brick = None # Properties ---------------------------------------------------------- self.add_property("useOscStartCbox", "boolean", False) self.add_property("useCompression", "boolean", False) #self.add_property("availableTasks", "string", "discrete char helical") self.add_property("showDiscreetTask", "boolean", True) self.add_property("showHelicalTask", "boolean", True) self.add_property("showCharTask", "boolean", True) self.add_property("showAdvancedTask", "boolean", True) self.add_property("showSsxTask", "boolean", False) self.add_property("showCollectNowButton", "boolean", False) # Signals ------------------------------------------------------------- self.define_signal("request_tree_brick", ()) # Slots --------------------------------------------------------------- self.define_slot("logged_in", ()) self.define_slot("set_session", ()) self.define_slot("selection_changed", ()) self.define_slot("user_group_saved", ()) self.define_slot("set_tree_brick", ()) # Graphic elements ---------------------------------------------------- self.task_tool_box_widget = TaskToolBoxWidget(self) # Layout -------------------------------------------------------------- self.main_layout = qt_import.QVBoxLayout(self) self.main_layout.addWidget(self.task_tool_box_widget) self.main_layout.setSpacing(0) self.main_layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.main_layout) # SizePolicies -------------------------------------------------------- # self.setSizePolicy(qt_import.QSizePolicy.MinimumExpanding, # qt_import.QSizePolicy.MinimumExpanding) # Other --------------------------------------------------------------- HWR.beamline.sample_view.connect("pointSelected", self.point_selected) def set_expert_mode(self, expert): self.task_tool_box_widget.set_expert_mode(expert) def run(self): if HWR.beamline.session.session_id: self.setEnabled(True) #self.task_tool_box_widget.set_available_tasks(self["availableTasks"]) self.request_tree_brick.emit() self.task_tool_box_widget.adjust_width(self.width()) def user_group_saved(self, new_user_group): HWR.beamline.session.set_user_group(str(new_user_group)) self.task_tool_box_widget.update_data_path_model() path = (HWR.beamline.session.get_base_image_directory() + "/" + str(new_user_group)) msg = "Image path is: %s" % path logging.getLogger("GUI").info(msg) @qt_import.pyqtSlot(BaseWidget) def set_tree_brick(self, brick): self.tree_brick = brick self.tree_brick.compression_state = self["useCompression"] == 1 self.task_tool_box_widget.set_tree_brick(brick) @qt_import.pyqtSlot(int, str, str, int, str, str, bool) def set_session( self, session_id, t_prop_code=None, prop_number=None, prop_id=None, start_date=None, prop_code=None, is_inhouse=None, ): """ Connected to the slot set_session and is called after a request to get the current session from LIMS (ISPyB) is made. The signal is normally emitted by the brick that handles LIMS login, ie ProposalBrick. The session_id is '' if no session could be retrieved. """ if session_id is "": self.logged_in(True) @qt_import.pyqtSlot(bool) def logged_in(self, logged_in): """ Handels the signal logged_in from the brick the handles LIMS (ISPyB) login, ie ProposalBrick. The signal is emitted when a user was succesfully logged in. """ logged_in = True self.ispyb_logged_in = logged_in if HWR.beamline.session is not None: HWR.beamline.session.set_user_group("") self.setEnabled(logged_in) self.task_tool_box_widget.ispyb_logged_in(logged_in) def property_changed(self, property_name, old_value, new_value): if property_name == "useOscStartCbox": self.task_tool_box_widget.use_osc_start_cbox(new_value) elif property_name == "useCompression": self.task_tool_box_widget.enable_compression(new_value) elif property_name == "showCollectNowButton": self.task_tool_box_widget.collect_now_button.setVisible(new_value) elif property_name == "showDiscreetTask": if not new_value: self.task_tool_box_widget.hide_task( self.task_tool_box_widget.discrete_page) elif property_name == "showHelicalTask": if not new_value: self.task_tool_box_widget.hide_task( self.task_tool_box_widget.helical_page) elif property_name == "showCharTask": if not new_value: self.task_tool_box_widget.hide_task( self.task_tool_box_widget.char_page) elif property_name == "showAdvancedTask": if not new_value: self.task_tool_box_widget.hide_task( self.task_tool_box_widget.advanced_page) elif property_name == "showSsxTask": if not new_value: self.task_tool_box_widget.hide_task( self.task_tool_box_widget.ssx_page) def selection_changed(self, items): """ Connected to the signal "selection_changed" of the TreeBrick. Called when the selection in the tree changes. """ self.task_tool_box_widget.selection_changed(items) def point_selected(self, selected_position): self.task_tool_box_widget.helical_page.centred_position_selection( selected_position) self.task_tool_box_widget.discrete_page.centred_position_selection( selected_position) self.task_tool_box_widget.char_page.centred_position_selection( selected_position) self.task_tool_box_widget.energy_scan_page.centred_position_selection( selected_position) self.task_tool_box_widget.xrf_spectrum_page.centred_position_selection( selected_position) self.task_tool_box_widget.discrete_page.refresh_current_item() self.task_tool_box_widget.helical_page.refresh_current_item() self.task_tool_box_widget.char_page.refresh_current_item() self.task_tool_box_widget.energy_scan_page.refresh_current_item() self.task_tool_box_widget.xrf_spectrum_page.refresh_current_item()
class CustomPeriodicTable(QPeriodicTable.QPeriodicTable): edgeSelectedSignal = qt_import.pyqtSignal(str, str) def __init__(self, *args): QPeriodicTable.QPeriodicTable.__init__(self, *args) self.elements_dict = {} if PYMCA_IMPORTED == 5: self.sigElementClicked.connect(self.table_element_clicked) else: qt_import.QObject.connect( self, qt_import.SIGNAL("elementClicked"), self.table_element_clicked ) for b in self.eltButton: self.eltButton[b].colors[0] = qt_import.QColor(qt_import.Qt.green) self.eltButton[b].colors[1] = qt_import.QColor(qt_import.Qt.darkGreen) self.eltButton[b].setEnabled(False) for el in QPeriodicTable.Elements: symbol = el[0] self.elements_dict[symbol] = el def elementEnter(self, symbol, z=None, name=None): if isinstance(symbol, tuple) and len(symbol) > 0: symbol = symbol[0] b = self.eltButton[symbol] if b.isEnabled(): b.setCurrent(True) def elementLeave(self, symbol): if isinstance(symbol, tuple) and len(symbol) > 0: symbol = symbol[0] b = self.eltButton[symbol] if b.isEnabled(): b.setCurrent(False) def table_element_clicked(self, symbol, energy=None): if isinstance(symbol, tuple) and len(symbol) > 0: symbol = symbol[0] if energy is None: energy = self.energies_dict[symbol] self.setSelection((symbol,)) if energy is None: energy = self.energies_dict[symbol] else: index = self.elements_dict[symbol][1] name = self.elements_dict[symbol][4] txt = "%s - %s (%s,%s)" % (symbol, energy, index, name) self.eltLabel.setText(txt) self.edgeSelectedSignal.emit(symbol, energy) # self.widgetSynchronizeSignal([symbol, energy]) def setElements(self, elements): self.energies_dict = {} for b in self.eltButton: self.eltButton[b].setEnabled(False) first_element = None for element in elements: symbol = element["symbol"] if first_element is None: first_element = symbol energy = element["energy"] self.energies_dict[symbol] = energy b = self.eltButton[symbol] b.setEnabled(True) def widgetSynchronize(self, state): symbol = state[0] self.tableElementChanged(symbol)
class ProcessingWidget(qt_import.QWidget): enableProcessingSignal = qt_import.pyqtSignal(bool, bool) def __init__(self, parent=None, name=None, fl=0, data_model=None): qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) if data_model is None: self._model = queue_model_objects.ProcessingParameters() else: self._model = data_model self._model_mib = DataModelInputBinder(self._model) self.processing_widget = qt_import.load_ui_file( "processing_widget_vertical_layout.ui") self.main_layout = qt_import.QVBoxLayout(self) self.main_layout.addWidget(self.processing_widget) self.main_layout.setSpacing(0) self.main_layout.setContentsMargins(0, 0, 0, 0) self.processing_widget.space_group_combo.addItems( queue_model_enumerables.XTAL_SPACEGROUPS) self._model_mib.bind_value_update("cell_a", self.processing_widget.a_ledit, float, None) self._model_mib.bind_value_update("cell_alpha", self.processing_widget.alpha_ledit, float, None) self._model_mib.bind_value_update("cell_b", self.processing_widget.b_ledit, float, None) self._model_mib.bind_value_update("cell_beta", self.processing_widget.beta_ledit, float, None) self._model_mib.bind_value_update("cell_c", self.processing_widget.c_ledit, float, None) self._model_mib.bind_value_update("cell_gamma", self.processing_widget.gamma_ledit, float, None) self._model_mib.bind_value_update( "num_residues", self.processing_widget.num_residues_ledit, float, None) self._model_mib.bind_value_update( "resolution_cutoff", self.processing_widget.resolution_cutoff_ledit, float, None) self._model_mib.bind_value_update( "pdb_file", self.processing_widget.pdb_file_ledit, str, None) self.processing_widget.space_group_combo.activated.connect( self._space_group_change) self.processing_widget.run_offline_processing_cbox.stateChanged.connect( self._run_offline_processing_toggled) self.processing_widget.run_online_processing_cbox.stateChanged.connect( self._run_online_processing_toggled) self.processing_widget.pdb_file_browse_button.clicked.connect( self._browse_clicked) self.processing_widget.resolution_cutoff_label.setHidden(True) self.processing_widget.resolution_cutoff_ledit.setHidden(True) self.processing_widget.pdb_file_label.setHidden(True) self.processing_widget.pdb_file_ledit.setHidden(True) self.processing_widget.pdb_file_browse_button.setHidden(True) if HWR.beamline.offline_processing_methods: cbox_text = "Run offline processing (" for method in HWR.beamline.offline_processing_methods: cbox_text += "%s, " % method cbox_text = cbox_text[:-2] + ")" self.processing_widget.run_offline_processing_cbox.setText( cbox_text) self.processing_widget.run_offline_processing_cbox.setChecked( HWR.beamline.run_offline_processing) else: self.processing_widget.run_offline_processing_cbox.setChecked( False) self.processing_widget.run_offline_processing_cbox.setEnabled( False) if HWR.beamline.online_processing_methods: cbox_text = "Run online processing (" for method in HWR.beamline.online_processing_methods: cbox_text += "%s, " % method cbox_text = cbox_text[:-2] + ")" self.processing_widget.run_online_processing_cbox.setText( cbox_text) self.processing_widget.run_online_processing_cbox.setChecked( HWR.beamline.run_online_processing) else: self.processing_widget.run_online_processing_cbox.setChecked(False) self.processing_widget.run_online_processing_cbox.setEnabled(False) def _space_group_change(self, index): self._model.space_group = queue_model_enumerables.XTAL_SPACEGROUPS[ index] def _set_space_group(self, space_group): index = 0 if space_group in queue_model_enumerables.XTAL_SPACEGROUPS: index = queue_model_enumerables.XTAL_SPACEGROUPS.index(space_group) self._space_group_change(index) self.processing_widget.space_group_combo.setCurrentIndex(index) def update_data_model(self, model): self._model = model self._model_mib.set_model(model) self._set_space_group(model.space_group) def _run_offline_processing_toggled(self, state): self.enableProcessingSignal.emit( self.processing_widget.run_offline_processing_cbox.isChecked(), self.processing_widget.run_online_processing_cbox.isChecked(), ) def _run_online_processing_toggled(self, state): self.enableProcessingSignal.emit( self.processing_widget.run_offline_processing_cbox.isChecked(), self.processing_widget.run_online_processing_cbox.isChecked(), ) def _browse_clicked(self): file_dialog = qt_import.QFileDialog(self) pdb_filename = str( file_dialog.getOpenFileName(self, "Select a PDB file", expanduser("~"))) self._model.pdb_file = pdb_filename self.processing_widget.pdb_file_ledit.setText(pdb_filename) def get_processing_state(self): return self.processing_widget.run_offline_processing_cbox.isChecked(), \ self.processing_widget.run_online_processing_cbox.isChecked()
class ChatBrick(BaseWidget): PRIORITY_COLORS = ("darkblue", "black", "red") MY_COLOR = "darkgrey" incoming_unread_messages = qt_import.pyqtSignal(int, bool) reset_unread_messages = qt_import.pyqtSignal(bool) def __init__(self, *args): BaseWidget.__init__(self, *args) # Properties ---------------------------------------------------------- self.add_property("mnemonic", "string", "") self.add_property("icons", "string", "") self.add_property("myTabLabel", "string", "") # Signals ------------------------------------------------------------ self.define_signal("incoming_unread_messages", ()) self.define_signal("reset_unread_message", ()) # Slots --------------------------------------------------------------- self.define_slot("tabSelected", ()) self.define_slot("sessionSelected", ()) # Hardware objects ---------------------------------------------------- self.instance_server_hwobj = None # Internal values ----------------------------------------------------- self.session_id = None self.nickname = "" self.role = BaseWidget.INSTANCE_ROLE_UNKNOWN # Graphic elements ---------------------------------------------------- self.conversation_textedit = qt_import.QTextEdit(self) self.conversation_textedit.setReadOnly(True) _controls_widget = qt_import.QWidget(self) _say_label = qt_import.QLabel("Say:", _controls_widget) self.message_ledit = qt_import.QLineEdit(_controls_widget) self.send_button = qt_import.QPushButton("Send", _controls_widget) self.send_button.setEnabled(False) # Layout -------------------------------------------------------------- _controls_widget_hlayout = qt_import.QHBoxLayout(_controls_widget) _controls_widget_hlayout.addWidget(_say_label) _controls_widget_hlayout.addWidget(self.message_ledit) _controls_widget_hlayout.addWidget(self.send_button) _controls_widget_hlayout.setSpacing(2) _controls_widget_hlayout.setContentsMargins(0, 0, 0, 0) _main_vlayout = qt_import.QVBoxLayout(self) _main_vlayout.addWidget(self.conversation_textedit) _main_vlayout.addWidget(_controls_widget) _main_vlayout.setSpacing(2) _main_vlayout.setContentsMargins(2, 2, 2, 2) # Qt signal/slot connections ------------------------------------------ self.send_button.clicked.connect(self.send_current_message) self.message_ledit.returnPressed.connect(self.send_current_message) self.message_ledit.textChanged.connect(self.message_changed) # self.setFixedHeight(120) # self.setFixedWidth(790) def run(self): self.set_role(self.role) def session_selected(self, *args): session_id = args[0] is_inhouse = args[-1] self.conversation_textedit.clear() if is_inhouse: self.session_id = None else: self.session_id = session_id self.load_chat_history() def load_chat_history(self): if self.instance_server_hwobj is not None: chat_history_filename = "/tmp/mxCuBE_chat_%s.%s" % ( self.session_id, self.instance_server_hwobj.isClient() and "client" or "server", ) else: return try: chat_history = open(chat_history_filename, "r") except BaseException: return if self.isEnabled(): for msg in chat_history.readlines(): self.conversation_textedit.append(msg) def instance_role_changed(self, role): self.set_role(role) def set_role(self, role): self.role = role if role != BaseWidget.INSTANCE_ROLE_UNKNOWN and not self.isEnabled(): self.setEnabled(True) self.load_chat_history() def message_changed(self, text): self.send_button.setEnabled(len(str(text)) > 0) def message_arrived(self, priority, user_id, message): color = ChatBrick.PRIORITY_COLORS[priority] msg_prefix = "" msg_suffix = "" if priority == QtInstanceServer.ChatInstanceMessage.PRIORITY_NORMAL: if user_id is None: header = "" else: header = " %s:" % self.instance_server_hwobj.idPrettyPrint( user_id) if user_id[0] == self.nickname: color = ChatBrick.MY_COLOR else: header = "" msg_prefix = "<i>" msg_suffix = "</i>" now = time.strftime("%T") new_line = "<font color=%s><b>(%s)%s</b> %s%s%s</font>" % ( color, now, header, msg_prefix, message, msg_suffix, ) self.conversation_textedit.append(new_line) if self.session_id is not None and self.instance_server_hwobj is not None: chat_history_filename = "/tmp/mxCuBE_chat_%s.%s" % ( self.session_id, self.instance_server_hwobj.isClient() and "client" or "server", ) try: if time.time() - os.stat( chat_history_filename).st_mtime > 24 * 3600: os.unlink(chat_history_filename) except OSError: pass chat_history_file = open(chat_history_filename, "a") chat_history_file.write(new_line) chat_history_file.write("\n") chat_history_file.close() # self.emit(QtCore.SIGNAL("incUnreadMessages"),1, True) self.incoming_unread_messages.emit(1, True) def new_client(self, client_id): msg = ("%s has joined the conversation." % self.instance_server_hwobj.idPrettyPrint(client_id)) self.message_arrived(QtInstanceServer.ChatInstanceMessage.PRIORITY_LOW, None, msg) def wants_control(self, client_id): msg = "%s wants to have control!" % self.instance_server_hwobj.idPrettyPrint( client_id) self.message_arrived( QtInstanceServer.ChatInstanceMessage.PRIORITY_HIGH, None, msg) def server_initialized(self, started, server_id=None): if started: # sg="I'm moderating the chat as %s." % server_id[0] # self.message_arrived(InstanceServer.ChatInstanceMessage.PRIORITY_LOW,None,msg) self.nickname = server_id[0] def client_closed(self, client_id): msg = ("%s has left the conversation..." % self.instance_server_hwobj.idPrettyPrint(client_id)) self.message_arrived(QtInstanceServer.ChatInstanceMessage.PRIORITY_LOW, None, msg) def client_initialized(self, connected, server_id=None, my_nickname=None, quiet=False): if connected: server_print = self.instance_server_hwobj.idPrettyPrint(server_id) msg = "I've joined the conversation as %s (moderator is %s)." % ( my_nickname, server_print, ) self.message_arrived( QtInstanceServer.ChatInstanceMessage.PRIORITY_LOW, None, msg) self.nickname = my_nickname def client_changed(self, old_client_id, new_client_id): # print "CHAT CLIENT CHANGED",old_client_id,new_client_id if old_client_id[0] == self.nickname: self.nickname = new_client_id[0] else: old_client_print = self.instance_server_hwobj.idPrettyPrint( old_client_id) new_client_print = self.instance_server_hwobj.idPrettyPrint( new_client_id) msg = "%s has changed to %s." % (old_client_print, new_client_print) self.message_arrived( QtInstanceServer.ChatInstanceMessage.PRIORITY_LOW, None, msg) def send_current_message(self): txt = str(self.message_ledit.text()) if len(txt): self.instance_server_hwobj.sendChatMessage( QtInstanceServer.ChatInstanceMessage.PRIORITY_NORMAL, txt) self.message_ledit.setText("") def property_changed(self, property_name, old_value, new_value): if property_name == "mnemonic": if self.instance_server_hwobj is not None: self.disconnect( self.instance_server_hwobj, "chatMessageReceived", self.message_arrived, ) self.disconnect(self.instance_server_hwobj, "newClient", self.new_client) self.disconnect( self.instance_server_hwobj, "serverInitialized", self.server_initialized, ) self.disconnect( self.instance_server_hwobj, "clientInitialized", self.client_initialized, ) self.disconnect(self.instance_server_hwobj, "serverClosed", self.client_closed) self.disconnect(self.instance_server_hwobj, "wantsControl", self.wants_control) self.disconnect(self.instance_server_hwobj, "haveControl", self.have_control) self.disconnect(self.instance_server_hwobj, "passControl", self.pass_control) self.disconnect(self.instance_server_hwobj, "clientClosed", self.client_closed) self.disconnect(self.instance_server_hwobj, "clientChanged", self.client_changed) self.instance_server_hwobj = self.get_hardware_object(new_value) if self.instance_server_hwobj is not None: self.connect( self.instance_server_hwobj, "chatMessageReceived", self.message_arrived, ) self.connect(self.instance_server_hwobj, "newClient", self.new_client) self.connect( self.instance_server_hwobj, "serverInitialized", self.server_initialized, ) self.connect( self.instance_server_hwobj, "clientInitialized", self.client_initialized, ) self.connect(self.instance_server_hwobj, "serverClosed", self.client_closed) self.connect(self.instance_server_hwobj, "wantsControl", self.wants_control) self.connect(self.instance_server_hwobj, "haveControl", self.have_control) self.connect(self.instance_server_hwobj, "passControl", self.pass_control) self.connect(self.instance_server_hwobj, "clientClosed", self.client_closed) self.connect(self.instance_server_hwobj, "clientChanged", self.client_changed) elif property_name == "icons": icons_list = new_value.split() try: self.send_button.setIcon(icons.load_icon(icons_list[0])) except IndexError: pass else: BaseWidget.property_changed(self, property_name, old_value, new_value) def have_control(self, have_control, gui_only=False): if not gui_only: if have_control: p = QtInstanceServer.ChatInstanceMessage.PRIORITY_HIGH msg = "I've gained control!" else: p = QtInstanceServer.ChatInstanceMessage.PRIORITY_HIGH msg = "I've lost control..." self.message_arrived(p, None, msg) def pass_control(self, has_control_id): has_control_print = self.instance_server_hwobj.idPrettyPrint( has_control_id) msg = "%s has control." % has_control_print self.message_arrived(QtInstanceServer.ChatInstanceMessage.PRIORITY_LOW, None, msg) def tabSelected(self, tab_name): if tab_name == self["myTabLabel"]: # self.emit(QtCore.SIGNAL("resetUnreadMessages"), True) self.reset_unread_messages.emit(True)
class GUISupervisor(qt_import.QWidget): """GUI supervisor""" brickChangedSignal = qt_import.pyqtSignal(str, str, str, tuple, bool) tabChangedSignal = qt_import.pyqtSignal(str, int) def __init__(self, design_mode=False, show_maximized=False, no_border=False): """Main mxcube gui widget""" qt_import.QWidget.__init__(self) self.framework = None self.gui_config_file = None self.user_file_dir = None self.configuration = None self.user_settings = None self.launch_in_design_mode = design_mode self.hardware_repository = HWR.get_hardware_repository() self.show_maximized = show_maximized self.no_border = no_border self.windows = [] self.splash_screen = SplashScreen(icons.load_pixmap("splash")) set_splash_screen(self.splash_screen) self.splash_screen.show() self.time_stamp = 0 def set_user_file_directory(self, user_file_directory): """Sets user file directory""" self.user_file_dir = user_file_directory BaseWidget.set_user_file_directory(user_file_directory) def load_gui(self, gui_config_file): """Loads gui""" self.configuration = configuration.Configuration() self.gui_config_file = gui_config_file if self.gui_config_file: load_from_dict = gui_config_file.endswith( ".json") or gui_config_file.endswith(".yml") if hasattr(self, "splash_screen"): self.splash_screen.set_gui_name( os.path.splitext(os.path.basename(gui_config_file))[0]) if os.path.exists(gui_config_file): filestat = os.stat(gui_config_file) self.time_stamp = filestat[stat.ST_MTIME] if filestat[stat.ST_SIZE] == 0: return self.new_gui() try: gui_file = open(gui_config_file) except BaseException: logging.getLogger().exception("Cannot open file %s", gui_config_file) qt_import.QMessageBox.warning( self, "Error", "Could not open file %s !" % gui_config_file, qt_import.QMessageBox.Ok, ) else: # find mnemonics to speed up loading # (using the 'require' feature from Hardware Repository) def __get_mnemonics(items_list): """Gets mnemonics""" mne_list = [] for item in items_list: if "brick" in item: try: if load_from_dict: props = item["properties"] else: props = pickle.loads( item["properties"]) except BaseException: logging.getLogger().exception( "Could not load properties for %s" % item["name"]) else: item["properties"] = props try: for prop in props: if load_from_dict: prop_value = prop["value"] else: prop_value = prop.get_value() if isinstance( prop_value, type("") ) and prop_value.startswith("/"): mne_list.append(prop_value) except BaseException: logging.exception( "Could not " + "build list of required " + "hardware objects") continue mne_list += __get_mnemonics(item["children"]) return mne_list failed_msg = ("Cannot read configuration from file %s. " % gui_config_file) failed_msg += "Starting in designer mode with clean GUI." raw_config = None try: if gui_config_file.endswith(".json"): raw_config = json.load(gui_file) elif gui_config_file.endswith(".yml"): raw_config = yaml.safe_load(gui_file) else: raw_config = eval(gui_file.read()) except BaseException: logging.getLogger().exception(failed_msg) self.splash_screen.set_message("Gathering H/O info...") self.splash_screen.set_progress_value(10) mnemonics = __get_mnemonics(raw_config) self.hardware_repository.require(mnemonics) gui_file.close() try: self.splash_screen.set_message( "Building GUI configuration...") self.splash_screen.set_progress_value(20) config = configuration.Configuration(raw_config) except BaseException: logging.getLogger("GUI").exception(failed_msg) qt_import.QMessageBox.warning(self, "Error", failed_msg, qt_import.QMessageBox.Ok) else: self.configuration = config try: user_settings_filename = os.path.join( self.user_file_dir, "settings.dat") user_settings_file = open(user_settings_filename) self.user_settings = eval(user_settings_file.read()) except BaseException: self.user_settings = [] logging.getLogger().error( "Unable to read user settings file: %s" % user_settings_filename) else: user_settings_file.close() if len(self.configuration.windows) == 0: return self.new_gui() #self.hardware_repository.print_report() if self.launch_in_design_mode: self.framework = gui_builder.GUIBuilder() qt_import.QApplication.setActiveWindow(self.framework) self.framework.filename = gui_config_file self.framework.configuration = config self.framework.setWindowTitle("GUI Builder - %s" % gui_config_file) self.framework.gui_editor_window.set_configuration( config) self.framework.gui_editor_window.draw_window_preview() self.framework.show() return self.framework else: main_window = self.execute(self.configuration) return main_window return self.new_gui() def new_gui(self): """Starts new gui""" self.time_stamp = 0 self.launch_in_design_mode = True self.framework = gui_builder.GUIBuilder() qt_import.QApplication.setActiveWindow(self.framework) self.framework.show() self.framework.new_clicked(self.gui_config_file) return self.framework def display(self): """Shows all defined windows""" self.windows = [] for window in self.configuration.windows_list: display = gui_display.WindowDisplayWidget(None, window["name"], execution_mode=True, no_border=self.no_border) self.windows.append(display) display.set_caption(window["properties"]["caption"]) display.draw_preview(window, id(display)) display.close_on_exit = window["properties"]["closeOnExit"] display.set_keep_open(window["properties"]["keepOpen"]) display.set_font_size(window["properties"]["fontSize"]) if window["properties"]["show"]: display._show = True else: display._show = False display.hide() for item in self.user_settings: if item["name"] == window["name"]: display.move(item["posx"], item["posy"]) display.resize(item["width"], item["height"]) for window in self.windows: window.append_windows_links(self.windows) def execute(self, config): """Start in execution mode""" self.splash_screen.set_message("Executing configuration...") self.splash_screen.set_progress_value(90) self.display() main_window = None if len(self.windows) > 0: main_window = self.windows[0] main_window.configuration = config qt_import.QApplication.setActiveWindow(main_window) if self.no_border: main_window.move(0, 0) width = qt_import.QApplication.desktop().width() height = qt_import.QApplication.desktop().height() main_window.resize(qt_import.QSize(width, height)) # make connections widgets_dict = dict([( isinstance(w.objectName, collections.Callable) and str(w.objectName()) or None, w, ) for w in qt_import.QApplication.allWidgets()]) def make_connections(items_list): """Creates connections""" for item in items_list: try: sender = widgets_dict[item["name"]] except KeyError: logging.getLogger().error( "Could not find receiver widget %s" % item["name"]) else: for connection in item["connections"]: _receiver = (connection["receiver"] or connection["receiverWindow"]) try: receiver = widgets_dict[_receiver] except KeyError: logging.getLogger().error( "Could not find " + "receiver widget %s", _receiver) else: try: slot = getattr(receiver, connection["slot"]) # etattr(sender, connection["signal"]).connect(slot) except AttributeError: logging.getLogger().error( "No slot '%s' " % connection["slot"] + "in receiver %s" % _receiver) else: if not isinstance(sender, NullBrick): getattr( sender, connection["signal"]).connect(slot) # sender.connect(sender, # QtCore.SIGNAL(connection["signal"]), # slot) make_connections(item["children"]) self.splash_screen.set_progress_value(95) self.splash_screen.set_message("Connecting bricks...") make_connections(config.windows_list) # set run mode for every brick self.splash_screen.set_progress_value(100) self.splash_screen.set_message("Setting run mode...") BaseWidget.set_run_mode(True) if self.show_maximized: main_window.showMaximized() else: main_window.show() for window in self.windows: if window._show: window.show() if BaseWidget._menubar: BaseWidget._menubar.set_exp_mode(False) HWR.beamline.force_emit_signals() return main_window def finalize(self): """Finalize gui load""" BaseWidget.set_run_mode(False) # call .stop() for each brick self.hardware_repository.close() qt_import.QApplication.sendPostedEvents() qt_import.QApplication.processEvents() self.save_size() def save_size(self): """Saves window size and coordinates in the gui file""" display_config_list = [] if not self.launch_in_design_mode: for window in self.windows: window_cfg = self.configuration.windows[str( window.objectName())] display_config_list.append({ "name": window_cfg.name, "posx": window.x(), "posy": window.y(), "width": window.width(), "height": window.height(), }) try: user_settings_filename = os.path.join(self.user_file_dir, "settings.dat") user_settings_file = open(user_settings_filename, "w") user_settings_file.write(repr(display_config_list)) os.chmod(user_settings_filename, 0o660) except BaseException: logging.getLogger().exception( "Unable to save window position and size in " + "configuration file: %s" % user_settings_filename) else: user_settings_file.close() def finish_init(self, gui_config_file): """Finalize gui init""" while True: try: self.hardware_repository.connect() except BaseException: logging.getLogger().exception( "Timeout while trying to " + "connect to Hardware Repository server.") message = ( "Timeout while connecting to Hardware " + "Repository server.\nMake sure the Hardware " + "Repository Server is running on host:\n%s." % str(self.hardware_repository.server_address).split(":")[0]) if (qt_import.QMessageBox.warning( self, "Cannot connect to Hardware Repository", message, qt_import.QMessageBox.Retry | qt_import.QMessageBox.Cancel | qt_import.QMessageBox.NoButton, ) == qt_import.QMessageBox.Cancel): logging.getLogger().warning( "Gave up trying to " + "connect to Hardware Repository server.") break else: logging.getLogger().info( "Connected to Hardware " + "Repository server %s" % self.hardware_repository.server_address) break try: main_widget = None main_widget = self.load_gui(gui_config_file) if main_widget: set_splash_screen(None) self.splash_screen.finish(main_widget) del self.splash_screen except BaseException: logging.getLogger().exception("exception while loading GUI file") qt_import.QApplication.exit() def customEvent(self, event): """Custom event""" self.finish_init(event.data)
class MoveBox(qt_import.QWidget): """Widget to set position and launch motor mouvement Widget composed of: A text box to enter new position Button to launch new position Button to recover last positions Button to stop motor """ # define signals move_motor_signal = qt_import.pyqtSignal(float) clicked_signal = qt_import.pyqtSignal(float) stop_motor_signal = qt_import.pyqtSignal() def __init__(self, parent=None): """" Constructor of MoveBox :param parent: MoveBox parent widget """ super(MoveBox, self).__init__(parent) self.old_positions = [] # history of motor positions self.label_move = qt_import.QLabel("go to ", self) self.text_move = qt_import.QLineEdit("", self) self.cmd_move = qt_import.QPushButton("", self) self.cmd_move.setCheckable(True) self.cmd_go_back = qt_import.QPushButton("", self) self.cmd_stop = qt_import.QPushButton("", self) self.text_move.setFixedWidth( self.text_move.fontMetrics().width("8888.8888")) self.cmd_move.setCheckable(True) self.cmd_stop.setIcon(icons.load_icon("stop_small")) self.cmd_stop.setEnabled(False) self.cmd_go_back.setIcon(icons.load_icon("goback_small")) self.cmd_move.setIcon(icons.load_icon("move_small")) self.cmd_go_back.setEnabled(False) # connections self.text_move.textChanged.connect(self.text_move_text_changed) self.cmd_move.clicked.connect(self.move_clicked) self.cmd_stop.clicked.connect(self.stop_motor_signal) self.text_move.returnPressed.connect(self.text_move_return_pressed) self.cmd_go_back.clicked.connect(self.go_back_clicked) # layout hboxlayout = qt_import.QHBoxLayout(self) hboxlayout.insertSpacerItem( 0, qt_import.QSpacerItem(0, 0, qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Fixed)) hboxlayout.addWidget(self.label_move) hboxlayout.addWidget(self.text_move) hboxlayout.addWidget(self.cmd_move) hboxlayout.addWidget(self.cmd_go_back) hboxlayout.addWidget(self.cmd_stop) hboxlayout.insertSpacerItem( 0, qt_import.QSpacerItem(0, 0, qt_import.QSizePolicy.Expanding, qt_import.QSizePolicy.Fixed)) self.setLayout(hboxlayout) def move_clicked(self): """Act when move clicked.""" self.text_move_return_pressed() def set_old_position(self, position): """Recover one of motor's old position.""" position = str(position) if len(self.old_positions) == 20: del self.old_positions[-1] if position in self.old_positions: return self.old_positions.insert(0, position) def text_move_return_pressed(self): """Selected one of motor's old position.""" try: move_position = float(str(self.text_move.text())) except BaseException: self.cmd_move.setChecked(False) else: self.move_motor_signal.emit(move_position) def text_move_text_changed(self, text): """Act when text value change.""" if text and not self.cmd_move.isChecked(): self.cmd_move.setEnabled(True) else: self.cmd_move.setEnabled(False) def go_back_clicked(self): """Act when 'go to old position' button clicked.""" old_positions_menu = qt_import.QMenu(self) old_positions_menu.addSection( str("<nobr><b>Last positions :</b></nobr>")) old_positions_menu.addSeparator() for i in range(len(self.old_positions)): receiver = lambda i: self.go_to_old_position(i) position_action = old_positions_menu.addAction( self.old_positions[i]) position_action.triggered.connect(receiver) old_positions_menu.exec_(qt_import.QCursor.pos()) def go_to_old_position(self, old_pos_id): """Move motor to selected old position.""" pos = self.old_positions[old_pos_id] self.text_move.setText(pos) self.text_move_return_pressed() def set_is_moving(self, moving): """Update display according to motor moving or not.""" if moving: self.text_move.setText("") self.cmd_move.setChecked(True) self.cmd_move.setEnabled(False) self.cmd_go_back.setEnabled(False) self.cmd_stop.setEnabled(True) else: self.cmd_move.setChecked(False) if len(self.text_move.text()) > 0: self.cmd_move.setEnabled(True) else: self.cmd_move.setEnabled(False) if len(self.old_positions) > 0: self.cmd_go_back.setEnabled(True) self.cmd_stop.setEnabled(False)
class AcquisitionSsxWidget(qt_import.QWidget): acqParametersChangedSignal = qt_import.pyqtSignal(list) def __init__( self, parent=None, name=None, fl=0, acq_params=None, path_template=None, layout="vertical", ): """ Loads ui file that defines the gui layout. Initiates QLineEdits by adding limits, precision Connects to qt signals to update acquisition parameters :param parent: :param name: :param fl: :param acq_params: :param path_template: :param layout: """ qt_import.QWidget.__init__(self, parent, qt_import.Qt.WindowFlags(fl)) if name is not None: self.setObjectName(name) # Internal variables -------------------------------------------------- self.value_changed_list = [] # Properties ---------------------------------------------------------- # Signals ------------------------------------------------------------- # Slots --------------------------------------------------------------- # Graphic elements ---------------------------------------------------- if acq_params is None: self._acquisition_parameters = queue_model_objects.AcquisitionParameters( ) else: self._acquisition_parameters = acq_params if path_template is None: self._path_template = queue_model_objects.PathTemplate() else: self._path_template = path_template self._acquisition_mib = DataModelInputBinder( self._acquisition_parameters) self.acq_widget_layout = qt_import.load_ui_file( "acquisition_widget_vertical_ssx_layout.ui") # Layout -------------------------------------------------------------- __main_vlayout = qt_import.QVBoxLayout(self) __main_vlayout.addWidget(self.acq_widget_layout) __main_vlayout.setSpacing(0) __main_vlayout.setContentsMargins(0, 0, 0, 0) # SizePolicies -------------------------------------------------------- # Qt signal/slot connections ------------------------------------------ self.acq_widget_layout.num_triggers_ledit.textChanged.connect( self.num_triggers_ledit_changed) self.acq_widget_layout.num_images_per_trigger_ledit.textChanged.connect( self.num_images_per_trigger_ledit_changed) self.acq_widget_layout.exp_time_ledit.textChanged.connect( self.exposure_time_ledit_changed) self.acq_widget_layout.detector_roi_mode_combo.activated.connect( self.detector_roi_mode_changed) self.acq_widget_layout.energy_ledit.textEdited.connect( self.energy_ledit_changed) self.acq_widget_layout.transmission_ledit.textEdited.connect( self.transmission_ledit_changed) self.acq_widget_layout.resolution_ledit.textEdited.connect( self.resolution_ledit_changed) # Other --------------------------------------------------------------- self.energy_validator = qt_import.QDoubleValidator( 4, 25, 4, self.acq_widget_layout.energy_ledit) self.resolution_validator = qt_import.QDoubleValidator( 0, 15, 3, self.acq_widget_layout.resolution_ledit) self.transmission_validator = qt_import.QDoubleValidator( 0, 100, 3, self.acq_widget_layout.transmission_ledit) self.exp_time_validator = qt_import.QDoubleValidator( 0.0001, 10000, 7, self.acq_widget_layout.exp_time_ledit) self.num_triggers_validator = qt_import.QIntValidator( 1, 9999999, self.acq_widget_layout.num_triggers_ledit) self.num_images_per_trigger_validator = qt_import.QIntValidator( 1, 9999999, self.acq_widget_layout.num_images_per_trigger_ledit) self.num_img_validator = qt_import.QIntValidator( 1, 9999999, self.acq_widget_layout.num_images_ledit) self.hare_num_validator = qt_import.QIntValidator( 1, 9999999, self.acq_widget_layout.hare_num_ledit) limits_dict = HWR.beamline.acquisition_limit_values tpl = limits_dict.get("exposure_time") if tpl: self.exp_time_validator.setRange(tpl[0], tpl[1], 6) self._acquisition_mib.bind_value_update( "exp_time", self.acq_widget_layout.exp_time_ledit, float, self.exp_time_validator, ) self._acquisition_mib.bind_value_update( "num_triggers", self.acq_widget_layout.num_triggers_ledit, int, self.num_triggers_validator, ) self._acquisition_mib.bind_value_update( "num_images_per_trigger", self.acq_widget_layout.num_images_per_trigger_ledit, int, self.num_images_per_trigger_validator, ) self._acquisition_mib.bind_value_update( "hare_num", self.acq_widget_layout.hare_num_ledit, int, self.hare_num_validator, ) tpl = limits_dict.get("number_of_images") if tpl: self.num_img_validator.setRange(tpl[0], tpl[1]) self._acquisition_mib.bind_value_update( "num_images", self.acq_widget_layout.num_images_ledit, int, self.num_img_validator, ) self._acquisition_mib.bind_value_update( "energy", self.acq_widget_layout.energy_ledit, float, self.energy_validator) self.update_energy_limits( (self.energy_validator.bottom(), self.energy_validator.top())) self._acquisition_mib.bind_value_update( "transmission", self.acq_widget_layout.transmission_ledit, float, self.transmission_validator, ) self.update_transmission_limits((self.transmission_validator.bottom(), self.transmission_validator.top())) self._acquisition_mib.bind_value_update( "resolution", self.acq_widget_layout.resolution_ledit, float, self.resolution_validator, ) self.init_detector_roi_modes() self.acq_widget_layout.detector_roi_mode_label.setEnabled(False) self.acq_widget_layout.detector_roi_mode_combo.setEnabled(False) self.update_exp_time_limits() def update_osc_total_range(self): """ :return: None """ return def use_osc_start(self, status): """ :param status: boolean :return: None """ return def use_max_osc_range(self, status): """ :param status: boolean :return: None """ return def use_kappa(self, status): """ :param status: boolean :return: None """ return def exposure_time_ledit_changed(self, value): """ Updates exposure time QLineEdit :param value: str :return: None """ self.update_total_exp_time() self.emit_acq_parameters_changed() def energy_ledit_changed(self, value): """ Fixes energy value. Energy change will not rewrite the typed energy value :param value: str :return: None """ if "energy" not in self.value_changed_list: self.value_changed_list.append("energy") self.emit_acq_parameters_changed() def update_energy(self, energy): """ Updates energy QLineEdit :param energy: energy in keV (float) :param wav: wavelength in A (float) :return: None """ if ("energy" not in self.value_changed_list and not self.acq_widget_layout.energy_ledit.hasFocus()): self.acq_widget_layout.energy_ledit.setText(str(energy)) self.emit_acq_parameters_changed() def transmission_ledit_changed(self, transmission): """ Event when a value in the transmission QLineEdit is changed :param transmission: in perc. (str) :return: None """ if "transmission" not in self.value_changed_list: self.value_changed_list.append("transmission") self.emit_acq_parameters_changed() def update_transmission(self, transmission): """ Updates transmission QLineEdit :param transmission: in perc. (float) :return: None """ if "transmission" not in self.value_changed_list: self.acq_widget_layout.transmission_ledit.setText( str(transmission)) self.emit_acq_parameters_changed() def resolution_ledit_changed(self, resolution): """ Method called when user changes resolution :param resolution: in A (float) :return: None """ if "resolution" not in self.value_changed_list: self.value_changed_list.append("resolution") self.emit_acq_parameters_changed() def update_resolution(self, resolution): """ Updates resolution QLineEdit :param resolution: A (float) :return: None """ if ("resolution" not in self.value_changed_list and not self.acq_widget_layout.resolution_ledit.hasFocus()): self.acq_widget_layout.resolution_ledit.setText(str(resolution)) self.emit_acq_parameters_changed() def update_energy_limits(self, limits): """ Updates energy limits :param limits: list of two floats :return: None """ if limits: self.energy_validator.setBottom(limits[0]) self.energy_validator.setTop(limits[1]) self.acq_widget_layout.energy_ledit.setToolTip( "Energy limits %0.4f : %0.4f keV\n" % (limits[0], limits[1]) + "4 digits precision.") self._acquisition_mib.validate_all() def update_transmission_limits(self, limits): """ Updates transmission limits :param limits: list of two floats :return: None """ if limits: self.transmission_validator.setBottom(limits[0]) self.transmission_validator.setTop(limits[1]) self.acq_widget_layout.transmission_ledit.setToolTip( "Transmission limits %0.2f : %0.2f %%\n" % (limits[0], limits[1]) + "2 digits precision.") self._acquisition_mib.validate_all() def update_resolution_limits(self, limits): """ Updates resolution limits :param limits: list of two floats :return: None """ if limits: self.resolution_validator.setBottom(limits[0]) self.resolution_validator.setTop(limits[1]) self.acq_widget_layout.resolution_ledit.setToolTip( "Resolution limits %0.4f : %0.4f %s\n" % (limits[0], limits[1], chr(197)) + "4 digits precision.") self._acquisition_mib.validate_all() def update_detector_exp_time_limits(self, limits): """ Updates exposure time limits :param limits: list of two floats :return: None """ if limits: self.exp_time_validator.setRange(limits[0], limits[1], 6) self.acq_widget_layout.exp_time_ledit.setToolTip( "Exposure time limits %0.6f s : %0.1f s\n" % (limits[0], limits[1]) + "6 digits precision.") self._acquisition_mib.validate_all() def init_detector_roi_modes(self): """ Initiates detetor ROI modes. Available modes are added to the combobox :return: None """ roi_modes = HWR.beamline.detector.get_roi_modes() if (len(roi_modes) > 0 and self.acq_widget_layout.detector_roi_mode_combo.count() == 0): for roi_mode in roi_modes: self.acq_widget_layout.detector_roi_mode_combo.addItem( roi_mode) self.acq_widget_layout.detector_roi_mode_label.setEnabled( len(roi_modes) > 1) self.acq_widget_layout.detector_roi_mode_combo.setEnabled( len(roi_modes) > 1) def update_detector_roi_mode(self, roi_mode_index): """ Method called when roi mode has been chaned :param roi_mode_index: int :return: None """ if (roi_mode_index is not None and self.acq_widget_layout.detector_roi_mode_combo.count() > 0): self.acq_widget_layout.detector_roi_mode_combo.setCurrentIndex( roi_mode_index) def detector_roi_mode_changed(self, roi_mode_index): """ Method called when user selects a detector roi mode :param roi_mode_index: int :return: """ HWR.beamline.detector.set_roi_mode(roi_mode_index) def update_osc_range_per_frame_limits(self): """ Updates osc range per frame limits :return: None """ return def update_exp_time_limits(self): self.update_detector_exp_time_limits( HWR.beamline.detector.get_exposure_time_limits()) def update_osc_start(self, value): """ Updates osc start :param value: float :return: None """ return def update_kappa(self, value): """ Updates kappa value :param value: float :return: """ return def update_kappa_phi(self, value): """ Updates kappa phi value :param value: float :return: None """ return def update_data_model(self, acquisition_parameters, path_template): """ Updates data model :param acquisition_parameters: AcquisitionParameters :param path_template: PathTemplate :return: None """ self._acquisition_parameters = acquisition_parameters self._path_template = path_template self._acquisition_mib.set_model(acquisition_parameters) self.emit_acq_parameters_changed() def check_parameter_conflict(self): """ Checks for parameter conflicts :return: list of conflicts """ return self._acquisition_mib.validate_all() def emit_acq_parameters_changed(self): """ Emits acqParametersChangedSignal :return: None """ self.acqParametersChangedSignal.emit( self._acquisition_mib.validate_all()) def set_energies(self, energies): """ Sets energies :param energies: :return: None """ return def num_triggers_ledit_changed(self, value): """ Updates num images and total exp time :param value: QString :return: None """ if "num_triggers" not in self.value_changed_list: self.value_changed_list.append("num_triggers") self.update_num_images() self.update_total_exp_time() self.emit_acq_parameters_changed() def num_images_per_trigger_ledit_changed(self, value): """ Updates num images and total exp time :param value: QString :return: None """ if "num_images_per_trigger" not in self.value_changed_list: self.value_changed_list.append("num_images_per_trigger") self.update_num_images() self.update_total_exp_time() self.emit_acq_parameters_changed() def update_num_images(self): """ Updates num images :return: None """ self.acq_widget_layout.num_images_ledit.setText( str(self._acquisition_parameters.num_triggers * self._acquisition_parameters.num_images_per_trigger)) def update_total_exp_time(self): """Updates total exposure time :return: None """ try: self.acq_widget_layout.exp_time_total_ledit.setText( "%.2f" % (float(self.acq_widget_layout.exp_time_ledit.text()) * float(self.acq_widget_layout.num_images_ledit.text()))) except BaseException: pass
class PlotWidget(qt_import.QWidget): mouseMovedSignal = qt_import.pyqtSignal(float, float) mouseClickedSignal = qt_import.pyqtSignal(float, float) mouseDoubleClickedSignal = qt_import.pyqtSignal(float, float) mouseLeftSignal = qt_import.pyqtSignal() def __init__(self, parent=None): qt_import.QWidget.__init__(self, parent) self.curves_dict = {} self.visible_curve = None self.view_box = CustomViewBox() self.one_dim_plot = pg.PlotWidget(viewBox=self.view_box) self.two_dim_plot = pg.ImageView() self.one_dim_plot.showGrid(x=True, y=True) self.two_dim_plot.ui.histogram.hide() self.two_dim_plot.ui.roiBtn.hide() self.two_dim_plot.ui.menuBtn.hide() self.two_dim_plot.setFixedWidth(400) hlayout = qt_import.QHBoxLayout(self) hlayout.addWidget(self.one_dim_plot) hlayout.addWidget(self.two_dim_plot) colors = [(0, 0, 0), (255, 0, 0), (255, 255, 0), (255, 255, 255)] cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 4), color=colors) self.two_dim_plot.setColorMap(cmap) self.one_dim_plot.scene().sigMouseMoved.connect(self.one_dim_plot_mouse_moved) self.two_dim_plot.scene.sigMouseMoved.connect(self.two_dim_plot_mouse_moved) def set_plot_type(self, plot_type): self.one_dim_plot.setVisible(plot_type == "1D") self.two_dim_plot.setVisible(plot_type == "2D") def add_curve(self, key, y_array, x_array, color): self.curves_dict[key] = self.one_dim_plot.plot( y=y_array, x=x_array, symbolPen='w', symbolBrush=color, symbolSize=3 ) self.visible_curve = key def add_energy_scan_plot(self, scan_info): self.one_dim_plot.setTitle(scan_info["title"]) pen = pg.mkPen('w', width=2) plot = self.one_dim_plot.plot(pen=pen) plot.setDownsampling(method="peak") plot.setClipToView(True) self.curves_dict["energyscan"] = plot def add_energy_scan_plot_point(self, x, y): if self.curves_dict["energyscan"].xData is None: x_data = [x] y_data = [y] else: x_data = np.append(self.curves_dict["energyscan"].xData, x) y_data = np.append(self.curves_dict["energyscan"].yData, y) self.curves_dict["energyscan"].setData(y=y_data, x=x_data) def plot_energy_scan_results(self, data, title): pen = pg.mkPen('w', width=2) #x_data = [item[0] for item in data] #y_data = [item[1] for item in data] self.one_dim_plot.plot(data, pen=pen) def plot_chooch_results( self, pk, fppPeak, fpPeak, ip, fppInfl, fpInfl, rm, chooch_graph_x, chooch_graph_y1, chooch_graph_y2, title, ): pen = pg.mkPen('r', width=3) self.one_dim_plot.plot(y=chooch_graph_y1, x=chooch_graph_x, pen=pen) pen = pg.mkPen('b', width=3) self.one_dim_plot.plot(y=chooch_graph_y2, x=chooch_graph_x, pen=pen) def update_curves(self, result): for key in result.keys(): if key in self.curves_dict: self.curves_dict[key].setData(y=result[key]) #, x=result['x_array']) def plot_result(self, result, aspect=None): self.two_dim_plot.setImage(result) def autoscale_axes(self): #self.one_dim_plot.enableAutoRange(self.view_box.XYAxes, True) self.view_box.autoRange(padding=0.02) self.view_box.setYRange(min=0, max=max(self.curves_dict[self.visible_curve].yData)) def clear(self): self.one_dim_plot.clear() self.two_dim_plot.clear() self.curves_dict = {} def hide_all_curves(self): for key in self.curves_dict.keys(): self.curves_dict[key].hide() self.visible_curve = None def show_curve(self, curve_key): for key in self.curves_dict.keys(): if key == curve_key: self.curves_dict[key].show() self.visible_curve = key return def one_dim_plot_mouse_moved(self, mouse_event): mouse_point = self.one_dim_plot.plotItem.vb.mapSceneToView(mouse_event) self.mouseMovedSignal.emit(mouse_point.x(), mouse_point.y()) def two_dim_plot_mouse_moved(self, mouse_event): mouse_point = self.two_dim_plot.imageItem.getViewBox().mapSceneToView(mouse_event) self.mouseMovedSignal.emit(mouse_point.x(), mouse_point.y()) def mouse_double_clicked(self, press_event, double): pass def set_yticks(self, ticks): y_axis = self.one_dim_plot.getAxis("left") y_axis.setTicks([ticks]) def set_x_axis_limits(self, limits): self.one_dim_plot.setRange(xRange=limits) def set_y_axis_limits(self, limits): self.one_dim_plot.setRange(yRange=limits)
class ConfigurationTable(qt_import.QTableWidget): propertyChangedSignal = qt_import.pyqtSignal(str, object, object) def __init__(self, parent): qt_import.QTableWidget.__init__(self, parent) self.display_hwobj = False self.property_bag = None self.setObjectName("configurationTable") self.setFrameShape(qt_import.QFrame.StyledPanel) self.setFrameShadow(qt_import.QFrame.Sunken) self.setContentsMargins(0, 3, 0, 3) self.setColumnCount(4) self.setSelectionMode(qt_import.QTableWidget.NoSelection) # self.setHorizontalHeaderLabels([self.trUtf8('Property'), # self.trUtf8('Value'), # self.trUtf8(''), # self.trUtf8('Comment')]) self.setHorizontalHeaderLabels(["Property", "Value", "", "Comment"]) self.setSizePolicy( qt_import.QSizePolicy.MinimumExpanding, qt_import.QSizePolicy.Fixed ) self.setHorizontalScrollBarPolicy(qt_import.Qt.ScrollBarAlwaysOff) self.cellChanged.connect(self.on_cell_changed) def clear(self): for i in range(self.rowCount()): self.removeRow(i) self.setRowCount(0) self.property_bag = None def set_property_bag(self, property_bag, show_hidden=False, display_hwobj=False): self.display_hwobj = display_hwobj self.clear() if self.property_bag is not None: for prop in self.property_bag: prop._editor = None self.property_bag = property_bag if self.property_bag is not None: i = 0 for prop in self.property_bag: prop._editor = weakref.ref(self) prop_name = prop.get_name() if not show_hidden and prop.hidden: continue if display_hwobj: if not prop_name.startswith("hwobj_"): continue else: prop_name = prop_name.replace("hwobj_", "") else: if prop_name.startswith("hwobj_"): continue self.setRowCount(i + 1) temp_table_item = qt_import.QTableWidgetItem(prop_name) temp_table_item.setFlags(qt_import.Qt.ItemIsEnabled) self.blockSignals(True) self.setItem(i, 0, temp_table_item) self.set_widget_from_property(i, prop) temp_table_item = qt_import.QTableWidgetItem(prop.comment) temp_table_item.setFlags(qt_import.Qt.ItemIsEnabled) self.setItem(i, 3, temp_table_item) self.blockSignals(False) validation_panel = ValidationTableItem(self) self.setCellWidget(i, 2, validation_panel) validation_panel.ok_button.clicked.connect(self.on_validate_click) validation_panel.cancel_button.clicked.connect(self.on_invalidate_click) validation_panel.reset_button.clicked.connect(self.on_reset_click) i += 1 self.setEnabled(i > 0) self.resizeColumnsToContents() self.setFixedHeight((self.rowCount() + 1) * (self.rowHeight(0) + 2)) # self.adjustSize() self.parent().adjustSize() # self.parent().resize(self.parent().sizeHint()) def set_widget_from_property(self, row, prop): """Adds new property to the propery table :param row: selected row :type row: int :param prop: property :type prop: dict """ if prop.get_type() == "boolean": new_property_item = qt_import.QTableWidgetItem("") self.setItem(row, 1, new_property_item) if prop.get_user_value(): self.item(row, 1).setCheckState(qt_import.Qt.Checked) else: self.item(row, 1).setCheckState(qt_import.Qt.Unchecked) elif prop.get_type() == "combo": choices_list = [] choices = prop.get_choices() for choice in choices: choices_list.append(choice) new_property_item = ComboBoxTableItem(self, row, 1, choices_list) new_property_item.setCurrentIndex( new_property_item.findText(prop.get_user_value()) ) self.setCellWidget(row, 1, new_property_item) elif prop.get_type() == "file": new_property_item = FileTableItem( self, row, 1, prop.get_user_value(), prop.getFilter() ) self.setCellWidget(row, 1, new_property_item) elif prop.get_type() == "color": new_property_item = ColorTableItem(self, row, 1, prop.get_user_value()) self.setCellWidget(row, 1, new_property_item) else: if prop.get_user_value() is None: temp_table_item = qt_import.QTableWidgetItem("") else: temp_table_item = qt_import.QTableWidgetItem(str(prop.get_user_value())) self.setItem(row, 1, temp_table_item) self.resizeColumnsToContents() # self.parent().adjustSize() def on_cell_changed(self, row, col): """Assignes new value to the property, clicked on the the property table """ col += 1 prop_name = str(self.item(row, 0).text()) if self.display_hwobj: prop_name = "hwobj_" + prop_name item_property = self.property_bag.get_property(prop_name) old_value = item_property.get_user_value() if item_property.get_type() == "boolean": item_property.set_value(self.item(row, 1).checkState()) elif item_property.get_type() == "combo": item_property.set_value(self.cellWidget(row, 1).currentText()) elif item_property.get_type() == "file": item_property.set_value(self.cellWidget(row, 1).get_filename()) elif item_property.get_type() == "color": item_property.set_value(self.cellWidget(row, 1).color) else: try: item_property.set_value(str(self.item(row, 1).text())) except BaseException: logging.getLogger().error( "Cannot assign value %s to property %s" % (str(self.item(row, 1).text()), prop_name) ) if item_property.get_user_value() is None: self.item(row, 1).setText("") else: self.item(row, 1).setText(str(item_property.get_user_value())) if not old_value == item_property.get_user_value(): self.propertyChangedSignal.emit( prop_name, old_value, item_property.get_user_value() ) def on_validate_click(self): # current row, col 1, accept = 1, replace = 0 self.endEdit(self.currentRow(), 1, 1, 0) self.activateNextCell() def on_invalidate_click(self): # current row, col 1, accept = 0, replace = 0 self.endEdit(self.currentRow(), 1, 0, 0) def on_reset_click(self): self.endEdit(self.currentRow(), 1, 0, 0) prop_name = str(self.item(self.currentRow(), 0).text()) if self.display_hwobj: prop_name = "hwobj_" + prop_name prop = self.property_bag.get_property(prop_name) default_value = prop.getDefaultValue() if not default_value is None: prop.set_value(default_value) self.set_widget_from_property(self.currentRow(), prop) def beginEdit(self, row, col, replace): if col == 1 and row >= 0: self.item(row, 2).setEnabled(1) return qt_import.QTableWidget.beginEdit(self, row, col, replace) def endEdit(self, row, col, accept, replace): if col == 1 and row >= 0: self.item(row, 2).setEnabled(0) if accept: prop_name = str(self.item(row, 0).text()) if self.display_hwobj: prop_name = "hwobj_" + prop_name prop = self.property_bag.get_property(prop_name) old_value = prop.get_user_value() if prop.get_type() == "boolean": prop.set_value(self.item(row, 1).isChecked()) elif prop.get_type() == "combo": prop.set_value(self.item(row, 1).currentText()) else: try: prop.set_value(str(self.text(row, 1))) except BaseException: logging.getLogger().error( "Cannot assign value to property %s" % prop_name ) if prop.get_user_value() is None: self.setText(row, 1, "") else: self.setText(row, 1, str(prop.get_user_value())) if not old_value == prop.get_user_value(): self.propertyChangedSignal.emit( prop_name, old_value, prop.get_user_value() ) # self.emit(QtCore.SIGNAL('propertyChanged'), # (prop_name, old_value, prop.get_user_value(), )) return qt_import.QTableWidget.endEdit(self, row, col, accept, replace)