class GphlDataDialog(qt.QDialog): def __init__(self, parent=None, name=None, fl=0): qt.QWidget.__init__(self, parent, name, fl) # AsyncResult to return values self._async_result = None # Layout qt.QVBoxLayout(self) main_layout = self.layout() main_layout.setSpacing(10) main_layout.setMargin(6) self.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding) self.setCaption("GPhL Workflow parameters") # Info box self.info_gbox = qt.QVGroupBox("Info", self, "info_gbox") main_layout.addWidget(self.info_gbox) self.info_text = qt.QTextEdit(self.info_gbox, "info_text") self.info_text.setTextFormat(0) # PlainText self.info_text.setFont(qt.QFont("Courier")) self.info_text.setReadOnly(True) # Special parameter box self.cplx_gbox = qt.QVGroupBox("Indexing solution", self, "cplx_gbox") main_layout.addWidget(self.cplx_gbox) self.cplx_widget = None # Parameter box self.parameter_gbox = qt.QVGroupBox("Parameters", self, "parameter_gbox") main_layout.addWidget(self.parameter_gbox) self.params_widget = None # Button bar button_layout = qt.QHBoxLayout(None, 0, 6, "button_layout") hspacer = qt.QSpacerItem(1, 20, qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum) button_layout.addItem(hspacer) self.continue_button = qt.QPushButton("Continue", self, "continue_button") button_layout.addWidget(self.continue_button) self.cancel_button = qt.QPushButton("Abort", self, "cancel_button") button_layout.addWidget(self.cancel_button) main_layout.addLayout(button_layout) qt.QObject.connect(self.continue_button, qt.SIGNAL("clicked()"), self.continue_button_click) qt.QObject.connect(self.cancel_button, qt.SIGNAL("clicked()"), self.cancel_button_click) self.resize(qt.QSize(1018, 472).expandedTo(self.minimumSizeHint())) self.clearWState(qt.Qt.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() self.parent.workflow_model.workflow_hwobj.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.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 EDNAParameters(BlissWidget): def __init__(self, *args): BlissWidget.__init__(self, *args) self.addProperty('mnemonic', 'string', '') self.addProperty('edna object', 'string', '') self.addProperty('workflows base dir', 'string', '') #when starting a workflow we emit this signal and expect #to get the beamline params through the slot self.defineSlot('updateBeamlineParameters', ()) self.defineSignal('beamlineParametersNeeded', ()) self.defineSignal('workflowAvailable', ()) #we need the session id self.defineSlot('login_changed', ()) self.beamline_params = dict() self.session_id = None self.params_widget = None self.workflow = None self.edna = None self.previous_workflow_state = None self.workflow_output_file = None self.process_dir = None QGridLayout(self, 4, 2) self.workflows_box = QHBoxLayout() self.ok_button = QPushButton('Continue', self) #self.abort_button = QPushButton('Abort', self) self.workflow_list = QComboBox(self) self.workflow_list.hide() self.start_button = QPushButton('Start', self) self.start_button.hide() self.workflows_box.addWidget(self.workflow_list) self.workflows_box.addWidget(self.start_button) self.layout().addMultiCellLayout(self.workflows_box, 0, 0, 0, 1) # UGLY DIRTY HACK, (c) Thomas: # We want the params and continue to be on the bottom left # Make it so the first row takes all space. The row is empty now # since someone modified the brick to not display the workflow list anymore self.layout().setRowStretch(0, 10) #self.info_label = QLabel(self) #self.info_label.setSizePolicy(QSizePolicy.MinimumExpanding, # QSizePolicy.Fixed) # Hack to get some space between the parameters and the continue button self.layout().addWidget(self.ok_button, 3, 0, Qt.AlignLeft | Qt.AlignBottom) # second row is empty but should take at least 10 px self.layout().setRowSpacing(2, 10) #self.layout().addWidget(self.abort_button, 2, 1) #self.layout().addMultiCellWidget(self.info_label, 3, 3, 0, 1) QObject.connect(self.ok_button, SIGNAL('clicked()'), self.send_parameters) #QObject.connect(self.abort_button, SIGNAL('clicked()'), # self.abort_workflow) QObject.connect(self.start_button, SIGNAL('clicked()'), self.start_workflow) # name -> model path mapping self.workflows = dict() def init(self): pass def setExpertMode(self, expert): self.setEnabled(True) self.emit(PYSIGNAL('workflowAvailable'), (True, )) #self.setEnabled(expert) #self.emit(PYSIGNAL('workflowAvailable'), (expert, )) def propertyChanged(self, prop, old_val, new_val): if prop == 'mnemonic': if self.workflow is not None: self.disconnect(self.workflow, PYSIGNAL('parametersNeeded'), self.prompt_parameters) self.workflow = self.getHardwareObject(new_val) if self.workflow is not None: #get the state state = self.workflow.state.getValue() self.workflow_state_changed((state, )) self.connect(self.workflow, PYSIGNAL('parametersNeeded'), self.prompt_parameters) self.connect(self.workflow, PYSIGNAL('stateChanged'), self.workflow_state_changed) #populate the available workflows list self.refresh_workflows() if prop == 'edna object': self.edna = self.getHardwareObject(new_val) logging.debug('%r: Edna object is now %s (%r)', self, new_val, self.edna) def prompt_parameters(self, xml): logging.debug('got back XML from server:\n%s', xml) # Edited by Olof 2013/04/16: Temporary fix for handling messages containing # XML markup #xml_root = etree.fromstring(xml) xml_root = etree.fromstring(xml, parser=etree.XMLParser(recover=True)) # Special case until Olof implements the saner XML format on the server side if xml_root.tag == 'message': message = dict(type='message', level='info', text='no message') for node in xml_root: option = node.tag.strip() # rename type to level, as type is used for the type # this is unfortunate if option == 'type': option = 'level' message[option] = node.text.strip() fields = [message] else: containers = get_field_containers(xml_root) if len(containers) == 0: return fields = get_fields(containers[0]) if self.params_widget is not None: self.layout().removeChild(self.params_widget) self.params_widget = FieldsWidget(fields, self) current_values = self.workflow.get_values_map() logging.debug('current values are: %s', current_values) self.params_widget.set_values(current_values) self.layout().addMultiCellWidget(self.params_widget, 1, 1, 0, 1, Qt.AlignBottom | Qt.AlignLeft) self.params_widget.show() self.ok_button.setEnabled(True) def send_parameters(self): if self.params_widget is not None: params_map = self.params_widget.get_parameters_map() logging.debug('setting values %r', params_map) self.workflow.set_values_map(params_map) # Try to change back to sample centering tab try: tabWidget = self.parent().parent().parent().parent().parent() tabWidget.setCurrentPage(0) except: # We don't care if it doesn't work.. pass def refresh_workflow_state(self, new_state, actor): #abort button should never be disabled new_state = str(new_state) #convert from DevState to string if self.params_widget is not None: self.params_widget.setEnabled(new_state == "OPEN") self.ok_button.setEnabled(new_state == "OPEN") # only available when engine is idle self.start_button.setEnabled(new_state == "ON") self.workflow_list.setEnabled(new_state == "ON") if new_state == "RUNNING": message = 'Workflow engine running' elif new_state == "STANDBY": message = 'Workflow engine paused' elif new_state == "ON": self.refresh_workflows() message = 'Workflow engine idle' elif new_state == "OPEN": message = 'Waiting for parameters' elif new_state == "None": message = 'Workflow engine is offline' else: message = 'Workflow engine is in a state it should not be in (%r)' % ( new_state, ) #self.start_button.setEnabled(new_state == "ON") #self.workflow_list.setEnabled(new_state == "ON") #self.info_label.setText(message) logging.info(message) def workflow_state_changed(self, new_state): logging.debug('%s: new workflow state is %r', self.name(), new_state) if type(new_state) == list or type(new_state) == tuple: new_state = str(new_state[0]) if new_state == "ON" and self.previous_workflow_state == "RUNNING": # workflow finished, open the output file and use an EDNACaracterize method to # continue the work if self.workflow_output_file is not None and os.path.exists( self.workflow_output_file): logging.debug('Workflow finished, sending the results to %r', self.edna) logging.debug('Workflow file is %s', self.workflow_output_file) try: data = XSDataResultMXCuBE.parseFile( self.workflow_output_file) self.edna.readEDNAResults( data.getCharacterisationResult(), self.workflow_output_file, self.beamline_params['directory'], self.beamline_params['prefix'], int(self.beamline_params['run_number']), process_dir=self.process_dir, do_inducedraddam=False) logging.debug('Results sent') except: logging.debug('Malformed or empty results file') else: logging.debug('Workflow finished, no result file') # then remove the current widget with the parameters if self.params_widget is not None: self.layout().removeChild(self.params_widget) self.previous_workflow_state = new_state def abort_workflow(self): if self.workflow is not None: self.workflow.abort() def refresh_workflows(self): # keep the name of the current workflow so we can reselect it # by default previous_workflow = str(self.workflow_list.currentText()) previous_workflow_index = None self.workflows.clear() workflows = self.workflow.get_available_workflows() self.workflow_list.clear() for (w, i) in zip(workflows, list(range(len(workflows)))): path = w['path'] name = w['name'] if name == previous_workflow: previous_workflow_index = i self.workflow_list.insertItem(name) self.workflows[name] = w if previous_workflow_index is not None: self.workflow_list.setCurrentItem(previous_workflow_index) def start_workflow(self): #get the beamline params logging.debug('requesting beamline parameters') self.emit(PYSIGNAL('beamlineParametersNeeded'), ()) name = str(self.workflow_list.currentText()) params = ['modelpath', self.workflows[name]['path']] #blparams = XSDataMXCuBEParameters() for k, v in self.beamline_params.items(): # we'll have to lookup how those are specified someday # it's a (bool, string, string) tuple if k in [ 'mad_1_energy', 'mad_2_energy', 'mad_3_energy', 'mad_4_energy' ]: if v[0] is False: param = None else: param = v[1] elif k == 'sum_images': param = v[0] elif k == 'inverse_beam': param = v[0] else: param = v logging.debug('setting %s to %r', k, param) #setattr(blparams, k, param) params.append(k) params.append(str(param)) # we also need the session id #blparams.sessionId = self.session_id #output_dir = self.beamline_params['directory'].replace('RAW_DATA', 'PROCESSED_DATA') # we'll need that one later to pass to the edna characterise HO #self.process_dir = output_dir #if not os.path.exists(output_dir): # os.makedirs(output_dir) #(handle, filename) = tempfile.mkstemp(suffix='.xml', prefix='edna_output_', dir=output_dir) #blparams.output_file = filename # convert that stuff to xml #params.append('mxcube_parameters') #params.append(blparams.marshal()) #self.workflow_output_file = filename logging.debug('starting workflow %s with params %r', name, params) #self.workflow.start(params) return params def updateBeamlineParameters(self, params): logging.debug('got beamline param update, new values: %r', params) self.beamline_params = dict() # as XSDataSomethingSomething marshalling routing uses format string # BUT does not enforces its arg type, we have to do some type # conversion, lest it will explode when we call marshal() on it floats = [ 'exposure_time', 'resolution', 'resolution_at_corner', 'x_beam', 'y_beam', 'beam_size_x', 'beam_size_y', 'overlap', 'osc_start', 'osc_range', 'kappaStart', 'current_detdistance', 'current_wavelength', 'phiStart', 'current_energy', 'current_osc_start' ] ints = [ 'sessionId', 'blSampleId', 'first_image', 'number_images', 'run_number', 'number_passes' ] for k, v in params.items(): value = v if k in floats: logging.debug('converting %s (%r) to float', k, v) value = float(v) if k in ints: logging.debug('converting %s (%r) to int', k, v) value = int(v) self.beamline_params[k] = value def login_changed(self, *login_infos): logging.debug('user logged in, logins_info: %r', login_infos) if len(login_infos) == 1 and login_infos[0] == None: self.session_id = None else: self.session_id = int(login_infos[0])
class GphlDataDialog(qt.QDialog): def __init__(self, parent = None, name = None, fl = 0): qt.QWidget.__init__(self, parent, name, fl) # AsyncResult to return values self._async_result = None # Layout qt.QVBoxLayout(self) main_layout = self.layout() main_layout.setSpacing(10) main_layout.setMargin(6) self.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding) self.setCaption('GPhL Workflow parameters') # Info box self.info_gbox = qt.QVGroupBox('Info', self, "info_gbox") main_layout.addWidget(self.info_gbox) self.info_text = qt.QTextEdit(self.info_gbox, 'info_text') self.info_text.setTextFormat(0) # PlainText self.info_text.setFont(qt.QFont("Courier")) self.info_text.setReadOnly(True) # Special parameter box self.cplx_gbox = qt.QVGroupBox('Indexing solution', self, "cplx_gbox") main_layout.addWidget(self.cplx_gbox) self.cplx_widget = None # Parameter box self.parameter_gbox = qt.QVGroupBox('Parameters', self, "parameter_gbox") main_layout.addWidget(self.parameter_gbox) self.params_widget = None # Button bar button_layout = qt.QHBoxLayout(None,0,6,"button_layout") hspacer = qt.QSpacerItem(1,20,qt.QSizePolicy.Expanding,qt.QSizePolicy.Minimum) button_layout.addItem(hspacer) self.continue_button = qt.QPushButton('Continue', self, 'continue_button') button_layout.addWidget(self.continue_button) self.cancel_button = qt.QPushButton('Abort', self, "cancel_button") button_layout.addWidget(self.cancel_button) main_layout.addLayout(button_layout) qt.QObject.connect(self.continue_button, qt.SIGNAL("clicked()"), self.continue_button_click) qt.QObject.connect(self.cancel_button, qt.SIGNAL("clicked()"), self.cancel_button_click) self.resize(qt.QSize(1018,472).expandedTo(self.minimumSizeHint())) self.clearWState(qt.Qt.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() self.parent.workflow_model.workflow_hwobj.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 dd in field_list: if info is None and dd.get('variableName') == '_info': # Info text - goes to info_gbox info = dd elif cplx is None and dd.get('variableName') == '_cplx': # Complex parameter - goes to cplx_gbox cplx = dd else: parameters.append(dd) # 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.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 dd in field_list: name = dd['variableName'] value = dd.get('defaultValue') if value is not None: dd[name] = value self.params_widget.set_values(values) self.parameter_gbox.show() else: self.parameter_gbox.hide() self.show() self.setEnabled(True)
class EDNAParameters(BlissWidget): def __init__(self, *args): BlissWidget.__init__(self, *args) self.addProperty("mnemonic", "string", "") self.addProperty("edna object", "string", "") self.addProperty("workflows base dir", "string", "") # when starting a workflow we emit this signal and expect # to get the beamline params through the slot self.defineSlot("updateBeamlineParameters", ()) self.defineSignal("beamlineParametersNeeded", ()) self.defineSignal("workflowAvailable", ()) # we need the session id self.defineSlot("login_changed", ()) self.beamline_params = dict() self.session_id = None self.params_widget = None self.workflow = None self.edna = None self.previous_workflow_state = None self.workflow_output_file = None self.process_dir = None QGridLayout(self, 4, 2) self.workflows_box = QHBoxLayout() self.ok_button = QPushButton("Continue", self) # self.abort_button = QPushButton('Abort', self) self.workflow_list = QComboBox(self) self.workflow_list.hide() self.start_button = QPushButton("Start", self) self.start_button.hide() self.workflows_box.addWidget(self.workflow_list) self.workflows_box.addWidget(self.start_button) self.layout().addMultiCellLayout(self.workflows_box, 0, 0, 0, 1) # UGLY DIRTY HACK, (c) Thomas: # We want the params and continue to be on the bottom left # Make it so the first row takes all space. The row is empty now # since someone modified the brick to not display the workflow list anymore self.layout().setRowStretch(0, 10) # self.info_label = QLabel(self) # self.info_label.setSizePolicy(QSizePolicy.MinimumExpanding, # QSizePolicy.Fixed) # Hack to get some space between the parameters and the continue button self.layout().addWidget(self.ok_button, 3, 0, Qt.AlignLeft | Qt.AlignBottom) # second row is empty but should take at least 10 px self.layout().setRowSpacing(2, 10) # self.layout().addWidget(self.abort_button, 2, 1) # self.layout().addMultiCellWidget(self.info_label, 3, 3, 0, 1) QObject.connect(self.ok_button, SIGNAL("clicked()"), self.send_parameters) # QObject.connect(self.abort_button, SIGNAL('clicked()'), # self.abort_workflow) QObject.connect(self.start_button, SIGNAL("clicked()"), self.start_workflow) # name -> model path mapping self.workflows = dict() def init(self): pass def setExpertMode(self, expert): self.setEnabled(True) self.emit(PYSIGNAL("workflowAvailable"), (True,)) # self.setEnabled(expert) # self.emit(PYSIGNAL('workflowAvailable'), (expert, )) def propertyChanged(self, prop, old_val, new_val): if prop == "mnemonic": if self.workflow is not None: self.disconnect( self.workflow, PYSIGNAL("parametersNeeded"), self.prompt_parameters ) self.workflow = self.getHardwareObject(new_val) if self.workflow is not None: # get the state state = self.workflow.state.getValue() self.workflow_state_changed((state,)) self.connect( self.workflow, PYSIGNAL("parametersNeeded"), self.prompt_parameters ) self.connect( self.workflow, PYSIGNAL("stateChanged"), self.workflow_state_changed ) # populate the available workflows list self.refresh_workflows() if prop == "edna object": self.edna = self.getHardwareObject(new_val) logging.debug("%r: Edna object is now %s (%r)", self, new_val, self.edna) def prompt_parameters(self, xml): logging.debug("got back XML from server:\n%s", xml) # Edited by Olof 2013/04/16: Temporary fix for handling messages containing # XML markup # xml_root = etree.fromstring(xml) xml_root = etree.fromstring(xml, parser=etree.XMLParser(recover=True)) # Special case until Olof implements the saner XML format on the server side if xml_root.tag == "message": message = dict(type="message", level="info", text="no message") for node in xml_root: option = node.tag.strip() # rename type to level, as type is used for the type # this is unfortunate if option == "type": option = "level" message[option] = node.text.strip() fields = [message] else: containers = get_field_containers(xml_root) if len(containers) == 0: return fields = get_fields(containers[0]) if self.params_widget is not None: self.layout().removeChild(self.params_widget) self.params_widget = FieldsWidget(fields, self) current_values = self.workflow.get_values_map() logging.debug("current values are: %s", current_values) self.params_widget.set_values(current_values) self.layout().addMultiCellWidget( self.params_widget, 1, 1, 0, 1, Qt.AlignBottom | Qt.AlignLeft ) self.params_widget.show() self.ok_button.setEnabled(True) def send_parameters(self): if self.params_widget is not None: params_map = self.params_widget.get_parameters_map() logging.debug("setting values %r", params_map) self.workflow.set_values_map(params_map) # Try to change back to sample centering tab try: tabWidget = self.parent().parent().parent().parent().parent() tabWidget.setCurrentPage(0) except BaseException: # We don't care if it doesn't work.. pass def refresh_workflow_state(self, new_state, actor): # abort button should never be disabled new_state = str(new_state) # convert from DevState to string if self.params_widget is not None: self.params_widget.setEnabled(new_state == "OPEN") self.ok_button.setEnabled(new_state == "OPEN") # only available when engine is idle self.start_button.setEnabled(new_state == "ON") self.workflow_list.setEnabled(new_state == "ON") if new_state == "RUNNING": message = "Workflow engine running" elif new_state == "STANDBY": message = "Workflow engine paused" elif new_state == "ON": self.refresh_workflows() message = "Workflow engine idle" elif new_state == "OPEN": message = "Waiting for parameters" elif new_state == "None": message = "Workflow engine is offline" else: message = "Workflow engine is in a state it should not be in (%r)" % ( new_state, ) # self.start_button.setEnabled(new_state == "ON") # self.workflow_list.setEnabled(new_state == "ON") # self.info_label.setText(message) logging.info(message) def workflow_state_changed(self, new_state): logging.debug("%s: new workflow state is %r", self.name(), new_state) if isinstance(new_state, types.ListType) or isinstance( new_state, types.TupleType ): new_state = str(new_state[0]) if new_state == "ON" and self.previous_workflow_state == "RUNNING": # workflow finished, open the output file and use an EDNACaracterize method to # continue the work if self.workflow_output_file is not None and os.path.exists( self.workflow_output_file ): logging.debug("Workflow finished, sending the results to %r", self.edna) logging.debug("Workflow file is %s", self.workflow_output_file) try: data = XSDataResultMXCuBE.parseFile(self.workflow_output_file) self.edna.readEDNAResults( data.getCharacterisationResult(), self.workflow_output_file, self.beamline_params["directory"], self.beamline_params["prefix"], int(self.beamline_params["run_number"]), process_dir=self.process_dir, do_inducedraddam=False, ) logging.debug("Results sent") except BaseException: logging.debug("Malformed or empty results file") else: logging.debug("Workflow finished, no result file") # then remove the current widget with the parameters if self.params_widget is not None: self.layout().removeChild(self.params_widget) self.previous_workflow_state = new_state def abort_workflow(self): if self.workflow is not None: self.workflow.abort() def refresh_workflows(self): # keep the name of the current workflow so we can reselect it # by default previous_workflow = str(self.workflow_list.currentText()) previous_workflow_index = None self.workflows.clear() workflows = self.workflow.get_available_workflows() self.workflow_list.clear() for (w, i) in zip(workflows, range(len(workflows))): path = w["path"] name = w["name"] if name == previous_workflow: previous_workflow_index = i self.workflow_list.insertItem(name) self.workflows[name] = w if previous_workflow_index is not None: self.workflow_list.setCurrentItem(previous_workflow_index) def start_workflow(self): # get the beamline params logging.debug("requesting beamline parameters") self.emit(PYSIGNAL("beamlineParametersNeeded"), ()) name = str(self.workflow_list.currentText()) params = ["modelpath", self.workflows[name]["path"]] # blparams = XSDataMXCuBEParameters() for k, v in self.beamline_params.iteritems(): # we'll have to lookup how those are specified someday # it's a (bool, string, string) tuple if k in ["mad_1_energy", "mad_2_energy", "mad_3_energy", "mad_4_energy"]: if v[0] is False: param = None else: param = v[1] elif k == "sum_images": param = v[0] elif k == "inverse_beam": param = v[0] else: param = v logging.debug("setting %s to %r", k, param) # setattr(blparams, k, param) params.append(k) params.append(str(param)) # we also need the session id # blparams.sessionId = self.session_id # output_dir = self.beamline_params['directory'].replace('RAW_DATA', 'PROCESSED_DATA') # we'll need that one later to pass to the edna characterise HO # self.process_dir = output_dir # if not os.path.exists(output_dir): # os.makedirs(output_dir) # (handle, filename) = tempfile.mkstemp(suffix='.xml', prefix='edna_output_', dir=output_dir) # blparams.output_file = filename # convert that stuff to xml # params.append('mxcube_parameters') # params.append(blparams.marshal()) # self.workflow_output_file = filename logging.debug("starting workflow %s with params %r", name, params) # self.workflow.start(params) return params def updateBeamlineParameters(self, params): logging.debug("got beamline param update, new values: %r", params) self.beamline_params = dict() # as XSDataSomethingSomething marshalling routing uses format string # BUT does not enforces its arg type, we have to do some type # conversion, lest it will explode when we call marshal() on it floats = [ "exposure_time", "resolution", "resolution_at_corner", "x_beam", "y_beam", "beam_size_x", "beam_size_y", "overlap", "osc_start", "osc_range", "kappaStart", "current_detdistance", "current_wavelength", "phiStart", "current_energy", "current_osc_start", ] ints = [ "sessionId", "blSampleId", "first_image", "number_images", "run_number", "number_passes", ] for k, v in params.iteritems(): value = v if k in floats: logging.debug("converting %s (%r) to float", k, v) value = float(v) if k in ints: logging.debug("converting %s (%r) to int", k, v) value = int(v) self.beamline_params[k] = value def login_changed(self, *login_infos): logging.debug("user logged in, logins_info: %r", login_infos) if len(login_infos) == 1 and login_infos[0] is None: self.session_id = None else: self.session_id = int(login_infos[0])