GenericDialog.py for Application Config

This commit is contained in:
2022-08-22 13:02:31 +02:00
parent 0f16af5810
commit 28c94dd297
4 changed files with 539 additions and 405 deletions

204
GenericDialog.py Normal file
View File

@@ -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_())

View File

@@ -24,6 +24,27 @@ class AppCfg(QSettings):
WINDOW_SPLITTER="window/splitter" WINDOW_SPLITTER="window/splitter"
WINDOW_STATE= "window/state" 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 ??? ---------- # ---------- OBSOLETE ??? ----------
#ZOOM_BUTTONS="sample_viewing/zoom_buttons" #ZOOM_BUTTONS="sample_viewing/zoom_buttons"
@@ -33,11 +54,11 @@ class AppCfg(QSettings):
CRYOJET_NOZZLE_OUT="cryojet/nozzle_out" CRYOJET_NOZZLE_OUT="cryojet/nozzle_out"
CRYOJET_NOZZLE_IN="cryojet/nozzle_in" CRYOJET_NOZZLE_IN="cryojet/nozzle_in"
DELTATAU_HOST="deltatau/host" #DELTATAU_HOST="deltatau/host"
DELTATAU_SHOW_PLOTS="deltatau/show_plots" #DELTATAU_SHOW_PLOTS="deltatau/show_plots"
DELTATAU_OMEGACOS="deltatau/omegacos" DELTATAU_OMEGACOS="deltatau/omegacos"
DELTATAU_SORT_POINTS="deltatau/sort_points" DELTATAU_SORT_POINTS="deltatau/sort_points"
DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale" #DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale"
CAMERA_TRANSFORMATIONS="camera/transformations" CAMERA_TRANSFORMATIONS="camera/transformations"
CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm" CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm"
@@ -108,8 +129,8 @@ class AppCfg(QSettings):
def setValue(self, key: str, val): #overload to debug def setValue(self, key: str, val): #overload to debug
return super(AppCfg, self).setValue(key,val) return super(AppCfg, self).setValue(key,val)
def value(self,key,*vargs): #overload to debug def value(self,key,*vargs,**kwargs): #overload to debug
val=super(AppCfg, self).value(key,*vargs) val=super(AppCfg, self).value(key,*vargs,**kwargs)
return val return val
#@property #@property
#def value(self): #def value(self):

View File

@@ -1,7 +1,6 @@
import logging import logging
_log=logging.getLogger(__name__) _log=logging.getLogger(__name__)
#from datetime import datetime
#from glob import glob #from glob import glob
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
@@ -14,293 +13,268 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
#dia_client = DetectorIntegrationClient(config['uri']) #dia_client = DetectorIntegrationClient(config['uri'])
class JungfrauWaiter(QObject): class JungfrauWaiter(QObject):
finished = pyqtSignal() finished = pyqtSignal()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@pyqtSlot() @pyqtSlot()
def work(self): def work(self):
pass
class JungfrauMissingConfiguration(Exception):
pass pass
class Jungfrau(QObject, metaclass=qsingleton.Singleton): class JungfrauMissingConfiguration(Exception):
pedestal_selected = pyqtSignal(str) pass
configured = pyqtSignal()
armed = pyqtSignal()
started = pyqtSignal()
triggered = pyqtSignal()
aborted = pyqtSignal()
def __init__(self, parent=None, **kwargs): class Jungfrau(QObject):
super(Jungfrau, self).__init__(parent, **kwargs) pedestal_selected = pyqtSignal(str)
self._simulated = kwargs.get("simulate", simulated) configured = pyqtSignal()
self._save_raw = True armed = pyqtSignal()
self._runJungfrau = False started = pyqtSignal()
self._number_of_frames = 1 triggered = pyqtSignal()
self._gain_maps = config["gain_maps"] aborted = pyqtSignal()
self._uid = None
self._serial_number = config['serialnr']
self.writer_config = {}
self.backend_config = {}
self.detector_config = {}
self.bsread_config = {}
@pyqtSlot(bool) def __init__(self, parent=None, **kwargs):
def set_run_jungfrau(self, run): super(Jungfrau, self).__init__(parent, **kwargs)
"""running or not jungfrau, global simulated flag overrides""" self._save_raw = True
logger.warning(f"{'' if run else 'not '}running jungfrau detector") self._runJungfrau = False
if not self._simulated: self._number_of_frames = 1
self._runJungfrau = run #self._gain_maps = config["gain_maps"]
else: self._uid = None
self._runJungfrau = False #self._serial_number = config['serialnr']
self.writer_config = {}
self.backend_config = {}
self.detector_config = {}
self.bsread_config = {}
@pyqtSlot(bool) @pyqtSlot(bool)
def set_save_raw(self, save_raw): def set_run_jungfrau(self, run):
"""enable or not writer_config""" """running or not jungfrau, global simulated flag overrides"""
logger.warning(f"{'' if save_raw else 'not '}saving data") _log.warning(f"{'' if run else 'not '}running jungfrau detector")
self._save_raw = save_raw if not self._simulated:
self._runJungfrau = run
else:
self._runJungfrau = False
@pyqtSlot(str) @pyqtSlot(bool)
def set_detector_serial(self, serial_number): def set_save_raw(self, save_raw):
"""set serial number for main detector""" """enable or not writer_config"""
logger.info(f"jungfrau serial number: {serial_number}") _log.warning(f"{'' if save_raw else 'not '}saving data")
self._serial_number = serial_number self._save_raw = save_raw
def is_saving_data(self): @pyqtSlot(str)
"""if False writer_config is not issued""" def set_detector_serial(self, serial_number):
return self._save_raw """set serial number for main detector"""
_log.info(f"jungfrau serial number: {serial_number}")
self._serial_number = serial_number
def is_running_detector(self): def is_saving_data(self):
"""if False nothing is actually called""" """if False writer_config is not issued"""
return self._runJungfrau return self._save_raw
@pyqtSlot(int) def is_running_detector(self):
def set_number_of_frames(self, frames): """if False nothing is actually called"""
logger.info(f"jungfrau number of frames: {frames}") return self._runJungfrau
self._number_of_frames = frames
@pyqtSlot(int) @pyqtSlot(int)
def set_data_owner_uid(self, uid): def set_number_of_frames(self, frames):
logger.info(f"data owner set to UID = {uid}") _log.info(f"jungfrau number of frames: {frames}")
self._uid = uid self._number_of_frames = frames
@pyqtSlot(str) @pyqtSlot(int)
def set_gain_filename(self, gainfile:str): def set_data_owner_uid(self, uid):
logger.info(f"gain file: {gainfile}") _log.info(f"data owner set to UID = {uid}")
self._gain_maps = gainfile self._uid = uid
def configure(self, outfile=None, outdir=None, **kwargs): @pyqtSlot(str)
if not self._runJungfrau: def set_gain_filename(self, gainfile:str):
logger.warning("jungfrau not required: configure dismissed") _log.info(f"gain file: {gainfile}")
return self._gain_maps = gainfile
if self._gain_maps is None: def configure(self, outfile=None, outdir=None, **kwargs):
raise JungfrauMissingConfiguration("missing gain maps filename") if not self._runJungfrau:
gain_filename = self._gain_maps _log.warning("jungfrau not required: configure dismissed")
return
if self._uid is None: if self._gain_maps is None:
raise JungfrauMissingConfiguration("missing data owner p-group UID") raise JungfrauMissingConfiguration("missing gain maps filename")
uid = self._uid 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( n_frames = self._number_of_frames
uid
)
try:
pf = sorted(glob(pede_pattern))[-1]
pf = pf[: pf.index(".JF07T32V01")]
except IndexError:
pf = None
pede_filename = pf pede_pattern = "/sf/bernina/data/p{}/res/JF_pedestals/pedestal_*.JF07T32V01.res.h5".format(
logger.info(f"Pedestal file {pede_filename}") uid
)
try:
pf = sorted(glob(pede_pattern))[-1]
pf = pf[: pf.index(".JF07T32V01")]
except IndexError:
pf = None
exptime = 0.000005 pede_filename = pf
instrument = "Bernina" _log.info(f"Pedestal file {pede_filename}")
self.writer_config = { exptime = 0.000005
"output_file": outdir + "/" + outfile, instrument = "Bernina"
"user_id": uid,
"n_frames": n_frames,
"general/user": str(uid),
"general/process": __name__,
"general/created": str(datetime.now()),
"general/instrument": instrument,
}
if not self._save_raw: self.writer_config = {
self.writer_config["output_file"] = "/dev/null" "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 = { if not self._save_raw:
"exptime": exptime, self.writer_config["output_file"] = "/dev/null"
"frames": 1,
"cycles": n_frames,
"timing": "trigger",
"dr": 16,
}
self.backend_config = { self.detector_config = {
"n_frames": n_frames, "exptime": exptime,
"bit_depth": 16, "frames": 1,
"run_name": outfile, "cycles": n_frames,
"beam_center_x": 2110.0, "timing": "trigger",
"beam_center_y": 2210.0, "dr": 16,
"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],
}
backend_extras = kwargs.get("backend_extras", None) self.backend_config = {
if backend_extras: "n_frames": n_frames,
self.backend_config.update(backend_extras) "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],
}
# """ backend_extras = kwargs.get("backend_extras", None)
# "swissmx_trajectory_method": "grid", if backend_extras:
# "swissmx_trajectory_details_1": [100], self.backend_config.update(backend_extras)
# "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,
}
if gain_filename != "" or pede_filename != "": # """
self.backend_config["gain_corrections_filename"] = gain_filename # "swissmx_trajectory_method": "grid",
self.backend_config["gain_corrections_dataset"] = "gains" # "swissmx_trajectory_details_1": [100],
self.backend_config["pede_corrections_filename"] = pede_filename # "swissmx_trajectory_details_2": [n_frames//100+1],
self.backend_config["pede_corrections_dataset"] = "gains" #
self.backend_config["pede_mask_dataset"] = "pixel_mask" # "swissmx_trajectory_method": "rotation",
self.backend_config["activate_corrections_preview"] = True # "swissmx_trajectory_details_1": [360],
logger.info("Corrections in online viewer activated") # "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 = { if gain_filename != "" or pede_filename != "":
"writer": self.writer_config, self.backend_config["gain_corrections_filename"] = gain_filename
"backend": self.backend_config, self.backend_config["gain_corrections_dataset"] = "gains"
"detector": self.detector_config, self.backend_config["pede_corrections_filename"] = pede_filename
"bsread": self.bsread_config, 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") configuration = {
dia_client.reset() "writer": self.writer_config,
dia_client.set_config(configuration) "backend": self.backend_config,
logger.info(dia_client.get_config()) "detector": self.detector_config,
"bsread": self.bsread_config,
}
def arm(self) -> int: _log.info("resetting & configuring detector: approx. 6 seconds")
"""arm detector and return an ID for this series""" dia_client.reset()
if not self._runJungfrau: dia_client.set_config(configuration)
logger.warning("jungfrau not required: arm dismissed") _log.info(dia_client.get_config())
return
logger.info("arming detector: approx. 2 seconds") def arm(self) -> int:
dia_client.start(trigger_start=False) """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): _log.info("arming detector: approx. 2 seconds")
"""close shutter after this wait is finished regardless of exceptions""" dia_client.start(trigger_start=False)
if not self._runJungfrau:
logger.warning("jungfrau not required: wait_finished dismissed")
return
try: def wait_finished(self):
dia_client.wait_for_status(["IntegrationStatus.FINISHED"], polling_interval=0.1) """close shutter after this wait is finished regardless of exceptions"""
except: if not self._runJungfrau:
logger.info("Got IntegrationStatus ERROR") _log.warning("jungfrau not required: wait_finished dismissed")
logger.info(dia_client.get_status()) return
logger.info(dia_client.get_status_details())
logger.info("Stopping acquisition") try:
dia_client.reset() 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: _log.info("Done")
"""arm detector and return an ID for this series"""
if not self._runJungfrau:
logger.warning("jungfrau not required: disarm dismissed")
return
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): dia_client.reset()
if not self._runJungfrau:
logger.warning("jungfrau not required: trigger dismissed")
return
logger.info("Trigger in detector is called")
eventer.start()
def abort(self): def trigger(self):
if not self._runJungfrau: if not self._runJungfrau:
logger.warning("jungfrau not required: abort dismissed") _log.warning("jungfrau not required: trigger dismissed")
return return
_log.info("Trigger in detector is called")
eventer.start()
self.disarm() def abort(self):
eventer.stop() if not self._runJungfrau:
_log.warning("jungfrau not required: abort dismissed")
return
self.disarm()
eventer.stop()
jungfrau_detector = Jungfrau() if __name__=='__main__':
def main():
def main(): jungfrau_detector = Jungfrau()
import argparse import argparse
from datetime import datetime
date_string = datetime.now().strftime("%Y%m%d_%H%M") date_string = datetime.now().strftime("%Y%m%d_%H%M")
parser = argparse.ArgumentParser(description="Create a pedestal file for Jungrau") 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("--api", default="http://sf-daq-1:10000", required=True)
parser.add_argument( parser.add_argument("--filename", default="run_%s.h5" % date_string, help="Output file name")
"--filename", default="run_%s.h5" % date_string, help="Output file name" parser.add_argument("--pede", default="", help="File containing pedestal corrections")
)
parser.add_argument(
"--pede", default="", help="File containing pedestal corrections"
)
parser.add_argument("--gain", default="", help="File containing gain corrections") parser.add_argument("--gain", default="", help="File containing gain corrections")
parser.add_argument( parser.add_argument("--directory", default="/sf/bernina/data/raw/p16582", help="Output directory")
"--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( parser.add_argument("--exptime",default=0.000010,help="Integration time (default 0.000010 - 10us)",type=float,)
"--uid", default=16582, help="User ID which needs to own the file", type=int 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( parser.add_argument("--highgain", default=False, help="Enable High Gain (HG0)", action="store_true")
"--period", default=0.01, help="Period (default is 10Hz - 0.01)", type=float parser.add_argument("--instrument", default="", help="Name of the instrument, e.g. Alvra")
)
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") # parser.add_argument("--caput", default=False, help="Use the CAPUT trick (experts only!!!)", action="store_true")
args = parser.parse_args() args = parser.parse_args()
main()

View File

@@ -42,7 +42,7 @@ class timestamp():
print(txt+f'{t-self.t:6.3g}') print(txt+f'{t-self.t:6.3g}')
self.t=t self.t=t
ts=timestamp() ts=timestamp()
ts.log('Import part 1/7:') ts.log('Import part 1/8:')
import sys, os, time import sys, os, time
import json, re import json, re
import random, signal import random, signal
@@ -68,7 +68,7 @@ TASK_GRID = "grid"
TASK_PRELOCATED = "prelocated" TASK_PRELOCATED = "prelocated"
TASK_HELICAL = "helical" TASK_HELICAL = "helical"
TASK_EMBL = "embl" TASK_EMBL = "embl"
ts.log('Import part 2/7:') ts.log('Import part 2/8:')
import PrelocatedCoordinatesModel # ZAC: orig. code import PrelocatedCoordinatesModel # ZAC: orig. code
from EmblModule import EmblWidget #ZAC: orig. code from EmblModule import EmblWidget #ZAC: orig. code
from HelicalTable import HelicalTableWidget #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 #import mx_swissfel #ZAC: orig. code
#swissfel = mx_swissfel.SwissFELMachine() #ZAC: orig. code #swissfel = mx_swissfel.SwissFELMachine() #ZAC: orig. code
#from bernina_pulse_picker import pulsePicker #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 qtawesome
import qutilities import qutilities
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
@@ -89,32 +89,38 @@ from PyQt5.QtWidgets import (
QMessageBox, QPlainTextEdit, QProgressBar, QProgressDialog, QPushButton, QShortcut, QSizePolicy, QSpinBox, QMessageBox, QPlainTextEdit, QProgressBar, QProgressDialog, QPushButton, QShortcut, QSizePolicy, QSpinBox,
QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,) QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,)
from PyQt5.uic import loadUiType from PyQt5.uic import loadUiType
ts.log('Import part 4/7:') ts.log('Import part 4/8:')
import CustomROI as CstROI import CustomROI as CstROI
import pyqtUsrObj as UsrGO import pyqtUsrObj as UsrGO
#from CustomROI import BeamMark, Grid, CrystalCircle #ZAC: orig. code #from CustomROI import BeamMark, Grid, CrystalCircle #ZAC: orig. code
import GenericDialog
#from GenericDialog import GenericDialog #ZAC: orig. code #from GenericDialog import GenericDialog #ZAC: orig. code
#from dialogs.PreferencesDialog import PreferencesDialog #ZAC: orig. code #from dialogs.PreferencesDialog import PreferencesDialog #ZAC: orig. code
#from epics_widgets import zoom #ZAC: orig. code #from epics_widgets import zoom #ZAC: orig. code
from epics_widgets.MotorTweak import MotorTweak from epics_widgets.MotorTweak import MotorTweak
from epics_widgets.SmaractMotorTweak import SmaractMotorTweak from epics_widgets.SmaractMotorTweak import SmaractMotorTweak
from epics_widgets.SimMotorTweak import SimMotorTweak from epics_widgets.SimMotorTweak import SimMotorTweak
ts.log('Import part 5/7:') ts.log('Import part 5/8:')
import matplotlib as mpl import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
import pyqtgraph.exporters import pyqtgraph.exporters
# use antialias for draw lines, interpret image data as row-major instead of col-major # use antialias for draw lines, interpret image data as row-major instead of col-major
pg.setConfigOptions(antialias=True,imageAxisOrder='row-major') pg.setConfigOptions(antialias=True,imageAxisOrder='row-major')
ts.log('Import part 6/7:') ts.log('Import part 6/8:')
import app_utils import app_utils
from app_config import AppCfg #settings, option, toggle_option from app_config import AppCfg #settings, option, toggle_option
import epics import epics
from epics.ca import pend_event from epics.ca import pend_event
import camera,backlight,zoom,illumination,geometry 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" #_URL = "http://PC12288:8080"
@@ -2231,28 +2237,17 @@ class Main(QMainWindow, Ui_MainWindow):
try: try:
dt=app._deltatau dt=app._deltatau
except AttributeError: except AttributeError:
import matplotlib.pyplot as plt
import deltatau
app._deltatau=dt=deltatau.Deltatau() app._deltatau=dt=deltatau.Deltatau()
try: try:
jf=app._jungfrau jf=app._jungfrau
except AttributeError: except AttributeError:
import detector
app._jungfrau=jf=detector.Jungfrau() app._jungfrau=jf=detector.Jungfrau()
dt=app._deltatau
sp=dt._shapepath sp=dt._shapepath
sp.gen_grid_points(w=15, h=15, pitch=3, rnd=0, ofs=(0, +2000)); sp.gen_grid_points(w=15, h=15, pitch=3, rnd=0, ofs=(0, +2000));
sp.sort_points(False, 15); sp.sort_points(False, 15);
sp.meta['pt2pt_time']=10 sp.meta['pt2pt_time']=10
sp.setup_gather()
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_sync(verbose=verbose&32, timeOfs=0.05) sp.setup_sync(verbose=verbose&32, timeOfs=0.05)
sp.setup_coord_trf() # reset to shape path system 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 # 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])); print('progress %d/%d'%(p, sp.points.shape[0]));
time.sleep(.1) time.sleep(.1)
sp.gather_upload(fnRec=fn+'.npz') sp.gather_upload(fnRec=fn+'.npz')
dp=deltatau.shapepath.DebugPlot(sp); dp=deltatau.shapepath.DebugPlot(sp)
dp.plot_gather(mode=11) dp.plot_gather(mode=11)
print('done') #plt.show(block=False)
plt.show(block=False) plt.show(block=True)
return return
@@ -3072,168 +3067,108 @@ class Main(QMainWindow, Ui_MainWindow):
fy_motor.move_motor_to_position(y) fy_motor.move_motor_to_position(y)
def set_beam_size_marker_dialog(self): def set_beam_size_marker_dialog(self):
w, h = settings.value(BEAM_SIZE) SPN=GenericDialog.Spinner
d = GenericDialog( app=QApplication.instance()
title="Beamsize", 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", message="Enter the size of the beam in microns",
inputs={ inputs={
"width": ( "bw": ("beam width um" ,w,SPN(w, min=1, max=200, suffix=" \u00B5m"),),
"Width", "bh": ("beam height um",h,SPN(h, min=1, max=200, suffix=" \u00B5m"),),
int(w),
Spinner(int(w), min=1, max=200, suffix=" \u00B5m"),
),
"height": (
"Height",
int(h),
Spinner(int(h), min=1, max=200, suffix=" \u00B5m"),
),
}, },
) )
if d.exec(): if d.exec():
results = d.results results = d.results
_log.info("Updating beamsize to {}".format(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))) _log.debug("types {}".format(type(w)))
settings.setValue(BEAM_SIZE, (w, h)) cfg.setValue(AppCfg.GEO_BEAM_SZ, bm_sz)
self._beammark.set_beam_size((w, h)) bm=self._goBeamMarker
settings.sync() bm.setSize(bm_sz)
#self._beammark.set_beam_size((w, h))
cfg.sync()
def set_posttube_references_dialog(self): def set_posttube_references_dialog(self):
x_up = settings.value("post_sample_tube/x_up", 0.0) SPN=GenericDialog.Spinner
y_up = settings.value("post_sample_tube/y_up", 0.0) app=QApplication.instance()
x_down = settings.value("post_sample_tube/x_down", 0.0) cfg=app._cfg
y_down = settings.value("post_sample_tube/y_down", 0.0) x_up = cfg.value(AppCfg.PST_X_UP , 0.0,type=float)
dx = settings.value("post_sample_tube/dx", 0.0) y_up = cfg.value(AppCfg.PST_Y_UP , 0.0,type=float)
dy = settings.value("post_sample_tube/dy", 0.0) x_down = cfg.value(AppCfg.PST_X_DOWN, 0.0,type=float)
tz_in = settings.value("post_sample_tube/z_in", 0.0) y_down = cfg.value(AppCfg.PST_Y_DOWN, 0.0,type=float)
tz_out = settings.value("post_sample_tube/z_out", 0.0) 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", title="Post Sample Tube Configuration",
message="Enter the relative displacements for X and Y to move the post sample tube either in or out.", message="Enter the relative displacements for X and Y to move the post sample tube either in or out.",
inputs={ inputs={
"post_sample_tube/x_up": ( AppCfg.PST_X_UP : ("Up X" , x_up , SPN(x_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ),
"Up X", AppCfg.PST_Y_UP : ("Up Y" , y_up , SPN(y_up , decimals=3, min=-45.0, max=15.0, suffix=" mm"), ),
float(x_up), AppCfg.PST_X_DOWN: ("Down X" , x_down, SPN(x_down, decimals=3, min=-45.0, max=15.0, suffix=" mm"), ),
Spinner(float(x_up), 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"), ),
"post_sample_tube/y_up": ( AppCfg.PST_DY : ("out delta Y" , dy , SPN(dy , decimals=3, min=-32.0, max=32.0, suffix=" mm"), ),
"Up Y", AppCfg.PST_TZ_IN : ("tube Z in position" , tz_in , SPN(tz_in , decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ),
float(y_up), AppCfg.PST_TZ_OUT: ("tube Z OUT position", tz_out, SPN(tz_out, decimals=3, min=-8.0 , max=1.0 , suffix=" mm"), ),
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"),
),
}, },
) )
if d.exec(): if d.exec():
results = d.results results = d.results
_log.info("setting post-sample-tube displacements {}".format(results)) _log.info("setting post-sample-tube displacements {}".format(results))
for k, v in results.items(): for k, v in results.items():
settings.setValue(k, v) cfg.setValue(k, v)
settings.sync() cfg.sync()
def set_collimator_reference_positions(self): def set_collimator_reference_positions(self):
x_out = float(settings.value("collimator/dx", 0.0)) SPN=GenericDialog.Spinner
y_out = float(settings.value("collimator/dy", 0.0)) app=QApplication.instance()
x_in = float(settings.value("collimator/x_in", 0.0)) cfg=app._cfg
y_in = float(settings.value("collimator/y_in", 0.0)) x_out = cfg.value(AppCfg.COL_DX , 0.0,type=float)
d = GenericDialog( 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", title="Collimator configuration",
message="Enter reference positions for the collimator", message="Enter reference positions for the collimator",
inputs={ inputs={
"collimator/dx": ( AppCfg.COL_DX: ("Collimator out deltaX", x_out, SPN(x_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
"Collimator out deltaX", AppCfg.COL_DY: ("Collimator out deltaY", y_out, SPN(y_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
x_out, AppCfg.COL_X_IN: ("Collimator in X", x_in, SPN(x_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
Spinner(x_out, 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"),),
),
"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"),
),
}, },
) )
if d.exec(): if d.exec():
results = d.results results = d.results
_log.info("setting collimator reference positions {}".format(results)) _log.info("setting collimator reference positions {}".format(results))
for k, v in results.items(): for k, v in results.items():
settings.setValue(k, v) cfg.setValue(k, v)
settings.sync() cfg.sync()
def set_backlight_positions_dialog(self): def set_backlight_positions_dialog(self):
p_in = int(settings.value("backlight/in")) SPN=GenericDialog.Spinner
p_out = int(settings.value("backlight/out")) app=QApplication.instance()
p_diode = int(settings.value("backlight/diode")) cfg=app._cfg
d = GenericDialog( 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", title="Back Light configuration",
message="Enter reference positions for the backlight", message="Enter reference positions for the backlight",
inputs={ inputs={
"backlight/in": ( AppCfg.BKLGT_IN: ("In position" , p_in , SPN(p_in, min=-30000, max=10), ),
"In position", AppCfg.BKLGT_OUT: ("Out position", p_out, SPN(p_out, min=-1000, max=10), ),
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),
),
}, },
) )
if d.exec(): if d.exec():
results = d.results results = d.results
_log.info("setting back light reference positions {}".format(results)) _log.info("setting back light reference positions {}".format(results))
for k, v in results.items(): for k, v in results.items():
settings.setValue(k, v) cfg.setValue(k, v)
settings.sync() cfg.sync()
def set_cryojet_positions_dialog(self): def set_cryojet_positions_dialog(self):
p_in = settings.value(CRYOJET_NOZZLE_IN, type=float) p_in = settings.value(CRYOJET_NOZZLE_IN, type=float)
@@ -3256,31 +3191,31 @@ class Main(QMainWindow, Ui_MainWindow):
settings.sync() settings.sync()
def set_deltatau_parameters(self): def set_deltatau_parameters(self):
a = settings.value(DELTATAU_VELOCITY_SCALE, 1, type=float) SPN=GenericDialog.Spinner
b = option(DELTATAU_SORT_POINTS) CB=GenericDialog.Checkbox
c = option(DELTATAU_OMEGACOS) app=QApplication.instance()
d = option(DELTATAU_SHOW_PLOTS) 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", title="Delta Tau Parameters",
message="These parameters affect the data collection.", message="These parameters affect the data collection.",
inputs={ inputs={
DELTATAU_VELOCITY_SCALE: ("Velocity Scale (1=optimal, 0=zero vel at target)", a, AppCfg.DT_HOST:("host name (host[:port:port_gather])", dt1, QLineEdit(),),
Spinner(a, min=0, max=1, suffix=""),), AppCfg.DT_VEL_SCL: ("Velocity Scale (1=optimal, 0=zero vel at target)", dt2,SPN(dt2, min=0, max=1, suffix=""),),
DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b, AppCfg.DT_SHOW_PLOTS: ("show plots after collection", dt3,CB(dt3, "active"),),
Checkbox(b, "sort points"),), #DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b,CB(b, "sort points"),),
DELTATAU_SHOW_PLOTS: ("show plots after collection", d,
Checkbox(d, "correct"),),
DELTATAU_OMEGACOS: ( "preloc. correct omega tilt", c,
Checkbox(c, "correct"),),
}, },
) )
if d.exec(): if d.exec():
results = d.results results = d.results
_log.info("setting delta tau parameters {}".format(results)) _log.info("setting delta tau parameters {}".format(results))
for k, v in results.items(): for k, v in results.items():
settings.setValue(k, v) cfg.setValue(k, v)
settings.sync() cfg.sync()
def set_tell_mount_positions(self): def set_tell_mount_positions(self):
AUTODRY_ENABLED = "tell/autodry_enabled" AUTODRY_ENABLED = "tell/autodry_enabled"
@@ -3293,7 +3228,7 @@ class Main(QMainWindow, Ui_MainWindow):
maxtime = settings.value(AUTODRY_MAXTIME, type=float) / SECS_HOURS maxtime = settings.value(AUTODRY_MAXTIME, type=float) / SECS_HOURS
maxmounts = settings.value(AUTODRY_MAXMOUNTS, type=int) maxmounts = settings.value(AUTODRY_MAXMOUNTS, type=int)
d = GenericDialog( d = GenericDialog.GenericDialog(
title="TELL Settings", title="TELL Settings",
message="These control some features of the TELL sample changer", message="These control some features of the TELL sample changer",
inputs={ inputs={