From 28c94dd2970336401afcd8fc23777433e5820c74 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Mon, 22 Aug 2022 13:02:31 +0200 Subject: [PATCH] GenericDialog.py for Application Config --- GenericDialog.py | 204 +++++++++++++++++++++ app_config.py | 31 +++- detector.py | 458 ++++++++++++++++++++++------------------------- swissmx.py | 251 ++++++++++---------------- 4 files changed, 539 insertions(+), 405 deletions(-) create mode 100644 GenericDialog.py diff --git a/GenericDialog.py b/GenericDialog.py new file mode 100644 index 0000000..cafb121 --- /dev/null +++ b/GenericDialog.py @@ -0,0 +1,204 @@ +import logging +_log = logging.getLogger(__name__) + +import re +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtWidgets import QLineEdit, QTextEdit, QGridLayout, QVBoxLayout, QWidget, QDialog, QDialogButtonBox, QPushButton +from PyQt5.QtWidgets import QDoubleSpinBox, QSpinBox, QCheckBox, QLabel + +def Spinner(value, min=None, max=None, decimals=None, single_step=None, prefix=None, suffix=None): + if type(value) is float: + s = QDoubleSpinBox() + else: + s = QSpinBox() + + if min is not None: + s.setMinimum(min) + if max is not None: + s.setMaximum(max) + try: + if decimals is not None: + s.setDecimals(decimals) + except: + pass + if single_step is not None: + s.setSingleStep(single_step) + if prefix is not None: + s.setPrefix(prefix) + if suffix is not None: + s.setSuffix(suffix) + return s + +def Checkbox(value: bool, label: str): + box = QCheckBox(label) + box.setChecked(value) + return box + +class MagicLabel(QLabel): + entered = pyqtSignal(str) + left = pyqtSignal() + + def __init__(self, label, help_str, help_buddy=None): + super(MagicLabel, self).__init__(label) + self._help_buddy = help_buddy + self._help_str = help_str + + def enterEvent(self, event): + print(self._help_str) + if self._help_buddy: + print(f"yay!: {self._help_str}") + else: + super().enterEvent(event) + + def leaveEvent(self, event): + super().leaveEvent(event) + + +class GenericDialog(QDialog): + def __init__(self, parent=None, settings=None, title=None, message=None, inputs={}, use_buttons=True): + super(GenericDialog, self).__init__() + + self.settings = settings + + # self.setModal(True) + self.setAccessibleName("genericDialog") + self.setLayout(QVBoxLayout()) + + + bbox = QDialogButtonBox() + doneButton = QPushButton("Done") + doneButton.clicked.connect(self.accept) + bbox.addButton(doneButton, QDialogButtonBox.AcceptRole) + + if use_buttons: + undoButton = QPushButton("Undo") + undoButton.clicked.connect(self.undo_all) + + discardButton = QPushButton("Discard") + discardButton.clicked.connect(self.discard) + + bbox.addButton(undoButton, QDialogButtonBox.ActionRole) + bbox.addButton(discardButton, QDialogButtonBox.RejectRole) + + lb = QLabel(title) + lb.setAccessibleName("genericDialogTitle") + lb.setAlignment(Qt.AlignHCenter) + self.layout().addWidget(lb) + + gbox = QWidget() + self.layout().addWidget(gbox) + gbox.setLayout(QVBoxLayout()) + mlabel = QLabel(message) + mlabel.setAccessibleName("genericDialogMessage") + + gbox.layout().addWidget(mlabel) + self.setWindowTitle(title) + + self.contents = QWidget() + self.contents.setAccessibleName("genericDialogContents") + self.layout().addWidget(self.contents) + self.layout().addStretch() + self.layout().addWidget(bbox) + if not inputs: + inputs = {'test': ('Text test', 'initial', QLineEdit()), + 'float': ('Text test 2', 3.14, QDoubleSpinBox(), 5.0), + 'integer': ('Text test 2', 42, QSpinBox()), + } + + self.contents.setLayout(QGridLayout()) + layout = self.contents.layout() + layout.setColumnStretch(1, 2) + self.results = {} + self._undo = {} + for row, config in enumerate(inputs.items()): + key, config = config + config = list(config) + if len(config) == 3: + config.extend([""]) + label, value, widget, help_str = config + labwig = MagicLabel(label, help_str) + layout.addWidget(labwig, row, 0) + layout.addWidget(widget, row, 1) + + if hasattr(widget, 'setChecked'): + widget.setChecked(value) + widget.stateChanged.connect(lambda v, k=key, w=widget.isChecked: self.update_results(k, w)) + + elif hasattr(widget, 'setValue'): + widget.setValue(value) + widget.valueChanged.connect(lambda v, k=key, w=widget.value: self.update_results(k, w)) + + elif hasattr(widget, 'setPlainText'): + widget.setPlainText(value) + widget.textChanged.connect(lambda k=key, w=widget.toPlainText: self.update_results(k, w)) + + elif hasattr(widget, 'setText'): + widget.setText(value) + widget.editingFinished.connect(lambda k=key, w=widget.text: self.update_results(k, w)) + + self.results[key] = value + self._undo[key] = (widget, value) + + def undo_all(self): + for k, uinfo in self._undo.items(): + widget, value = uinfo + if hasattr(widget, 'setChecked'): + widget.setChecked(value) + + elif hasattr(widget, 'setValue'): + widget.setValue(value) + + elif hasattr(widget, 'setPlainText'): + widget.setPlainText(value) + + elif hasattr(widget, 'setText'): + widget.setText(value) + + def discard(self): + self.undo_all() + self.reject() + + def set_value(self, widget, url): + m = re.match("^(.*?)://(.*)", url, re.MULTILINE|re.DOTALL) + method, value = m.groups() + if method == "setValue": + widget.setValue(float(value)) + elif method == "setText": + widget.setText(value) + else: + widget.setPlainText(value) + if self.settings is not None: + self.settings.setValue(k, value) + + def update_results(self, key, get_value): + value = get_value() + _log.debug(f"settigns {key} => {value}") + self.results[key] = value + if self.settings is not None: + self.settings.setValue(k, value) + + def keyPressEvent(self, key_event): + if key_event != Qt.Key_Escape: + super().keyPressEvent(key_event) + + +if __name__ == '__main__': + import sys + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + title = 'A Lovely Title' + message = 'thisn sohudl explain what do tod ow with this dialog and in fact we should make sure that it will be wrapping aorufnnfb' + main = GenericDialog(title=title, message=message, inputs={ + 'bananas': ('Number of Bananas', 2, QSpinBox(), 10), + 'apples': ('Number of Apples', 5.5, QDoubleSpinBox(), 23), + 'really': ('Indeed?', True, Checkbox(True, "Contr")), + 'words': ('Words', 'words go here', QLineEdit(), "a few words"), + 'texts': ('Words', 'big words go here', QTextEdit(), """quite a drama\n to write here, really something awfull long"""), + }) + if main.exec(): + print(main.results) + for k, v in main.results.items(): + print('{} {} = {}'.format(k, type(v), v)) + sys.exit(app.exec_()) + diff --git a/app_config.py b/app_config.py index 96d6e28..51d8b47 100644 --- a/app_config.py +++ b/app_config.py @@ -24,6 +24,27 @@ class AppCfg(QSettings): WINDOW_SPLITTER="window/splitter" WINDOW_STATE= "window/state" + PST_X_UP ="post_sample_tube/x_up" + PST_Y_UP ="post_sample_tube/y_up" + PST_X_DOWN="post_sample_tube/x_down" + PST_Y_DOWN="post_sample_tube/y_down" + PST_DX ="post_sample_tube/dx" + PST_DY ="post_sample_tube/dy" + PST_TZ_IN ="post_sample_tube/z_in" + PST_TZ_OUT="post_sample_tube/z_out" + + COL_DX ="collimator/dx" + COL_DY ="collimator/dy" + COL_X_IN ="collimator/x_in" + COL_Y_IN ="collimator/y_in" + + BKLGT_IN ="backlight/in" + BKLGT_OUT ="backlight/out" + + DT_HOST="deltatau/host" + DT_SHOW_PLOTS="deltatau/show_plots" + DT_VEL_SCL="deltatau/velocity_scale" + # ---------- OBSOLETE ??? ---------- #ZOOM_BUTTONS="sample_viewing/zoom_buttons" @@ -33,11 +54,11 @@ class AppCfg(QSettings): CRYOJET_NOZZLE_OUT="cryojet/nozzle_out" CRYOJET_NOZZLE_IN="cryojet/nozzle_in" - DELTATAU_HOST="deltatau/host" - DELTATAU_SHOW_PLOTS="deltatau/show_plots" + #DELTATAU_HOST="deltatau/host" + #DELTATAU_SHOW_PLOTS="deltatau/show_plots" DELTATAU_OMEGACOS="deltatau/omegacos" DELTATAU_SORT_POINTS="deltatau/sort_points" - DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale" + #DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale" CAMERA_TRANSFORMATIONS="camera/transformations" CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm" @@ -108,8 +129,8 @@ class AppCfg(QSettings): def setValue(self, key: str, val): #overload to debug return super(AppCfg, self).setValue(key,val) - def value(self,key,*vargs): #overload to debug - val=super(AppCfg, self).value(key,*vargs) + def value(self,key,*vargs,**kwargs): #overload to debug + val=super(AppCfg, self).value(key,*vargs,**kwargs) return val #@property #def value(self): diff --git a/detector.py b/detector.py index ffae769..a199bd5 100644 --- a/detector.py +++ b/detector.py @@ -1,7 +1,6 @@ import logging _log=logging.getLogger(__name__) -#from datetime import datetime #from glob import glob from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot @@ -14,293 +13,268 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot #dia_client = DetectorIntegrationClient(config['uri']) class JungfrauWaiter(QObject): - finished = pyqtSignal() + finished = pyqtSignal() - def __init__(self): - super().__init__() + def __init__(self): + super().__init__() - @pyqtSlot() - def work(self): - pass - -class JungfrauMissingConfiguration(Exception): + @pyqtSlot() + def work(self): pass -class Jungfrau(QObject, metaclass=qsingleton.Singleton): - pedestal_selected = pyqtSignal(str) - configured = pyqtSignal() - armed = pyqtSignal() - started = pyqtSignal() - triggered = pyqtSignal() - aborted = pyqtSignal() +class JungfrauMissingConfiguration(Exception): + pass - def __init__(self, parent=None, **kwargs): - super(Jungfrau, self).__init__(parent, **kwargs) - self._simulated = kwargs.get("simulate", simulated) - self._save_raw = True - self._runJungfrau = False - self._number_of_frames = 1 - self._gain_maps = config["gain_maps"] - self._uid = None - self._serial_number = config['serialnr'] - self.writer_config = {} - self.backend_config = {} - self.detector_config = {} - self.bsread_config = {} +class Jungfrau(QObject): + pedestal_selected = pyqtSignal(str) + configured = pyqtSignal() + armed = pyqtSignal() + started = pyqtSignal() + triggered = pyqtSignal() + aborted = pyqtSignal() - @pyqtSlot(bool) - def set_run_jungfrau(self, run): - """running or not jungfrau, global simulated flag overrides""" - logger.warning(f"{'' if run else 'not '}running jungfrau detector") - if not self._simulated: - self._runJungfrau = run - else: - self._runJungfrau = False + def __init__(self, parent=None, **kwargs): + super(Jungfrau, self).__init__(parent, **kwargs) + self._save_raw = True + self._runJungfrau = False + self._number_of_frames = 1 + #self._gain_maps = config["gain_maps"] + self._uid = None + #self._serial_number = config['serialnr'] + self.writer_config = {} + self.backend_config = {} + self.detector_config = {} + self.bsread_config = {} - @pyqtSlot(bool) - def set_save_raw(self, save_raw): - """enable or not writer_config""" - logger.warning(f"{'' if save_raw else 'not '}saving data") - self._save_raw = save_raw + @pyqtSlot(bool) + def set_run_jungfrau(self, run): + """running or not jungfrau, global simulated flag overrides""" + _log.warning(f"{'' if run else 'not '}running jungfrau detector") + if not self._simulated: + self._runJungfrau = run + else: + self._runJungfrau = False - @pyqtSlot(str) - def set_detector_serial(self, serial_number): - """set serial number for main detector""" - logger.info(f"jungfrau serial number: {serial_number}") - self._serial_number = serial_number + @pyqtSlot(bool) + def set_save_raw(self, save_raw): + """enable or not writer_config""" + _log.warning(f"{'' if save_raw else 'not '}saving data") + self._save_raw = save_raw - def is_saving_data(self): - """if False writer_config is not issued""" - return self._save_raw + @pyqtSlot(str) + def set_detector_serial(self, serial_number): + """set serial number for main detector""" + _log.info(f"jungfrau serial number: {serial_number}") + self._serial_number = serial_number - def is_running_detector(self): - """if False nothing is actually called""" - return self._runJungfrau + def is_saving_data(self): + """if False writer_config is not issued""" + return self._save_raw - @pyqtSlot(int) - def set_number_of_frames(self, frames): - logger.info(f"jungfrau number of frames: {frames}") - self._number_of_frames = frames + def is_running_detector(self): + """if False nothing is actually called""" + return self._runJungfrau - @pyqtSlot(int) - def set_data_owner_uid(self, uid): - logger.info(f"data owner set to UID = {uid}") - self._uid = uid + @pyqtSlot(int) + def set_number_of_frames(self, frames): + _log.info(f"jungfrau number of frames: {frames}") + self._number_of_frames = frames - @pyqtSlot(str) - def set_gain_filename(self, gainfile:str): - logger.info(f"gain file: {gainfile}") - self._gain_maps = gainfile + @pyqtSlot(int) + def set_data_owner_uid(self, uid): + _log.info(f"data owner set to UID = {uid}") + self._uid = uid - def configure(self, outfile=None, outdir=None, **kwargs): - if not self._runJungfrau: - logger.warning("jungfrau not required: configure dismissed") - return + @pyqtSlot(str) + def set_gain_filename(self, gainfile:str): + _log.info(f"gain file: {gainfile}") + self._gain_maps = gainfile - if self._gain_maps is None: - raise JungfrauMissingConfiguration("missing gain maps filename") - gain_filename = self._gain_maps + def configure(self, outfile=None, outdir=None, **kwargs): + if not self._runJungfrau: + _log.warning("jungfrau not required: configure dismissed") + return - if self._uid is None: - raise JungfrauMissingConfiguration("missing data owner p-group UID") - uid = self._uid + if self._gain_maps is None: + raise JungfrauMissingConfiguration("missing gain maps filename") + gain_filename = self._gain_maps - n_frames = self._number_of_frames + if self._uid is None: + raise JungfrauMissingConfiguration("missing data owner p-group UID") + uid = self._uid - pede_pattern = "/sf/bernina/data/p{}/res/JF_pedestals/pedestal_*.JF07T32V01.res.h5".format( - uid - ) - try: - pf = sorted(glob(pede_pattern))[-1] - pf = pf[: pf.index(".JF07T32V01")] - except IndexError: - pf = None + n_frames = self._number_of_frames - pede_filename = pf - logger.info(f"Pedestal file {pede_filename}") + pede_pattern = "/sf/bernina/data/p{}/res/JF_pedestals/pedestal_*.JF07T32V01.res.h5".format( + uid + ) + try: + pf = sorted(glob(pede_pattern))[-1] + pf = pf[: pf.index(".JF07T32V01")] + except IndexError: + pf = None - exptime = 0.000005 - instrument = "Bernina" + pede_filename = pf + _log.info(f"Pedestal file {pede_filename}") - self.writer_config = { - "output_file": outdir + "/" + outfile, - "user_id": uid, - "n_frames": n_frames, - "general/user": str(uid), - "general/process": __name__, - "general/created": str(datetime.now()), - "general/instrument": instrument, - } + exptime = 0.000005 + instrument = "Bernina" - if not self._save_raw: - self.writer_config["output_file"] = "/dev/null" + self.writer_config = { + "output_file": outdir + "/" + outfile, + "user_id": uid, + "n_frames": n_frames, + "general/user": str(uid), + "general/process": __name__, + "general/created": str(datetime.now()), + "general/instrument": instrument, + } - self.detector_config = { - "exptime": exptime, - "frames": 1, - "cycles": n_frames, - "timing": "trigger", - "dr": 16, - } + if not self._save_raw: + self.writer_config["output_file"] = "/dev/null" - self.backend_config = { - "n_frames": n_frames, - "bit_depth": 16, - "run_name": outfile, - "beam_center_x": 2110.0, - "beam_center_y": 2210.0, - "beam_energy": 6000.0, - "detector_distance": 0.06, - "hitfinder_min_snr": 6.0, - "hitfinder_min_pix_count": 5, - "hitfinder_adc_thresh": 20.0, - "swissmx_trajectory_method": "grid", - "swissmx_trajectory_details_1": [100], - "swissmx_trajectory_details_2": [n_frames // 100 + 1], - } + self.detector_config = { + "exptime": exptime, + "frames": 1, + "cycles": n_frames, + "timing": "trigger", + "dr": 16, + } - backend_extras = kwargs.get("backend_extras", None) - if backend_extras: - self.backend_config.update(backend_extras) + self.backend_config = { + "n_frames": n_frames, + "bit_depth": 16, + "run_name": outfile, + "beam_center_x": 2110.0, + "beam_center_y": 2210.0, + "beam_energy": 6000.0, + "detector_distance": 0.06, + "hitfinder_min_snr": 6.0, + "hitfinder_min_pix_count": 5, + "hitfinder_adc_thresh": 20.0, + "swissmx_trajectory_method": "grid", + "swissmx_trajectory_details_1": [100], + "swissmx_trajectory_details_2": [n_frames // 100 + 1], + } - # """ - # "swissmx_trajectory_method": "grid", - # "swissmx_trajectory_details_1": [100], - # "swissmx_trajectory_details_2": [n_frames//100+1], - # - # "swissmx_trajectory_method": "rotation", - # "swissmx_trajectory_details_1": [360], - # "swissmx_trajectory_details_2": [0.1], - # - # "swissmx_trajectory_method": "trajectory", - # "swissmx_trajectory_details_1": [random()*100 for i in range(int(n_frames*0.9))], - # "swissmx_trajectory_details_2": [random()*100 for i in range(int(n_frames*0.9))], - # - # """ - self.bsread_config = { - "output_file": outdir + "/" + outfile, - "user_id": uid, - "general/user": str(uid), - "general/process": __name__, - "general/created": str(datetime.now()), - "general/instrument": instrument, - } + backend_extras = kwargs.get("backend_extras", None) + if backend_extras: + self.backend_config.update(backend_extras) - if gain_filename != "" or pede_filename != "": - self.backend_config["gain_corrections_filename"] = gain_filename - self.backend_config["gain_corrections_dataset"] = "gains" - self.backend_config["pede_corrections_filename"] = pede_filename - self.backend_config["pede_corrections_dataset"] = "gains" - self.backend_config["pede_mask_dataset"] = "pixel_mask" - self.backend_config["activate_corrections_preview"] = True - logger.info("Corrections in online viewer activated") + # """ + # "swissmx_trajectory_method": "grid", + # "swissmx_trajectory_details_1": [100], + # "swissmx_trajectory_details_2": [n_frames//100+1], + # + # "swissmx_trajectory_method": "rotation", + # "swissmx_trajectory_details_1": [360], + # "swissmx_trajectory_details_2": [0.1], + # + # "swissmx_trajectory_method": "trajectory", + # "swissmx_trajectory_details_1": [random()*100 for i in range(int(n_frames*0.9))], + # "swissmx_trajectory_details_2": [random()*100 for i in range(int(n_frames*0.9))], + # + # """ + self.bsread_config = { + "output_file": outdir + "/" + outfile, + "user_id": uid, + "general/user": str(uid), + "general/process": __name__, + "general/created": str(datetime.now()), + "general/instrument": instrument, + } - configuration = { - "writer": self.writer_config, - "backend": self.backend_config, - "detector": self.detector_config, - "bsread": self.bsread_config, - } + if gain_filename != "" or pede_filename != "": + self.backend_config["gain_corrections_filename"] = gain_filename + self.backend_config["gain_corrections_dataset"] = "gains" + self.backend_config["pede_corrections_filename"] = pede_filename + self.backend_config["pede_corrections_dataset"] = "gains" + self.backend_config["pede_mask_dataset"] = "pixel_mask" + self.backend_config["activate_corrections_preview"] = True + _log.info("Corrections in online viewer activated") - logger.info("resetting & configuring detector: approx. 6 seconds") - dia_client.reset() - dia_client.set_config(configuration) - logger.info(dia_client.get_config()) + configuration = { + "writer": self.writer_config, + "backend": self.backend_config, + "detector": self.detector_config, + "bsread": self.bsread_config, + } - def arm(self) -> int: - """arm detector and return an ID for this series""" - if not self._runJungfrau: - logger.warning("jungfrau not required: arm dismissed") - return + _log.info("resetting & configuring detector: approx. 6 seconds") + dia_client.reset() + dia_client.set_config(configuration) + _log.info(dia_client.get_config()) - logger.info("arming detector: approx. 2 seconds") - dia_client.start(trigger_start=False) + def arm(self) -> int: + """arm detector and return an ID for this series""" + if not self._runJungfrau: + _log.warning("jungfrau not required: arm dismissed") + return - def wait_finished(self): - """close shutter after this wait is finished regardless of exceptions""" - if not self._runJungfrau: - logger.warning("jungfrau not required: wait_finished dismissed") - return + _log.info("arming detector: approx. 2 seconds") + dia_client.start(trigger_start=False) - try: - dia_client.wait_for_status(["IntegrationStatus.FINISHED"], polling_interval=0.1) - except: - logger.info("Got IntegrationStatus ERROR") - logger.info(dia_client.get_status()) - logger.info(dia_client.get_status_details()) + def wait_finished(self): + """close shutter after this wait is finished regardless of exceptions""" + if not self._runJungfrau: + _log.warning("jungfrau not required: wait_finished dismissed") + return - logger.info("Stopping acquisition") - dia_client.reset() + try: + dia_client.wait_for_status(["IntegrationStatus.FINISHED"], polling_interval=0.1) + except: + _log.info("Got IntegrationStatus ERROR") + _log.info(dia_client.get_status()) + _log.info(dia_client.get_status_details()) - logger.info("Done") + _log.info("Stopping acquisition") + dia_client.reset() - def disarm(self) -> int: - """arm detector and return an ID for this series""" - if not self._runJungfrau: - logger.warning("jungfrau not required: disarm dismissed") - return + _log.info("Done") - dia_client.reset() + def disarm(self) -> int: + """arm detector and return an ID for this series""" + if not self._runJungfrau: + _log.warning("jungfrau not required: disarm dismissed") + return - def trigger(self): - if not self._runJungfrau: - logger.warning("jungfrau not required: trigger dismissed") - return - logger.info("Trigger in detector is called") - eventer.start() + dia_client.reset() - def abort(self): - if not self._runJungfrau: - logger.warning("jungfrau not required: abort dismissed") - return + def trigger(self): + if not self._runJungfrau: + _log.warning("jungfrau not required: trigger dismissed") + return + _log.info("Trigger in detector is called") + eventer.start() - self.disarm() - eventer.stop() + def abort(self): + if not self._runJungfrau: + _log.warning("jungfrau not required: abort dismissed") + return + self.disarm() + eventer.stop() -jungfrau_detector = Jungfrau() - -def main(): +if __name__=='__main__': + def main(): + jungfrau_detector = Jungfrau() import argparse - + from datetime import datetime date_string = datetime.now().strftime("%Y%m%d_%H%M") - parser = argparse.ArgumentParser(description="Create a pedestal file for Jungrau") parser.add_argument("--api", default="http://sf-daq-1:10000", required=True) - parser.add_argument( - "--filename", default="run_%s.h5" % date_string, help="Output file name" - ) - parser.add_argument( - "--pede", default="", help="File containing pedestal corrections" - ) + parser.add_argument("--filename", default="run_%s.h5" % date_string, help="Output file name") + parser.add_argument("--pede", default="", help="File containing pedestal corrections") parser.add_argument("--gain", default="", help="File containing gain corrections") - parser.add_argument( - "--directory", default="/sf/bernina/data/raw/p16582", help="Output directory" - ) - parser.add_argument( - "--uid", default=16582, help="User ID which needs to own the file", type=int - ) - parser.add_argument( - "--period", default=0.01, help="Period (default is 10Hz - 0.01)", type=float - ) - parser.add_argument( - "--exptime", - default=0.000010, - help="Integration time (default 0.000010 - 10us)", - type=float, - ) - parser.add_argument( - "--frames", default=10, help="Number of frames to take", type=int - ) - parser.add_argument( - "--save", default=False, help="Save data file", action="store_true" - ) - parser.add_argument( - "--highgain", default=False, help="Enable High Gain (HG0)", action="store_true" - ) - parser.add_argument( - "--instrument", default="", help="Name of the instrument, e.g. Alvra" - ) + parser.add_argument("--directory", default="/sf/bernina/data/raw/p16582", help="Output directory") + parser.add_argument("--uid", default=16582, help="User ID which needs to own the file", type=int) + parser.add_argument("--period", default=0.01, help="Period (default is 10Hz - 0.01)", type=float) + parser.add_argument("--exptime",default=0.000010,help="Integration time (default 0.000010 - 10us)",type=float,) + parser.add_argument("--frames", default=10, help="Number of frames to take", type=int) + parser.add_argument("--save", default=False, help="Save data file", action="store_true") + parser.add_argument("--highgain", default=False, help="Enable High Gain (HG0)", action="store_true") + parser.add_argument("--instrument", default="", help="Name of the instrument, e.g. Alvra") # parser.add_argument("--caput", default=False, help="Use the CAPUT trick (experts only!!!)", action="store_true") - args = parser.parse_args() + + main() diff --git a/swissmx.py b/swissmx.py index f1574fe..13aabbf 100755 --- a/swissmx.py +++ b/swissmx.py @@ -42,7 +42,7 @@ class timestamp(): print(txt+f'{t-self.t:6.3g}') self.t=t ts=timestamp() -ts.log('Import part 1/7:') +ts.log('Import part 1/8:') import sys, os, time import json, re import random, signal @@ -68,7 +68,7 @@ TASK_GRID = "grid" TASK_PRELOCATED = "prelocated" TASK_HELICAL = "helical" TASK_EMBL = "embl" -ts.log('Import part 2/7:') +ts.log('Import part 2/8:') import PrelocatedCoordinatesModel # ZAC: orig. code from EmblModule import EmblWidget #ZAC: orig. code from HelicalTable import HelicalTableWidget #ZAC: orig. code @@ -78,7 +78,7 @@ from HelicalTable import HelicalTableWidget #ZAC: orig. code #import mx_swissfel #ZAC: orig. code #swissfel = mx_swissfel.SwissFELMachine() #ZAC: orig. code #from bernina_pulse_picker import pulsePicker #ZAC: orig. code -ts.log('Import part 3/7:') +ts.log('Import part 3/8:') import qtawesome import qutilities from PyQt5 import QtCore, QtGui @@ -89,32 +89,38 @@ from PyQt5.QtWidgets import ( QMessageBox, QPlainTextEdit, QProgressBar, QProgressDialog, QPushButton, QShortcut, QSizePolicy, QSpinBox, QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,) from PyQt5.uic import loadUiType -ts.log('Import part 4/7:') +ts.log('Import part 4/8:') import CustomROI as CstROI import pyqtUsrObj as UsrGO #from CustomROI import BeamMark, Grid, CrystalCircle #ZAC: orig. code + +import GenericDialog #from GenericDialog import GenericDialog #ZAC: orig. code #from dialogs.PreferencesDialog import PreferencesDialog #ZAC: orig. code #from epics_widgets import zoom #ZAC: orig. code from epics_widgets.MotorTweak import MotorTweak from epics_widgets.SmaractMotorTweak import SmaractMotorTweak from epics_widgets.SimMotorTweak import SimMotorTweak -ts.log('Import part 5/7:') +ts.log('Import part 5/8:') import matplotlib as mpl +import matplotlib.pyplot as plt import numpy as np import pyqtgraph as pg import pyqtgraph.exporters # use antialias for draw lines, interpret image data as row-major instead of col-major pg.setConfigOptions(antialias=True,imageAxisOrder='row-major') -ts.log('Import part 6/7:') +ts.log('Import part 6/8:') import app_utils from app_config import AppCfg #settings, option, toggle_option import epics from epics.ca import pend_event import camera,backlight,zoom,illumination,geometry -ts.log('Import part 7/7:') +ts.log('Import part 7/8:') +import deltatau +import detector +ts.log('Import part 8/8:') #_URL = "http://PC12288:8080" @@ -2231,28 +2237,17 @@ class Main(QMainWindow, Ui_MainWindow): try: dt=app._deltatau except AttributeError: - import matplotlib.pyplot as plt - import deltatau app._deltatau=dt=deltatau.Deltatau() try: jf=app._jungfrau except AttributeError: - import detector app._jungfrau=jf=detector.Jungfrau() - dt=app._deltatau - - sp=dt._shapepath - sp.gen_grid_points(w=15, h=15, pitch=3, rnd=0, ofs=(0, +2000)); sp.sort_points(False, 15); sp.meta['pt2pt_time']=10 - - gtMaxLn=116508 - ovhdTime=100 - acq_per=int(np.ceil((sp.meta['pt2pt_time']*sp.points.shape[0]+ovhdTime)/(gtMaxLn*sp.meta['srv_per']))) - sp.setup_gather(acq_per=acq_per) + sp.setup_gather() sp.setup_sync(verbose=verbose&32, timeOfs=0.05) sp.setup_coord_trf() # reset to shape path system # sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs @@ -2268,11 +2263,11 @@ class Main(QMainWindow, Ui_MainWindow): print('progress %d/%d'%(p, sp.points.shape[0])); time.sleep(.1) sp.gather_upload(fnRec=fn+'.npz') - dp=deltatau.shapepath.DebugPlot(sp); + dp=deltatau.shapepath.DebugPlot(sp) dp.plot_gather(mode=11) - print('done') - plt.show(block=False) + #plt.show(block=False) + plt.show(block=True) return @@ -3072,168 +3067,108 @@ class Main(QMainWindow, Ui_MainWindow): fy_motor.move_motor_to_position(y) def set_beam_size_marker_dialog(self): - w, h = settings.value(BEAM_SIZE) - d = GenericDialog( - title="Beamsize", + SPN=GenericDialog.Spinner + app=QApplication.instance() + cfg=app._cfg + w, h = map(float,cfg.value(AppCfg.GEO_BEAM_SZ)) + d = GenericDialog.GenericDialog( + title="geometry", message="Enter the size of the beam in microns", inputs={ - "width": ( - "Width", - int(w), - Spinner(int(w), min=1, max=200, suffix=" \u00B5m"), - ), - "height": ( - "Height", - int(h), - Spinner(int(h), min=1, max=200, suffix=" \u00B5m"), - ), + "bw": ("beam width um" ,w,SPN(w, min=1, max=200, suffix=" \u00B5m"),), + "bh": ("beam height um",h,SPN(h, min=1, max=200, suffix=" \u00B5m"),), }, ) if d.exec(): results = d.results _log.info("Updating beamsize to {}".format(results)) - w, h = results["width"], results["height"] + bm_sz= (results["bw"], results["bh"]) _log.debug("types {}".format(type(w))) - settings.setValue(BEAM_SIZE, (w, h)) - self._beammark.set_beam_size((w, h)) - settings.sync() + cfg.setValue(AppCfg.GEO_BEAM_SZ, bm_sz) + bm=self._goBeamMarker + bm.setSize(bm_sz) + #self._beammark.set_beam_size((w, h)) + cfg.sync() def set_posttube_references_dialog(self): - x_up = settings.value("post_sample_tube/x_up", 0.0) - y_up = settings.value("post_sample_tube/y_up", 0.0) - x_down = settings.value("post_sample_tube/x_down", 0.0) - y_down = settings.value("post_sample_tube/y_down", 0.0) - dx = settings.value("post_sample_tube/dx", 0.0) - dy = settings.value("post_sample_tube/dy", 0.0) - tz_in = settings.value("post_sample_tube/z_in", 0.0) - tz_out = settings.value("post_sample_tube/z_out", 0.0) + SPN=GenericDialog.Spinner + app=QApplication.instance() + cfg=app._cfg + x_up = cfg.value(AppCfg.PST_X_UP , 0.0,type=float) + y_up = cfg.value(AppCfg.PST_Y_UP , 0.0,type=float) + x_down = cfg.value(AppCfg.PST_X_DOWN, 0.0,type=float) + y_down = cfg.value(AppCfg.PST_Y_DOWN, 0.0,type=float) + dx = cfg.value(AppCfg.PST_DX , 0.0,type=float) + dy = cfg.value(AppCfg.PST_DY , 0.0,type=float) + tz_in = cfg.value(AppCfg.PST_TZ_IN , 0.0,type=float) + tz_out = cfg.value(AppCfg.PST_TZ_OUT, 0.0,type=float) - d = GenericDialog( + d = GenericDialog.GenericDialog( title="Post Sample Tube Configuration", message="Enter the relative displacements for X and Y to move the post sample tube either in or out.", inputs={ - "post_sample_tube/x_up": ( - "Up X", - float(x_up), - Spinner(float(x_up), decimals=3, min=-45.0, max=15.0, suffix=" mm"), - ), - "post_sample_tube/y_up": ( - "Up Y", - float(y_up), - Spinner(float(y_up), decimals=3, min=-45.0, max=15.0, suffix=" mm"), - ), - "post_sample_tube/x_down": ( - "Down X", - float(x_down), - Spinner( - float(x_down), decimals=3, min=-45.0, max=15.0, suffix=" mm" - ), - ), - "post_sample_tube/y_down": ( - "Down Y", - float(y_down), - Spinner( - float(y_down), decimals=3, min=-45.0, max=15.0, suffix=" mm" - ), - ), - "post_sample_tube/dx": ( - "out delta X", - float(dx), - Spinner(float(dx), decimals=3, min=-32.0, max=32.0, suffix=" mm"), - ), - "post_sample_tube/dy": ( - "out delta Y", - float(dy), - Spinner(float(dy), decimals=3, min=-32.0, max=32.0, suffix=" mm"), - ), - "post_sample_tube/z_in": ( - "tube Z in position", - float(tz_in), - Spinner(float(tz_in), decimals=3, min=-8.0, max=1.0, suffix=" mm"), - ), - "post_sample_tube/z_out": ( - "tube Z OUT position", - float(tz_out), - Spinner(float(tz_out), decimals=3, min=-8.0, max=1.0, suffix=" mm"), - ), + AppCfg.PST_X_UP : ("Up X" , x_up , SPN(x_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), + AppCfg.PST_Y_UP : ("Up Y" , y_up , SPN(y_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), + AppCfg.PST_X_DOWN: ("Down X" , x_down, SPN(x_down, decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), + AppCfg.PST_Y_DOWN: ("Down Y" , y_down, SPN(y_down, decimals=3, min=-45.0, max=15.0, suffix=" mm"), ), + AppCfg.PST_DX : ("out delta X" , dx , SPN(dx , decimals=3, min=-32.0, max=32.0, suffix=" mm"), ), + AppCfg.PST_DY : ("out delta Y" , dy , SPN(dy , decimals=3, min=-32.0, max=32.0, suffix=" mm"), ), + AppCfg.PST_TZ_IN : ("tube Z in position" , tz_in , SPN(tz_in , decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ), + AppCfg.PST_TZ_OUT: ("tube Z OUT position", tz_out, SPN(tz_out, decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ), }, ) if d.exec(): results = d.results _log.info("setting post-sample-tube displacements {}".format(results)) for k, v in results.items(): - settings.setValue(k, v) - settings.sync() + cfg.setValue(k, v) + cfg.sync() def set_collimator_reference_positions(self): - x_out = float(settings.value("collimator/dx", 0.0)) - y_out = float(settings.value("collimator/dy", 0.0)) - x_in = float(settings.value("collimator/x_in", 0.0)) - y_in = float(settings.value("collimator/y_in", 0.0)) - d = GenericDialog( + SPN=GenericDialog.Spinner + app=QApplication.instance() + cfg=app._cfg + x_out = cfg.value(AppCfg.COL_DX , 0.0,type=float) + y_out = cfg.value(AppCfg.COL_DY , 0.0,type=float) + x_in = cfg.value(AppCfg.COL_X_IN, 0.0,type=float) + y_in = cfg.value(AppCfg.COL_Y_IN, 0.0,type=float) + d = GenericDialog.GenericDialog( title="Collimator configuration", message="Enter reference positions for the collimator", inputs={ - "collimator/dx": ( - "Collimator out deltaX", - x_out, - Spinner(x_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"), - ), - "collimator/dy": ( - "Collimator out deltaY", - y_out, - Spinner(y_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"), - ), - "collimator/x_in": ( - "Collimator in X", - x_in, - Spinner(x_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"), - ), - "collimator/y_in": ( - "Collimator in Y", - y_in, - Spinner(y_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"), - ), + AppCfg.COL_DX: ("Collimator out deltaX", x_out, SPN(x_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), + AppCfg.COL_DY: ("Collimator out deltaY", y_out, SPN(y_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), + AppCfg.COL_X_IN: ("Collimator in X", x_in, SPN(x_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), + AppCfg.COL_Y_IN: ("Collimator in Y", y_in, SPN(y_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),), }, ) if d.exec(): results = d.results _log.info("setting collimator reference positions {}".format(results)) for k, v in results.items(): - settings.setValue(k, v) - settings.sync() + cfg.setValue(k, v) + cfg.sync() def set_backlight_positions_dialog(self): - p_in = int(settings.value("backlight/in")) - p_out = int(settings.value("backlight/out")) - p_diode = int(settings.value("backlight/diode")) - d = GenericDialog( + SPN=GenericDialog.Spinner + app=QApplication.instance() + cfg=app._cfg + p_in = cfg.value(AppCfg.BKLGT_IN,0,type=int) + p_out = cfg.value(AppCfg.BKLGT_OUT,0,type=int) + d = GenericDialog.GenericDialog( title="Back Light configuration", message="Enter reference positions for the backlight", inputs={ - "backlight/in": ( - "In position", - p_in, - Spinner(p_in, min=-30000, max=10), - ), - "backlight/out": ( - "Out position", - p_out, - Spinner(p_out, min=-1000, max=10), - ), - "backlight/diode": ( - "Diode position", - p_diode, - Spinner(p_diode, min=-40000, max=-20000), - ), + AppCfg.BKLGT_IN: ("In position" , p_in , SPN(p_in, min=-30000, max=10), ), + AppCfg.BKLGT_OUT: ("Out position", p_out, SPN(p_out, min=-1000, max=10), ), }, ) if d.exec(): results = d.results _log.info("setting back light reference positions {}".format(results)) for k, v in results.items(): - settings.setValue(k, v) - settings.sync() + cfg.setValue(k, v) + cfg.sync() def set_cryojet_positions_dialog(self): p_in = settings.value(CRYOJET_NOZZLE_IN, type=float) @@ -3256,31 +3191,31 @@ class Main(QMainWindow, Ui_MainWindow): settings.sync() def set_deltatau_parameters(self): - a = settings.value(DELTATAU_VELOCITY_SCALE, 1, type=float) - b = option(DELTATAU_SORT_POINTS) - c = option(DELTATAU_OMEGACOS) - d = option(DELTATAU_SHOW_PLOTS) + SPN=GenericDialog.Spinner + CB=GenericDialog.Checkbox + app=QApplication.instance() + cfg=app._cfg + #dt1 = cfg.value(AppCfg.DT_HOST,'SAR-CPPM-EXPMX1') + dt1 = cfg.value(AppCfg.DT_HOST,'localhost:10001:10002') + dt2 = cfg.value(AppCfg.DT_VEL_SCL, 1, type=float) + dt3 = cfg.option(AppCfg.DT_SHOW_PLOTS) - d = GenericDialog( + d = GenericDialog.GenericDialog( title="Delta Tau Parameters", message="These parameters affect the data collection.", inputs={ - DELTATAU_VELOCITY_SCALE: ("Velocity Scale (1=optimal, 0=zero vel at target)", a, - Spinner(a, min=0, max=1, suffix=""),), - DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b, - Checkbox(b, "sort points"),), - DELTATAU_SHOW_PLOTS: ("show plots after collection", d, - Checkbox(d, "correct"),), - DELTATAU_OMEGACOS: ( "preloc. correct omega tilt", c, - Checkbox(c, "correct"),), + AppCfg.DT_HOST:("host name (host[:port:port_gather])", dt1, QLineEdit(),), + AppCfg.DT_VEL_SCL: ("Velocity Scale (1=optimal, 0=zero vel at target)", dt2,SPN(dt2, min=0, max=1, suffix=""),), + AppCfg.DT_SHOW_PLOTS: ("show plots after collection", dt3,CB(dt3, "active"),), + #DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b,CB(b, "sort points"),), }, ) if d.exec(): results = d.results _log.info("setting delta tau parameters {}".format(results)) for k, v in results.items(): - settings.setValue(k, v) - settings.sync() + cfg.setValue(k, v) + cfg.sync() def set_tell_mount_positions(self): AUTODRY_ENABLED = "tell/autodry_enabled" @@ -3293,7 +3228,7 @@ class Main(QMainWindow, Ui_MainWindow): maxtime = settings.value(AUTODRY_MAXTIME, type=float) / SECS_HOURS maxmounts = settings.value(AUTODRY_MAXMOUNTS, type=int) - d = GenericDialog( + d = GenericDialog.GenericDialog( title="TELL Settings", message="These control some features of the TELL sample changer", inputs={