Files
SwissMX/swissmx.py
2023-06-20 16:10:03 +02:00

4090 lines
144 KiB
Python
Executable File

#!/usr/bin/env python
# *-----------------------------------------------------------------------*
# | |
# | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) |
# | Based on Zac great first implementation |
# | Author Thierry Zamofing (thierry.zamofing@psi.ch) |
# *-----------------------------------------------------------------------*
"""
SwissMx experiment application.
Based on Zac great first implementation
bitmask for simulation:
0x01: backlight
0x02: illumination
0x04: zoom
0x08: camera
0x10: Deltatau motors
0x20: SmarAct motors
"""
import logging
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.getLogger('PyQt5.uic').setLevel(logging.INFO)
logging.getLogger('requests').setLevel(logging.INFO)
logging.getLogger('urllib3').setLevel(logging.INFO)
logging.getLogger('paramiko').setLevel(logging.INFO)
logging.getLogger('matplotlib').setLevel(logging.INFO)
logging.getLogger('PIL').setLevel(logging.INFO)
logging.getLogger('illumination').setLevel(logging.INFO)
logging.getLogger('zoom').setLevel(logging.INFO)
logging.getLogger('pbtools.misc.pp_comm').setLevel(logging.INFO)
_log = logging.getLogger("swissmx")
#_log.setLevel(logging.INFO)
import time
class timestamp():
def __init__(self):
self.t=time.time()
def log(self,txt):
t=time.time()
print(txt+f'{t-self.t:6.3g}')
self.t=t
ts=timestamp()
ts.log('Import part 1/8:')
import sys, os, time
import json, re
import random, signal, subprocess
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use('Qt5Agg') # needed to avoid blocking of ui !
TASK_JUNGFRAU_SETTINGS = "jungfrau_settings"
TASK_SETUP_GEOMETRY_CALIB = "geometry_calib"
TASK_SETUP_PPM_CALIBRATION_TBOX = "ppm_calibration_tbox"
TASK_SETUP_BEAM_CENTER = "beamcenter_calibration"
TASK_SETUP_CAMERA = "setup_camera"
TASK_SETUP_ROI = "setup_rois"
TASK_SAMPLE_SELECTION = "task_sample_selection"
TASK_SCREENING = "screening"
TASK_FIX_TARGET = "fix_trg"
TASK_GRID = "grid"
TASK_PRELOCATED = "prelocated"
TASK_HELICAL = "helical"
TASK_EMBL = "embl"
ts.log('Import part 2/8:')
import ModuleFixTarget
import PrelocatedCoordinatesModel # ZAC: orig. code
from EmblModule import EmblWidget #ZAC: orig. code
from HelicalTable import HelicalTableWidget #ZAC: orig. code
ts.log('Import part 3/8:')
import qtawesome
import qutilities
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, pyqtSlot, QSize, QRegExp, pyqtSignal, QObject, QThread, QRectF,QT_VERSION_STR
from PyQt5.QtGui import QKeySequence, QPixmap, QRegExpValidator, QFont
from PyQt5.QtWidgets import (
QAction, QApplication, QDoubleSpinBox, QFileDialog, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QMessageBox, QPlainTextEdit, QProgressBar, QProgressDialog, QPushButton, QShortcut, QSizePolicy, QSpinBox,
QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,)
from PyQt5.uic import loadUiType
ts.log('Import part 4/8:')
import pyqtUsrObj as UsrGO
from epics_widgets.MotorTweak import MotorTweak
from epics_widgets.SmaractMotorTweak import SmaractMotorTweak
from epics_widgets.SimMotorTweak import SimMotorTweak
# from epics import caput, caget # TODO: shutter
ts.log('Import part 5/8:')
import numpy as np
np.set_printoptions(suppress=True,linewidth=196)
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/8:')
import app_utils
from app_config import AppCfg,WndParameter #settings, option, toggle_option
import epics
from epics.ca import pend_event
import camera,backlight,zoom,illumination,geometry
ts.log('Import part 7/8:')
import psi_device
ts.log('Import part 8/8:')
def tdstamp():
return time.strftime("%Y%m%dH%H%M%S")
def datestamp():
return time.strftime("%Y%m%d")
def get_version(path='.'):
#sys.stdout.write('getVersion() -> using git command -> ')
p = subprocess.Popen(f'git -C {path} describe --match ''*.*.*'' --long --tags --dirty', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
retval = p.wait()
res=p.stdout.readline()
p.stdout.close()
#res=res.decode()[1:-1].split('-',2)
res=res.decode()[:-1].split('-',2)
ver='.'.join(res[:2])
gitcmt=res[2][1:]
return (ver,gitcmt)
def sigint_handler(*args):
"""Handler for the SIGINT signal."""
app=QApplication.instance()
app.quit()
class AcquisitionAbortedException(Exception):
pass
class Sequencer(QObject):
finished = pyqtSignal()
timeoutExpired = pyqtSignal()
errorHappened = pyqtSignal(str)
def __init__(self, steps):
super().__init__()
self.steps = steps
def run_sequence(self):
num_steps = len(self.steps)
for n, step in enumerate(self.steps):
_log.info("runing step {}/{}".format(n, num_steps))
step()
_log.info("emiting finished")
self.finished.emit()
class StartupSplash:
def __init__(self):
splash_pix = QPixmap("artwork/logo/256x256.png")
self._wnd=splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
splash.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
splash.setEnabled(False)
self._prgsBar=prgs=QProgressBar(splash)
prgs.setMaximum(100)
prgs.setGeometry(0, splash_pix.height() - 50, splash_pix.width(), 20)
splash.show()
def set(self,i,msg):
self._prgsBar.setValue(i)
self._wnd.showMessage(f"<font color='red'>{msg}</font></h1>", int(Qt.AlignBottom|Qt.AlignCenter), Qt.black)
app=QApplication.instance()
app.processEvents()
time.sleep(.1)
Ui_MainWindow, QMainWindow = loadUiType("swissmx.ui")
class WndSwissMx(QMainWindow, Ui_MainWindow):
sigNewCamImg = pyqtSignal() # index in self._grids
#pixelsPerMillimeter = pyqtSignal(float)
#beamCameraCoordinatesChanged = pyqtSignal(float, float)
addGridRequest = pyqtSignal(float, float)
#zoomChanged = pyqtSignal(float)
#folderChanged = pyqtSignal(str)
prefixChanged = pyqtSignal(str)
projectChanged = pyqtSignal(str)
gridUpdated = pyqtSignal(int) # index in self._grids
gonioMoveRequest = pyqtSignal(float, float, float, float, float)
#fast_x_position = pyqtSignal(float)
#fast_y_position = pyqtSignal(float)
#fast_dx_position = pyqtSignal(float)
#fast_dy_position = pyqtSignal(float)
fiducialPositionSelected = pyqtSignal(float, float, float, float) # camx, camy, gonx, gony
appendPrelocatedPosition = pyqtSignal(float, float, float, float)
appendPrelocatedFiducial = pyqtSignal(bool, float, float, float, float)
increaseRunNumberRequest = pyqtSignal()
daqAborted = pyqtSignal()
def __init__(self,):
super(WndSwissMx, self).__init__()
self.setupUi(self)
app=QApplication.instance()
if os.getenv("DEVELOPMENT_VERSION"):
title = "[DEVELOPMENT] SwissMX - Solid support scanning for FEL sources"
else:
title = "SwissMX - Solid support scanning for FEL sources"
self.setWindowTitle(title)
self._do_quit = False
qtawesome.load_font("material", "MaterialIcons-Regular.ttf", "MaterialIcons-Regular.json", "fonts/",)
QtGui.QFontDatabase.addApplicationFont("fonts/Inconsolata-Bold.ttf")
QtGui.QFontDatabase.addApplicationFont("fonts/Baloo-Regular.ttf")
self.init_settings()
# self.create_escape_toolbar()
self.tweakers = {}
self.setup_sliders()
self.init_graphics()
self._esc_state ="Maintenance"
self._pin_mounting_offset = 0.0
self._mouse_tracking = False
self._rois = []
# add a generic ellipse
# pen = QtGui.QPen(QColor(Qt.yellow))
# pen.setWidth(5)
# ellipse1 = QtWidgets.QGraphicsEllipseItem(650, 700, 500, 300)
# ellipse1.setFlag(QGraphicsItem.ItemIsSelectable)
# ellipse1.setFlag(QGraphicsItem.ItemIsMovable)
# ellipse1.setPen(pen)
# self.vb.addItem(ellipse1)
# self.vb.addItem(Grid(pos=(650, 700), size=(100, 200), cell_size=(3, 4), parent=self))
# self.vb.addItem(Retangulo(pos=(750, 700), size=(100, 200), pen=pg.fn.mkPen(color=[230, 150, 200], width=4.7)))
# self.vb.addItem(pg.PolyLineROI([[80, 60], [90, 30], [60, 40]], pen=(6,9), closed=True))
# test
# x2 = np.linspace(-100, 100, 1000)
# data2 = np.sin(x2) / x2
# # p8 = self.glw.addPlot(row=1, col=0, title="Region Selection")
# plitem = pg.PlotItem()
# plitem.plot(data2, pen=(255, 255, 255, 200))
# # lr = pg.LinearRegionItem([400, 700])
# # lr.setZValue(-10)
# self.vb.addItem(plitem)
self._message_critical_fault = QLabel(None)
self._message_critical_fault.setAccessibleName("device_fault")
self.statusbar.insertWidget(0, self._message_critical_fault)
self._lb_coords=lbl=QLabel(None)
#f=QFont('monospace', 10)
#lbl.setFont(f)
self.statusbar.addPermanentWidget(lbl)
self._fel_status = QLabel(None)
self._fel_status.setAccessibleName("fel_status_statusbar")
self.statusbar.addPermanentWidget(self._fel_status)
#swissfel.statusStringUpdated.connect(self._fel_status.setText) #ZAC: orig. code
self._status_task = QLabel(None)
self._status_task.setAccessibleName("status_task_label")
self.statusbar.addPermanentWidget(self._status_task)
#self.add_beam_marker()
#self._beam_markers = {}
# ppm calibration
#self._zoom_to_ppm = {}
#self._ppm_click = None
self.load_stylesheet()
#self.folderChanged.connect(lambda t: print("folder now: {}".format(t))) # FIXME
self.init_actions()
#self.prepare_microscope_page()
self.prepare_wd_tabs_left()
#self.update_beam_marker(qoptic_zoom.get_sp()) #ZAC: orig. code
self._wd_stack.setCurrentIndex(0)
self._wd_stack.currentChanged.connect(self.cb_update_center_widget)
#self._OLD_init_validators()
#self.init_settings_tracker() ? not needed, was for TELL ?
#self._OLD_wire_storage()
self.cb_update_center_widget(0) # start camera updater
curzoom = app._zoom.get_val()
_log.debug(f"starting app with zoom at {curzoom}")
self.cb_zoom_changed(curzoom)
self._tabs_daq_methods.currentChanged.connect(self.cb_switch_task)
self.cb_switch_task()
def init_settings(self):
app = QApplication.instance()
cfg = app._cfg
keys = cfg.allKeys()
#if EXPERIMENT_PGROUP not in keys:
# self.update_user_and_storage()
if cfg.WINDOW_GEOMETRY in keys:
self.restoreGeometry(cfg.value(cfg.WINDOW_GEOMETRY, ""))
self.restoreState(cfg.value(cfg.WINDOW_STATE, ""))
if cfg.WINDOW_SPLITTER in keys:
sz = [int(s) for s in cfg.value(cfg.WINDOW_SPLITTER)]
else:
sz=(500, 1200)
self._wd_splitter.setSizes(sz)
#if AppCfg.BEAM_MARKER_POSITIONS in keys:
# self._beam_markers = s.value(AppCfg.BEAM_MARKER_POSITIONS)
# self.update_beam_marker_fitters()
# _log.info("read beam markers {}".format(self._beam_markers))
#if AppCfg.CAMERA_TRANSFORMATIONS in keys:
# app._camera.set_transformations(s.value(AppCfg.CAMERA_TRANSFORMATIONS))
#if AppCfg.CAMERA_ZOOM_TO_PPM in keys:
# self._zoom_to_ppm = s.value(AppCfg.CAMERA_ZOOM_TO_PPM)
# _log.info(f"{AppCfg.CAMERA_ZOOM_TO_PPM} updated: {self._zoom_to_ppm}")
# self.update_ppm_fitters()
def init_graphics(self):
app = QApplication.instance()
cfg = app._cfg
geo = app._geometry
self.glw = pg.GraphicsLayoutWidget()
self.microscope_page.setLayout(QVBoxLayout())
self.microscope_page.layout().addWidget(self.glw)
self.glw.show()
self.glw.scene().sigMouseMoved.connect(self.cb_mouse_move)
self.glw.scene().sigMouseClicked.connect(self.cb_mouse_click)
#--- viewbox ---
self.vb=vb=self.glw.addViewBox(invertY=False,border='r',enableMenu=True)
#TODO: vb.enableAutoRange(enable=True), vb.autoRange() does not work for ItemGroups
#therefore set the vieweRange manually
pad=10
vb.setRange(QRectF(-1200-pad,-1000-pad,1200+2*pad,1000+2*pad))
vb.setAspectLocked(True)
vb.setBackgroundColor((120, 90, 90))
tr=QtGui.QTransform() # prepare ImageItem transformation:
opt_ctr=app._geometry._opt_ctr
#--- image group ---
# uses image transformation
# contains image and opticalcenter
self._goImgGrp=grp=pg.ItemGroup()
self.vb.addItem(grp)
trf=cfg.value(AppCfg.GEO_CAM_TRF)
# be aware: setTransform is transposed!
# Qt uses: p'=(p.T*A.T).T , but I am used: p'=A*p, with p=[x,y,1].T
tr.setMatrix(trf[0,0],trf[1,0],trf[2,0], #(-1, 0, 0,
trf[0,1],trf[1,1],trf[2,1], # 0,-1, 0,
trf[0,2],trf[1,2],trf[2,2]) # 0, 0, 1)
grp.setTransform(tr) # assign transform
#--- image ---
self._goImg=img=pg.ImageItem() #border=pg.mkPen('r',width=2))
grp.addItem(img)
#--- opctical center ----
oc_sz=np.array((50,50))
#self._goOptCtr=obj=UsrGO.Marker(-opt_ctr+oc_sz/2, oc_sz,mode=1)
self._goOptCtr=obj=UsrGO.Marker(opt_ctr-oc_sz/2, oc_sz,mode=1, movable=False)
obj.sigRegionChangeFinished.connect(self.cb_marker_moved)
grp.addItem(obj)
#--- grid ---
try:
self._goGrid=grid=pg.GridItem(pen=(0,255,0),textPen=(0,255,0)) #green grid and labels
except NameError:
_log.debug('workaround for typo in pyqtgraph:0.11.0')
from PyQt5.QtGui import QPen,QColor
self._goGrid=grid=pg.GridItem() # green grid and labels
grid.opts['pen']=QPen(QColor(0, 255, 0))
grid.opts['textPen']=QPen(QColor(0, 255, 0))
#tr.reset()
#grid.setTransform(tr) # assign transform
vb.addItem(grid)
#--- fixed group ---
# uses pix2pos transformation with a fixed fx,fy value =(0,0)
# contains beam marker
self._goFixGrp=grp=pg.ItemGroup()
geo.interp_zoom(1)
pix2pos=geo._pix2pos
A=np.asarray(pix2pos.I)
tr=grp.transform()
p1=np.hstack((opt_ctr,1)) #position of optical center on image item
p2=np.matmul(trf, p1) #position of optical center on viewbox
tr.setMatrix(A[0,0], A[0,1], 0,
A[1,0], A[1,1], 0,
p2[0], p2[1], 1) # translate dx,dy
grp.setTransform(tr)
self.vb.addItem(grp)
#--- beam marker ---
size_eu=cfg.value(AppCfg.GEO_BEAM_SZ)/1000 # convert from um to mm
pos_eu=cfg.value(AppCfg.GEO_BEAM_POS)/1000 # convert from um to mm
self._goBeamMarker=obj=UsrGO.Marker(pos_eu-size_eu/2,size_eu,mode=0,movable=False)
obj.sigRegionChangeFinished.connect(self.cb_marker_moved)
#bm.setTransform(tr) # assign transform
grp.addItem(obj)
#--- testing scan grid ---
#self.track_objects() # first call is needed to initialize the structure self._goTracked
# #go=UsrGO.Grid((120, -100), (200, 150), (30, 22), 2)
# go=UsrGO.Grid((120, -100), (1000, 500), (10, 5), 2)
# go.setTransform(tr) # assign transform
# vb.addItem(go)
# self._goTracked['objLst'].append(go)
# self.track_objects() #tracking now the objects
# #UsrGO.obj_tree(vb)
def init_actions(self):
app = QApplication.instance()
cfg = app._cfg
self.toolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_S), self)
self.shortcut.activated.connect(self.cb_save_cam_image)
self.shortcut = QShortcut(QKeySequence(Qt.Key_F2), self)
self.shortcut.activated.connect(self.cb_save_cam_image)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_S), self)
self.shortcut.activated.connect(self.cb_save_cam_image)
for k in range(10):
qkey = k + Qt.Key_0
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + qkey), self)
self.shortcut.activated.connect(lambda key=k: self.saveBookmark(key))
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + qkey), self)
self.shortcut.activated.connect(lambda key=k: self.gotoBookmark(key))
self._button_collect.clicked.connect(self.cb_execute_collection)
# Toolbar buttons
icon_size = QSize(50, 50)
icon = qtawesome.icon("material.photo_camera")
action = QAction(icon, "Save Original", self)
action.setToolTip("(Ctrl+Shift+S) Take a screenshot of the sample image, without markers. Saves in current folder.")
action.triggered.connect(self.cb_save_cam_image)
self.toolBar.addAction(action)
action = QAction(icon, "Save View", self)
action.setToolTip("(Ctrl+S) Take a screenshot of the currently visible sample image, including markers. Saves in current folder.")
action.triggered.connect(lambda: self.cb_save_cam_image(True))
self.toolBar.addAction(action)
if os.getenv("DEVELOPMENT_VERSION"):
icon = qtawesome.icon("material.style")
action = QAction(icon, "Reload Stylesheet", self)
action.triggered.connect(self.load_stylesheet)
self.toolBar.addAction(action)
# icon = qtawesome.icon("material.shutter_speed")
#action = QAction("Use Pulse Picker", self)
#action.setCheckable(True)
#action.setChecked(cfg.option(AppCfg.ACTIVATE_PULSE_PICKER))
#action.triggered.connect(lambda: cfg.toggle_option(AppCfg.ACTIVATE_PULSE_PICKER))
#self.toolBar.addAction(action)
icon = qtawesome.icon("material.timeline")
action = QAction(icon, "Add Line", self)
action.triggered.connect(self._OLD_roi_add_line)
self.toolBar.addAction(action)
icon = qtawesome.icon("material.tab_unselected")
action = QAction(icon, "Add Rect", self)
action.triggered.connect(self._OLD_roi_add_rect)
self.toolBar.addAction(action)
icon = qtawesome.icon("material.play_for_work")
action = QAction(icon, "TELL Mount", self)
action.setToolTip("BookMark(0) => Move stages CZ, CX, FX, FY, and Omega to Tell sample changer mount position")
action.triggered.connect(self._OLD_escape_goToTellMountPosition)
self.toolBar.addAction(action)
action = QAction(icon, "Auto\nFocus", self)
action.triggered.connect(self.cb_autofocus)
self.toolBar.addAction(action)
action = QAction(icon, "Find\nFiducial", self)
action.triggered.connect(self.cb_find_fiducial)
self.toolBar.addAction(action)
action = QAction(icon, "Test\nCode", self)
action.triggered.connect(self.cb_testcode)
self.toolBar.addAction(action)
self.toolBar.addWidget(qutilities.horiz_spacer())
icon = qtawesome.icon("material.sync")
action = QAction(icon, "Sample\nExchange", self)
action.setToolTip("Move devices so a sample can be exchanged.")
action.setObjectName("action_SampleExchange")
action.triggered.connect(self.cb_esc_sample_exchange)
self.toolBar.addAction(action)
self.toolBar.widgetForAction(action).setAccessibleName("action_SampleExchange")
icon = qtawesome.icon("material.my_location")
action = QAction(icon, "Sample\nAlignment", self)
action.setToolTip("Move devices so a sample can be aligned.")
action.triggered.connect(self.cb_esc_sample_alignment)
self.toolBar.addAction(action)
self.toolBar.widgetForAction(action).setAccessibleName("action_SampleAlignment")
icon = qtawesome.icon("material.fingerprint")
action = QAction(icon, "Data\nCollection", self)
action.setToolTip("Move devices so a sample can be collected.")
action.triggered.connect(self.cb_esc_data_collection)
self.toolBar.addAction(action)
self.toolBar.widgetForAction(action).setAccessibleName("action_DataCollection")
self.actionQuit.triggered.connect(self.cb_really_quit)
self.actionPreferences.triggered.connect(self.cb_modify_app_param)
self.actionHome_Fast_Stages.triggered.connect(self.cb_deltatau_home_faststages)
self.actionAbout.triggered.connect(self.cb_about)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_L), self)
self.shortcut.activated.connect(self._OLD_roi_add_line)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_R), self)
self.shortcut.activated.connect(self._OLD_roi_add_rect)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_M), self)
self.shortcut.activated.connect(self._OLD_toggle_mouse_tracking)
self.shortcut = QShortcut(QKeySequence(Qt.Key_F11), self)
self.shortcut.activated.connect(self._OLD_toggle_maximized)
self.shortcut = QShortcut(QKeySequence(Qt.Key_F12), self)
self.shortcut.activated.connect(self._OLD_show_window_configuration)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_B), self)
self.shortcut.activated.connect(lambda: self._OLD_beammark.toggle_handle())
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_P), self)
self.shortcut.activated.connect(self._OLD_camera_pause_toggle)
# adding a menu entry to one of the menus
#action = QAction("parameters", self)
#action.setToolTip("modify application parameters")
#action.setStatusTip("modify application parameters")
#action.triggered.connect(self.cb_modify_app_param)
#self.menuAdvanced.addAction(action)
#action = QAction("geometry", self)
#action.setToolTip("Update optical center, beam marker size etc.")
#action.setStatusTip("Update optical center, beam marker size etc.")
#action.triggered.connect(lambda x: cfg.dlg_geometry(self))
#self.menuAdvanced.addAction(action)
#action = QAction("Backlight Reference Positions", self)
#action.setToolTip("Update the reference positions for the backlight")
#action.setStatusTip("Update the reference positions for the backlight")
#action.triggered.connect(cfg.dlg_backlight_positions)
#self.menuAdvanced.addAction(action)
#action = QAction("Collimator Reference Positions", self)
#action.setToolTip("Update the reference positions for the collimator")
#action.setStatusTip("Update the reference positions for the collimator")
#action.triggered.connect(cfg.dlg_collimator_reference_positions)
#self.menuAdvanced.addAction(action)
#action = QAction("Post sample tube Reference Positions", self)
#action.setToolTip("Update the reference positions for the post tube")
#action.setStatusTip("Update the reference positions for the post tube")
#action.triggered.connect(cfg.dlg_posttube_references)
#self.menuAdvanced.addAction(action)
#action = QAction("Delta Tau Parameters", self)
#action.setToolTip("Parameters affecting the Delta Tau")
#action.setStatusTip("Parameters affecting the Delta Tau")
#action.triggered.connect(cfg.dlg_deltatau_parameters)
#self.menuAdvanced.addAction(action)
#action = QAction("Cryojet Reference Positions", self)
#action.setToolTip("Update the reference positions for the cryojet nozzle position")
#action.setStatusTip("Update the reference positions for the cryojet nozzle position")
#action.triggered.connect(self.set_cryojet_positions_dialog)
#self.menuAdvanced.addAction(action)
#action = QAction("Tell Mount Positions", self)
#action.setToolTip("Mount positions for TELL sample changer")
#action.setStatusTip("Parameters affecting the Delta Tau")
#action.triggered.connect(cfg.dlg_tell_mount_positions)
#self.menuAdvanced.addAction(action)
def closeEvent(self, event): #overloaded function
"""this is called when the user clicks the window's cross icon"""
if (self._do_quit or QMessageBox.question( self, "", "Are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No,) == QMessageBox.Yes):
app=QApplication.instance()
cfg=app._cfg
try:
pv=app._camera._pv['pic']
pv.clear_auto_monitor() # disconnect PV monitor callback -> program exit faster.
except AttributeError:
_log.warning('disconnect PV callback failed.')
_log.info('disconnect PV callback')
cfg.setValue(cfg.WINDOW_GEOMETRY, self.saveGeometry())
cfg.setValue(cfg.WINDOW_STATE, self.saveState())
cfg.setValue(cfg.WINDOW_SPLITTER, self._wd_splitter.sizes())
cfg.setValue('last_active', time.time())
_log.info('save settings')
#QMainWindow.closeEvent(self, event)
_log.info('closeEvent done')
else:
event.ignore()
def cb_update_center_widget(self, index):
_log.debug('TODO: check to not connect smultiple igNewCamImg')
if index > 0: # not showing camera image
_log.warning("listening to zescape")
pass
#self.timer.stop()
#zescape.start_listening()
#self.timer = QtCore.QTimer(self)
#self.timer.timeout.connect(self.check_zescape)
#self.timer.start(20)
else:
app=QApplication.instance()
try:
self.cb_new_frame_sim()
except AttributeError:
self.sigNewCamImg.connect(self.cb_update_img)
cam=app._camera
cfg=app._cfg
param=cam.get_param()
paramSv=cfg.value(AppCfg.GEO_CAM_PARAM)
if not paramSv:
cfg.setValue(AppCfg.GEO_CAM_PARAM, param)
else:
for k,v in param.items():
if v!=paramSv[k]:
if type(v)==tuple:
if v==tuple(paramSv[k]):
continue
cam.set_param(**paramSv)
#cfg.setValue(AppCfg.GEO_CAM_PARAM, param)
#break
_log.debug(f'diff:{k},{v} != {paramSv[k]}')
cam.run(self.cb_new_frame_pv)
def cb_new_frame_pv(self, **kwargs):
#thrd=threading.current_thread()
#_log.debug(f'thread:{thrd.getName()}, {thrd.native_id}')
#_log.debug(f"{kwargs['timestamp']}")
app=QApplication.instance()
cam=app._camera
sz=cam._sz
if kwargs['count']==sz[0]*sz[1]:
pic=kwargs['value'].reshape(sz[::-1])
else:
sz=app._camera.update_size()
pic=kwargs['value'].reshape(sz[::-1])
if pic.dtype==np.int16:
pic.dtype=np.uint16
camera.epics_cam.set_fiducial(pic, 255)
cam._pic=pic
cam._timestamp=kwargs['timestamp']
try:
cam.process()
except AttributeError as e:
pass
# self._goImg.setImage(cam._pic) caused some deadlocks.
# therefore try to update the image with signals instead
self.sigNewCamImg.emit()
def cb_update_img(self):
#thrd=threading.current_thread()
#_log.debug(f'thread:{thrd.getName()}, {thrd.native_id}')
app=QApplication.instance()
cam=app._camera
self._goImg.setImage(cam._pic)
#vb.setRange(QRectF(-1300,-1100,1400,1200))
def cb_new_frame_sim(self, **kwargs):
app=QApplication.instance()
sim=app._camera._sim
imgSeq=sim['imgSeq']
idx=sim['imgIdx']
sim['imgIdx']=(idx+1)%imgSeq.shape[0]
# _log.info('simulated idx:{}'.format(idx))
pic=imgSeq[idx]
self._goImg.setImage(pic)
delay=500 # ms -> 2fps
QtCore.QTimer.singleShot(delay, self.cb_new_frame_sim)
def load_stylesheet(self):
with open("swissmx.css", "r") as sheet:
self.setStyleSheet(sheet.read())
def setup_sliders(self):
cont = self._wd_right
self._tweak_container = QWidget()
self._tweak_container.setLayout(QVBoxLayout())
cont.layout().addWidget(self._tweak_container)
layout = self._tweak_container.layout()
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
self.zoombox = zoom.Zoom()
self.zoombox.init_settings()
self.zoombox.zoomChanged.connect(self.cb_zoom_changed)
self.zoombox.moveBacklight.connect(self.cb_move_backlight_safe)
layout.addWidget(self.zoombox)
toolbox = QToolBox()
layout.addWidget(toolbox)
self.build_group_faststage(toolbox)
# self.build_slits_group(toolbox)
self.build_group_collimator(toolbox)
self.build_group_posttube(toolbox)
self.build_group_xeye(toolbox)
#self.build_cryo_group(toolbox)
# monitor all axis for an axis fault
for key, tweaker in self.tweakers.items():
tweaker.event_axis_fault.connect(self.cb_axis_fault)
#self.tweakers["fast_x"].event_readback.connect(lambda alias, value, kw: self.fast_x_position.emit(value))
#self.tweakers["fast_y"].event_readback.connect(lambda alias, value, kw: self.fast_y_position.emit(value))
self.tweakers["fast_x"].event_rbv.connect(lambda rec_name, kw: self.track_objects())
self.tweakers["fast_y"].event_rbv.connect(lambda rec_name, kw: self.track_objects())
# layout.addStretch()
def cb_marker_moved(self,obj,*args,**kwargs):
_log.debug(args)
_log.debug(kwargs)
app=QApplication.instance()
cfg=app._cfg
geo=app._geometry
opt_ctr=geo._opt_ctr
if obj==self._goOptCtr:
oc_pos=obj.pos()
oc_sz=obj.size()
geo._opt_ctr=opt_ctr=np.array(oc_pos+oc_sz/2)
if (QMessageBox.question(self, "save config", f"save optical center position {opt_ctr} permanently?",
QMessageBox.Yes|QMessageBox.No, QMessageBox.No, )==QMessageBox.Yes):
cfg.setValue(AppCfg.GEO_OPT_CTR,opt_ctr)
elif obj==self._goBeamMarker:
bm_pos=obj.pos()
bm_sz=obj.size()
pos=np.array(bm_pos+bm_sz/2)*1000 # convert from mm to um
if (QMessageBox.question(self, "save config", f"save beam marker position {pos} permanently?",
QMessageBox.Yes|QMessageBox.No, QMessageBox.No, )==QMessageBox.Yes):
cfg.setValue(AppCfg.GEO_BEAM_POS,pos)
self.track_objects()
def cb_zoom_changed(self, value):
#self.zoomChanged.emit(value)
app=QApplication.instance()
geo=app._geometry
try:
geo.interp_zoom(value)
except AttributeError as e:
_log.warning(e)
else:
grp=self._goFixGrp
opt_ctr=geo._opt_ctr
A=np.asarray(geo._pix2pos.I)
#trf=cfg.value(AppCfg.GEO_CAM_TRF)
tr=self._goImgGrp.transform()
p=tr.map(opt_ctr[0],opt_ctr[1])
tr.setMatrix(A[0, 0], A[0, 1], 0,
A[1, 0], A[1, 1], 0,
p[0], p[1], 1) # translate dx,dy
grp.setTransform(tr)
#bm=self._goBeamMarker
#bm_sz=np.abs(geo.pos2pix(bm._size_eu))
#bm_pos=-opt_ctr-geo.pos2pix(bm._pos_eu)-bm_sz/2
#bm.blockSignals(True) # avoid to call cb_marker_moved
#bm.setPos(bm_pos,finish=False)
#bm.setSize(bm_sz)
#bm.blockSignals(False)
#_log.debug(f"zoom->{value} beam marker pos:{bm_pos} sz:{bm_sz})")
self.track_objects()
def cb_move_backlight_safe(self, pos):
app=QApplication.instance()
bl=app._backlight
# any move of backlight requires post sample tube out
try:
self.assert_post_tube_position(pos="out")
except:
self.move_post_tube("out")
bl.move(pos)
def get_tweaker(self, rec, mtype=0, **kwargs):
app = QApplication.instance()
sim=app._args.sim
if mtype == 0: # default epics motor record
if sim&0x10:
m_tw=SimMotorTweak()
else:
m_tw=MotorTweak()
elif mtype==1: # smaract motor record
if sim&0x20:
m_tw=SimMotorTweak()
else:
m_tw=SmaractMotorTweak()
m_tw.connect_motor(rec, **kwargs)
self.tweakers[kwargs['alias']] = m_tw
return m_tw
def build_post_tube_tandem_tweaker(self):
block = QWidget()
block.setAccessibleName("post tube tamden")
block.setContentsMargins(0, 0, 0, 0)
block.setLayout(QVBoxLayout())
c = QWidget()
glay = QGridLayout()
c.setLayout(glay)
b = QWidget()
blay = QHBoxLayout()
b.setLayout(blay)
blay.addWidget(QLabel("Tweak (mm)"))
self._post_tandem_tweak_val = QLineEdit("1.0")
blay.addWidget(self._post_tandem_tweak_val)
glay.addWidget(b, 0, 0, 1, 2)
icon = qtawesome.icon("material.arrow_back")
but = QPushButton(icon, "")
but.clicked.connect(lambda m, pos="x+": self.move_post_tube(pos))
glay.addWidget(but, 1, 0)
# c.setLayout(glay)
icon = qtawesome.icon("material.arrow_forward")
but = QPushButton(icon, "")
but.clicked.connect(lambda m, pos="x-": self.move_post_tube(pos))
glay.addWidget(but, 1, 1)
# c.setLayout(glay)
icon = qtawesome.icon("material.arrow_downward")
but = QPushButton(icon, "")
but.clicked.connect(lambda m, pos="y-": self.move_post_tube(pos))
glay.addWidget(but, 2, 0)
# c.setLayout(glay)
icon = qtawesome.icon("material.arrow_upward")
but = QPushButton(icon, "")
but.clicked.connect(lambda m, pos="y+": self.move_post_tube(pos))
glay.addWidget(but, 2, 1)
block.layout().addWidget(c)
return block
def cb_axis_fault(self, pvname, kw):
"""
swissmx - {'pvname': 'SAR-EXPMX:MOT_FY.STAT', 'value': 0, 'char_value': 'NO_ALARM', 'status': 0, 'ftype': 17, 'chid': 38980392,
'host': 'SAR-CPPM-EXPMX1.psi.ch:5064', 'count': 1, 'access': 'read-only', 'write_access': False, 'read_access': True,
'severity': 0,
'timestamp': 1537867290.914189, 'precision': None, 'units': None, 'enum_strs': ('NO_ALARM', 'READ', 'WRITE', 'HIHI', 'HIGH', 'LOLO', 'LOW', 'STATE', 'COS', 'COMM', 'TIMEOUT', 'HWLIMIT', 'CALC', 'SCAN', 'LINK', 'SOFT'), 'upper_disp_limit': None, 'lower_disp_limit': None, 'upper_alarm_limit': None, 'lower_alarm_limit': None, 'lower_warning_limit': None, 'upper_warning_limit': None, 'upper_ctrl_limit': None, 'lower_ctrl_limit': None, 'nelm': 1, 'type': 'time_enum', 'typefull': 'time_enum', 'motor_field': 'STAT', 'source_field': 'STAT', 'cb_info': (1, <PV 'SAR-EXPMX:MOT_FY.STAT', count=1, type=time_enum, access=read-only>), 'alias': 'fast Y'}
:param pvname:
:param kw:
:return:
"""
_log.debug(f"AXIS FAULT: {kw}")
if kw["severity"]:
msg = f"axis {pvname} has a fault"
_log.critical(msg)
_log.critical(f"{kw}")
else:
msg = ""
self._message_critical_fault.setText(msg)
def build_group_faststage(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
c=QWidget()
c.setLayout(QGridLayout())
f=lambda v: lambda x:self.move_detector(v) #needs nested lambda to work ...
for i,v in enumerate(('in','out')):
but=QPushButton('detector '+v)
but.clicked.connect(f(v))
c.layout().addWidget(but, 0, i)
widgets=[
# self.get_tweaker('f"{pfx}:MOT_BLGT', alias='backlight', label='backlight'),
self.get_tweaker(f"{pfx}:MOT_FY", alias="fast_y", label="fast Y"),
self.get_tweaker(f"{pfx}:MOT_FX", alias="fast_x", label="fast X"),
self.get_tweaker(f"{pfx}:MOT_ROT_Y", alias="omega", label="omega", tweak_min=0.001, tweak_max=180.0, ),
self.get_tweaker(f"{pfx}:MOT_CX", alias="base_x", label="base X"),
self.get_tweaker(f"{pfx}:MOT_CZ", alias="base_z", label="base Z"),
self.get_tweaker(f"{pfx}:MOT_DET_Z", alias="det_z", label="detector Z"),
c
]
qutilities.add_item_to_toolbox(toolbox,"Fast Stage",widget_list=widgets)
def build_group_collimator(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
c=QWidget()
c.setLayout(QGridLayout())
f=lambda v: lambda x:self.move_collimator(v) #needs nested lambda to work ...
for i,v in enumerate(('in','out','ready')):
but=QPushButton(v)
but.clicked.connect(f(v))
c.layout().addWidget(but, 0, i)
widgets = [
self.get_tweaker(f"{pfx}1", alias="colli_x", label="colli X", mtype=1,),
self.get_tweaker(f"{pfx}2", alias="colli_y", label="colli Y", mtype=1,),
c,
]
qutilities.add_item_to_toolbox(toolbox,"Collimator",widget_list=widgets)
def build_group_posttube(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
widgets = [
self.get_tweaker(f"{pfx}4", alias="tube_usx", label="upstream X", mtype=1,),
self.get_tweaker(f"{pfx}6", alias="tube_usy", label="upstream Y", mtype=1,),
self.get_tweaker(f"{pfx}5", alias="tube_dsx", label="downstream X", mtype=1,),
self.get_tweaker(f"{pfx}7", alias="tube_dsy", label="downstream Y", mtype=1,),
self.get_tweaker(f"{pfx}8", alias="tube_z", label="tube Z", mtype=1),
]
c = QWidget()
c.setLayout(QGridLayout())
but = QPushButton("post tube out")
but.clicked.connect(lambda m, pos="out": self.move_post_tube(pos))
c.layout().addWidget(but, 0, 0)
but = QPushButton("post tube in")
but.clicked.connect(lambda m, pos="in": self.move_post_tube(pos))
c.layout().addWidget(but, 0, 1)
widgets.append(c)
label = "Post Sample Tube"
block = QWidget()
block.setAccessibleName(label)
block.setContentsMargins(0, 0, 0, 0)
block.setLayout(QVBoxLayout())
for w in widgets:
block.layout().addWidget(w)
block.layout().addWidget(self.build_post_tube_tandem_tweaker())
block.layout().addStretch()
toolbox.addItem(block, label)
def build_group_xeye(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[1]
widgets=[
self.get_tweaker(f"{pfx}14", alias="xeye_x", label="X", mtype=1),
self.get_tweaker(f"{pfx}15", alias="xeye_y", label="Y", mtype=1),
]
qutilities.add_item_to_toolbox(toolbox,"X-Ray Eye",widget_list=widgets)
def set_active_task(self, task):
_log.info("TASK == {}".format(task))
self._active_task = task
def active_task(self):
return self._active_task
def cb_mouse_move(self, pos):
app = QApplication.instance()
geo = app._geometry
#pos = pixel position on the widget
task = self.active_task()
z = app._zoom.get_val()
bm=self._goBeamMarker
#pos=event.scenePos()
pImg=pg.Point(self._goImg.mapFromScene(pos))
pTrk=pg.Point(self._goTracked.mapFromScene(pos))
pFix=pg.Point(self._goFixGrp.mapFromScene(pos))
pFix-=bm.pos()+bm.size()/2
fx=self.tweakers["fast_x"].get_val()
fy=self.tweakers["fast_y"].get_val()
pRel=pTrk-(fx,fy)
#s=f'pImg{pImg} pTrk{pTrk} bm._pos_eu{bm._pos_eu}'
try:
pln=geo._fitPlane
except AttributeError:
cz=np.nan
else:
cz=pln[0]*pTrk[0]+pln[1]*pTrk[1]+pln[2] # z=ax+by+c
s=\
f'img pix ({pImg[0]:0.1f} {pImg[1]:0.1f})px \u23A2 ' \
f'stage ({pTrk[0]:0.4f} {pTrk[1]:>0.4f} {cz:>0.4f})mm \u23A2 ' \
f'dist to beam ({pFix[0]:>0.4f} {pFix[1]:>0.4f})mm '
#f'dist to beam ({pRel[0]:>0.6g} {pRel[1]:>0.6g}mm) '
#_log.debug(s)
self._lb_coords.setText(s)
#self._lb_coords.setText(
# "\u23FA{:>}:{:>6.0f} {:<.0f}"
# "\u23FA{:>}:{:>6.0f} {:<.0f}"
# "\u23FA{:>}:{:<.0f}"
# "\u23FA{:>}:{:>6.1f} {:<.1f} \u00B5"
# "\u23FA{:>}:{:>7.3f} {:<.3f} mm".format(
# "Beam at", bx, by,
# "Pixel coord ", x, y,
# "PPM", ppm,
# "Distance to beam", 1000 * dx, 1000 * dy,
# "Stage", fx, fy,
# )
#)
def cb_mouse_click_geometry_calib(self, event):
app=QApplication.instance()
zoom = app._zoom.get_val()
fast_x=self.tweakers["fast_x"];fast_y=self.tweakers["fast_y"]
if self._btn_pix2pos.isChecked():
try:
raw=app._raw_pix2pos[zoom]
except KeyError as e:
raw=app._raw_pix2pos[zoom]=list()
imgPos=self._goImg.mapFromScene(event.scenePos())
fx=fast_x.get_rbv();
fy=fast_y.get_rbv()
x=imgPos.x();y=imgPos.y()
_log.debug('append calib: zoom:{} stage_x_y:{}/{} cam_pix_x_y:{}/{}'.format(zoom, fx, fy, x, y))
raw.append((fx, fy, x, y))
self._wd_calib_tree.setData(app._raw_pix2pos)
elif self._btn_opt_ctr.isChecked():
try:
raw=app._raw_opt_ctr[zoom]
except KeyError as e:
raw=app._raw_opt_ctr[zoom]=list()
imgPos=self._goImg.mapFromScene(event.scenePos())
x=imgPos.x();y=imgPos.y()
_log.debug('append calib: zoom:{} cam_pix_x_y:{}/{}'.format(zoom, x, y))
raw.append(( x, y))
self._wd_calib_tree.setData(app._raw_opt_ctr)
else:
QMessageBox.warning(self, "calibration", "no calibration started yet.\nPress 'pix2pos' or 'opt_ctr' button first")
def cb_mouse_click(self, event):
#_log.debug("{}".format(event))
#_log.debug("screen pos {}".format(event.screenPos())) #pixel position on the whole screen
#_log.debug("scene pos {}".format(event.scenePos())) #pixel position on the scene (including black frame)
#_log.debug(" pos {}".format(event.pos())) #pixel position of the ckicked object mapped to its coordinates
#p=event.scenePos()
#_log.debug(f"vb pos {self.vb.mapFromScene(p)}") #pixel position on the scene (including black frame)
#for o in self.vb.childGroup.childItems():
# _log.debug(f"{type(o)} pos {o.mapFromScene(p)}") #pixel position on the scene (including black frame)
#_log.debug(f"currentItem:{event.currentItem}")
app=QApplication.instance()
mod=event.modifiers()
btn=event.button()
task = self.active_task()
if task==TASK_SETUP_GEOMETRY_CALIB:
if btn==Qt.LeftButton and not mod&Qt.ControlModifier:
self.cb_mouse_click_geometry_calib(event)
return
################################
# Ctrl-left : move to position
# Ctrl-right : object tree
################################
#dblclick = event.double()
if btn==Qt.LeftButton:
if mod&Qt.ShiftModifier:
pass
elif mod&Qt.ControlModifier:
pos=event.scenePos()
_log.debug(f'move to position :scene pos {pos}')
geo=app._geometry
bm=self._goBeamMarker
pImg=pg.Point(self._goImg.mapFromScene(pos))
pTrk=pg.Point(self._goTracked.mapFromScene(pos))
pFix=pg.Point(self._goFixGrp.mapFromScene(pos))
pFix-=bm.pos()+bm.size()/2
#create trace
self.add_camera_trace(pFix)
_log.debug(f'dist to beam ({pFix[0]:>0.6g} {pFix[1]:>0.6g}mm)')
fx_motor=self.tweakers["fast_x"]
fy_motor=self.tweakers["fast_y"]
fx_motor.move_rel(pFix[0])
fy_motor.move_rel(pFix[1])
#fx_motor.move_abs(fx_motor.get_val()+pFix[0])
#fy_motor.move_abs(fy_motor.get_val()+pFix[1])
#if none of the image is visible, recenter it
r1=self.vb.viewRect()
r2=self.vb.itemBoundingRect(self._goImg)
if not r1.intersects(r2):
r1.translate(r2.center()-r1.center())
self.vb.setRange(r1)
try:
pln=geo._fitPlane
except AttributeError: pass
else:
cz_motor=self.tweakers["base_z"]
cz=pln[0]*pTrk[0]+pln[1]*pTrk[1]+pln[2] #z=ax+by+c
if cz:
cz_motor.move_abs(cz)
else:
_log.critical(f'{cz} {pTrk} {pln}')
_log.debug(f'cz={pln[0]:.4g}*{pTrk[0]:.4g}+{pln[1]:.4g}*{pTrk[1]:.4g}+{pln[2]:.4g}={cz}')
pass
elif mod&Qt.AltModifier:
pass
else:
pass
elif btn==Qt.RightButton:
if mod&Qt.ShiftModifier:
pass
elif mod&Qt.ControlModifier:
_log.debug('object tree')
UsrGO.obj_tree(self.vb)
pass
elif mod&Qt.AltModifier:
pass
else:
pass
return
return
assert(event.currentItem==self.vb)
#UsrGO.obj_info(self.img)
event.pos()# return Point(self.currentItem.mapFromScene(self._scenePos))
app=QApplication.instance()
if event.button() == Qt.RightButton:
UsrGO.obj_info(self.vb.childTransform())
event.ignore()
return
pos = event.scenePos()
zoom_level = app._zoom.get_val()
_log.debug(" zoom {}".format(zoom_level))
try:
ppm = self.ppm_fitter(zoom_level)
except:
ppm = 1E3
dblclick = event.double()
shft = Qt.ShiftModifier & event.modifiers()
ctrl = Qt.ControlModifier & event.modifiers()
alt = Qt.AltModifier & event.modifiers()
xy = self._goImg.mapFromScene(pos)
x, y = xy.x(), xy.y()
bx, by = self.get_beam_mark_on_camera_xy()
a = np.asarray([x, y])
b = np.asarray([bx, by])
pixel_dist = np.linalg.norm(a - b)
_log.debug("distance to beam: {:.1f} pixels".format(pixel_dist))
omega = omega_motor.get_position()
_log.debug("omega at {:.03f} degrees".format(omega))
fx = fx_motor.get_position()
fy = fy_motor.get_position()
dx = (x - bx) / ppm
dy = -(y - by) / ppm
fx += dx
fy += dy
_log.debug("click at : {:.3f} {:.3f}".format(x, y))
_log.debug("beam at : {:.3f} {:.3f}".format(bx, by))
_log.debug("gonio at : {:.3f} {:.3f}".format(fx, fy))
# publisher.submit(
# {"event": "gonio", "data": self.get_gonio_positions(as_json=True)}
# )
_log.debug("TASK = {}".format(task))
# Click without modifers moves clicked position to beam mark
if task not in (TASK_SETUP_PPM_CALIBRATION, TASK_SETUP_BEAM_CENTER):
if not (ctrl or shft or alt):
if ppm == 0:
_log.warning("PPM is not defined - do the PPM calibration")
if task == TASK_HELICAL:
dx = (x - bx) / ppm
dy = -(y - by) / ppm
_log.info("moving base_X, fast_Y {:.3f}, {:.3f}".format(dx, dy))
bx_motor.move_rel(dx)
fy_motor.move_rel(dy)
else:
dx = np.cos(omega * np.pi / 180) * (x - bx) / ppm
dy = -(y - by) / ppm
_log.info("moving fast x/y to {:.3f}, {:.3f}".format(dx, dy))
fx_motor.move_rel(dx)
fy_motor.move_rel(dy)
self.fast_dx_position.emit(dx)
self.fast_dy_position.emit(dy)
return
# CTRL-Click always just moves Z
if ctrl and not (shft or alt):
mm = np.sign(y - by) * pixel_dist / 10000.
_log.debug("moving Z stage by {:.3f} mm".format(mm))
bz_motor.move_rel(mm)
return
if task in (TASK_SETUP_PPM_CALIBRATION, TASK_SETUP_BEAM_CENTER):
if ctrl and self._ppm_calibration.isChecked():
if self._ppm_click is not None:
feature_dist = self._ppm_feature_size_spinbox.value() / 1000.
b = self._ppm_click
pixel_dist = np.linalg.norm(a - b)
ppm = pixel_dist / feature_dist
_log.debug("calib save a {}".format(a))
_log.debug("calib save b {}".format(b))
_log.debug(" feature {}".format(feature_dist))
_log.debug("a -> b pixels {}".format(pixel_dist))
_log.debug(" ppm({}) {}".format(zoom_level, pixel_dist))
self._zoom_to_ppm[zoom_level] = ppm
settings.setValue(CAMERA_ZOOM_TO_PPM, self._zoom_to_ppm)
self._ppm_click = None
_log.debug("ppm data so far {}".format(self._zoom_to_ppm))
else:
_log.debug("calib save b {}".format(a))
self._ppm_click = a
return
elif task == TASK_SETUP_BEAM_CENTER:
if ctrl and not (shft or alt):
_log.info("beam mark add: zoom {} => {:.1f} {:.1f}".format(zoom_level, x, y))
self.append_to_beam_markers(x, y, zoom_level)
else:
_log.warning("click combination not available")
elif task == TASK_GRID:
if shft and not (ctrl or alt):
self.addGridRequest.emit(fx, fy)
elif task == TASK_PRELOCATED:
if ctrl and shft:
omegacos = option(DELTATAU_OMEGACOS)
if omegacos:
fx = fx / np.cos(omega * np.pi / 180)
_log.info(
"selectin fiducial (omegacos): {:.3f}, {:.3f}".format(fx, fy)
)
else:
_log.info("selectin fiducial: {:.3f}, {:.3f}".format(fx, fy))
self.fiducialPositionSelected.emit(x, y, fx, fy)
elif shft and alt:
self.appendPrelocatedPosition.emit(x, y, fx, fy)
elif task == TASK_HELICAL:
_log.info("no action associated ")
elif task == TASK_EMBL:
pass
else:
pass
def track_objects(self):
#self._goTracked= dictionary with infos about tracked objects
# objLst = list of objects to track
# state = (zoom, fx, fy) (created at first call of self.track_objects() )
app=QApplication.instance()
geo=app._geometry
zoom = app._zoom.get_val()
twk_fx=self.tweakers["fast_x"];twk_fy=self.tweakers["fast_y"]
# TODO: get_position() is delayed as it is the RBV. do same as for the zoom
# TODO: and return the last set_point
fx=twk_fx.get_rbv()
fy=twk_fy.get_rbv()
try:
grp=self._goTracked
grpTrc=self._goTrace
zoom_old,fx_old,fy_old=grp._state
except AttributeError: # at initialization
self._goTracked=grp=pg.ItemGroup()
grp.setZValue(20) #higher than beam marker and opt ctr that have z=10
self.vb.addItem(grp)
grp._state=(zoom,fx,fy)
zoom_old=fx_old=fy_old=None
#--- camera tracing images ---
self._goTrace=grpTrc=pg.ItemGroup() # the transformation is the same as for tracked objects
grpTrc.setZValue(-20) #lower than other objects
self.vb.addItem(grpTrc)
try:
_log.debug(f"zoom:{zoom_old}->{zoom:d} fx:{fx_old:.3f}->{fx:.3f} fy:{fy_old:.3f}->{fy:.3f}")
except TypeError:
_log.debug(f"zoom:{zoom_old}->{zoom} fx:{fx_old}->{fx:.3f} fy:{fy_old}->{fy:.3f}")
if zoom_old!=zoom:
geo.interp_zoom(zoom)
bm=self._goBeamMarker
opt_ctr=geo._opt_ctr
A=np.asarray(geo._pix2pos.I)
# trf=cfg.value(AppCfg.GEO_CAM_TRF)
tr=self._goFixGrp.transform()
p1=bm.pos()+bm.size()/2+(-fx,-fy)
#p=tr.map(opt_ctr[0], opt_ctr[1])
p=tr.map(p1[0], p1[1])
tr.setMatrix(A[0, 0], A[0, 1], 0,
A[1, 0], A[1, 1], 0,
p[0], p[1], 1) # translate dx,dy
grp.setTransform(tr)
grpTrc.setTransform(tr)
def add_camera_trace(self,delta_mm):
# add up to tracelen image when moveing motors to new positions
try:
tracelen=self._goTrace._tracelen
except AttributeError:
app=QApplication.instance()
cfg=app._cfg
tracelen=self._goTrace._tracelen=cfg.value(AppCfg.GBL_MISC)['img_trace_len']
if tracelen==0:
return
trImg=self._goImgGrp.transform()
trTrk=self._goTracked.transform()
tr=trImg*trTrk.inverted()[0] # TODO: check if oder correct !!!
cm=pg.ColorMap((0,255),((0,0,64),(255,255,128)))#,opacity=.5)
img=pg.ImageItem(self._goImg.image,lut=cm.getLookupTable(0,255))
img.setTransform(tr)
self._goTrace.addItem(img) # automatically added to self.vb
cldLst=self._goTrace.childItems()
if len(cldLst)>tracelen: #check if one image needs to be removed
r1=self.vb.itemBoundingRect(img)
for c in cldLst[:-1]:
#check overlap with newest image, and remove previous if it is more than 30%
r2=self.vb.itemBoundingRect(c)
r3=r1.intersected(r2)
s3=r3.size()
s1=r1.size()
if s3.width()/s1.width()>0.3 and s3.height()/s1.height()>0.3:
self.vb.removeItem(c)
return
self.vb.removeItem(cldLst[0]) #if no one was removes, remove oldest
def cb_modify_app_param(self):
wnd=WndParameter(self)
wnd.show()
@pyqtSlot()
def cb_save_cam_image(self,overlays=False):
app=QApplication.instance()
cam=app._camera
filename, _ = QFileDialog.getSaveFileName(self,"Save data file",'', 'PNG files (*.png);;all files (*)')
if not filename:
return
_log.info(f"saving view screenshot: {filename}")
#try:
if not overlays:
import PIL.Image
#img=PIL.Image.fromarray(cam.pic.astype(np.uint8))
try:
pic=cam._pic
mx=pic.max()
if pic.max()>255:
scl=2**int(round(np.log2(mx)-8))
pic=np.array(pic/scl,dtype=np.uint8)
elif pic.dtype!=np.uint8:
pic=np.array(pic, dtype=np.uint8)
except AttributeError:
sim=app._camera._sim
pic=cam._sim['imgSeq'][sim['imgIdx']]
img=PIL.Image.fromarray(pic)
img.save(filename)
else:
exporter = pg.exporters.ImageExporter(self.vb)
# set export parameters if needed
#exporter.parameters()["width"] = 2000 # (note this also affects height parameter)
# save to file
exporter.export(filename)
#except Exception as e:
# _log.warning(e)
# QMessageBox.warning(self, "Screenshot: failed to save image", "Failed to save screenshot!")
def cb_execute_collection(self):
app=QApplication.instance()
geo=app._geometry
#zoom=app._zoom.get_val()
task = self.active_task()
#self._is_aborted = False
#method = self._tabs_daq_methods.currentWidget().accessibleName()
if task == TASK_FIX_TARGET:
#self.re_connect_collect_button(callback=self.collect_abort_grid, accessibleName="grid_abort", label="Abort Grid Scan",)
#self._inspect = self._grid_inspect_area
#self._inspect.setPlainText("")
fast_x=self.tweakers["fast_x"];
fast_y=self.tweakers["fast_y"]
fx=fast_x.get_val()
fy=fast_y.get_val()
opt_ctr=geo._opt_ctr
grp=self._goTracked
cldLst=grp.childItems()
for go in cldLst:
if type(go)==UsrGO.Fiducial:
continue
t=type(go)
if t not in(UsrGO.FixTargetFrame,UsrGO.Path):
_log.warning(f'{t} not supported for FixTargetFrame ->skipped:{go}')
continue
try:
param=go.get_scan_param() #points in coordinate system of ROI
except AttributeError as e:
_log.warning(f'no scan parameters for object->skipped:{go}')
continue
p=param['points']
trf=np.asmatrix(param['trf']*1000) #fix shear/rotation mm->um
trf[:,0]*=-1 #X axis has inverted sign !
if t==UsrGO.FixTargetFrame:
pos=np.array(param['grid']['pos']) #in um
pitch=np.array(param['grid']['pitch']) #in um
m=np.hstack((p*pitch+pos,np.ones((p.shape[0],1))))
p=(np.asmatrix(m)*trf).A
param['pts_trf']=p # transformed points
else:
m=np.hstack((p,np.ones((p.shape[0],1))))
p=(np.asmatrix(m)*trf).A
param['pts_trf']=p # transformed points
param['num_pts']=p.shape[0]
vb=self.vb
grp=self._goTracked
mft=self._moduleFixTarget
try:
tmpGoLst=self._goTmp
except AttributeError:
tmpGoLst=self._goTmp=[]
else:
for go in tmpGoLst:
vb.removeItem(go)
tmpGoLst.clear()
# adding debug fiducials
#n=int(p.shape[0]/100)+1
#for i in range(0,p.shape[0],n):
# fx,fy=p[i, :]/1000
# fx=-fx #X axis has inverted sign !
# l=.06
# go=UsrGO.Fiducial((fx-l/2, fy-l/2), (l, l), i)
# grp.addItem(go)
# tmpGoLst.append(go)
#mft._tree.setData(grp.childItems())
#_log.debug(f'first point:{p[0,:]}')
#_log.debug(f'step to 2nd point:{p[1,:]-p[0,:]}')
#print('DEBUG: difference from point to point:')
#print(np.diff(p, axis=0))
self.daq_collect(**param)
elif task == TASK_GRID:
self.re_connect_collect_button(
callback=self.collect_abort_grid,
accessibleName="grid_abort",
label="Abort Grid Scan",
)
self._inspect = self._grid_inspect_area
self._inspect.setPlainText("")
geo=app._geometry
zoom=app._zoom.get_val()
fast_x=self.tweakers["fast_x"];
fast_y=self.tweakers["fast_y"]
fx=fast_x.get_val()
fy=fast_y.get_val()
opt_ctr=geo._opt_ctr
grp=self._goTracked
cldLst=grp.childItems()
for go in cldLst:
points=go.get_points() #points in coordinate system of ROI
# names consists of abrevations
# part 0: po=position sz=size dt=delta
# part 1: px=pixel eu=engineering units (e.g. mm)
po_px=go.pos()
sz_px=go.size()
tr=go.transform() # TODO: this is not yet used
UsrGO.obj_info(tr)
dt_px=-opt_ctr-po_px
dt_eu=geo.pix2pos(dt_px)+(fx,fy)
for i in range(points.shape[0]):
points[i,:]=dt_eu+geo.pix2pos(points[i,:])#*tr
method="grid"
params=None #grid._grid_dimensions
# params = ([grid._grid_dimensions[0]], [grid._grid_dimensions[1]]) # FIXME something wrong here<
self.daq_collect_points(points, visualizer_method=method, visualizer_params=params)
#self.daq_grid_collect_grid()
elif task == TASK_PRELOCATED:
self._inspect = self._preloc_inspect_area
self._inspect.setPlainText("")
self.daq_prelocated_collect_points()
elif task == TASK_HELICAL:
self.daq_helical_collect()
elif task == TASK_EMBL:
self.daq_embl_collect_points()
def cb_esc_sample_exchange(self):
app=QApplication.instance()
steps = []
#if option(CRYOJET_MOTION_ENABLED):
# steps.append(lambda: self.move_cryojet_nozzle("out"))
steps=[
lambda:self.move_detector("out"),
lambda: self.move_post_tube("out"),
lambda: app._backlight.move("out"),
lambda: self.move_collimator("out"),
lambda:self.move_gonio("mount"),
]
self.esc_run_steps(steps, "Transitioning to Sample Exchange","ManualSampleExchange")
def cb_esc_sample_alignment(self):
app=QApplication.instance()
steps = [
# lambda: sample_selection.tell.set_current(30.0),
lambda:self.move_detector("out"),
lambda: self.move_collimator("out"),
lambda:self.move_detector("out"),
lambda:self.move_post_tube("out"),
lambda:app._backlight.move("in"),
lambda:self.move_collimator("out"),
lambda:self.move_gonio("align"),
]
#if option(CRYOJET_MOTION_ENABLED):
# steps.extend([lambda: self.move_cryojet_nozzle("in")])
#steps.extend([lambda: self.move_post_tube("out"), lambda: app._backlight.move("in")])
self.esc_run_steps(steps, "Transitioning to Sample Alignment","SampleAlignment")
def cb_esc_data_collection(self):
app=QApplication.instance()
steps = [
# lambda: sample_selection.tell.set_current(30.0),
lambda: app._backlight.move("out"),
lambda: self.move_post_tube("in"),
lambda: self.move_collimator("in"),
lambda:self.move_detector("in"),
]
#if option(CRYOJET_MOTION_ENABLED):
# steps.extend([lambda: self.move_cryojet_nozzle("in")])
self.esc_run_steps(steps, "Transitioning to Data Collection","DataCollection")
def cb_really_quit(self):
"""called when user Ctrl-Q the app"""
if QMessageBox.question(self, "", "Are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No,) == QMessageBox.Yes:
self._do_quit = True
self.close()
def cb_deltatau_home_faststages(self):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
_log.info("homing fast stages")
epics.PV(f"{pfx}1:ASYN.AOUT").put(b"enable plc 1")
def cb_about(self):
try:
ver,gitcmt=get_version()
v_SMX=f'{ver} git:{gitcmt}'
except:
v_SMX='git version failed'
try:
ver,gitcmt=get_version('..')
v_SMXT=f'{ver} git:{gitcmt}'
except:
v_SMXT='git version failed'
txt=f'''About Swissmx:
SwissMX: {v_SMX}
SwissMXTools: {v_SMXT}
qt:{QT_VERSION_STR}
pyqtgraph:{pg.__version__}
numpy:{np.__version__}
matplotlib:{mpl.__version__}
epics:{epics.__version__}
Copyright (c) 2022 by Paul Scherrer Institute
(http://www.psi.ch)
Based on Zac great first implementation
Author Thierry Zamofing (thierry.zamofing@psi.ch)
'''
QMessageBox.about(self, "SwissMX", txt)
pass
def cb_autofocus(self):
app=QApplication.instance()
cfg=app._cfg.value(AppCfg.GEO_AUTOFOC)
geo=app._geometry
#geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=30,saveImg=True)
af_range=cfg['range']
n=cfg['steps']
vel=cfg['velocity']
rng=(-af_range/2, af_range/2)
#geo.autofocus(app._camera, self.tweakers['base_z'],rng, n,dlg)
af=geometry.autofocus(app._camera, self.tweakers['base_z'])
# af.run(rng, n, dlg)
#with pg.ProgressDialog('Progress', 0, n) as dlg:
af.run_continous(rng=rng, velo=vel)
def cb_find_fiducial(self):
app=QApplication.instance()
geo=app._geometry
cfg=app._cfg.value(AppCfg.GEO_FND_FID)
scl=np.linalg.norm(geo.pos2pix((.001,0))) #pix/um
sz=int(cfg['sz']*scl)
brd=int(cfg['brd']*scl)
# sz=( 84, 84); brd=( 7, 7) # zoom 001
# sz=(128, 128); brd=( 9, 9) # zoom 200
# sz=(200, 200); brd=(13, 13) # zoom 400
_log.debug(f'sz:{sz} brd:{brd}')
pos,corr=geo.find_fiducial(app._camera, sz=(sz,sz),brd=(brd,brd))
bz=self.tweakers["base_z"].get_rbv()
pos_scn=self._goImg.mapToScene(pg.Point(pos))
pos_eu=self._goTracked.mapFromScene(pos_scn)
l=.120
#go=UsrGO.Fiducial((fx-l/2, fy-l/2), (l, l), bz)
go=UsrGO.Fiducial((pos_eu.x()-l/2, pos_eu.y()-l/2), (l, l), bz)
go.sigRegionChangeFinished.connect(self.cb_fiducial_update_z)
grp=self._goTracked
grp.addItem(go)
self._moduleFixTarget._tree.setData(grp.childItems())
pass
def cb_testcode(self):
app=QApplication.instance()
cfg=app._cfg
geo=app._geometry
try:
tc=self._testCode
tc['idx']+=1
except AttributeError:
self._testCode=tc={'idx':0}
step=tc['idx']
def testMatplotlib():
import matplotlib.pyplot as plt
x=np.linspace(0.1, 2*np.pi, 41)
y=np.exp(np.sin(x))
plt.stem(x, y)
plt.show(block=False)
step=6
if step==0:
vb=self.vb
vb.autoRange(items=(self._goImg,))
elif step==1:
testMatplotlib()
elif step==2:
vb=self.vb
grp=pg.ItemGroup()
vb.addItem(grp)
obj=UsrGO.Marker((100, 100), (100, 100), mode=1)
grp.addItem(obj)
obj=UsrGO.Marker((150, 100), (50, 50), mode=1)
grp.addItem(obj)
obj=UsrGO.Marker((200, 100), (100, 100), mode=1)
grp.addItem(obj)
tc['grp']=grp
vb.autoRange(items=(obj,))
elif step==3:
grp=tc['grp']
tr=grp.transform()
# UsrGO.obj_info(tr)
tr.setMatrix(1, .2, 0,
-.2, 1, 0,
0, 0, 1)
grp.setTransform(tr)
elif step==4:
geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=30,saveImg=True)
elif step==5:
geo.autofocus(app._camera, self.tweakers['base_z'],rng=(-1, 1), n=10)
elif step==6:
geo.find_fiducial(app._camera)
#print(vb.childGroup.childItems())
pass
def prepare_wd_tabs_left(self):
tabs = self._wd_tabs_left
tabs.currentChanged.connect(self.cb_switch_task)
setup_tab = self._tab_setup
exp_tab = self._tab_experiment
# add layouts
exp_tab.setLayout(QVBoxLayout())
setup_tab.setLayout(QVBoxLayout())
setup_tab.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self._setup_toolbox = tbox = QToolBox()
tbox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored))
# tbox.setMinimumWidth(itemWidget.sizeHint().width())
setup_tab.layout().addWidget(tbox)
# -------- DAQ Methods Tabs --------
self.build_tab_module_fix_target()
self._OLD_build_daq_methods_grid_tab()
self._OLD_build_daq_methods_prelocated_tab() #ZAC: orig. code
self._OLD_create_helical_widgets() #ZAC: orig. code
self._OLD_build_daq_methods_embl_tab()
#self._OLD_build_sample_selection_tab()
# -------- group geometry calibration --------
block = QWidget()
block.setAccessibleName(TASK_SETUP_GEOMETRY_CALIB)
block.setContentsMargins(0, 0, 0, 0)
block.setLayout(QVBoxLayout())
grp = QWidget()
block.layout().addWidget(grp)
block.layout().addStretch()
grp.setLayout(QGridLayout())
tbox.addItem(block, "geometry calibration")
#grp.layout().addWidget(QLabel("feature size (µM)"), 0, 0)
#self._ppm_feature_size_spinbox = QDoubleSpinBox()
#self._ppm_feature_size_spinbox.setRange(5, 10000)
#self._ppm_feature_size_spinbox.setValue(50)
#self._ppm_feature_size_spinbox.setDecimals(0)
#self._ppm_feature_size_spinbox.setSuffix(" µM")
#grp.layout().addWidget(self._ppm_feature_size_spinbox, 0, 1)
self._btn_pix2pos = but = QPushButton("Pixel to position calibration")
but.setCheckable(True)
but.clicked.connect(self.cb_update_pix2pos)
grp.layout().addWidget(but, 0, 0)
self._btn_opt_ctr = but = QPushButton("Optical center calibration")
but.setCheckable(True)
but.clicked.connect(self.cb_update_opt_ctr)
grp.layout().addWidget(but, 1, 0)
self._wd_calib_tree=tree=pg.DataTreeWidget(data=None)
grp.layout().addWidget(tree, 2, 0)
tbox.currentChanged.connect(self.cb_switch_task)
# final stretch
# setup_tab.layout().addStretch()
exp_tab.layout().addStretch()
# Jungfrau parameters
block=QWidget()
block.setAccessibleName(TASK_JUNGFRAU_SETTINGS)
block.setContentsMargins(0, 0, 0, 0)
block.setLayout(QVBoxLayout())
# self.jungfrau = jungfrau_widget.JungfrauWidget() #ZAC: orig. code
# block.layout().addWidget(self.jungfrau) #ZAC: orig. code
block.layout().addStretch()
tbox.addItem(block, "Jungfrau Parameters")
# # -------- group beam box --------
# block = QWidget()
# block.setAccessibleName(TASK_SETUP_BEAM_CENTER)
# block.setContentsMargins(0, 0, 0, 0)
# block.setLayout(QVBoxLayout())
# grp = QWidget()
# block.layout().addWidget(grp)
# block.layout().addStretch()
# grp.setLayout(QGridLayout())
# tbox.addItem(block, "Beam Box")
# # setup_tab.layout().addWidget(grp)
#
# row = grp.layout().rowCount
#
# if self._pv_shutter is not None:
# but = QPushButton("open/close shutter")
# but.setCheckable(True)
# but.setChecked(1 == self._pv_shutter.get())
# but.clicked.connect(self.toggle_shutter)
# self._button_shutter = but
# grp.layout().addWidget(but, 0, 0, 1, 2)
# self._pv_shutter.add_callback(self.update_shutter_label)
# #else: #ZAC: orig. code
# # self._picker = bernina_pulse_picker.PickerWidget()
# # grp.layout().addWidget(self._picker, 0, 0, 1, 2)
# but = QPushButton("clear beam markers")
# but.clicked.connect(self.remove_beam_markers)
# grp.layout().addWidget(but, 1, 1)
# help = QTextBrowser()
# grp.layout().addWidget(help, 2, 0, 5, 2)
# help.setHtml(
# """<body>
# <h1>Updating Beam Mark</h1>
# <ol>
# <li>clear beam markers</li>
# <li>for each zoom level, CTRL-Click (left mouse button) the center of the beam</li>
# </ol>
# </body>
# """
# )
# # -------- group regions --------
# block = QWidget()
# block.setAccessibleName(TASK_SETUP_ROI)
# block.setContentsMargins(0, 0, 0, 0)
# block.setLayout(QVBoxLayout())
# grp = QWidget()
# block.layout().addWidget(grp)
# block.layout().addStretch()
# grp.setLayout(QGridLayout())
# tbox.addItem(block, "Regions of Interest")
# but = QPushButton("add RECT roi")
# but.clicked.connect(self.roi_add_rect)
# grp.layout().addWidget(but, 0, 0)
# but = QPushButton("add LINE roi")
# but.clicked.connect(self.roi_add_line)
# grp.layout().addWidget(but, 0, 1)
#
# # -------- group camera settings --------
# block = QWidget()
# block.setAccessibleName(TASK_SETUP_CAMERA)
# block.setContentsMargins(0, 0, 0, 0)
# block.setLayout(QVBoxLayout())
# grp = QWidget()
# block.layout().addWidget(grp)
# block.layout().addStretch()
# grp.setLayout(QGridLayout())
# tbox.addItem(block, "Camera Settings")
# but = QPushButton("remove transformations")
# but.clicked.connect(lambda bogus: self.modify_camera_transform("remove_all"))
# grp.layout().addWidget(but, 0, 0)
# but = QPushButton("remove last")
# but.clicked.connect(lambda bogus: self.modify_camera_transform("undo_last"))
# grp.layout().addWidget(but, 0, 1)
# but = QPushButton("Turn 90 CCW")
# #but.clicked.connect(
# # lambda bogus, n=camera.Transforms.ROTATE_90: self.modify_camera_transform(n)
# #) #ZAC: orig. code
# grp.layout().addWidget(but, 1, 0)
# but = QPushButton("Turn 90 CW")
# #but.clicked.connect(lambda bogus, n=camera.Transforms.ROTATE_270: self.modify_camera_transform(n)) #ZAC: orig. code
# grp.layout().addWidget(but)
# but = QPushButton("Turn 180 CCW")
# #but.clicked.connect(lambda bogus, n=camera.Transforms.ROTATE_180: self.modify_camera_transform(n)) #ZAC: orig. code
# grp.layout().addWidget(but)
# but = QPushButton("Transpose")
# #but.clicked.connect(lambda bogus, n=camera.Transforms.TRANSPOSE: self.modify_camera_transform(n)) #ZAC: orig. code
# grp.layout().addWidget(but)
# but = QPushButton("Flip L/R")
# #but.clicked.connect(lambda bogus, n=camera.Transforms.FLIP_LR: self.modify_camera_transform(n)) #ZAC: orig. code
# grp.layout().addWidget(but)
# but = QPushButton("Flip U/D")
# #but.clicked.connect(lambda bogus, n=camera.Transforms.FLIP_UD: self.modify_camera_transform(n)) #ZAC: orig. code
# grp.layout().addWidget(but)
# row = grp.layout().rowCount()
# self._label_transforms = QLabel()
# self._label_transforms.setWordWrap(True)
# self.modify_camera_transform(None)
#
# grp.layout().addWidget(self._label_transforms, row, 0, 1, 2)
def build_tab_module_fix_target(self):
#tab = self._tab_daq_method_prelocated
try:
objLst=self._goTracked.childItems()
except AttributeError:
_log.debug('NOT YET INITIALIZED')
objLst=None
self._moduleFixTarget =mft = ModuleFixTarget.WndFixTarget(self,objLst)
tab = self._tabs_daq_methods.insertTab(0,mft,'Fix Target')
mft.setAccessibleName(TASK_FIX_TARGET)
self._tabs_daq_methods.setCurrentWidget(mft) #set this as the active tabs
mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(<param>)", "Grid(<param>)", "SwissMX(<param>)", "SwissFEL(<param>)"])
mft._btnAdd.clicked.connect(self.module_fix_target_add_obj)
mft._btnDelAll.clicked.connect(self.module_fix_target_del_all_obj)
mft._btnFit.clicked.connect(self.module_fix_target_fit_fiducial)
#tab.layout().addWidget(mft)
mft.prefixSelected.connect(lambda prefix: self._le_prefix.setText(prefix))
mft.dataFileLoaded.connect(self._OLD_daq_method_prelocated_update_markers)
mft.prelocatedDataUpdated.connect(self._OLD_daq_method_prelocated_update_markers)
mft.markersDeleted.connect(self._OLD_daq_method_prelocated_remove_markers)
mft.moveFastStageRequest.connect(self._OLD_move_fast_stage)
self.fiducialPositionSelected.connect(self._OLD_daq_method_prelocated_set_fiducial)
self.appendPrelocatedPosition.connect(self._OLD_daq_method_prelocated_append_data)
#self._preloc_inspect_area = QPlainTextEdit()
#tab.layout().addWidget(self._preloc_inspect_area)
def cb_switch_task(self, index=0):
stack = self._wd_stack
task = self._wd_tabs_left.currentWidget().accessibleName()
setup_task = self._setup_toolbox.currentWidget().accessibleName()
method = self._tabs_daq_methods.currentWidget().accessibleName()
_log.debug("Task switching: top_tabs={} exp.method={} setup_task={}".format(task, method, setup_task))
if task == "task_experiment":
stack.setCurrentIndex(0)
active_task = method
elif task == "task_setup":
active_task = setup_task
elif task == "task_sample_selection":
active_task = TASK_SAMPLE_SELECTION
stack.setCurrentIndex(1)
else:
_log.error("BUG: un-handled task")
QMessageBox.warning(self, "BUG unhandled task!", "unhandled task: top_tabs={} method={}".format(task, method),)
self.set_active_task(active_task)
self._status_task.setText(active_task)
def cb_update_pix2pos(self, calib):
app=QApplication.instance()
if calib:
_log.info("received new pix2pos calibration")
QMessageBox.information(self,'Pixel to position calibration','''\
- for each zoom level:
- choose a good fiducial mark
- move the stage x/y at min. 3 different locations
- click (left mouse button) on that fiducial mark
- un-click button "Pixel to position calibration"
''')
app._raw_pix2pos=dict()
else:
s=str(app._raw_pix2pos).replace('),','),\n ').replace('],','],\n')
print(s)
res=QMessageBox.question(self, "calibration", f"use calibration\n{s} ? ")
if res==QMessageBox.Yes:
geo=app._geometry
geo.update_pix2pos(app._raw_pix2pos)
app._cfg.setValue(AppCfg.GEO_PIX2POS, geo._lut_pix2pos)
del app._raw_pix2pos
def cb_update_opt_ctr(self, calib):
app=QApplication.instance()
if calib:
_log.info("received new opt_ctr calibration")
QMessageBox.information(self,'Optical center calibration','''\
- choose the biggest zoom level to start
- choose at least 3 fiducial marks
- for at least two zoom level:
- Click (left mouse button) on the fiducial marks (always in the same order)
- un-click button "Optical center calibration"
''')
app._raw_opt_ctr=dict()
else:
geo=app._geometry
ocOld=geo._opt_ctr
try:
geo.update_optical_center(app._raw_opt_ctr,True)
except BaseException as e:
_log.warning(e)
QMessageBox.warning(self, "calibration", f"rejected\n{e}")
del app._raw_opt_ctr
return
del app._raw_opt_ctr
go=self._goOptCtr
oc_sz=go.size()
go.blockSignals(True) # avoid to call cb_marker_moved
go.setPos(geo._opt_ctr-oc_sz/2)
res=QMessageBox.question(self, "calibration", f"keep new optical center\n{geo._opt_ctr} ? ")
if res==QMessageBox.Yes:
app._cfg.setValue(AppCfg.GEO_OPT_CTR, geo._opt_ctr)
else:
geo._opt_ctr=ocOld
go.setPos(geo._opt_ctr-oc_sz/2)
go.blockSignals(False) # allow to call cb_marker_moved
def cb_fiducial_update_z(self,obj,*vargs):
obj._z=self.tweakers["base_z"].get_rbv()
def module_fix_target_add_obj(self,*args,**kwargs):
mft=self._moduleFixTarget
fx=self.tweakers["fast_x"].get_rbv()
fy=self.tweakers["fast_y"].get_rbv()
bz=self.tweakers["base_z"].get_rbv()
app=QApplication.instance()
geo=app._geometry
oc=geo._opt_ctr
bm_pos=self._goBeamMarker.pos()
bm_sz=self._goBeamMarker.size()
idx=mft._cbType.currentIndex()
param=mft._txtParam.text()
#mft._cbType.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(<param>)", "Grid(<param>)", "SwissMX-path"])
#bm_pos_eu=self._goBeamMarker._pos_eu
#bm_size_eu=self._goBeamMarker._size_eu
if idx==0:
#go=UsrGO.Fiducial(bm_pos+bm_sz/2-(20, 20), (40, 40),(fx,fy,bz))
l=.120
go=UsrGO.Fiducial((fx-l/2,fy-l/2), (l, l),bz)
go.sigRegionChangeFinished.connect(self.cb_fiducial_update_z)
elif idx==1:
v=geo.pos2pix((12.5, 0))
l=np.linalg.norm(v)
l=12.5
#ctr=bm_pos+bm_sz/2
go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='12.5x12.5')
elif idx==2:
v=geo.pos2pix((23, 0))
l=np.linalg.norm(v)
l=23
go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='23.0x23.0')
elif idx==3:
w,h=(.120*12, .120*8)
go=UsrGO.FixTargetFrame((fx-w/2,fy-h/2), (w, h), tpl='test')
elif idx==4:
w,h=(30, 22)
go=UsrGO.Grid((fx-w/2,fy-h/2), (w, h), (30, 22), .1)
elif idx==5:
ofs=(.2, .2); width=10; fidScl=.02
fiducial=((.1, .1), (.1, 2.7), (10.3, .1), (10.3, 2.7))
if param:
p=json.loads('{'+param+'}') # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]]
locals().update(p)
fiducial=np.array(fiducial)
gp=psi_device.shapepath.GenPath(); gp.swissmx_points(ofs=ofs, width=width, flipy=True)
sz=gp.points.max(0)+np.array(ofs)*2
go=UsrGO.Path((fx-sz[0]/2, fy-sz[1]/2), sz, gp.points, fiducial, fidScl)
elif idx==6:
ofs=(.2, .2); width=10; fidScl=.02
fiducial=((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2))
if param:
p=json.loads('{'+param+'}') # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]]
locals().update(p)
fiducial=np.array(fiducial)
gp=psi_device.shapepath.GenPath(); gp.swissfel_points(ofs=ofs, width=width,flipy=True)
sz=gp.points.max(0)+np.array(ofs)*2
go=UsrGO.Path((fx-sz[0]/2, fy-sz[1]/2), sz, gp.points, fiducial, fidScl)
else:
_log.error('index not handeled')
grp=self._goTracked
grp.addItem(go)
mft._tree.setData(grp.childItems())
def module_fix_target_del_all_obj(self):
app=QApplication.instance()
geo=app._geometry
try: #remove plane fitter if it esists
del geo._fitPlane
except AttributeError:
pass
mft=self._moduleFixTarget
vb=self.vb
grp=self._goTracked
for go in grp.childItems():
vb.removeItem(go)
grp.setFlag(grp.ItemHasNoContents)
mft._tree.setData(None)
def module_fix_target_fit_fiducial(self):
mft=self._moduleFixTarget
vb=self.vb
grp=self._goTracked
cldLst=grp.childItems()
n=0
for go in cldLst:
if type(go)==UsrGO.Fiducial:
n+=1
ptFitTrf=np.ndarray((4,2)) # 4 (x,y) points to fit a transformation of a parallelogram
ptFitPlane=np.ndarray((n,3)) #n (x,y,z) points to fit a plane transformation z=ax+by+c
i=j=0
for go in reversed(cldLst):
if type(go)==UsrGO.FixTargetFrame:
if j==4:
#fid=np.array(((0.1, 0.1), (0.1, 1.1), (1.1, 0.1), (1.1, 1.1)))
fid=np.array(go._dscr['fiducial']['pos'])
sz=np.array(go._dscr['size'])
fid=fid/sz
trf=geometry.geometry.least_square_trf(ptFitTrf,fid)
print(trf)
tr=go.transform()
tr.setMatrix(1, trf[1,0]/trf[0,0], 0,
trf[0,1]/trf[1,1], 1, 0,
0, 0, 1)
go.setTransform(tr)
go.setPos(trf[:,2])
#go.setSize((1,1))
sz=(trf[0,0],trf[1,1])
go.setSize(sz)
j=0
elif type(go)==UsrGO.Path:
if j==4:
fid=go._fiducial
sz=go.szOrig
fid=fid/sz
trf=geometry.geometry.least_square_trf(ptFitTrf, fid)
print(trf)
tr=go.transform()
tr.setMatrix(1, trf[1, 0]/trf[0, 0], 0,
trf[0, 1]/trf[1, 1], 1, 0,
0, 0, 1)
go.setTransform(tr)
go.setPos(trf[:, 2])
# go.setSize((1,1))
sz=(trf[0, 0], trf[1, 1])
go.setSize(sz)
j=0
elif type(go)==UsrGO.Fiducial and go.size()[0]==0.12:
if j<ptFitTrf.shape[0]:
ptFitTrf[j]=go.pos()+go.size()/2
j+=1
ptFitPlane[i]=go.ctr()
i+=1
app=QApplication.instance()
geo=app._geometry
if n>=3:
geo.least_square_plane(ptFitPlane)
def cb_progress(self,i,sz,dlg):
dlg.setValue(int(i*90/sz))
if dlg.wasCanceled():
raise AttributeError('canceled')
def daq_collect(self, **kwargs):# points, visualizer_method, visualizer_params):
'''
kwargs:
code_gen: 0 pvt motion using ptsTrf
1 pvt motion using trf and points
2 pvt motion, compact grid code
grid: grid dictionary with orig, pitch, count
trf: transformation matrix
points: list of points without transformation
pts_trf: list of points with transformation
'''
app = QApplication.instance()
cfg = app._cfg
dt_misc = cfg.value(AppCfg.DT_MISC)
#_log.critical('abort for test');return # RET
geo = app._geometry
fn='/tmp/shapepath'
try:
dt=app._deltatau
except AttributeError:
app._deltatau=dt=psi_device.Deltatau()
try:
jf=app._jungfrau
except AttributeError:
app._jungfrau=jf=psi_device.Jungfrau()
sp=dt._shapepath
sp.verbose=dt_misc['verbose']
sp.meta['pt2pt_time']=dt_misc['pt2pt_time']
sp.meta['sync_mode']=dt_misc['sync_mode']
sp.meta['sync_flag']=dt_misc['sync_flag']
with pg.ProgressDialog('Progress', 0, 100) as dlg:
dlg.setWindowModality(Qt.WindowModal)
#dlg.setRange(0, 0)
#dlg.setCancelButtonText("Abort Acquisition")
#dlg.canceled.connect(self.complete_daq)
#dlg.setAutoClose(True)
#dlg.show()
dlg.setLabelText("Setup Gather/Sync");dlg+=5
sp.setup_gather(kwargs['num_pts'])
sp.setup_sync(verbose=sp.verbose&0x40, timeOfs=dt_misc['time_ofs'], timeCor=dt_misc['time_cor'])
dlg.setLabelText("Download motion program");dlg+=5
codeGen=kwargs['code_gen']
if codeGen==0:
sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10, points=kwargs['pts_trf'])
elif codeGen==1:
sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10, points=kwargs['points'])
elif codeGen==2:
sp.setup_motion(fnPrg=fn+'.prg', mode=4, scale=1., dwell=10, grid=kwargs['grid'])
try:
p=geo._fitPlane
if codeGen==0:
# X has inverted sign !
# Z is in um -> therefore the offset must be multiplied with 1000 ! Z motor has opposite sign !
cz=f'{+p[0]:+.18g}X{-p[1]:+.18g}Y{-p[2]*1000:+.18g}'
else:
cz=f'{+p[0]:+.18g}X{-p[1]:+.18g}Y{-p[2]*1000:+.18g}'
except AttributeError:
cz='0'
_log.warning('no plane fitting done. z does not move')
trf=kwargs['trf']*1000 # fix shear/rotation mm->um
trf[:, 0]*=-1 # X axis has inverted sign !
if codeGen==0:
fx='X';fy='Y'
else:
fx=f'{trf[0,0]:+.18g}X{trf[1,0]:+.18g}Y{trf[2,0]:+.18g}'
fy=f'{trf[0,1]:+.18g}X{trf[1,1]:+.18g}Y{trf[2,1]:+.18g}'
print(f'pts_trf[0,:]={kwargs["pts_trf"][0, :]}')
print(f'points[0,:]={kwargs["points"][0, :]}')
print(f'trf=\n{trf}')
sp.setup_coord_trf(fx, fy, cz) # reset to shape path system
try:
sp.comm.gpascii._cb_func=lambda i, sz:self.cb_progress(i, sz, dlg)
except AttributeError:
pass
if dlg.wasCanceled():
return
dlg.setLabelText("Homing and get ready");dlg+=5
sp.homing() # homing if needed
# TODO: shutter
# open laser shutter
# caput("SLAAR02-LMOT-M262:MOT.VAL", 1)
# time.sleep(4)
# TODO: shutter
# open fast shutter
#caput("SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", 1)
sp.run() # start motion program
sp.wait_armed() # wait until motors are at first position
#time.sleep(1.0) # wait armed does not wor yet ?!?
jf.acquire(sp.points.shape[0])
sp.trigger(0.5) # send a start trigger (if needed) after given time
if not dt._comm is None:
dlg.setLabelText("run motion/acquisition")
dlg.setMaximum(sp.points.shape[0])
while True:
p=sp.progress()
if p<0:
break
elif dlg.wasCanceled():
dt._comm.gpascii.send_block('&1a;Gather.Enable=0')
break
#_log.info(f'progress {p}/{sp.points.shape[0]}')
dlg.setValue(p)
time.sleep(.1)
if not dlg.wasCanceled():
jf.gather_upload()
dlg.setLabelText("upload gather data")
sp.gather_upload(fnRec=fn+'.npz')
if dt_misc['show_plots']:
dp=psi_device.shapepath.DebugPlot(sp)
dp.plot_gather(mode=11)
plt.show(block=False)
#plt.show(block=True)
# TODO: shutter
# clos fast shutter
# caput("SARES30-LTIM01-EVR0:RearUniv0-Ena-SP", 0)
# TODO: shutter
# close laser shutter
# caput("SLAAR02-LMOT-M262:MOT.VAL", 40)
def esc_run_steps(self, steps, title, esc_state):
self._esc_state ="busy"
with pg.ProgressDialog(title, 0, len(steps)) as dlg:
for step in steps:
step()
dlg += 1
if dlg.wasCanceled():
QMessageBox.warning(self, "escape steps", "ABORTED" + title)
break
self._esc_state = esc_state
def move_gonio(self, pos):
# gonio="{'pos_mount': (0.0,0.0,0.0,0.0), 'pos_align': (0.0,0.0,0.0,0.0)}"
# positions are: fast_x,fast_y,base_x,base_z,(omega=0,det_z, no change)
app=QApplication.instance()
cfg=app._cfg
pos_gonio = cfg.value(AppCfg.DFT_POS_GONIO)
if pos_gonio is None:
msg="gonio default positions are not configured."
_log.warning(msg)
QMessageBox.warning(self, "gonio not configured", msg)
return
tw_fx = self.tweakers["fast_x"]
tw_fy = self.tweakers["fast_y"]
tw_cx = self.tweakers["base_x"]
tw_cz = self.tweakers["base_z"]
tw_ry = self.tweakers["omega"]
try:
p_fx, p_fy, p_cx, p_cz,=pos_gonio['pos_'+pos]
p_ry=0.
except KeyError:
raise ValueError("Goniometer position *{}* is not known!!")
_log.info(f"moving goniometer {pos} to fx:{p_fx:.5g},fy:{p_fy:.5g},cx:{p_cx:.5g},cz_{p_cz:.5g},ry:{p_ry:.5g}")
tw_fx.move_abs(p_fx)
tw_fy.move_abs(p_fy)
tw_cx.move_abs(p_cx)
tw_cz.move_abs(p_cz)
tw_ry.move_abs(p_ry)
app_utils.assert_tweaker_positions([
(tw_fx, p_fx, 0.1),
(tw_fy, p_fy, 0.1),
(tw_cx, p_cx, 0.1),
(tw_cz, p_cz, 0.1),
(tw_ry, p_ry, 0.1), ], timeout=20.0,
)
def move_post_tube(self, pos):
# post_sample_tube="{'x_up': -0.2, 'y_up': 0.0, 'x_down': 0.0, 'y_down': 0.0, 'x_out_delta': 0.0, 'y_out_delta': 0.0, 'z_in': 0.0, 'z_out': 0.0}"
app=QApplication.instance()
cfg=app._cfg
pos_pst = cfg.value(AppCfg.DFT_POS_PST)
if pos_pst is None:
msg = "SwissMX *POST-SAMPLE-TUBE* configuration is incomplete!!!"
_log.warning(msg)
QMessageBox.warning(self, "post tube not configured", msg)
return
x_in_us=pos_pst['x_in_us']
y_in_us=pos_pst['y_in_us']
x_in_ds=pos_pst['x_in_ds']
y_in_ds=pos_pst['y_in_ds']
x_out_d=pos_pst['x_out_delta']
y_out_d=pos_pst['y_out_delta']
z_in =pos_pst['z_in']
z_out =pos_pst['z_out']
tw_x_us = self.tweakers["tube_usx"]
tw_y_us = self.tweakers["tube_usy"]
tw_x_ds = self.tweakers["tube_dsx"]
tw_y_ds = self.tweakers["tube_dsy"]
tw_z = self.tweakers["tube_z"]
tandem_twv = float(self._post_tandem_tweak_val.text())
_log.info(f"move post sample tube {pos}")
if pos == "in":
tw_x_us.move_abs(x_in_us)
tw_y_us.move_abs(y_in_us)
tw_x_ds.move_abs(x_in_ds)
tw_y_ds.move_abs(y_in_ds)
try:
app_utils.assert_tweaker_positions([
(tw_x_us, x_in_us, 0.1),
(tw_y_us, y_in_us, 0.1),
(tw_x_ds, x_in_ds, 0.1),
(tw_y_ds, y_in_ds, 0.1), ],timeout=20.0,
)
except app_utils.PositionsNotReached as e:
msg=f"failed to move post sample tube {pos}: {e}"
_log.warning(msg)
QMessageBox.warning(self, "failed to move post sample tube", msg,)
raise
tw_z.move_abs(z_in, wait=True)
try:
app_utils.assert_tweaker_positions([(tw_z, z_in, 0.1)])
except app_utils.PositionsNotReached as e:
msg=f"failed to move post sample tube {pos}: {e}"
_log.warning(msg)
QMessageBox.warning(self, "failed to move post sample tube", msg,)
raise
elif pos == "out":
tw_z.move_abs(z_out, wait=True)
try:
app_utils.assert_tweaker_positions([(tw_z, z_out, 0.1)])
except app_utils.PositionsNotReached as e:
msg=f"failed to move post sample tube {pos}: {e}"
_log.warning(msg)
QMessageBox.warning(self, "failed to move post sample tube", msg,)
raise
x_out_us=x_in_us+x_out_d; tw_x_us.move_abs(x_out_us)
y_out_us=y_in_us+y_out_d; tw_y_us.move_abs(y_out_us)
x_out_ds=x_in_ds+x_out_d; tw_x_ds.move_abs(x_out_ds)
y_out_ds=y_in_ds+y_out_d; tw_y_ds.move_abs(y_out_ds)
try:
app_utils.assert_tweaker_positions([
(tw_x_us, x_out_us, 0.1),
(tw_y_us, y_out_us, 0.1),
(tw_x_ds, x_out_ds, 0.1),
(tw_y_ds, y_out_ds, 0.1), ], timeout=20.0,
)
except app_utils.PositionsNotReached as e:
msg=f"failed to move post sample tube {pos}: {e}"
_log.warning(msg)
QMessageBox.warning(self, "failed to move post sample tube", msg,)
raise
elif pos == "x+":
tw_x_us.move_rel(tandem_twv)
tw_x_ds.move_rel(tandem_twv)
elif pos == "x-":
tw_x_us.move_rel(-tandem_twv)
tw_x_ds.move_rel(-tandem_twv)
elif pos == "y+":
tw_y_us.move_rel(tandem_twv)
tw_y_ds.move_rel(tandem_twv)
elif pos == "y-":
tw_y_us.move_rel(-tandem_twv)
tw_y_ds.move_rel(-tandem_twv)
else:
raise ValueError("post sample tube *{}* is not known!!")
def move_detector(self, pos):
# detector="{'pos_in': -0.1, 'pos_out': 0.0}"
app=QApplication.instance()
cfg=app._cfg
pos_det = cfg.value(AppCfg.DFT_POS_DET)
if pos_det is None:
msg="detector default positions are not configured."
_log.warning(msg)
QMessageBox.warning(self, "post tube not configured", msg)
return
det_z = self.tweakers["det_z"]
try:
p_z=pos_det['pos_'+pos]
except KeyError:
raise ValueError("Collimator position *{}* is not known!!")
_log.info(f"moving detector {pos} to {p_z:.5g}")
det_z.move_abs(p_z, assert_position=True)
def move_collimator(self, pos):
# collimator="{'x_in': 0.0, 'y_in': -0.1, 'x_out_delta': 0.0, 'y_out_delta': 0.0}"
app=QApplication.instance()
cfg=app._cfg
cx = self.tweakers["colli_x"]
cy = self.tweakers["colli_y"]
pos_col = cfg.value(AppCfg.DFT_POS_COL)
if pos_col is None:
msg="COLLIMATOR configuration is incomplete!"
_log.warning(msg)
QMessageBox.warning(self, "post tube not configured", msg)
return
x_in = pos_col['x_in']
y_in = pos_col['y_in']
x_out = pos_col['x_out']
y_out = pos_col['y_out']
if pos == "out":
x_pos=x_out
y_pos=y_out
elif pos == "in":
x_pos=x_in
y_pos=y_in
elif pos == "ready":
x_pos=x_in
y_pos=y_out
else:
raise ValueError("Collimator position *{}* is not known!!")
_log.info(f"moving collimator {pos} to X,Y = {x_pos:.3f}, {y_pos:.3f}")
cx.move_abs(x_pos, assert_position=True)
cy.move_abs(y_pos, assert_position=True)
# **************** OBSOLETE AND/OR OLD STUFF ****************
# functions are prefixed with _OLD_
def _OLD_build_daq_methods_grid_tab(self):
self._grids = []
tab = self._tab_daq_method_grid
layout = tab.layout() # gridlayout
self._sb_grid_x_step.setValue(30)
self._sb_grid_y_step.setValue(30)
self._bt_add_grid.clicked.connect(self._OLD_daq_grid_add_grid)
self._bt_remove_all_grids.clicked.connect(self._OLD_daq_grid_remove_all)
self._find_targets_from_microscope_image.clicked.connect(self._OLD_daq_grid_findxtals)
self.addGridRequest.connect(self._OLD_daq_grid_add_grid)
self.gridUpdated.connect(self._OLD_daq_grid_update)
def _OLD_build_daq_methods_prelocated_tab(self):
tab = self._tab_daq_method_prelocated
self.prelocationModule = PrelocatedCoordinatesModel.PrelocatedCoordinates(parent=self)
tab.layout().addWidget(self.prelocationModule)
self.prelocationModule.prefixSelected.connect(lambda prefix: self._le_prefix.setText(prefix))
self.prelocationModule.dataFileLoaded.connect(self._OLD_daq_method_prelocated_update_markers)
self.prelocationModule.prelocatedDataUpdated.connect(self._OLD_daq_method_prelocated_update_markers)
self.prelocationModule.markersDeleted.connect(self._OLD_daq_method_prelocated_remove_markers)
self.fiducialPositionSelected.connect(self._OLD_daq_method_prelocated_set_fiducial)
self.appendPrelocatedPosition.connect(self._OLD_daq_method_prelocated_append_data)
self.prelocationModule.moveFastStageRequest.connect(self._OLD_move_fast_stage)
self._preloc_inspect_area = QPlainTextEdit()
tab.layout().addWidget(self._preloc_inspect_area)
def _OLD_build_daq_methods_embl_tab(self):
app = QApplication.instance()
self._tab_daq_method_embl.setLayout(QVBoxLayout())
layout = self._tab_daq_method_embl.layout()
#motors = self.get_gonio_motors()
self._embl_module = EmblWidget(self) #ZAC: orig. code
#self._embl_module.configure(motors, app._camera, app._zoom)
layout.addWidget(self._embl_module)
def _OLD_create_helical_widgets(self):
tbox = self._helical_tablebox
htab = self._helical_scan_table = HelicalTableWidget()
htab.gonioMoveRequest.connect(self._OLD_move_gonio_to_position)
tbox.setLayout(QVBoxLayout())
grp = QWidget()
grp.setLayout(QFormLayout())
le = QSpinBox()
le.setRange(1, 100)
le.setValue(htab.scanHorizontalCount())
le.valueChanged.connect(lambda cnt: htab.setScanHorizontalCount(cnt))
grp.layout().addRow("Horizontal Count", le)
le = QSpinBox()
le.setRange(1, 100)
le.setValue(htab.scanVerticalCount())
le.valueChanged.connect(lambda cnt: htab.setScanVerticalCount(cnt))
grp.layout().addRow("Vertical Count", le)
le = QDoubleSpinBox()
le.setRange(-180.0, 180.0)
le.setSingleStep(5.0)
le.setSuffix(" degrees")
le.valueChanged.connect(htab.setStartAngle)
grp.layout().addRow("Start angle", le)
tbox.layout().addWidget(grp)
widgi = QWidget()
widgi.setLayout(QHBoxLayout())
tbox.layout().addWidget(widgi)
but = QPushButton("Add Crystal")
but.clicked.connect(htab.add_xtal)
widgi.layout().addWidget(but)
but = QPushButton("Set START")
but.clicked.connect(lambda: htab.set_xtal_start(self.get_gonio_positions()))
widgi.layout().addWidget(but)
but = QPushButton("Set END")
but.clicked.connect(lambda: htab.set_xtal_end(self.get_gonio_positions()))
widgi.layout().addWidget(but)
tbox.layout().addWidget(htab)
def _OLD_add_beam_marker(self):
app = QApplication.instance()
cfg = app._cfg
w, h = cfg.value(AppCfg.GEO_BEAM_SZ)
self._beammark = bm = CstROI.BeamMark([100, 100], (int(w), int(h)), parent=self)
tr=QtGui.QTransform() # prepare ImageItem transformation:
tr.rotate(30)
bm.setTransform(tr) # assign transform
self.vb.addItem(self._beammark)
bm=UsrGO.BeamMark([50, 120], [30, 20])
self.vb.addItem(bm)
vi=UsrGO.Grid((120, -100), (200, 150), (30, 20), 2)
self.vb.addItem(vi)
def _OLD_camera_pause_toggle(self):
app=QApplication.instance()
app._camera.pause()
def _OLD_init_settings_tracker(self):
app=QApplication.instance()
cfg=app._cfg
_log.info("configuring widget persistence")
fields = {
# 'folder': (self._label_folder, str),
"project": (self._le_project, str),
"prefix": (self._le_prefix, str),
"actual_prefix": (self._label_actual_prefix, str),
"exposureTime": (self._dsb_exposure_time, float),
"oscillationAngle": (self._dsb_oscillation_step, float),
"blastRadius": (self._dsb_blast_radius, float),
}
for key, f_config in fields.items():
widget, conv = f_config
value = cfg.value(key)
try:
wset, wget = widget.setText, widget.text
_log.debug("tracking text field {}".format(key))
except AttributeError:
_log.debug("tracking {} number field {}".format(conv, key))
wset, wget = widget.setValue, lambda fget=widget.value: conv(fget())
except Exception as e:
_log.error(e)
try:
wset(conv(value))
except Exception as e:
_log.debug(e)
_log.warning('failed for "{}" updating field of type {} with {}'.format( key, type(widget), value))
finally:
# _log.debug('wget = {}; wset = {}'.format(wget, wset))
widget.editingFinished.connect(lambda w=widget, k=key, func_get=wget: self.persist_setting(k, func_get() ) )
#self.storage_cascade_prefix(None) #ZAC: orig. code
def _OLD_init_validators(self):
identifier_regex = QRegExp("[a-z-A-Z_0-9%]+")
self._le_project.setValidator(QRegExpValidator(identifier_regex, self._le_project))
self._le_prefix.setValidator(QRegExpValidator(identifier_regex, self._le_prefix))
def _OLD_wire_storage(self):
self._le_prefix.textChanged.connect(self._OLD_storage_cascade_prefix)
self._le_prefix.textChanged.connect(lambda newtext: self.prefixChanged.emit(newtext))
self._le_project.textChanged.connect(self._OLD_storage_cascade_prefix)
self._le_project.textChanged.connect(lambda newtext: self.projectChanged.emit(newtext))
self._le_prefix.editingFinished.connect(self._OLD_prepare_daq_folder)
self._le_project.editingFinished.connect(self._OLD_prepare_daq_folder)
self.increaseRunNumberRequest.connect(self._OLD_increase_run_number)
def _OLD_storage_cascade_prefix(self, val):
prefix = self._le_prefix.text()
if 0 == len(prefix):
_log.warning("empty prefix is not accepted")
self._le_prefix.setAccessibleName("invalid_input")
self._le_prefix.blockSignals(True)
self._le_prefix.setText("INVALID=>" + prefix)
QMessageBox.warning(self, "prefix is not valid", "Prefix is not valid!")
self._le_prefix.blockSignals(False)
return
else:
self._le_prefix.setAccessibleName("")
project = self._le_project.text()
folders.set_prefix(prefix)
folders.set_project(project)
folders.run = settings.value("run_number", type=int)
self._label_runnumber.setText(f"{folders.run:04d}")
self._data_folder = folders.raw_folder
self.folderChanged.emit(folders.raw_folder)
self._label_actual_prefix.setText(folders.prefix)
self._label_folder.setText(folders.raw_folder)
def _OLD_increase_run_number(self):
run = settings.value("run_number", type=int)
run += 1
settings.setValue("run_number", run)
folders.run = run
self._label_runnumber.setText(f"{run:04d}")
def _OLD_prepare_daq_folder(self):
global home, folders
prefix = folders.prefix
folder = folders.res_folder
if 0 == len(prefix):
return
try:
os.makedirs(folder, 0o750, exist_ok=True)
except:
msg = "Failed to create folder: {}".format(folder)
_log.warning(msg)
QMessageBox.warning(self, "Screenshot: failed to create folder", "Failed to create output folder for screenshot!\n\n\tScreenshot not taken!",)
raise
fname = os.path.join(folders.pgroup_folder, ".latest_raw")
try:
with open(fname, "w") as f:
f.write(folders.raw_folder)
_log.info("wrote: {}".format(fname))
except:
_log.warning("failed writing {}".format(fname))
fname = os.path.join(folders.pgroup_folder, ".latest_res")
try:
with open(fname, "w") as f:
f.write(folders.res_folder)
_log.info("wrote: {}".format(fname))
except:
_log.warning("failed writing {}".format(fname))
def _OLD_persist_setting(self, s, v):
app=QApplication.instance()
cfg=app._cfg
_log.debug("persisting {} = {}".format(s, v))
cfg.setValue(s, v)
def _OLD_method_changed(self, index):
method = self._tabs_daq_methods.currentWidget().accessibleName()
_log.info("method now => {}".format(method))
def _OLD_check_zescape(self):
msg = zescape.check()
if msg is None:
return
if "current" in msg:
_log.warning(f"current state: {self._esc_state}")
zescape.reply(self._esc_state)
elif "goto" in msg:
state = msg.split()[1].lower()
_log.warning(f"TELL requests to go to {state}")
try:
if "sampleexchange" in state:
_log.debug(
f"moving to mount with offset = {self._pin_mounting_offset}"
)
self.move_gonio_to_mount_position(offset=self._pin_mounting_offset)
elif "samplealignment" in state:
self.cb_esc_sample_alignment()
except:
zescape.reply("Maintenance")
zescape.reply(self._esc_state)
else: # JSON
data = json.loads(msg)
if "sampleName" in data:
_log.debug(f"TELL SAMPLE DATA => {data}")
self.tell2storage(data)
zescape.reply("ack")
elif "pin_offset" in data:
_log.debug(f"TELL pin offset => {data}")
self._pin_mounting_offset = data["pin_offset"]
zescape.reply("ack")
elif "get_pin_offset" in data:
_log.debug(f"TELL get pin offset => {data}")
zescape.reply_json({"pin_offset": self._pin_mounting_offset})
def _OLD_tell2storage(self, sample):
_log.debug(f"2 TELL SAMPLE DATA => {type(sample)}")
self._le_prefix.setText(sample["sampleName"])
self._le_project.setText(sample["sampleFolder"])
tstf = folders.get_prefixed_file("_newsample")
self.storage_cascade_prefix(None)
_log.warning(f"sample info: {tstf}")
def _OLD_is_task(self, task):
return task == self._active_task
def _OLD_get_task_menu(self):
pass
def _OLD_toggle_shutter(self, **kwargs):
if self._pv_shutter:
if 0 == self._pv_shutter.get():
self._pv_shutter.put(1)
self._button_shutter.setText("shutter opened\n\u2622")
else:
self._pv_shutter.put(0)
self._button_shutter.setText("shutter closed\n\u2622")
elif self._has_pulse_picker:
pulsePicker.toggle()
def _OLD_update_shutter_label(self, pvname, value, char_value, **kwargs):
if 0 == value:
self._button_shutter.setText("shutter closed")
else:
self._button_shutter.setText("shutter opened")
def _OLD_build_sample_selection_tab(self):
self._sample_selection = sample_selection.SampleSelection(self)
self._sample_selection.move_to_mount_position = (self.move_gonio_to_mount_position)
self._tab_sample_selection.setLayout(QVBoxLayout())
self._tab_sample_selection.layout().addWidget(self._sample_selection)
self._tab_sample_selection.layout().addStretch(2)
def _OLD_build_embl_group(self):
grp = QGroupBox("EMBL Acquisition")
layout = QFormLayout()
grp.setLayout(layout)
layout.addWidget(QLabel("Prefix"))
self._embl_prefix = QLineEdit("img")
layout.addWidget(self._embl_prefix)
def _OLD_abort_measurement(self, ev=None):
if settings.value(ACTIVATE_PULSE_PICKER):
pulsePicker.close()
jungfrau_detector.abort()
delta_tau.abort()
_log.debug("aborting measurement")
def _OLD_trigger_detector(self, **kwargs):
if self._pv_shutter is not None:
self._pv_shutter.put(0)
# self._eiger_button_collect.show()
# self._eiger_button_abort.hide()
# self._eiger_now_collecting_label.setText(
# "Finished sequence id: {}\n"
# "Data in: Data10/{}".format(
# self._detector_sequence_id, self._eiger_now_collecting_file
# )
# )
def _OLD_modify_camera_transform(self, t):
if t == "remove_all":
sample_camera.set_transformations([])
elif t == "undo_last":
sample_camera._transformations.pop()
#elif type(t) ==type(camera.Transforms): #ZAC: orig. code
# sample_camera.append_transform(t)
try:
label = ", ".join([t.name for t in sample_camera._transformations])
except:
label = ""
self._label_transforms.setText(label)
#settings.setValue(CAMERA_TRANSFORMATIONS, sample_camera._transformations) #ZAC: orig. code
def _OLD_roi_add_line(self):
roi = pg.LineSegmentROI(
[200, 200],
[300, 300],
pen="r",
scaleSnap=True,
translateSnap=True,
rotateSnap=True,
removable=True,
)
# roi.sigRegionChanged.connect(self.track_roi)
roi.sigRemoveRequested.connect(self.remove_roi)
self.vb.addItem(roi)
self._rois.append(roi)
def _OLD_roi_add_rect(self):
roi = pg.RectROI(
[200, 200],
[50, 50],
pen="r",
scaleSnap=True,
translateSnap=True,
rotateSnap=True,
removable=True,
)
roi.sigRegionChanged.connect(self.track_roi)
roi.sigRemoveRequested.connect(self.remove_roi)
self.vb.addItem(roi)
self._rois.append(roi)
def _OLD_remove_roi(self, roi):
self.vb.removeItem(roi)
self._rois.remove(roi)
def _OLD_prepare_microscope_page(self):
layout = self.microscope_page.layout()
container = QWidget()
hlay = QHBoxLayout()
container.setLayout(hlay)
layout.addWidget(container)
def _OLD_update_beam_marker(self, zoom_lvl):
w, h = settings.value(BEAM_SIZE)
try:
bx = self.beamx_fitter(zoom_lvl)
by = self.beamy_fitter(zoom_lvl)
ok = True
except:
ok = False
_log.warning("beam marker not defined")
return
_log.debug("updating beam mark to {:.1f}x{:.1f}".format(bx, by))
self.beamCameraCoordinatesChanged.emit(bx, by)
def _OLD_update_beam_marker_fitters(self):
if len(self._beam_markers) > 2:
_log.debug("defining beam marker")
bx = [(n, x[0]) for n, x in self._beam_markers.items()]
by = [(n, x[1]) for n, x in self._beam_markers.items()]
nbx = np.asarray(bx).T
nby = np.asarray(by).T
bx_coefs = np.polyfit(nbx[0], nbx[1], 3)
by_coefs = np.polyfit(nby[0], nby[1], 3)
_log.debug(".... beam marker X coeficients {}".format(bx_coefs))
_log.debug(".... beam marker Y coeficients {}".format(by_coefs))
self.beamx_fitter = np.poly1d(bx_coefs)
self.beamy_fitter = np.poly1d(by_coefs)
def _OLD_append_to_beam_markers(self, x, y, zoom):
self._beam_markers[zoom] = (x, y)
_log.info("beam markers {}".format(self._beam_markers))
settings.setValue(BEAM_MARKER_POSITIONS, self._beam_markers)
self.update_beam_marker_fitters()
def _OLD_remove_beam_markers(self):
self._beam_markers = {}
self.beamx_fitter = None
self.beamy_fitter = None
def _OLD_track_roi(self, roi):
x, y = roi.pos()
w, h = roi.size()
# area = roi.getArrayRegion(self._im, self.img)
# sum = np.sum(area)
# _log.info('{} => sum {}'.format((x,y), sum))
bx, by = x + w / 2., y + h / 2.
_log.info("beam pos {}".format((bx, by)))
_log.info("marker pos = {} ; size = {}".format((x, y), (w, h)))
def _OLD_toggle_mouse_tracking(self):
if self._mouse_tracking:
self.disengage_mouse_tracking()
else:
self.engage_mouse_tracking()
def _OLD_engage_mouse_tracking(self):
self.glw.scene().sigMouseMoved.connect(self.cb_mouse_move)
self.glw.scene().sigMouseMoved.emit()
self._mouse_tracking = True
def _OLD_disengage_mouse_tracking(self):
self.glw.scene().sigMouseMoved.disconnect(self.cb_mouse_move)
self._mouse_tracking = False
self._lb_coords.setText("")
def _OLD_get_beam_mark_on_camera_xy(self):
app=QApplication.instance()
z = app._zoom.get_val()
try:
bx = self.beamx_fitter(z)
by = self.beamy_fitter(z)
except:
bx, by = 500, 500
return (bx, by)
def _OLD_move_gonio_to_position(self, fx, fy, bx, bz, omega):
self.tweakers["fast_x"].motor.move(fx, wait=False, ignore_limits=True)
self.tweakers["fast_y"].motor.move(fy, wait=False, ignore_limits=True)
self.tweakers["base_x"].motor.move(bx, wait=False, ignore_limits=True)
self.tweakers["base_z"].motor.move(bz, wait=False, ignore_limits=True)
self.tweakers["omega"].motor.move(omega, wait=False, ignore_limits=True)
def _OLD_get_gonio_motors(self, as_json=False):
if as_json:
return {
"fast_x": self.tweakers["fast_x"].motor,
"fast_y": self.tweakers["fast_y"].motor,
"base_x": self.tweakers["base_x"].motor,
"base_z": self.tweakers["base_z"].motor,
"omega": self.tweakers["omega"].motor,
}
else:
return (
self.tweakers["fast_x"].motor,
self.tweakers["fast_y"].motor,
self.tweakers["base_x"].motor,
self.tweakers["base_z"].motor,
self.tweakers["omega"].motor,
)
def _OLD_get_gonio_tweakers(self):
return (
self.tweakers["fast_x"],
self.tweakers["fast_y"],
self.tweakers["base_x"],
self.tweakers["base_z"],
self.tweakers["omega"],
)
def _OLD_get_gonio_positions(self, as_json: bool = False):
fx, fy, cx, cz, omega = (
self.tweakers["fast_x"].motor,
self.tweakers["fast_y"].motor,
self.tweakers["base_x"].motor,
self.tweakers["base_z"].motor,
self.tweakers["omega"].motor,
)
a, b, c, d, e = (
fx.get_position(),
fy.get_position(),
cx.get_position(),
cz.get_position(),
omega.get_position(),
)
if as_json:
return {"fx": a, "fy": b, "bx": c, "bz": d, "omega": e}
else:
return (a, b, c, d, e)
def _OLD_escape_goToTellMountPosition(self):
self.move_gonio_to_mount_position()
self.lock_goniometer()
def _OLD_move_gonio_to_mount_position(self, offset: float = 0.0):
fx, fy, cx, cz, omega = self.get_gonio_motors()
bmark = "bookmark_0"
try:
t_fx = float(settings.value(bmark + "/mount_fx"))
t_fy = -offset + float(settings.value(bmark + "/mount_fy"))
t_cx = float(settings.value(bmark + "/mount_cx"))
t_cz = float(settings.value(bmark + "/mount_cz"))
t_omega = float(settings.value(bmark + "/mount_omega"))
except:
raise IncompleteConfiguration("TELL sample changer mount position is not configured!!!")
fx.move(t_fx, wait=True, ignore_limits=True)
fy.move(t_fy, wait=True, ignore_limits=True)
cx.move(t_cx, wait=True, ignore_limits=True)
cz.move(t_cz, wait=True, ignore_limits=True)
omega.move(t_omega, wait=True, ignore_limits=True)
app_utils.assert_motor_positions(
[
(fx, t_fx, 0.01),
(fy, t_fy, 0.01),
(cx, t_cx, 0.01),
(cz, t_cz, 0.01),
(omega, t_omega, 0.01),
]
)
self.cb_esc_sample_exchange()
def _OLD_lock_goniometer(self):
# tell.set_in_mount_position(True)
res = QMessageBox.question(self, "", "Mount a sample from console and click ok once the sample is mounted.", QMessageBox.Ok, QMessageBox.Ok,)
res = QMessageBox.question(self, "", "Is the sample is mounted?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No,)
if res in (QMessageBox.Yes, QMessageBox.No):
# tell.set_in_mount_position(False)
pass
@pyqtSlot(int)
def _OLD_saveBookmark(self, key: int):
"""save a bookmark for the corresponding key is the Qt.Key_* code """
fx, fy, cx, cz, omega = (
self.tweakers["fast_x"].motor,
self.tweakers["fast_y"].motor,
self.tweakers["base_x"].motor,
self.tweakers["base_z"].motor,
self.tweakers["omega"].motor,
)
bmark = "bookmark_{}".format(key)
if key == 0:
ans = QMessageBox.question(self, "Override TELL mount position", "This will overwrite the positions used for TELL MOUNTING!!!\n\n\tContinue ?",)
if ans != QMessageBox.Yes:
return
_log.info(
"saving bookmark {}: {}, {}, {}, {}, {}".format(
bmark,
fx.get_position(),
fy.get_position(),
cx.get_position(),
cz.get_position(),
omega.get_position(),
)
)
settings.setValue(bmark + "/mount_fx", fx.get_position())
settings.setValue(bmark + "/mount_fy", fy.get_position())
settings.setValue(bmark + "/mount_cx", cx.get_position())
settings.setValue(bmark + "/mount_cz", cz.get_position())
settings.setValue(bmark + "/mount_omega", omega.get_position())
def _OLD_gotoBookmark(self, key: int):
"""save a bookmark for the corresponding key"""
fx, fy, cx, cz, omega = (
self.tweakers["fast_x"].motor,
self.tweakers["fast_y"].motor,
self.tweakers["base_x"].motor,
self.tweakers["base_z"].motor,
self.tweakers["omega"].motor,
)
bmark = "bookmark_{}".format(key)
try:
t_fx = float(settings.value(bmark + "/mount_fx"))
t_fy = float(settings.value(bmark + "/mount_fy"))
t_cx = float(settings.value(bmark + "/mount_cx"))
t_cz = float(settings.value(bmark + "/mount_cz"))
t_omega = float(settings.value(bmark + "/mount_omega"))
except:
return
fx.move(t_fx, wait=True, ignore_limits=True)
fy.move(t_fy, wait=True, ignore_limits=True)
cx.move(t_cx, wait=True, ignore_limits=True)
cz.move(t_cz, wait=True, ignore_limits=True)
omega.move(t_omega, wait=True, ignore_limits=True)
def _OLD_move_cryojet_nozzle(self, pos):
cx = self.tweakers["cryo"]
if "in" == pos.lower():
key = CRYOJET_NOZZLE_IN
elif "out" == pos.lower():
key = CRYOJET_NOZZLE_OUT
to_pos = settings.value(key, 1e10, type=float)
if to_pos > 1e9:
raise IncompleteConfiguration(f"CRYOJET configuration is incomplete! Missing {key}")
cx.move_abs(to_pos, assert_position=True)
def _OLD_build_cryo_group(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
qutilities.add_item_to_toolbox(
toolbox,
"Cryojet",
widget_list=[
self.get_tweaker(f"{pfx}:MOT_CRYO", alias="cryo", label="cryo X")
],
)
def _OLD_build_wegde_group(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
qutilities.add_item_to_toolbox(toolbox,"Wedge Mover",
widget_list=[
self.get_tweaker(f"{pfx}:MOT_WEDGE1", alias="wedge_1", label="wedge_1"),
self.get_tweaker(f"{pfx}:MOT_WEDGE2", alias="wedge_2", label="wedge_2"),
self.get_tweaker(f"{pfx}:MOT_WEDGE3", alias="wedge_3", label="wedge_3"),
self.get_tweaker(f"{pfx}:MOT_WEDGE4", alias="wedge_4", label="wedge_4"),
self.get_tweaker(f"{pfx}:MOT_WEDGEX", alias="wedge_x", label="wedge_x"),
self.get_tweaker(f"{pfx}:MOT_WEDGEY", alias="wedge_y", label="wedge_y"),
self.get_tweaker(f"{pfx}:MOT_WEDGEA", alias="wedge_a", label="wedge_a"),
self.get_tweaker(f"{pfx}:MOT_WEDGEB", alias="wedge_b", label="wedge_b"),
],
)
def _OLD_build_slits_group(self, toolbox):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
qutilities.add_item_to_toolbox(
toolbox,
"Slits",
widget_list=[
self.get_tweaker(f"{pfx}10", alias="slit_right", label="left", mtype=1, ),
self.get_tweaker(f"{pfx}11", alias="slit_left", label="right", mtype=1,),
self.get_tweaker(f"{pfx}12", alias="slit_bottom", label="bottom", mtype=1,),
self.get_tweaker(f"{pfx}13",alias="slit_top",label="top",mtype=1,),
],
)
def _OLD_assert_post_tube_position(self, pos):
x_up = settings.value("post_sample_tube/x_up", 1e10, type=float)
y_up = settings.value("post_sample_tube/y_up", 1e10, type=float)
x_down = settings.value("post_sample_tube/x_down", 1e10, type=float)
y_down = settings.value("post_sample_tube/y_down", 1e10, type=float)
dx = settings.value("post_sample_tube/dx", 1e10, type=float)
dy = settings.value("post_sample_tube/dy", 1e10, type=float)
tz_in = settings.value("post_sample_tube/z_in", 1e10, type=float)
tz_out = settings.value("post_sample_tube/z_out", 1e10, type=float)
if (x_up + y_up + x_down + y_down + dx + dy + tz_in + tz_out) > 1e9:
raise Exception("miscofingured positions for post-sample tube")
usy = self.tweakers["tube_usy"]
dsy = self.tweakers["tube_dsy"]
usx = self.tweakers["tube_usx"]
dsx = self.tweakers["tube_dsx"]
tbz = self.tweakers["tube_z"]
if pos == "in":
yu = y_up
xu = x_up
yd = y_down
xd = x_down
z = tz_in
elif pos == "out":
yu = y_up + dy
xu = x_up + dx
yd = y_down + dy
xd = x_down + dx
z = tz_out
app_utils.assert_tweaker_positions([
(usy, yu, 0.1),
(dsy, yd, 0.1),
(usx, xu, 0.1),
(dsx, xd, 0.1),
(tbz, z, 0.1),],timeout=2.0,
)
def _OLD_add_tweaker(self, pv, alias=None, label=None, mtype="epics_motor", layout=None):
if layout is None:
layout = self._tweak_container.layout()
if mtype == "epics_motor":
m = MotorTweak()
else:
m = SmaractMotorTweak()
layout.addWidget(m)
m.connect_motor(pv, label)
self.tweakers[alias] = m
def _OLD_done_sliding(self):
print("done sliding at {}".format(self.slider_fast_x.value()))
def _OLD_daq_grid_add_grid(self, gx=None, gy=None):
grid_index = len(self._grids)
if gx in (False, None):
gx=self.tweakers["fast_x"].get_rbv()
gy=self.tweakers["fast_y"].get_rbv()
xstep = self._sb_grid_x_step.value()
ystep = self._sb_grid_y_step.value()
xoffset = self._sb_grid_x_offset.value()
yoffset = self._sb_grid_y_offset.value()
app=QApplication.instance()
geo=app._geometry
oc=geo._opt_ctr
if xstep==0:
go=UsrGO.Grid((120, -100), (200, 150), (30, 22), 2)
elif xstep==1:
go=UsrGO.FixTargetFrame((120, -100), (200, 150), tpl='test')
elif xstep==2:
v=geo.pos2pix((12.5, 0))
l=np.linalg.norm(v)
go=UsrGO.FixTargetFrame(-oc, (l,l), tpl='12.5x12.5')
elif xstep==3:
v=geo.pos2pix((23, 0))
l=np.linalg.norm(v)
go=UsrGO.FixTargetFrame(-oc, (l,l), tpl='23.0x23.0')
else:
_log.error('set xstep 0..2 for tests')
self.vb.addItem(go)
self._goTracked['objLst'].append(go)
#grid = CstROI.Grid( x_step=xstep, y_step=ystep, x_offset=xoffset, y_offset=yoffset, gonio_xy=(gx, gy), grid_index=grid_index, parent=self,)
#self.vb.addItem(grid)
#grid.calculate_gonio_xy()
#grid.sigRemoveRequested.connect(lambda g=grid: self.remove_grid(g))
def _OLD_daq_grid_remove_all(self):
vb=self.vb
for go in self._goTracked['objLst']:
vb.removeItem(go)
self._goTracked['objLst']=[]
def _OLD_grids_pause_stage_tracking(self):
for grid in self._grids:
grid.disable_stage_tracking()
def _OLD_grids_start_stage_tracking(self):
for grid in self._grids:
grid.enable_stage_tracking()
def _OLD_remove_grid(self, grid):
self.vb.removeItem(grid)
self._grids.remove(grid)
def _OLD_daq_grid_update(self, grid_index):
try:
grid = self._grids[grid_index]
except:
print("grid index not yet there")
return
points = grid.get_grid_targets()
num_points = len(points)
etime = float(settings.value("exposureTime"))
doc = f"grid_{grid_index} = ["
for n, pos in enumerate(points):
x, y = pos
doc += "[{:8.3f}, {:8.3f}],\n".format(x, y)
doc += "]"
self._grid_inspect_area.setPlainText(doc)
m = "Number of points: {}\nEstimated Time: {:.1f} minutes".format(num_points, num_points * etime / 60.)
self._label_grid_parameters.setText(m)
def _OLD_daq_embl_collect_points(self):
coords = self._embl_module.coords
points = [[x, y] for x, y, bx, bz, o in coords]
points = np.array(points)
method = "trajectory"
xp = (1000 * points).astype(int) # microns then round int
params = (xp[:, 0].tolist(), xp[:, 1].tolist())
self.daq_collect_points(points, visualizer_method=method, visualizer_params=params)
def _OLD_daq_prelocated_collect_points(self):
points = []
data = self.prelocationModule.get_collection_targets()
for n, cc in enumerate(data):
is_fiducial, gx, gy, cx, cy, ox, oy = cc
points.append([gx, gy])
points = np.array(points)
method = "trajectory"
xp = (1000 * points).astype(int) # microns then round int
params = (xp[:, 0].tolist(), xp[:, 1].tolist())
self.daq_collect_points(points, visualizer_method=method, visualizer_params=params)
def _OLD_daq_grid_findxtals(self):
feature_size = self._sb_findxtals_feature_size.value()
image = sample_camera.get_image()
findObj(-image, objSize=feature_size, viz=1)
def _OLD_check_jungfrau_save(self) -> bool:
if jungfrau_detector.is_running_detector():
saveRaw = jungfrau_detector.is_saving_data()
if not saveRaw:
box = QMessageBox()
box.setText("Jungfrau save data disabled!")
box.setInformativeText("Jungfrau save data is disabled!")
box.setIcon(QMessageBox.Warning)
box.setDetailedText("Choose to abort, enable and continue, or continue without saving raw data")
btContinue = box.addButton("Continue", QMessageBox.YesRole)
btAbort = box.addButton("OMG! Abort", QMessageBox.NoRole)
btEnable = box.addButton("Enable save and continue", QMessageBox.YesRole)
box.exec_()
ans = box.clickedButton()
if ans == btEnable:
jungfrau_detector.set_save_raw(True)
return True
elif ans == btAbort:
_log.info("not doing helical scan")
return False
return True
return True
def _OLD_daq_collect_points(self, points, visualizer_method, visualizer_params):
app = QApplication.instance()
cfg = app._cfg
verbose=0xff
fn='/tmp/shapepath'
try:
dt=app._deltatau
except AttributeError:
app._deltatau=dt=deltatau.Deltatau()
try:
jf=app._jungfrau
except AttributeError:
app._jungfrau=jf=detector.Jungfrau()
sp=dt._shapepath
sp.gen_grid_points(w=15, h=15, pitch=3, rnd=0, ofs=(0, +2000))
#sp.gen_grid_points(w=5, h=10, pitch=1, rnd=0, ofs=(0, 0));sp.sort_points(False, 10);sp.points
sp.sort_points(False, 15);
sp.meta['pt2pt_time']=10
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
sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1., dwell=10)
sp.homing() # homing if needed
sp.run() # start motion program
sp.wait_armed() # wait until motors are at first position
sp.trigger(0.5) # send a start trigger (if needed) ater given time
if not dt._comm is None:
while True:
p=sp.progress()
if p<0: break
print('progress %d/%d'%(p, sp.points.shape[0]));
time.sleep(.1)
sp.gather_upload(fnRec=fn+'.npz')
#dp=deltatau.shapepath.DebugPlot(sp)
#dp.plot_gather(mode=11)
#plt.show(block=False)
#plt.show(block=True)
return
task = self.active_task()
XDIR = -1
#folders.make_if_needed()
#if ( cfg.option(AppCfg.ACTIVATE_PULSE_PICKER) and not jungfrau_detector.is_running_detector()):
# if QMessageBox.No == QMessageBox.question(self, "X-rays but no Jungfrau",
# "X-rays will be used bu the Jungfrau will not run.\n\n\tContinue?",):
# _log.warning("user forgot to turn on the jungfrau")
# return
#if option(ACTIVATE_PULSE_PICKER) or not option(SKIP_ESCAPE_TRANSITIONS_IF_SAFE):
# self.escape_goToDataCollection()
points *= 1000 # delta tau uses micrometers
points[:, 0] *= XDIR # fast X axis is reversed
# sync_mode : default=2
# 0 : no sync at all
# 1 : synchronize start
# 2 : synchronize start and adapt motion speed
# this function generates the code blocks:
# self.sync_wait and self.sync_run
# sync_wait can be put in the program to force a timing sync
# sync_run are the commands to run the whole program
# sync_flag if not using jungfrau =1 otherwise =0
# D.O. shapepath.meta.update(sync_mode=2, sync_flag=1)
sp.meta.update(sync_mode=0, sync_flag=0)
maxacq_points = 116508
samp_time = 0.0002 # s
overhead_time = 0.1
etime=10
vscale=1.0
#etime = settings.value("exposureTime", type=float)
#vscale = settings.value(DELTATAU_VELOCITY_SCALE, 1.0, type=float)
#sort_points = option(DELTATAU_SORT_POINTS)
acq_per = int(np.ceil((etime * len(points) + overhead_time) / (maxacq_points * samp_time)))
_log.info(f"gather acquisotion period = {acq_per}")
_log.info(f"velocity scale {vscale}")
sp.setup_gather(acq_per=acq_per)
sp.setup_sync(verbose=True)
sp.setup_coord_trf()
assert(points.dtcfgype==np.float)
sp.points = points
if TASK_GRID == task:
# width, height = visualizer_params
# _log.debug(f"grid: {width} x {height}")
# details_1 = [width]
# details_2 = [height]
# sp.sort_points(xy=False, grp_sz=height)
pass
elif task in (TASK_PRELOCATED, TASK_EMBL):
if sort_points:
shapepath.sort_points()
self.daq_method_prelocated_remove_markers()
details_1, details_2 = visualizer_params
sp.setup_motion(
mode=3, # 1 = bad pvt 3 = pft (pvt via inverse fourier transform)
pt2pt_time=etime * 1000.,
#fnPrg=folders.get_prefixed_file("_program.prg"),
scale=vscale, # velocity at target position scaling: 1=optimal speed, 0=zero speed
dwell=10, # milli-seconds wait
)
sp.run()
self.qprogress = QProgressDialog(self)
self.qprogress.setRange(0, 0)
self.qprogress.setLabelText("Acquiring GRID")
self.qprogress.setCancelButtonText("Abort Acquisition")
self.qprogress.canceled.connect(self.complete_daq)
self.qprogress.setWindowModality(Qt.WindowModal)
self.qprogress.setAutoClose(True)
self.qprogress.show()
sequencer_steps = [lambda: self.grids_pause_stage_tracking()]
if jungfrau_detector.is_running_detector():
if not self.check_jungfrau_save():
# user aborted run from save data dialog
return
n_frames = ntrigger
uid = settings.value(EXPERIMENT_UID, type=int)
backend_extras = self.jungfrau.get_parameters()
backend_extras.update(
{
"swissmx_trajectory_method": visualizer_method,
"swissmx_trajectory_details_1": details_1,
"swissmx_trajectory_details_2": details_2,
}
)
jungfrau_detector.set_number_of_frames(n_frames)
jungfrau_detector.set_data_owner_uid(uid)
sequencer_steps.extend(
[
lambda: jungfrau_detector.configure(
n_frames=n_frames,
outfile=folders.prefix,
outdir=folders.raw_folder,
uid=uid,
backend_extras=backend_extras,
),
lambda: jungfrau_detector.arm(),
]
)
sequencer_steps.append(lambda: shapepath.wait_armed())
if option(ACTIVATE_PULSE_PICKER):
sequencer_steps.append(lambda: pulsePicker.open())
# if settings.value("scanning/trigger_microscope_camera", type=bool):
# sample_camera.switch_to_trigger(True)
sequencer_steps.append(lambda: shapepath.trigger(wait=0.5))
def _OLD_shapepath_progress():
while True:
p = shapepath.progress()
if p < 0:
break
time.sleep(0.1)
self.qprogress.setLabelText(f"Acquiring GRID {p:.0f} / {ntrigger}")
_log.warning(f"motion complete!")
# sample_camera.stop_camera()
# sample_camera.switch_to_trigger(False)
# sample_camera.save_buffer_series(folders.prefix)
sequencer_steps.append(shapepath_progress)
if option(ACTIVATE_PULSE_PICKER):
sequencer_steps.append(lambda: pulsePicker.close())
sequencer_steps.append(lambda: jungfrau_detector.wait_finished())
sequencer_steps.append(lambda: self.grids_start_stage_tracking())
self.sequencer = Sequencer(steps=sequencer_steps)
self._thread = QThread()
self._thread.setObjectName("acquisition_thread")
self.sequencer.moveToThread(self._thread)
self.sequencer.finished.connect(self.daq_collect_points_wrapup)
self._thread.started.connect(self.sequencer.run_sequence)
self._thread.start()
def _OLD_run_steps(self, steps, title, at_end=None, cancelable=False):
dlg = QProgressDialog(self)
dlg.setWindowTitle(title)
dlg.setWindowModality(Qt.WindowModal)
dlg.setMinimumDuration(0)
if not cancelable:
dlg.setCancelButton(None)
dlg.setRange(0, 0)
dlg.setLabelText(f"<b>{title}</b><br/>")
dlg.setAutoClose(True)
dlg.show()
dlg.setValue(random.randint(1, 20))
class Runner(QObject):
finito = pyqtSignal()
timeoutExpired = pyqtSignal()
errorHappened = pyqtSignal(str)
result = pyqtSignal(str)
def __init__(self, step_to_run):
super().__init__()
self.step = step_to_run
self.exception = None
self.done = False
def run(self):
try:
self.step()
except Exception as e:
_log.debug(" +> step exception")
self.exception = str(e)
self.errorHappened.emit(str(e))
self.finito.emit()
for n, step in enumerate(steps):
_log.info(f"running step {step.title}")
dlg.setLabelText(f"<b>{title}</b><br/><font color=\"blue\">{step.title}</font>")
dlg.setValue(n)
thread = QThread()
runner = Runner(step)
runner.moveToThread(thread)
thread.started.connect(runner.run)
runner.finito.connect(thread.quit)
thread.start()
while thread.isRunning():
dlg.setValue(random.randint(1, 20))
time.sleep(0.01)
if dlg.wasCanceled():
# FIXME ensure we abort the running thread
break
if dlg.wasCanceled():
break
if runner.exception is not None:
QMessageBox.critical(self, step.title, str(runner.exception))
break
if dlg.wasCanceled():
_log.error(f"sequence {title} was cancelled by user")
raise AcquisitionAbortedException(f"sequence {title} was cancelled by user")
if at_end is not None:
at_end()
dlg.reset()
def _OLD_daq_collect_points_wrapup(self):
self.qprogress.reset()
if self._thread.isRunning():
self._thread.quit()
shapepath.gather_upload(os.path.join(folders.res_folder, folders.prefix + ".npz"))
if option(DELTATAU_SHOW_PLOTS):
dp = DebugPlot(shapepath)
dp.plot_gather(mode=1)
pyplot.show()
if TASK_PRELOCATED == self.active_task():
self.daq_method_prelocated_update_markers()
if option(ACTIVATE_PULSE_PICKER) or not option(SKIP_ESCAPE_TRANSITIONS_IF_SAFE):
self.cb_esc_sample_alignment()
sequence = {"delta tau program": shapepath.prg, "points": shapepath.points.tolist(), "timestamp": tdstamp(),}
sfname = folders.get_prefixed_file("_ScanInfo.json")
try:
with open(sfname, "w") as sf:
_log.info("writing scan info into: {}".format(sfname))
sf.write(json.dumps(sequence))
except:
_log.warning(f"failed to write scan info to {sfname}")
self.re_connect_collect_button()
jungfrau_detector.abort()
self.increaseRunNumberRequest.emit()
def _OLD_daq_collect_update_inspect(self, msg):
te = self._inspect
m = te.toPlainText()
te.setPlainText(m + msg + "\n")
te.verticalScrollBar().setValue(te.verticalScrollBar().maximum())
def _OLD_daq_helical_collect(self):
"""[
[{
0: (0.0, 1.238401694734829, 3.527, 0.936, 0.001),
120: (0.0, 1.3890393596274455, -1.957242824091597, -0.5639999999999998, 120.001),
240: (0.0, 1.3866130349657206, -1.1642619618562273, 3.105, 240.001)
},
{
0: (0.0, 1.238401694734829, 3.527, 0.936, 0.001),
120: (0.0, 1.3890393596274455, -1.957242824091597, -0.5639999999999998, 120.001),
240: (0.0, 1.3866130349657206, -1.1642619618562273, 3.105, 240.001)
}]
]
"""
_log.info("executing collection")
htab = self._helical_scan_table
num_h = htab.scanHorizontalCount()
num_v = htab.scanVerticalCount()
if ( settings.value(ACTIVATE_PULSE_PICKER) and not jungfrau_detector.is_running_detector()):
if QMessageBox.No == QMessageBox.question(self, "X-rays but no Jungfrau",
"X-rays will be used bu the Jungfrau will not run.\n\n\tContinue?",):
_log.warning("user forgot to turn on the jungfrau")
return
if option(ACTIVATE_PULSE_PICKER) or not option(SKIP_ESCAPE_TRANSITIONS_IF_SAFE):
self.cb_esc_data_collection()
folders.make_if_needed()
data = htab.get_data()
_log.debug(data)
start, end = data[0]
FX, FY, BX, BZ, O = range(5)
x = (
(-1000 * start[0][BX], -1000 * start[120][BX], -1000 * start[240][BX]),
(-1000 * end[0][BX], -1000 * end[120][BX], -1000 * end[240][BX]),
)
y = (1000 * start[0][FY], 1000 * end[0][FY])
z = (
(-1000 * start[0][BZ], -1000 * start[120][BZ], -1000 * start[240][BZ]),
(-1000 * end[0][BZ], -1000 * end[120][BZ], -1000 * end[240][BZ]),
)
if jungfrau_detector.is_running_detector():
if not self.check_jungfrau_save():
return
saveRaw = jungfrau_detector.is_saving_data()
_log.debug(f"x = {x}")
_log.debug(f"y = {y}")
_log.debug(f"z = {z}")
oscillationAngle = settings.value("oscillationAngle", type=float)
exposureTime = 1000 * settings.value("exposureTime", type=float)
blastRadius = settings.value("blastRadius", type=float)
totalRange = num_v * num_h * oscillationAngle
# sync_mode : default=2
# 0 : no sync at all
# 1 : synchronize start
# 2 : synchronize start and adapt motion speed
# this function generates the code blocks:
# self.sync_wait and self.sync_run
# sync_wait can be put in the program to force a timing sync
# sync_run are the commands to run the whole program
# sync_flag if not using jungfrau =1 otherwise =0
# D.O. helical.meta.update(sync_mode=2, sync_flag=1)
helical.meta.update(sync_mode=0, sync_flag=0)
helical.calcParam(x=x, y=y, z=z)
helical.setup_gather()
helical.setup_sync()
helical.setup_coord_trf()
mode = 1
hRng = (-blastRadius * num_h, blastRadius * num_h)
w_start = 1000 * htab.startAngle()
wRng = (w_start, w_start + (totalRange * 1000))
_log.info(
f"helical params mode={mode}, cnthor={num_h}, cntvert={num_v}, hrng={hRng}, wrng={wRng}"
)
helical.setup_motion(
mode=mode,
cntHor=num_h,
cntVert=num_v,
hRng=hRng,
wRng=wRng,
pt2pt_time=exposureTime,
) # hRng in micrometers
helical.run()
try:
with open(folders.get_prefixed_file("_helical_debug.cmd"), "w") as fd:
fd.write("calcParam(x={}, y={}, z={})".format(x, y, z))
except:
pass
self.qprogress = QProgressDialog(self)
self.qprogress.setRange(0, 0)
self.qprogress.setLabelText("Acquiring HELICAL")
self.qprogress.setCancelButtonText("Abort Acquisition")
self.qprogress.canceled.connect(self.complete_daq)
self.qprogress.setWindowModality(Qt.WindowModal)
self.qprogress.setAutoClose(True)
self.qprogress.show()
sequencer_steps = [lambda: self.grids_pause_stage_tracking()]
n_frames = num_h * num_v
if jungfrau_detector.is_running_detector():
uid = settings.value(EXPERIMENT_UID, type=int)
backend_extras = self.jungfrau.get_parameters()
backend_extras.update(
{
"swissmx_trajectory_method": "grid",
"swissmx_trajectory_details_1": [-num_h],
"swissmx_trajectory_details_2": [num_v],
}
)
jungfrau_detector.set_number_of_frames(n_frames)
jungfrau_detector.set_data_owner_uid(uid)
sequencer_steps.extend(
[
lambda: jungfrau_detector.configure(
n_frames=n_frames,
outfile=folders.prefix,
outdir=folders.raw_folder,
uid=uid,
backend_extras=backend_extras,
),
lambda: jungfrau_detector.arm(),
]
)
sequencer_steps.append(lambda: helical.wait_armed())
if settings.value(ACTIVATE_PULSE_PICKER):
sequencer_steps.extend([lambda: pulsePicker.open(), lambda: pend_event(0.1)])
sequencer_steps.append(lambda: helical.trigger())
def _OLD_motion_progress():
while True:
p = helical.progress()
if p < 0:
break
time.sleep(0.1)
self.qprogress.setLabelText(f"Acquiring HELICAL {p:.0f} / {n_frames}")
_log.warning(f"helical motion complete!")
sequencer_steps.append(motion_progress)
if settings.value(ACTIVATE_PULSE_PICKER):
sequencer_steps.append(lambda: pulsePicker.close())
sequencer_steps.append(lambda: self.grids_start_stage_tracking())
self.sequencer = Sequencer(steps=sequencer_steps)
self._thread = QThread()
self._thread.setObjectName("acquisition_thread")
self.sequencer.moveToThread(self._thread)
self.sequencer.finished.connect(self.daq_helical_collect_wrapup)
self._thread.started.connect(self.sequencer.run_sequence)
self._thread.start()
def _OLD_daq_helical_collect_wrapup(self):
try:
self.qprogress.reset()
except:
pass
if self._thread.isRunning():
self._thread.quit()
helical.gather_upload(os.path.join(folders.res_folder, folders.prefix + ".npz"))
self.re_connect_collect_button()
jungfrau_detector.abort()
if option(ACTIVATE_PULSE_PICKER) or not option(SKIP_ESCAPE_TRANSITIONS_IF_SAFE):
self.cb_esc_sample_alignment()
self.increaseRunNumberRequest.emit()
if option(DELTATAU_SHOW_PLOTS):
hsgui = HelicalScanGui(helical)
hsgui.interactive_anim()
def _OLD_complete_daq(self):
_log.info("daq completed: cleaning up")
try:
self.qprogress.reset()
except:
pass
try:
if self._thread.isRunning():
self._thread.quit()
except:
pass
finally:
self.abort_measurement()
self.re_connect_collect_button()
def _OLD_re_connect_collect_button(self, callback=None, **kwargs):
_log.debug("re_connect_collect_button() {} => {}".format(callback, kwargs))
return
# button = self._button_collect
# if callback is None:
# callback = self.execute_collection
# button.setAccessibleName("collect_button")
# kwargs["label"] = "Collect"
#
# try:
# button.disconnect()
# finally:
# button.clicked.connect(callback)
#
# if "accessibleName" in kwargs:
# button.setAccessibleName(kwargs["accessibleName"])
#
# if "label" in kwargs:
# button.setText(kwargs["label"])
# self.load_stylesheet()
def _OLD_collect_abort_grid(self):
self._is_aborted = True
try:
self._eigerwaitthread._is_aborted = True
except:
pass
_log.warning("aborting grid scan")
self.abort_measurement()
delta_tau.abort()
jungfrau_detector.disarm()
self.re_connect_collect_button()
def _OLD_create_escape_toolbar(self):
cont = self._wd_right
w = QGroupBox("Escape")
layout = QHBoxLayout()
w.setLayout(layout)
but = QPushButton("Exchange\nSample")
but.setAccessibleName("escape_button_se")
but.setObjectName("action_SampleExchange")
but.clicked.connect(self.cb_esc_sample_exchange)
layout.addWidget(but)
but = QPushButton("Alignment")
but.setAccessibleName("escape_button_sa")
but.clicked.connect(self.cb_esc_sample_alignment)
layout.addWidget(but)
but = QPushButton("Collection")
but.setAccessibleName("escape_button_dc")
but.clicked.connect(self.cb_esc_data_collection)
layout.addWidget(but)
cont.layout().addWidget(w)
def _OLD_daq_method_prelocated_remove_markers(self):
try:
for m in self._marker_rois:
m.disconnect_signals()
self.vb.removeItem(m)
except Exception as e:
_log.warning("maybe failed removing markers: {}".format(e))
self._marker_rois = []
def _OLD_pause_all_markers(self):
for m in self._marker_rois:
m.disconnect_signals()
def _OLD_resume_all_markers(self):
for m in self._marker_rois:
m.reconnect_signals()
def _OLD_daq_method_prelocated_update_markers(self):
self.daq_method_prelocated_remove_markers()
data = self.prelocationModule.get_data()
add_xtals = self.prelocationModule._xtals_transformed
draw_xtals = self.prelocationModule.set_draw_crystal_marks
vb = self.vb
self._marker_rois = []
ppm = self.getPpm()
for n, cc in enumerate(data):
is_fiducial, gx, gy, cx, cy, ox, oy = cc
if not is_fiducial:
if not (add_xtals and draw_xtals):
continue
_log.debug(f"adding {'fiducial' if is_fiducial else 'xtal'} mark #{n}: {is_fiducial} {gx:.3f}, {gy:.3f}, {cx:.1f}, {cy:.1f}")
m = CstROI.CrystalCircle(
gonio_xy=(gx, gy),
parent=self,
model_row_index=n,
is_fiducial=is_fiducial,
ppm=ppm,
)
# m.sigRegionChangeFinished.connect(lambda roi=m: self.daq_method_prelocated_update_model(roi))
self._marker_rois.append(m)
vb.addItem(m)
for c in self._marker_rois:
c.reconnect_signals()
c.follow_stage()
def _OLD_daq_method_prelocated_set_fiducial(self, camx, camy, gx, gy):
_log.debug(f"camx, camy: {camx}, {camy}, fx, fy: {gx, gy}")
self.prelocationModule.set_fiducial_coords(camx, camy, gx, gy)
def _OLD_daq_method_prelocated_append_data(self, x, y, gx, gy):
_log.debug("appending to model: {} {}".format((x, y), (gx, gy)))
self.prelocationModule.append_data((x, y, gx, gy))
self.daq_method_prelocated_update_markers()
def _OLD_daq_method_prelocated_update_model(self, roi):
row = roi.get_model_row()
pos = roi.pos()
self.prelocationModule.set_data_camera(row, pos)
_log.debug("updating row {} => {}".format(row, pos))
def _OLD_daq_method_prelocated_add_reference(self):
self._references.append(pg.CircleROI())
def _OLD_move_fast_stage(self, x, y):
_log.info(f"received request to move gonio to x, y = {x:.3f}, {y:.3f} mm")
fx_motor, fy_motor, bx_motor, bz_motor, omega_motor = self.get_gonio_tweakers()
fx_motor.move_abs(x)
fy_motor.move_abs(y)
def _OLD_toggle_maximized(self):
if self.isMaximized():
self.showNormal()
else:
self.showMaximized()
def _OLD_show_window_configuration(self):
_log.debug("maximized? {}".format(self.isMaximized()))
def _OLD_update_user_and_storage(self):
global folders
pgroup = settings.value(EXPERIMENT_PGROUP, "not_set_yet")
diag = GenericDialog( title="P-group for experiment", message="Enter the p-group to be used",
inputs={EXPERIMENT_PGROUP: ("P-group", pgroup, QLineEdit(pgroup))}, use_buttons=False,)
diag.setModal(True)
run_user = getpass.getuser()
pgrp_re = re.compile(r"^p(?P<uid>\d{5})$")
if not self.is_recently_active():
diag.exec()
results = diag.results
for k, v in results.items():
m = pgrp_re.match(v)
if m:
settings.setValue(k, v)
settings.setValue(EXPERIMENT_UID, int(m["uid"]))
else:
QMessageBox.critical(self, "wrong P-group format", "P-groups are in the format:\n\n\t\tp?????\n\n\twhere ? = digit",)
self.update_user_and_storage()
return
settings.sync()
folders.set_pgroup(settings.value(EXPERIMENT_PGROUP))
try:
folders.check_permissons()
except:
folder = folders.pgroup_folder
pgroup = settings.value(EXPERIMENT_PGROUP)
box = QMessageBox()
box.setText("No Write Permission!")
box.setInformativeText("User {} has no write access to p-group {} folder:\n{}\n".format(run_user, pgroup, folder))
box.setIcon(QMessageBox.Warning)
box.setDetailedText("The folder /sf/bernina/data/{pgroup}/res/ has to writable by the user {user}, currently running the SwissMX app.".format(pgroup=pgroup, user=run_user))
btIgnore = box.addButton("Ignore", QMessageBox.NoRole)
btRetry = box.addButton("Le'me try again", QMessageBox.YesRole)
box.exec_()
ans = box.clickedButton()
if ans == btRetry:
self.update_user_and_storage()
elif ans == btIgnore:
_log.warning("no write access to pgroup but user didn't care")
self._label_pgroup.setText(settings.value(EXPERIMENT_PGROUP))
def _OLD_is_recently_active(self):
last_active = settings.value("last_active", 0, type=float)
minutes_since_last = int((time.time() - last_active) / 60.0)
return minutes_since_last < 60
def _OLD_openPreferencesDialog(self):
PreferencesDialog(self).exec_()
def _OLD_set_app_icon(self):
scriptDir = os.path.dirname(os.path.realpath(__file__))
self.setWindowIcon(QtGui.QIcon(os.path.join(scriptDir + os.path.sep + "logo.png")))
def _OLD_set_widget_background_color(self, color):
"""change a widget's color
:param color:
:return:
"""
try:
color = QtGui.QColor.colorNames().index(color)
except:
return
p = self.palette()
p.setColor(self.backgroundRole(), color)
self.setPalette(p)
if __name__=="__main__":
def main():
#_log.info(f'Version: pyqtgraph:{pg.__version__} matplotlib:{mpl.__version__} numpy:{np.__version__} epics:{epics.__version__} qt:{QT_VERSION_STR}' )
_log.info(f'Version: pyqtgraph:{pg.__version__} epics:{epics.__version__} qt:{QT_VERSION_STR}' )
import argparse, socket
hostname=socket.gethostname()
if hostname=='ganymede':
#use EPICS locally
#os.environ['EPICS_CA_ADDR_LIST']='localhost'
#use EPICS if connected to ESC network
os.environ['EPICS_CA_ADDR_LIST'] ='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066'
#(h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>20 else sys.argv[0])+' '
#exampleCmd=('', '-m0xf -v0')
epilog=__doc__ #+'\nExamples:'+''.join(map(lambda s:cmd+s, exampleCmd))+'\n'
parser=argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--sim", "-s", type=lambda x: int(x,0), help="simulate devices (see bitmasks) default=0x%(default)x", default=0)
args = parser.parse_args()
_log.info('Arguments:{}'.format(args.__dict__))
from PyQt5.QtWidgets import QApplication
# set app icon
app = QApplication(sys.argv)
app_icon = QtGui.QIcon()
app_icon.addFile("artwork/logo/16x16.png", QtCore.QSize(16, 16))
app_icon.addFile("artwork/logo/24x24.png", QtCore.QSize(24, 24))
app_icon.addFile("artwork/logo/32x32.png", QtCore.QSize(32, 32))
app_icon.addFile("artwork/logo/48x48.png", QtCore.QSize(48, 48))
app_icon.addFile("artwork/logo/256x256.png", QtCore.QSize(256, 256))
app.setWindowIcon(app_icon)
startupWin=StartupSplash()
#for i in range(1, 20):
# startupWin.set(i,f'sample startup text {i}')
# #app.processEvents()
# t = time.time()
# #while time.time() < t + 0.1:
# # app.processEvents()
#app._settings=QSettings("PSI", "SwissMX")
app._args=args
startupWin.set(5, f'load settings')
app._cfg=cfg=AppCfg()
app._geometry=geometry.geometry()
app._geometry._lut_pix2pos=cfg.value(cfg.GEO_PIX2POS)
app._geometry._opt_ctr=cfg.value(cfg.GEO_OPT_CTR)
startupWin.set(15, f'connect backlight')
pfx=app._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
dft_pos_bklgt=app._cfg.value(AppCfg.DFT_POS_BKLGT)
if args.sim&0x01:
app._backlight = backlight.Backlight(None,dft_pos_bklgt)
else:
app._backlight = backlight.Backlight(f'{pfx}:MOT_BLGT',dft_pos_bklgt)
startupWin.set(20, f'connect illumination')
if args.sim&0x02:
app._illumination = illumination.IlluminationControl(None)
else:
app._illumination = illumination.IlluminationControl()
startupWin.set(40, f'connect zoom')
if args.sim&0x04:
app._zoom = zoom.QopticZoom(None)
else:
app._zoom = zoom.QopticZoom()
startupWin.set(60, f'connect camera')
if args.sim&0x08:
app._camera = camera.epics_cam(None)
app._camera.sim_gen(mode=1)
else:
app._camera = camera.epics_cam()
#app._camera.run() is called in center_piece_update
startupWin.set(60, f'start main window')
app._mainWnd=wnd=WndSwissMx()
wnd.show()
startupWin._wnd.finish(wnd)
# needed so pycharm can restart application
signal.signal(signal.SIGINT, sigint_handler)
#main.update_user_and_storage() #ZAC: orig. code
sys.exit(app.exec_())
main()