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_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):

View File

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

View File

@@ -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={