This commit is contained in:
2022-09-09 15:54:18 +02:00
parent f70421d2f3
commit 189c98951a
7 changed files with 206 additions and 1548 deletions

View File

@@ -1,595 +0,0 @@
import logging
from PyQt5 import QtGui, QtCore
from PyQt5.QtGui import QColor, QTransform
from PyQt5.QtWidgets import QMenu, QAction, QSpinBox, QMenu
#from GenericDialog import GenericDialog #ZAC: orig. code
from pyqtgraph import ROI, Point, RectROI, mkPen
from pyqtgraph import functions as fn
from app_config import AppCfg# settings
from math import *
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
class Grid(ROI):
"""a grid"""
def __init__(
self,
x_step,
y_step,
x_offset,
y_offset,
grid_index=0,
parent=None,
gonio_xy=(0, 0),
**kargs
):
self._grid_index = grid_index
self._size = None
self._pos = None
self._shape = None
ROI.__init__(self, (500, 500), (300, 300), **kargs)
self.parent = parent
self.addScaleHandle([1, 1], [0, 0])
self.addScaleHandle([0, 0], [1, 1])
self.addScaleRotateHandle([1, 0], [0, 0])
# self.addScaleRotateHandle([1, 0.5], [0.5, 0.5])
self._steps = x_step / 1000., y_step / 1000.
self._offsets = x_offset / 1000, y_offset / 1000
self._beam_position = parent.get_beam_mark_on_camera_xy()
self.set_gonio_xy(*gonio_xy)
self.setToolTip("grid")
parent.pixelsPerMillimeter.connect(self.update_ppm)
parent.beamCameraCoordinatesChanged.connect(self.update_beam_position)
self.enable_stage_tracking()
self._ppm = parent.getPpm()
self._mm_size = 300 / self._ppm, 300 / self._ppm
self._original_ppm = self._ppm # marker only works at original zoom
# because zoom axis is not aligned
# with viewing axis
self._fx, self._fy = self.parent.getFastX(), self.parent.getFastY()
self.invertible = True
self.sigRegionChanged.connect(self.invalidate)
self.sigRegionChangeFinished.connect(self.calculate_gonio_xy)
self.scaleSnap = False
self.aspectLocked = False
self.translatable = True
self.rotateAllowed = False
self.removable = True
self.follow_stage()
# self.calculate_gonio_xy()
def enable_stage_tracking(self):
self.parent.fast_x_position.connect(self.follow_x)
self.parent.fast_y_position.connect(self.follow_y)
def disable_stage_tracking(self):
self.parent.fast_x_position.disconnect(self.follow_x)
self.parent.fast_y_position.disconnect(self.follow_y)
def follow_x(self, v):
self.follow_stage(fx=v)
def follow_y(self, v):
self.follow_stage(fy=v)
def removeClicked(self):
self.parent.pixelsPerMillimeter.disconnect(self.update_ppm)
self.parent.beamCameraCoordinatesChanged.disconnect(self.update_beam_position)
self.parent.fast_x_position.disconnect(self.follow_x)
self.parent.fast_y_position.disconnect(self.follow_y)
super(Grid, self).removeClicked()
def calculate_gonio_xy(self):
self.update_grid_pos()
state = self.getState()
angle = state["angle"]
w, h = state["size"]
w, h = 1000 * w / self._ppm, 1000 * h / self._ppm
self._mm_size = w / 1000, h / 1000
self.calculate_targets()
def update_ppm(self, ppm):
self._ppm = ppm
self.update_grid_size(ppm)
self.update_grid_pos()
self.stateChanged()
def update_grid_size(self, ppm, finish=False):
mmw, mmh = self._mm_size
w, h = mmw * ppm, mmh * ppm
self.setSize((w, h), update=False)
def follow_stage(self, fx=None, fy=None):
self._fx, self._fy = self.parent.getFastX(), self.parent.getFastY()
self.update_grid_pos()
self.stateChanged()
def set_gonio_xy(self, x, y):
self._gx, self._gy = x, y
def update_grid_pos(self):
idx = self._grid_index
ppm = self._ppm
bx, by = self.parent.get_beam_mark_on_camera_xy()
gx, gy = self._gx, self._gy # marker position in gonio coords
fx, fy = self._fx, self._fy
dx, dy = (
ppm * (fx - gx),
ppm * (fy - gy),
) # distance to beam in pixels for new ppm
x, y = bx - dx, by + dy
self.setPos(x, y, update=False)
mmw, mmh = self._mm_size
def calculate_targets(self):
state = self.getState()
angle = radians(state["angle"])
cosa = cos(angle)
sina = sin(angle)
w, h = state["size"]
xs, ys = self._steps
w, h = w / self._ppm, h / self._ppm # mm
gx, gy = self._gx, self._gy
nx = int(abs(w) / xs)
ny = int(abs(h) / ys)
points = []
dx = xs
dy = ys
for yi in range(ny):
ly = yi * dy
y = gy + ly
for xi in range(nx):
lx = xi * dx
x = gx + lx
l = sqrt((lx) ** 2 + (ly) ** 2)
try:
alpha = acos((lx) / l)
except:
alpha = 0
x2 = gx + l * cos(alpha + angle)
y2 = gy - l * sin(alpha + angle)
# print(
# "l={:8.3f} alpha={:.3f} xy={:8.3f}{:8.3f} xy2={:8.3f}{:8.3f}".format(
# l, degrees(alpha), x, y, x2, y2
# )
# )
points.append((x2, y2))
self._grid_dimensions = (nx, ny)
self._points = points
self.parent.gridUpdated.emit(self._grid_index)
def get_sorted_grid_targets(self):
pass
def get_grid_targets(self):
return self._points
def update_beam_position(self, pos):
self._beam_position = pos
def invalidate(self):
state = self.getState()
x, y = state["pos"]
w, h = state["size"]
self._mm_size = w / self._ppm, h / self._ppm
fx, fy = self.camera2gonio(x, y)
self.set_gonio_xy(fx, fy)
self._shape = None
self.prepareGeometryChange()
def boundingRect(self):
return self.shape().boundingRect()
def shape(self):
w, h = self.getState()["size"]
if self._shape is None:
radius = self.getState()["size"][1]
p = QtGui.QPainterPath()
p.addRect(0, 0, w, h)
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(10)
outline = stroker.createStroke(p)
self._shape = outline
return self._shape
def paint(self, p, *args):
state = self.getState()
w, h = state["size"]
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(fn.mkPen(width=5, color=[200, 200, 100]))
p.drawRect(0, 0, w, h)
p.setPen(fn.mkPen(width=3, color=[200, 100, 100]))
ppm = self._ppm
xs, ys = self._steps
w1, h1 = w / ppm, h / ppm # mm
nx = int(abs(w1) / xs)
ny = int(abs(h1) / ys)
sw = (xs) * ppm
sh = (ys) * ppm
# print((nx,ny), (w1, h1), (sw, sh))
r = 10
a, b = state["pos"]
if nx * ny > 1000:
return
for y in range(ny):
for x in range(nx):
p.drawEllipse((x * sw) - r, (y * sh) - r, 2 * r, 2 * r)
def camera2gonio(self, x, y):
bx, by = self.parent.get_beam_mark_on_camera_xy()
ppm = self._ppm
dx, dy = (x - bx) / ppm, (y - by) / ppm
fx, fy = self._fx + dx, self._fy - dy
return fx, fy
def gonio2camera(self, x, y):
ppm = self._ppm
bx, by = self.parent.get_beam_mark_on_camera_xy()
gx, gy = self._gx, self._gy # marker position in gonio coords
fx, fy = self._fx, self._fy
dx, dy = (
ppm * (fx - gx),
ppm * (fy - gy),
) # distance to beam in pixels for new ppm
x, y = bx - dx, by + dy
return x, y
class BeamMark(ROI):
"""A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable."""
def __init__(self, pos=None, size=None, parent=None, **kargs):
if size is None:
size = [20, 20]
if pos is None:
pos = [0, 0]
self._size = size
self._pos = pos
self._shape = None
ROI.__init__(self, pos, size, **kargs)
parent.pixelsPerMillimeter.connect(self.my_update_ppm)
parent.beamCameraCoordinatesChanged.connect(lambda x, y: self.setPos((x, y)))
self._scale_handler = None
self.sigRegionChanged.connect(self.invalidate)
self.aspectLocked = False
self.translatable = False
self.rotateAllowed = False
self.invertible = False
self.removable = False
self.scaleSnap = True
self.snapSize = 1
self.sigRegionChangeFinished.connect(self.show_size)
self.flip_ud()
def show_size(self):
try:
ppm = self._ppm
except:
return
w, h = self.getState()["size"]
# convert to micrometer
nw, nh = (w * 1000) / ppm, (h * 1000) / ppm
logger.debug("persisting new beam marker size: {}".format((nw, nh)))
settings.setValue("beam/size", (nw, nh))
def toggle_handle(self):
if self._scale_handler is None:
logger.debug("adding beam marker resize handle")
self._scale_handler = self.addScaleHandle([0.5, 0.5], [0, 0])
else:
logger.debug("removing beam marker resize handle")
self.removeHandle(self._scale_handler)
self._scale_handler = None
def get_camera_beam_position(self):
return self.getState()["pos"]
def set_beam_size_marker_dialog(self):
w, h = settings.value("beam/size")
d = GenericDialog(
title="Beamsize",
message="Enter the size of the beam in microns",
inputs={
"width": ("Width (\u00B5)", int(w), QSpinBox()),
"height": ("Height (\u00B5)", int(h), QSpinBox()),
},
)
if d.exec():
results = d.results
logger.info("Updating beamsize to {}".format(results))
w, h = results["width"], results["height"]
logger.debug("types {}".format(type(w)))
settings.setValue("beam/size", (w, h))
self.set_beam_size((w, h))
def flip_ud(self):
"""flip up-down"""
t = self.transform()
m11 = t.m11()
m12 = t.m12()
m13 = t.m13()
m21 = t.m21()
m22 = t.m22()
m23 = t.m23()
m31 = t.m31()
m32 = t.m32()
m33 = t.m33()
t.setMatrix(m11, m12, m13, m21, -m22, m23, m31, m32, m33)
self.setTransform(t)
def set_beam_size(self, size=None):
if size is not None:
self._size = size
ppm = self._ppm
w, h = self._size
nw, nh = (w / 1000) * ppm, (h / 1000) * ppm
self.setSize((nw, nh))
self.setToolTip("Beamsize (W x H): {:.1f}\u00B5 x {:.1f}\u00B5".format(w, h))
def my_update_ppm(self, ppm):
self._ppm = ppm
self.set_beam_size()
def invalidate(self):
self._shape = None
self.prepareGeometryChange()
def boundingRect(self):
return self.shape().boundingRect()
def shape(self):
w, h = self.size()
if self._shape is None:
p = QtGui.QPainterPath()
p.moveTo(Point(0, -h))
p.lineTo(Point(0, h))
p.moveTo(Point(-w, 0))
p.lineTo(Point(w, 0))
#
p.moveTo(Point(-w, -h))
p.lineTo(Point(w, -h))
p.lineTo(Point(w, h))
p.lineTo(Point(-w, -h))
stroker = QtGui.QPainterPathStroker()
outline = stroker.createStroke(p)
self._shape = outline
return self._shape
def paint(self, p, *args):
w, h = self.getState()["size"]
v1, v2 = -(h * 0.8) / 2, (h * 0.8) / 2
h1, h2 = -(w * 0.8) / 2, (w * 0.8) / 2
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(fn.mkPen(width=3, color=[200, 100, 100]))
p.drawLine(Point(0, v1), Point(0, v2))
p.drawLine(Point(h1, 0), Point(h2, 0))
p.setPen(fn.mkPen(width=3, color=[200, 200, 100]))
p.drawRect(-w / 2, -h / 2, w, h)
# p.drawText(-w, -h, '{:.0f}x{:.0f}'.format(*self._size))
class CrystalCircle(ROI):
"""A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable."""
def __init__(
self,
pos=None,
size=None,
parent=None,
gonio_xy=(0, 0),
model_row_index=1,
is_fiducial=False,
ppm=False,
**kargs
):
if size is None:
size = [20, 20]
if pos is None:
pos = [0, 0]
self._size = size
self._shape = None
self.parent = parent
self._model_row_index = model_row_index
self._is_fiducial = is_fiducial
ROI.__init__(self, pos, size, **kargs)
self._beam_position = parent.get_beam_mark_on_camera_xy()
self.set_gonio_xy(*gonio_xy)
self.setToolTip(f"{'fiducial' if is_fiducial else 'crystal'} {model_row_index}")
# parent.pixelsPerMillimeter.connect(self.update_pixels_per_millimeter)
# parent.beamCameraCoordinatesChanged.connect(self.update_beam_position)
# parent.fast_x_position.connect(self.follow_x)
# parent.fast_y_position.connect(self.follow_y)
self._ppm = ppm
self._original_ppm = ppm # marker only works at original zoom
# because zoom axis is not aligned
# with viewing axis
# self.calculate_gonio_xy()
self.sigRegionChanged.connect(self.invalidate)
# self.sigRegionChangeFinished.connect(self.calculate_gonio_xy)
# self.sigHoverEvent.connect(self.my_hovering)
self.aspectLocked = False
self.translatable = True
self.rotateAllowed = False
self.invertible = False
self.deletable = False
def enable_stage_tracking(self):
self.parent.fast_x_position.connect(self.follow_x)
self.parent.fast_y_position.connect(self.follow_y)
def disable_stage_tracking(self):
self.parent.fast_x_position.disconnect(self.follow_x)
self.parent.fast_y_position.disconnect(self.follow_y)
def follow_x(self, v):
self.follow_stage(fx=v)
def follow_y(self, v):
self.follow_stage(fy=v)
def my_hovering(self, event):
print("hovering: {}".format(self.mouseHovering))
def get_model_row(self):
return self._model_row_index
def update_ppm(self, ppm):
self._ppm = ppm
self.recalc_camera_position()
def follow_stage(self, fx=None, fy=None):
self._fx, self._fy = self.parent.getFastX(), self.parent.getFastY()
self.recalc_camera_position()
def set_gonio_xy(self, x, y):
self._gx, self._gy = x, y
def disconnect_signals(self):
self.parent.pixelsPerMillimeter.disconnect(self.update_ppm)
self.parent.beamCameraCoordinatesChanged.disconnect(self.update_beam_position)
self.parent.fast_x_position.disconnect(self.follow_x)
self.parent.fast_y_position.disconnect(self.follow_y)
def reconnect_signals(self):
self.parent.pixelsPerMillimeter.connect(self.update_ppm)
self.parent.beamCameraCoordinatesChanged.connect(self.update_beam_position)
self.parent.fast_x_position.connect(self.follow_x)
self.parent.fast_y_position.connect(self.follow_y)
def removeClicked(self):
self.disconnect_signals()
super(CrystalCircle, self).removeClicked()
def recalc_camera_position(self):
idx = self._model_row_index
ppm = self._ppm
bx, by = self.parent.get_beam_mark_on_camera_xy()
gx, gy = self._gx, self._gy # marker position in gonio coords
fx, fy = self._fx, self._fy
# distance to beam in pixels for new ppm
dx, dy = (ppm * (fx - gx), ppm * (fy - gy))
x, y = bx - dx, by + dy
# logger.debug(f"recalc {idx}: cam = {(x,y)}, gonio = {(gx, gy)}")
self.setPos(x, y)
def update_beam_position(self, pos):
self._beam_position = pos
def show_size(self):
try:
ppm = self._ppm
except:
return
w, h = self.getState()["size"]
# convert to micrometer
nw, nh = (w * 1000) / ppm, (h * 1000) / ppm
def invalidate(self):
self._shape = None
self.prepareGeometryChange()
def boundingRect(self):
return self.shape().boundingRect()
def shape(self):
if self._shape is None:
radius = self.getState()["size"][1]
p = QtGui.QPainterPath()
p.moveTo(Point(0, -radius))
p.lineTo(Point(0, radius))
p.moveTo(Point(-radius, 0))
p.lineTo(Point(radius, 0))
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(10)
outline = stroker.createStroke(p)
self._shape = outline
return self._shape
def paint(self, p, *args):
if abs(self._ppm - self._original_ppm) < 1.:
if self._is_fiducial:
pen = mkPen(color="r", width=2)
else:
pen = mkPen(color="y", width=2)
else:
pen = mkPen(color="#ffffaa88", width=2)
x, y = self.getState()["size"]
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(pen)
p.drawLine(Point(0, -x), Point(0, x))
p.drawLine(Point(-x, 0), Point(x, 0))
p.drawEllipse(-x, -y, 2 * x, 2 * y)
class Crosshair(ROI):
"""A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable."""
def __init__(self, pos=None, size=None, **kargs):
if size is None:
size = [10, 10]
if pos is None:
pos = [0, 0]
self._shape = None
ROI.__init__(self, pos, size, **kargs)
self.sigRegionChanged.connect(self.invalidate)
self.aspectLocked = True
def invalidate(self):
self._shape = None
self.prepareGeometryChange()
def boundingRect(self):
return self.shape().boundingRect()
def shape(self):
if self._shape is None:
radius = self.getState()["size"][1]
p = QtGui.QPainterPath()
p.moveTo(Point(0, -radius))
p.lineTo(Point(0, radius))
p.moveTo(Point(-radius, 0))
p.lineTo(Point(radius, 0))
# p = self.mapToDevice(p)
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(10)
outline = stroker.createStroke(p)
# self._shape = self.mapFromDevice(outline)
self._shape = outline
return self._shape
def paint(self, p, *args):
radius = self.getState()["size"][1]
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.currentPen)
p.drawLine(Point(0, -radius), Point(0, radius))
p.drawLine(Point(-radius, 0), Point(radius, 0))

View File

@@ -1,204 +0,0 @@
import logging
_log = logging.getLogger(__name__)
import re
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QLineEdit, QTextEdit, QGridLayout, QVBoxLayout, QWidget, QDialog, QDialogButtonBox, QPushButton
from PyQt5.QtWidgets import QDoubleSpinBox, QSpinBox, QCheckBox, QLabel
def Spinner(value, min=None, max=None, decimals=None, single_step=None, prefix=None, suffix=None):
if type(value) is float:
s = QDoubleSpinBox()
else:
s = QSpinBox()
if min is not None:
s.setMinimum(min)
if max is not None:
s.setMaximum(max)
try:
if decimals is not None:
s.setDecimals(decimals)
except:
pass
if single_step is not None:
s.setSingleStep(single_step)
if prefix is not None:
s.setPrefix(prefix)
if suffix is not None:
s.setSuffix(suffix)
return s
def Checkbox(value: bool, label: str):
box = QCheckBox(label)
box.setChecked(value)
return box
class MagicLabel(QLabel):
entered = pyqtSignal(str)
left = pyqtSignal()
def __init__(self, label, help_str, help_buddy=None):
super(MagicLabel, self).__init__(label)
self._help_buddy = help_buddy
self._help_str = help_str
def enterEvent(self, event):
print(self._help_str)
if self._help_buddy:
print(f"yay!: {self._help_str}")
else:
super().enterEvent(event)
def leaveEvent(self, event):
super().leaveEvent(event)
class GenericDialog(QDialog):
def __init__(self, parent=None, settings=None, title=None, message=None, inputs={}, use_buttons=True):
super(GenericDialog, self).__init__()
self.settings = settings
# self.setModal(True)
self.setAccessibleName("genericDialog")
self.setLayout(QVBoxLayout())
bbox = QDialogButtonBox()
doneButton = QPushButton("Done")
doneButton.clicked.connect(self.accept)
bbox.addButton(doneButton, QDialogButtonBox.AcceptRole)
if use_buttons:
undoButton = QPushButton("Undo")
undoButton.clicked.connect(self.undo_all)
discardButton = QPushButton("Discard")
discardButton.clicked.connect(self.discard)
bbox.addButton(undoButton, QDialogButtonBox.ActionRole)
bbox.addButton(discardButton, QDialogButtonBox.RejectRole)
lb = QLabel(title)
lb.setAccessibleName("genericDialogTitle")
lb.setAlignment(Qt.AlignHCenter)
self.layout().addWidget(lb)
gbox = QWidget()
self.layout().addWidget(gbox)
gbox.setLayout(QVBoxLayout())
mlabel = QLabel(message)
mlabel.setAccessibleName("genericDialogMessage")
gbox.layout().addWidget(mlabel)
self.setWindowTitle(title)
self.contents = QWidget()
self.contents.setAccessibleName("genericDialogContents")
self.layout().addWidget(self.contents)
self.layout().addStretch()
self.layout().addWidget(bbox)
if not inputs:
inputs = {'test': ('Text test', 'initial', QLineEdit()),
'float': ('Text test 2', 3.14, QDoubleSpinBox(), 5.0),
'integer': ('Text test 2', 42, QSpinBox()),
}
self.contents.setLayout(QGridLayout())
layout = self.contents.layout()
layout.setColumnStretch(1, 2)
self.results = {}
self._undo = {}
for row, config in enumerate(inputs.items()):
key, config = config
config = list(config)
if len(config) == 3:
config.extend([""])
label, value, widget, help_str = config
labwig = MagicLabel(label, help_str)
layout.addWidget(labwig, row, 0)
layout.addWidget(widget, row, 1)
if hasattr(widget, 'setChecked'):
widget.setChecked(value)
widget.stateChanged.connect(lambda v, k=key, w=widget.isChecked: self.update_results(k, w))
elif hasattr(widget, 'setValue'):
widget.setValue(value)
widget.valueChanged.connect(lambda v, k=key, w=widget.value: self.update_results(k, w))
elif hasattr(widget, 'setPlainText'):
widget.setPlainText(value)
widget.textChanged.connect(lambda k=key, w=widget.toPlainText: self.update_results(k, w))
elif hasattr(widget, 'setText'):
widget.setText(value)
widget.editingFinished.connect(lambda k=key, w=widget.text: self.update_results(k, w))
self.results[key] = value
self._undo[key] = (widget, value)
def undo_all(self):
for k, uinfo in self._undo.items():
widget, value = uinfo
if hasattr(widget, 'setChecked'):
widget.setChecked(value)
elif hasattr(widget, 'setValue'):
widget.setValue(value)
elif hasattr(widget, 'setPlainText'):
widget.setPlainText(value)
elif hasattr(widget, 'setText'):
widget.setText(value)
def discard(self):
self.undo_all()
self.reject()
def set_value(self, widget, url):
m = re.match("^(.*?)://(.*)", url, re.MULTILINE|re.DOTALL)
method, value = m.groups()
if method == "setValue":
widget.setValue(float(value))
elif method == "setText":
widget.setText(value)
else:
widget.setPlainText(value)
if self.settings is not None:
self.settings.setValue(k, value)
def update_results(self, key, get_value):
value = get_value()
_log.debug(f"settigns {key} => {value}")
self.results[key] = value
if self.settings is not None:
self.settings.setValue(k, value)
def keyPressEvent(self, key_event):
if key_event != Qt.Key_Escape:
super().keyPressEvent(key_event)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
title = 'A Lovely Title'
message = 'thisn sohudl explain what do tod ow with this dialog and in fact we should make sure that it will be wrapping aorufnnfb'
main = GenericDialog(title=title, message=message, inputs={
'bananas': ('Number of Bananas', 2, QSpinBox(), 10),
'apples': ('Number of Apples', 5.5, QDoubleSpinBox(), 23),
'really': ('Indeed?', True, Checkbox(True, "Contr")),
'words': ('Words', 'words go here', QLineEdit(), "a few words"),
'texts': ('Words', 'big words go here', QTextEdit(), """quite a drama\n to write here, really something awfull long"""),
})
if main.exec():
print(main.results)
for k, v in main.results.items():
print('{} {} = {}'.format(k, type(v), v))
sys.exit(app.exec_())

View File

@@ -195,63 +195,20 @@ class WndFixTarget(QWidget):
#btnDelAll.clicked.connect(lambda x: _log.warning("TODO: IMPLEMENT") ) #btnDelAll.clicked.connect(lambda x: _log.warning("TODO: IMPLEMENT") )
bl.addWidget(btnFit, 2, 1,1,1) bl.addWidget(btnFit, 2, 1,1,1)
# but = QPushButton("Dump to console")
# but.clicked.connect(self.dump_data)
# bl.addWidget(but, 10, 0, 1, 1)
but = QCheckBox("collect fiducials")
but.setChecked(False)
but.setToolTip("Collect or not the fiducial positions.")
self._collect_fiducials = False
but.stateChanged.connect(self.set_collect_fiducials)
bl.addWidget(but, 10, 0, 1, 1)
but = QCheckBox("draw crystal marks")
but.setChecked(False)
self._draw_crystal_marks = False
but.stateChanged.connect(self.set_draw_crystal_marks)
bl.addWidget(but, 10, 1, 1, 1)
but = QPushButton("Transform")
but.clicked.connect(self.transform_non_fiducials_in_model)
bl.addWidget(but, 20, 0, 2, 2)
layout.addWidget(frame) layout.addWidget(frame)
self.markersTable = QTableWidget() #self.markersTable.itemSelectionChanged.connect(self.selection_changed)
self.markersTable.setSelectionMode(QAbstractItemView.SingleSelection) #self.markersTable.setContextMenuPolicy(Qt.ActionsContextMenu)
self.markersTable.setSelectionBehavior(QAbstractItemView.SelectRows)
self.markersTable.setItemDelegate(MarkerDelegate(self))
# self.markersTable.horizontalHeader().setDefaultSectionSize(80) #deleteRowAction = QAction("Delete this row", self)
self.markersTable.setColumnCount(5) #deleteRowAction.triggered.connect(self.delete_selected)
self.markersTable.setHorizontalHeaderLabels(("Fiducial?", "orig X", "orig Y", "X", "Y")) #self.markersTable.addAction(deleteRowAction)
hh=self.markersTable.horizontalHeader()
hh.setSectionResizeMode(0, QHeaderView.ResizeToContents)
hh.setSectionResizeMode(1, QHeaderView.ResizeToContents)
hh.setSectionResizeMode(2, QHeaderView.ResizeToContents)
hh.setSectionResizeMode(3, QHeaderView.ResizeToContents)
hh.setSectionResizeMode(4, QHeaderView.ResizeToContents)
hh.setSectionResizeMode(QHeaderView.Stretch)
self.markersTable.itemSelectionChanged.connect(self.selection_changed) #moveRequestAction = QAction("Move Here", self)
#moveRequestAction.triggered.connect(self.request_stage_movement)
#self.markersTable.addAction(moveRequestAction)
self.markersTable.setContextMenuPolicy(Qt.ActionsContextMenu) #layout.addWidget(self.markersTable, stretch=2)
#self.connect_all_signals()
deleteRowAction = QAction("Delete this row", self)
deleteRowAction.triggered.connect(self.delete_selected)
self.markersTable.addAction(deleteRowAction)
moveRequestAction = QAction("Move Here", self)
moveRequestAction.triggered.connect(self.request_stage_movement)
self.markersTable.addAction(moveRequestAction)
layout.addWidget(self.markersTable, stretch=2)
self.connect_all_signals()
#self._yamlFn=fn=os.path.expanduser('~/.config/PSI/SwissMX.yaml') #self._yamlFn=fn=os.path.expanduser('~/.config/PSI/SwissMX.yaml')
@@ -259,15 +216,74 @@ class WndFixTarget(QWidget):
self._data=data self._data=data
self._tree=tree=pg.DataTreeWidget(data=data) self._tree=tree=pg.DataTreeWidget(data=data)
layout.addWidget(tree, stretch=2) layout.addWidget(tree, stretch=2)
#Context menu: by default the function
# def contextMenuEvent(self, event) is called... but this requests to overload the function
# and to build an own context menu
# much simpler is to use Qt.ActionsContextMenu
# and to handle the context in the parent plass
tree.setContextMenuPolicy(Qt.ActionsContextMenu)
act = QAction("delete", self)
act.triggered.connect(self.tree_ctx_delete)
tree.addAction(act)
act = QAction("center in view", self)
act.triggered.connect(self.tree_ctx_center)
tree.addAction(act)
#contextMenuEvent
def tree_get_path(self):
path=[]
it=self._tree.currentItem()
while it:
d=it.data(0,0)
try:
d=int(d)
except ValueError:
pass
if d!= '':
path.append(d)
it=it.parent()
return path
def tree_ctx_delete(self):
app=QApplication.instance()
path=self.tree_get_path()
if len(path)==1:
try:
wnd=app._mainWnd
except AttributeError:
_log.info('_mainWnd not handeled')
else:
vb=wnd.vb
grp=wnd._goTracked
go=grp.childItems()[path[0]]
vb.removeItem(go)
data=grp.childItems()
if not len(data):
grp.setFlag(grp.ItemHasNoContents)
self._tree.setData(data)
def tree_ctx_center(self):
app=QApplication.instance()
path=self.tree_get_path()
if len(path)==1:
try:
wnd=app._mainWnd
except AttributeError:
_log.info('_mainWnd not handeled')
else:
vb=wnd.vb
grp=wnd._goTracked
go=grp.childItems()[path[0]]
vb.autoRange(items=(go,))
#r1=vb.viewRect()
#r2=vb.itemBoundingRect(go)
#if not r1.intersects(r2):
def load_file(self, filename=None): def load_file(self, filename=None):
app = QApplication.instance() app = QApplication.instance()
#cfg = app._cfg
#logger = logging.getLogger("preloc.loadMarkers")
#def_folder = join(folders.pgroup_folder, "preloc_sheets") #ZAC: orig. code
#def_folder =''
#data_folder = cfg.value("folders/last_prelocation_folder", def_folder)
data_folder=''
if filename is None: if filename is None:
filename, _ = QFileDialog.getOpenFileName(self,"Load data file",None, filename, _ = QFileDialog.getOpenFileName(self,"Load data file",None,
"json files (*.json);;all files (*)",) "json files (*.json);;all files (*)",)
@@ -282,14 +298,7 @@ class WndFixTarget(QWidget):
data=json.load(f,object_hook=MyJsonDecoder) data=json.load(f,object_hook=MyJsonDecoder)
else: else:
raise(IOError('unsupported file type')) raise(IOError('unsupported file type'))
#elif ext=='yaml':
# with open(filename, 'r') as f:
# data = yaml.load(f)
#elif ext=='pkl':
# with open(filename, 'rb') as f:
# data=pickle.load(f)
#self._data=data
self._tree.setData(data) self._tree.setData(data)
try: try:
wnd=app._mainWnd wnd=app._mainWnd
@@ -399,6 +408,14 @@ class WndFixTarget(QWidget):
# pickle.dump(self._data, f) # pickle.dump(self._data, f)
#print(self._data) #print(self._data)
def dump_data(self):
print(self._data)
#for ref, x, y, cx, cy, ox, oy in self.get_data():
# print(f"fiducial:{ref} [{x}, {y}, {cx}, {cy}]")
# **********3 OBSOLETE
def delete_selected(self): def delete_selected(self):
row = self._current_row row = self._current_row
try: try:
@@ -418,337 +435,6 @@ class WndFixTarget(QWidget):
self.selectedRowChanged.emit(row) self.selectedRowChanged.emit(row)
_log.debug("selection changed: current row {}".format(row)) _log.debug("selection changed: current row {}".format(row))
def connect_all_signals(self):
self.prefixSelected.connect(lambda t: self._label_prefix.setText(t))
self.dataFileLoaded.connect(lambda t: self._label_datafile.setText(t))
def markerItemChanged(self, item):
print(item.row(), item.column())
def set_fiducial_coords(self, camx, camy, gx, gy):
tbl = self.markersTable
row = self._current_row
try:
row += 0
except:
_log.warning("select a row first")
QMessageBox.warning(self,"Select a marker first","You must select a marker first before updating its goniometer position",)
return
origx = tbl.item(row, ORIGINAL_X).data(RoleOriginalCoord_X)
origy = tbl.item(row, ORIGINAL_Y).data(RoleOriginalCoord_Y)
item = tbl.item(row, DATA_ITEM)
item.setData(RoleCameraCoord_X, camx)
item.setData(RoleCameraCoord_Y, camy)
item.setData(RoleGoniometerCoord_X, gx)
item.setData(RoleGoniometerCoord_Y, gy)
# item.setData(RoleOriginalCoord_X, origx)
# item.setData(RoleOriginalCoord_Y, origy)
# logger.debug(': [{}] = Original: {}, {} | Camera: {}, {} | Gonio: {}, {}'.format(1+row, origx, origy, camx, camy, gx, gy))
logger.debug(f": [{1+row}] = Original: {origx}, {origy} | Camera: {camx}, {camy} | Gonio: {gx}, {gy}")
tbl.item(row, GONIO_X).setData(Qt.DisplayRole, f"{gx:8.5f} mm\n{camx:8.1f} px")
tbl.item(row, GONIO_Y).setData(Qt.DisplayRole, f"{gy:8.5f} mm\n{camy:8.1f} px")
# mark row as a fiducial
tbl.item(row, 0).setCheckState(Qt.Checked)
tbl.item(row, 0).setData(Qt.UserRole, True)
tbl.selectRow(row + 1)
self.prelocatedDataUpdated.emit()
def set_selected_gonio_coords(self, xy):
tbl = self.markersTable
row = self._current_row
try:
row += 0
except:
_log.warning("select a row first")
QMessageBox.warning(self,"Select a marker first","You must select a marker first before updating its goniometer position",)
return
for n, v in enumerate(xy):
idx = 3 + n
tbl.setCurrentCell(row, idx) # gonio X
_log.debug("item: [{},{}] = {}".format(row, idx, v))
item = tbl.currentItem()
item.setData(Qt.EditRole, "{:.3f}".format(v))
item.setData(Qt.UserRole, v)
# mark row as a fiducial
tbl.setCurrentCell(row, 0)
tbl.currentItem().setCheckState(Qt.Checked)
tbl.currentItem().setData(Qt.UserRole, True)
def set_data_goniometer(self, row: int, xy):
tbl = self.markersTable
row = self._current_row
try:
row += 0
except:
_log.warning("select a row first")
QMessageBox.warning(self,"Select a marker first","You must select a marker first before updating its goniometer position",)
return
for n, v in enumerate(xy):
idx = 3 + n
tbl.setCurrentCell(row, idx) # gonio X
item = tbl.currentItem()
item.setData(Qt.EditRole, "{:.3f}".format(v))
item.setData(Qt.UserRole, v)
@pyqtSlot(int)
def set_draw_crystal_marks(self, val):
logger.info(f"{'' if val else 'not '}drawing crystal markers")
self._draw_crystal_marks = val
@pyqtSlot(int)
def set_collect_fiducials(self, val):
logger.info(f"{'' if val else 'not '}collecting fiducials")
self._collect_fiducials = val
def get_collection_targets(self):
return self.get_data(fiducials=self._collect_fiducials)
def get_data(self, fiducials=True, crystals=True, as_numpy=False):
"""return a list of tuples with all defined data
[
(is_fiducial(boolean), x, y, gonio_x, gonio_y),...
]
"""
data = []
item = self.markersTable.item
for row in range(self.markersTable.rowCount()):
is_fiducial = Qt.Checked == item(row, 0).checkState()
ditem = item(row, DATA_ITEM)
x = ditem.data(RoleGoniometerCoord_X)
y = ditem.data(RoleGoniometerCoord_Y)
cx = ditem.data(RoleCameraCoord_X)
cy = ditem.data(RoleCameraCoord_Y)
origx = ditem.data(RoleOriginalCoord_X)
origy = ditem.data(RoleOriginalCoord_Y)
if is_fiducial and fiducials:
data.append([is_fiducial, x, y, cx, cy, origx, origy])
if not is_fiducial and crystals:
data.append([is_fiducial, x, y, cx, cy, origx, origy])
if as_numpy:
data = np.asarray(data)
return data
def get_original_coordinates(self, fiducials=True):
"""return a numpy array with the original prelocated coordinates of the fiducial entries"""
data = self.get_data(fiducials=fiducials, crystals=not fiducials, as_numpy=True)
data = data[:, 5:]
zeros = np.zeros(data.shape)
zeros[:, 1] = 1
return np.concatenate((data, zeros), axis=1)
def get_goniometer_coordinates(self, fiducials=True):
"""return a numpy array with the goniometer coordinates of the fiducial entries"""
data = self.get_data(fiducials=fiducials, crystals=not fiducials, as_numpy=True)
data = data[:, 1:3]
zeros = np.zeros(data.shape)
zeros[:, 1] = 1
return np.concatenate((data, zeros), axis=1)
def get_camera_coordinates(self, fiducials=True):
"""return a numpy array with the goniometer coordinates of the fiducial entries"""
data = self.get_data(fiducials=fiducials, crystals=not fiducials, as_numpy=True)
data = data[:, 3:5]
zeros = np.zeros(data.shape)
zeros[:, 1] = 1
return np.concatenate((data, zeros), axis=1)
def dump_matrix(self, M):
scale, shear, angles, translate, perspective = tfs.decompose_matrix(M)
angles_deg = [a * 180 / np.pi for a in angles]
print("Transformation matrix Aerotech => SwissMX")
print(M)
print((" scale {:9.4f} {:9.4f} {:9.4f}".format(*scale)))
print((" shear {:9.4f} {:9.4f} {:9.4f}".format(*shear)))
print((" angles rad {:9.4f} {:9.4f} {:9.4f}".format(*angles)))
print((" angles deg {:9.4f} {:9.4f} {:9.4f}".format(*angles_deg)))
print((" translate {:9.4f} {:9.4f} {:9.4f}".format(*translate)))
print(("perspective {:9.4f} {:9.4f} {:9.4f}".format(*perspective)))
def transform_non_fiducials_in_model(self):
forg = self.get_original_coordinates(fiducials=True)
fgon = self.get_goniometer_coordinates(fiducials=True)
fcam = self.get_camera_coordinates(fiducials=True)
gmat = sort_cmass_3d(fgon)
omat = sort_cmass_3d(forg)
try:
M_org2gon = tfs.superimposition_matrix(omat.T, gmat.T, scale=True)
M_org2cam = tfs.superimposition_matrix(forg.T, fcam.T, scale=True)
except:
QMessageBox.warning(self.parent(), title="failed to find superimposition matrix", text="Failed to find superimposition matrix.\n\tPlease try again.")
return
# scale, shear, angles, translate, perspective = tfs.decompose_matrix(M_org2gon)
org_data = self.get_original_coordinates(fiducials=False)
gon_data = np.dot(M_org2gon, org_data.T)
cam_data = np.dot(M_org2cam, org_data.T)
tbl = self.markersTable
num_fiducials = forg.shape[0]
gon_data = (gon_data.T)[:, 0:2] # only X,Y matters
cam_data = (cam_data.T)[:, 0:2] # only X,Y matters
combined = np.concatenate((gon_data, cam_data), axis=1)
item = self.markersTable.item # function alias
for row, data in enumerate(
combined, num_fiducials
): # enumeration starts at *num_fiducials*
gx, gy, cx, cy = data
ditem = item(row, DATA_ITEM)
ditem.setData(RoleCameraCoord_X, cx)
ditem.setData(RoleCameraCoord_Y, cy)
ditem.setData(RoleGoniometerCoord_X, gx)
ditem.setData(RoleGoniometerCoord_Y, gy)
item(row, GONIO_X).setData(Qt.DisplayRole, f"{gx:8.5f} mm\n{cx:8.1f} px")
item(row, GONIO_Y).setData(Qt.DisplayRole, f"{gy:8.5f} mm\n{cy:8.1f} px")
self._xtals_transformed = True
self.prelocatedDataUpdated.emit()
def request_stage_movement(self):
logger = logging.getLogger("preloc.move_stage")
row = self._current_row
x = self.markersTable.item(row, DATA_ITEM).data(RoleGoniometerCoord_X)
y = self.markersTable.item(row, DATA_ITEM).data(RoleGoniometerCoord_Y)
logger.info(f"request move gonio to {x:.3f}, {y:.3f} mm")
self.moveFastStageRequest.emit(x, y)
def dump_numpy(self):
R = tfs.random_rotation_matrix(np.random.random(3))
d = self.get_goniometer_coordinates()
print("dumping")
print(d)
dR = np.dot(R, d)
print(dR.T)
def get_fiducials(self, as_numpy=False):
return self.get_data(crystals=False, as_numpy=as_numpy)
def get_crystals(self, as_numpy=False):
return self.get_data(fiducials=False, as_numpy=as_numpy)
def dump_data(self):
print(self._data)
#for ref, x, y, cx, cy, ox, oy in self.get_data():
# print(f"fiducial:{ref} [{x}, {y}, {cx}, {cy}]")
def append_data(self, data):
"""append data (a list of values) to the model
len(data) == 2 => (X, Y) from prelocated coordinate
len(data) == 4 => (X, Y, GX, GY) plus gonio coordinates
len(data) == 5 => (fiducial?, X, Y, GX, GY) plus fiducial
"""
row = self.markersTable.rowCount()
self.markersTable.setRowCount(row + 1)
data = list(data)
if len(data) == 2:
data.extend([0, 0])
if len(data) == 4:
data.insert(0, False) # fiducial flag
self.addSingleMarker(row, data)
self.prelocatedDataUpdated.emit()
def clearMarkers(self):
self.markersTable.setRowCount(0)
self.markersDeleted.emit()
def generate_random_data(self):
import io
data = io.StringIO()
npoints = self._num_random_points.value()
for n in range(npoints):
x, y, a, b = (
np.random.randint(0, 2000),
np.random.randint(0, 2000),
np.random.randint(-4000, 4000) / 1000.,
np.random.randint(-4000, 4000) / 1000.,
)
data.write("{}\t{}\t{}\t{}\n".format(x, y, a, b))
data.seek(0)
data.name = "random.csv"
self.loadMarkers(data)
def addSingleMarker(self, row, marker):
is_fiducial, origx, origy, gx, gy = marker
_log.debug(f": [{1+row}] | Original: {origx}, {origy} | Gonio: {gx}, {gy}")
item0 = QTableWidgetItem()
item0.setData(Qt.UserRole, is_fiducial)
item0.setFlags(item0.flags() & ~Qt.ItemIsEditable)
item0.setCheckState(Qt.Checked if is_fiducial else Qt.Unchecked)
item0.setTextAlignment(Qt.AlignCenter)
item1 = QTableWidgetItem("{:.1f}".format(origx))
item1.setTextAlignment(Qt.AlignRight)
item2 = QTableWidgetItem("{:.1f}".format(origy))
item2.setTextAlignment(Qt.AlignRight)
item3 = QTableWidgetItem("{:.3f} mm".format(gx))
item3.setFlags(item3.flags() & ~Qt.ItemIsEditable)
item3.setTextAlignment(Qt.AlignRight)
item4 = QTableWidgetItem("{:.3f} mm".format(gy))
item4.setFlags(item4.flags() & ~Qt.ItemIsEditable)
item4.setTextAlignment(Qt.AlignRight)
self.markersTable.setItem(row, 0, item0)
self.markersTable.setItem(row, ORIGINAL_X, item1)
self.markersTable.setItem(row, ORIGINAL_Y, item2)
self.markersTable.setItem(row, GONIO_X, item3)
self.markersTable.setItem(row, GONIO_Y, item4)
self.markerAdded.emit(is_fiducial, [origx, origy, gx, gy])
item = self.markersTable.item(row, DATA_ITEM)
item.setData(RoleCameraCoord_X, origx) # initially set to original
item.setData(RoleCameraCoord_Y, origy) # initially set to original
item.setData(RoleGoniometerCoord_X, gx)
item.setData(RoleGoniometerCoord_Y, gy)
item.setData(RoleOriginalCoord_X, origx)
item.setData(RoleOriginalCoord_Y, origy)
"""
Signals QTableWidget
void cellActivated(int row, int column)
void cellChanged(int row, int column)
void cellClicked(int row, int column)
void cellDoubleClicked(int row, int column)
void cellEntered(int row, int column)
void cellPressed(int row, int column)
void currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
void currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
void itemActivated(QTableWidgetItem *item)
void itemChanged(QTableWidgetItem *item)
void itemClicked(QTableWidgetItem *item)
void itemDoubleClicked(QTableWidgetItem *item)
void itemEntered(QTableWidgetItem *item)
void itemPressed(QTableWidgetItem *item)
void itemSelectionChanged()
"""
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys

View File

@@ -12,13 +12,9 @@ _log = logging.getLogger(__name__)
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSettings from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication, QMainWindow #QLineEdit, QWidget, QGridLayout, QLabel from PyQt5.QtWidgets import QApplication, QMainWindow
import json import json
import pyqtgraph as pg
import numpy as np import numpy as np
import GenericDialog
#from pyqtgraph.Qt import QtCore, QtGui
class MyJsonEncoder(json.JSONEncoder): class MyJsonEncoder(json.JSONEncoder):
""" Special json encoder for numpy types """ """ Special json encoder for numpy types """
@@ -60,26 +56,20 @@ class AppCfg(QSettings):
DT_MISC="deltatau/miscellaneous" DT_MISC="deltatau/miscellaneous"
# ---------- OBSOLETE ??? ---------- # ---------- OBSOLETE ??? ----------
#ZOOM_BUTTONS="sample_viewing/zoom_buttons" #ZOOM_BUTTONS="sample_viewing/zoom_buttons"
#SKIP_ESCAPE_TRANSITIONS_IF_SAFE="escape/skip_transitions_if_safe"
SKIP_ESCAPE_TRANSITIONS_IF_SAFE="escape/skip_transitions_if_safe" #CRYOJET_MOTION_ENABLED="cryojet/motion_enabled"
#CRYOJET_NOZZLE_OUT="cryojet/nozzle_out"
CRYOJET_MOTION_ENABLED="cryojet/motion_enabled" #CRYOJET_NOZZLE_IN="cryojet/nozzle_in"
CRYOJET_NOZZLE_OUT="cryojet/nozzle_out"
CRYOJET_NOZZLE_IN="cryojet/nozzle_in"
#DELTATAU_HOST="deltatau/host" #DELTATAU_HOST="deltatau/host"
#DELTATAU_SHOW_PLOTS="deltatau/show_plots" #DELTATAU_SHOW_PLOTS="deltatau/show_plots"
DELTATAU_OMEGACOS="deltatau/omegacos" #DELTATAU_OMEGACOS="deltatau/omegacos"
DELTATAU_SORT_POINTS="deltatau/sort_points" #DELTATAU_SORT_POINTS="deltatau/sort_points"
#DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale" #DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale"
#CAMERA_TRANSFORMATIONS="camera/transformations"
CAMERA_TRANSFORMATIONS="camera/transformations" #CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm"
CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm" #EXPERIMENT_PGROUP="experiment/pgroup"
#EXPERIMENT_UID="experiment/uid"
EXPERIMENT_PGROUP="experiment/pgroup" #ACTIVATE_PULSE_PICKER="scanning/activate_pulse_picker"
EXPERIMENT_UID="experiment/uid"
ACTIVATE_PULSE_PICKER="scanning/activate_pulse_picker"
def __init__(self): def __init__(self):
super(AppCfg, self).__init__("PSI", "SwissMX") super(AppCfg, self).__init__("PSI", "SwissMX")
@@ -220,6 +210,8 @@ from pyqtgraph.parametertree import Parameter, ParameterTree
class WndParameter(QMainWindow): class WndParameter(QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
super(WndParameter, self).__init__(parent) super(WndParameter, self).__init__(parent)
self.setWindowTitle('SwissMX Preferences')
app=QApplication.instance() app=QApplication.instance()
cfg=app._cfg cfg=app._cfg
#GBL_FLD_SCR_SHOT="global/folder_screenshot" #GBL_FLD_SCR_SHOT="global/folder_screenshot"

View File

@@ -35,7 +35,7 @@ class Deltatau:
try: try:
self._comm=comm=PPComm(**param,timeout=2.0) self._comm=comm=PPComm(**param,timeout=2.0)
self._gather=gather=Gather(comm) self._gather=gather=Gather(comm)
except socket.timeout as e: except (socket.timeout,socket.gaierror) as e:
_log.critical(f'can not connect to deltatau:"{host}" -> {e}') _log.critical(f'can not connect to deltatau:"{host}" -> {e}')
self._comm=comm=None self._comm=comm=None
self._gather=gather=None self._gather=gather=None

View File

@@ -45,20 +45,10 @@ ts=timestamp()
ts.log('Import part 1/8:') ts.log('Import part 1/8:')
import sys, os, time import sys, os, time
import json, re import json, re
import random, signal,threading import random, signal, subprocess
import matplotlib as mpl import matplotlib as mpl
mpl.use('Qt5Agg') # needed to avoid blocking of ui ! mpl.use('Qt5Agg') # needed to avoid blocking of ui !
#import Wigis #ZAC: orig. code
#import jungfrau_widget #ZAC: orig. code
#import bernina_pulse_picker #ZAC: orig. code
#import storage #ZAC: orig. code
#from zmq_escape import zescape #ZAC: orig. code
#from helicalscan import HelicalScanGui #ZAC: orig. code
# publisher = pubber.Publisher()
TASK_JUNGFRAU_SETTINGS = "jungfrau_settings" TASK_JUNGFRAU_SETTINGS = "jungfrau_settings"
TASK_SETUP_GEOMETRY_CALIB = "geometry_calib" TASK_SETUP_GEOMETRY_CALIB = "geometry_calib"
TASK_SETUP_PPM_CALIBRATION_TBOX = "ppm_calibration_tbox" TASK_SETUP_PPM_CALIBRATION_TBOX = "ppm_calibration_tbox"
@@ -78,17 +68,12 @@ import ModuleFixTarget
import PrelocatedCoordinatesModel # ZAC: orig. code import PrelocatedCoordinatesModel # ZAC: orig. code
from EmblModule import EmblWidget #ZAC: orig. code from EmblModule import EmblWidget #ZAC: orig. code
from HelicalTable import HelicalTableWidget #ZAC: orig. code from HelicalTable import HelicalTableWidget #ZAC: orig. code
#from Wigis import Spinner, Checkbox #ZAC: orig. code
#from exceptions import * #ZAC: orig. code
#import qoptic #ZAC: orig. code
#import mx_swissfel #ZAC: orig. code
#swissfel = mx_swissfel.SwissFELMachine() #ZAC: orig. code
#from bernina_pulse_picker import pulsePicker #ZAC: orig. code
ts.log('Import part 3/8:') ts.log('Import part 3/8:')
import qtawesome import qtawesome
import qutilities import qutilities
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, pyqtSlot, QSize, QRegExp, pyqtSignal, QObject, QThread, QRectF 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.QtGui import QKeySequence, QPixmap, QRegExpValidator, QFont
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAction, QApplication, QDoubleSpinBox, QFileDialog, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QAction, QApplication, QDoubleSpinBox, QFileDialog, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
@@ -96,20 +81,11 @@ from PyQt5.QtWidgets import (
QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,) QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,)
from PyQt5.uic import loadUiType from PyQt5.uic import loadUiType
ts.log('Import part 4/8:') ts.log('Import part 4/8:')
import CustomROI as CstROI
import pyqtUsrObj as UsrGO import pyqtUsrObj as UsrGO
#from CustomROI import BeamMark, Grid, CrystalCircle #ZAC: orig. code
#from GenericDialog import GenericDialog #ZAC: orig. code
#from dialogs.PreferencesDialog import PreferencesDialog #ZAC: orig. code
#from epics_widgets import zoom #ZAC: orig. code
from epics_widgets.MotorTweak import MotorTweak from epics_widgets.MotorTweak import MotorTweak
from epics_widgets.SmaractMotorTweak import SmaractMotorTweak from epics_widgets.SmaractMotorTweak import SmaractMotorTweak
from epics_widgets.SimMotorTweak import SimMotorTweak from epics_widgets.SimMotorTweak import SimMotorTweak
ts.log('Import part 5/8:') ts.log('Import part 5/8:')
#import matplotlib as mpl
#import matplotlib.pyplot as plt
import numpy as np import numpy as np
np.set_printoptions(suppress=True,linewidth=196) np.set_printoptions(suppress=True,linewidth=196)
import pyqtgraph as pg import pyqtgraph as pg
@@ -128,47 +104,24 @@ import deltatau
import detector import detector
ts.log('Import part 8/8:') ts.log('Import part 8/8:')
#_URL = "http://PC12288:8080"
# import TellClient
# tell = TellClient.TellClient(_URL)
#import eventer #ZAC: orig. code
#from detector_control import jungfrau_detector #ZAC: orig. code
#from findxtal import findObj #ZAC: orig. code
#if simulated: #ZAC: orig. code #ZAC: orig. code
# _log.warning("simulation mode enabled")
# qoptic_zoom = qoptic.FeturaClientBogus()
#else:
# qoptic_zoom = qoptic.FeturaClient()
#from epics_widgets import MotorTweak
#user = getpass.getuser() #ZAC: orig. code
#home = os.path.expanduser("~")
#just_quit = user in ["e10003", "gac-esbmx"]
#folders = storage.Folders() #ZAC: orig. code
#from deltatau import DeltaTau, shapepath, helical, DebugPlot #ZAC: orig. code
#delta_tau = DeltaTau() #ZAC: orig. code
#BROKER_SERVER = "127.0.0.1" #ZAC: orig. code
#BROKER_PORT = 61613 #ZAC: orig. code
# sample_camera = camera.camera_server(basename="SARES20-PROF142-M1")
#_log.info(f"connecting to microscope to camera server: {appsconf['microscope']['sample_camera']['pv_prefix']} ")
#sample_camera = camera.camera_server(basename=appsconf["microscope"]["sample_camera"]["pv_prefix"]) #ZAC: orig. code
def tdstamp(): def tdstamp():
return time.strftime("%Y%m%dH%H%M%S") return time.strftime("%Y%m%dH%H%M%S")
def datestamp(): def datestamp():
return time.strftime("%Y%m%d") 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): def sigint_handler(*args):
"""Handler for the SIGINT signal.""" """Handler for the SIGINT signal."""
app=QApplication.instance() app=QApplication.instance()
@@ -177,7 +130,6 @@ def sigint_handler(*args):
class AcquisitionAbortedException(Exception): class AcquisitionAbortedException(Exception):
pass pass
class Sequencer(QObject): class Sequencer(QObject):
finished = pyqtSignal() finished = pyqtSignal()
timeoutExpired = pyqtSignal() timeoutExpired = pyqtSignal()
@@ -260,13 +212,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
QtGui.QFontDatabase.addApplicationFont("fonts/Inconsolata-Bold.ttf") QtGui.QFontDatabase.addApplicationFont("fonts/Inconsolata-Bold.ttf")
QtGui.QFontDatabase.addApplicationFont("fonts/Baloo-Regular.ttf") QtGui.QFontDatabase.addApplicationFont("fonts/Baloo-Regular.ttf")
# TODO: Cleanup many member functions that are unused or obsolete
self._pv_shutter = None # epics.PV('X06SA-ES-MD2:SHUTTER')
self._has_pulse_picker = False
self._at_x06sa = False
self._at_cristalina = True
self._at_lab_eh060 = False
self.init_settings() self.init_settings()
# self.create_escape_toolbar() # self.create_escape_toolbar()
@@ -333,13 +278,13 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.init_actions() self.init_actions()
#self.prepare_microscope_page() #self.prepare_microscope_page()
self.prepare_left_tabs() self.prepare_wd_tabs_left()
#self.update_beam_marker(qoptic_zoom.get_sp()) #ZAC: orig. code #self.update_beam_marker(qoptic_zoom.get_sp()) #ZAC: orig. code
self._centerpiece_stack.setCurrentIndex(0) self._wd_stack.setCurrentIndex(0)
self._centerpiece_stack.currentChanged.connect(self.cb_update_center_widget) self._wd_stack.currentChanged.connect(self.cb_update_center_widget)
self._OLD_init_validators() #self._OLD_init_validators()
#self.init_settings_tracker() ? not needed, was for TELL ? #self.init_settings_tracker() ? not needed, was for TELL ?
self._OLD_wire_storage() #self._OLD_wire_storage()
self.cb_update_center_widget(0) # start camera updater self.cb_update_center_widget(0) # start camera updater
curzoom = app._zoom.get_val() curzoom = app._zoom.get_val()
@@ -364,7 +309,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
sz = [int(s) for s in cfg.value(cfg.WINDOW_SPLITTER)] sz = [int(s) for s in cfg.value(cfg.WINDOW_SPLITTER)]
else: else:
sz=(500, 1200) sz=(500, 1200)
self._main_splitter.setSizes(sz) self._wd_splitter.setSizes(sz)
#if AppCfg.BEAM_MARKER_POSITIONS in keys: #if AppCfg.BEAM_MARKER_POSITIONS in keys:
# self._beam_markers = s.value(AppCfg.BEAM_MARKER_POSITIONS) # self._beam_markers = s.value(AppCfg.BEAM_MARKER_POSITIONS)
@@ -523,11 +468,11 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.toolBar.addAction(action) self.toolBar.addAction(action)
# icon = qtawesome.icon("material.shutter_speed") # icon = qtawesome.icon("material.shutter_speed")
action = QAction("Use Pulse Picker", self) #action = QAction("Use Pulse Picker", self)
action.setCheckable(True) #action.setCheckable(True)
action.setChecked(cfg.option(AppCfg.ACTIVATE_PULSE_PICKER)) #action.setChecked(cfg.option(AppCfg.ACTIVATE_PULSE_PICKER))
action.triggered.connect(lambda: cfg.toggle_option(AppCfg.ACTIVATE_PULSE_PICKER)) #action.triggered.connect(lambda: cfg.toggle_option(AppCfg.ACTIVATE_PULSE_PICKER))
self.toolBar.addAction(action) #self.toolBar.addAction(action)
icon = qtawesome.icon("material.timeline") icon = qtawesome.icon("material.timeline")
action = QAction(icon, "Add Line", self) action = QAction(icon, "Add Line", self)
@@ -575,9 +520,9 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.toolBar.widgetForAction(action).setAccessibleName("action_DataCollection") self.toolBar.widgetForAction(action).setAccessibleName("action_DataCollection")
self.actionQuit.triggered.connect(self.cb_really_quit) self.actionQuit.triggered.connect(self.cb_really_quit)
self.actionPreferences.triggered.connect(self._OLD_openPreferencesDialog) self.actionPreferences.triggered.connect(self.cb_modify_app_param)
self.actionHome_Fast_Stages.triggered.connect(self.cb_deltatau_home_faststages) self.actionHome_Fast_Stages.triggered.connect(self.cb_deltatau_home_faststages)
self.actionUser_Storage.triggered.connect(self._OLD_update_user_and_storage) self.actionAbout.triggered.connect(self.cb_about)
self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_L), self) self.shortcut = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_L), self)
self.shortcut.activated.connect(self._OLD_roi_add_line) self.shortcut.activated.connect(self._OLD_roi_add_line)
@@ -601,6 +546,12 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.shortcut.activated.connect(self._OLD_camera_pause_toggle) self.shortcut.activated.connect(self._OLD_camera_pause_toggle)
# adding a menu entry to one of the menus # 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 = QAction("geometry", self)
#action.setToolTip("Update optical center, beam marker size etc.") #action.setToolTip("Update optical center, beam marker size etc.")
#action.setStatusTip("Update optical center, beam marker size etc.") #action.setStatusTip("Update optical center, beam marker size etc.")
@@ -631,14 +582,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
#action.triggered.connect(cfg.dlg_deltatau_parameters) #action.triggered.connect(cfg.dlg_deltatau_parameters)
#self.menuAdvanced.addAction(action) #self.menuAdvanced.addAction(action)
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("Cryojet Reference Positions", self) #action = QAction("Cryojet Reference Positions", self)
#action.setToolTip("Update the reference positions for the cryojet nozzle position") #action.setToolTip("Update the reference positions for the cryojet nozzle position")
#action.setStatusTip("Update the reference positions for the cryojet nozzle position") #action.setStatusTip("Update the reference positions for the cryojet nozzle position")
@@ -665,7 +608,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
_log.info('disconnect PV callback') _log.info('disconnect PV callback')
cfg.setValue(cfg.WINDOW_GEOMETRY, self.saveGeometry()) cfg.setValue(cfg.WINDOW_GEOMETRY, self.saveGeometry())
cfg.setValue(cfg.WINDOW_STATE, self.saveState()) cfg.setValue(cfg.WINDOW_STATE, self.saveState())
cfg.setValue(cfg.WINDOW_SPLITTER, self._main_splitter.sizes()) cfg.setValue(cfg.WINDOW_SPLITTER, self._wd_splitter.sizes())
cfg.setValue('last_active', time.time()) cfg.setValue('last_active', time.time())
_log.info('save settings') _log.info('save settings')
#QMainWindow.closeEvent(self, event) #QMainWindow.closeEvent(self, event)
@@ -755,19 +698,13 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.setStyleSheet(sheet.read()) self.setStyleSheet(sheet.read())
def setup_sliders(self): def setup_sliders(self):
cont = self._rightmost cont = self._wd_right
self._tweak_container = QWidget() self._tweak_container = QWidget()
self._tweak_container.setLayout(QVBoxLayout()) self._tweak_container.setLayout(QVBoxLayout())
cont.layout().addWidget(self._tweak_container) cont.layout().addWidget(self._tweak_container)
layout = self._tweak_container.layout() layout = self._tweak_container.layout()
layout.setSpacing(0) layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0)
if self._at_x06sa:
zoom_base = "ESBMX-SAMCAM" # fetura IOC by Jose Gabadinho
elif self._at_lab_eh060:
zoom_base = "/dev/ttyUSB0" # direct connection using fetura.py package
elif self._at_cristalina:
zoom_base = ("rest://pc12818.psi.ch:9999") # direct connection using fetura.py package
self.zoombox = zoom.Zoom() self.zoombox = zoom.Zoom()
self.zoombox.init_settings() self.zoombox.init_settings()
@@ -1506,46 +1443,44 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
def cb_esc_sample_exchange(self): def cb_esc_sample_exchange(self):
app=QApplication.instance() app=QApplication.instance()
self._esc_state ="busy"
steps = [] steps = []
#if option(CRYOJET_MOTION_ENABLED): #if option(CRYOJET_MOTION_ENABLED):
# steps.append(lambda: self.move_cryojet_nozzle("out")) # steps.append(lambda: self.move_cryojet_nozzle("out"))
steps.extend( steps=[
[ lambda:self.move_detector("out"),
lambda: self.move_post_tube("out"), lambda: self.move_post_tube("out"),
lambda: app._backlight.move("out", wait=True), lambda: app._backlight.move("out"),
lambda: self.move_collimator("out"), lambda: self.move_collimator("out"),
] ]
) self.esc_run_steps(steps, "Transitioning to Sample Exchange","ManualSampleExchange")
self.esc_run_steps(steps, "Transitioning to Sample Exchange")
self._esc_state ="ManualSampleExchange"
def cb_esc_sample_alignment(self): def cb_esc_sample_alignment(self):
app=QApplication.instance() app=QApplication.instance()
self._esc_state ="busy"
steps = [ steps = [
# lambda: sample_selection.tell.set_current(30.0), # lambda: sample_selection.tell.set_current(30.0),
lambda: self.move_collimator("ready") lambda: self.move_collimator("ready"),
lambda:self.move_detector("out"),
lambda:self.move_post_tube("out"),
lambda:app._backlight.move("in"),
lambda:self.move_collimator("out"),
] ]
#if option(CRYOJET_MOTION_ENABLED): #if option(CRYOJET_MOTION_ENABLED):
# steps.extend([lambda: self.move_cryojet_nozzle("in")]) # steps.extend([lambda: self.move_cryojet_nozzle("in")])
steps.extend([lambda: self.move_post_tube("out"), lambda: app._backlight.move("in")]) #steps.extend([lambda: self.move_post_tube("out"), lambda: app._backlight.move("in")])
self.esc_run_steps(steps, "Transitioning to Sample Alignment") self.esc_run_steps(steps, "Transitioning to Sample Alignment","SampleAlignment")
self._esc_state ="SampleAlignment"
def cb_esc_data_collection(self): def cb_esc_data_collection(self):
app=QApplication.instance() app=QApplication.instance()
self._esc_state ="busy"
steps = [ steps = [
# lambda: sample_selection.tell.set_current(30.0), # lambda: sample_selection.tell.set_current(30.0),
lambda: app._backlight.move("out"), lambda: app._backlight.move("out"),
lambda: self.move_post_tube("in"), lambda: self.move_post_tube("in"),
lambda: self.move_collimator("in"), lambda: self.move_collimator("in"),
lambda:self.move_detector("in"),
] ]
#if option(CRYOJET_MOTION_ENABLED): #if option(CRYOJET_MOTION_ENABLED):
# steps.extend([lambda: self.move_cryojet_nozzle("in")]) # steps.extend([lambda: self.move_cryojet_nozzle("in")])
self.esc_run_steps(steps, "Transitioning to Data Collection") self.esc_run_steps(steps, "Transitioning to Data Collection","DataCollection")
self._esc_state ="DataCollection"
def cb_really_quit(self): def cb_really_quit(self):
"""called when user Ctrl-Q the app""" """called when user Ctrl-Q the app"""
@@ -1555,9 +1490,43 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
def cb_deltatau_home_faststages(self): def cb_deltatau_home_faststages(self):
pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0] pfx=QApplication.instance()._cfg.value(AppCfg.GBL_DEV_PREFIX)[0]
_log.warning("homing fast stages") _log.info("homing fast stages")
epics.PV(f"{pfx}1:ASYN.AOUT").put(b"enable plc 1") 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_testcode(self): def cb_testcode(self):
try: try:
tc=self._testCode tc=self._testCode
@@ -1602,8 +1571,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
#print(vb.childGroup.childItems()) #print(vb.childGroup.childItems())
pass pass
def prepare_left_tabs(self): def prepare_wd_tabs_left(self):
tabs = self._left_tabs tabs = self._wd_tabs_left
tabs.currentChanged.connect(self.cb_switch_task) tabs.currentChanged.connect(self.cb_switch_task)
setup_tab = self._tab_setup setup_tab = self._tab_setup
@@ -1832,8 +1801,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
def cb_switch_task(self, index=0): def cb_switch_task(self, index=0):
stack = self._centerpiece_stack stack = self._wd_stack
task = self._left_tabs.currentWidget().accessibleName() task = self._wd_tabs_left.currentWidget().accessibleName()
setup_task = self._setup_toolbox.currentWidget().accessibleName() setup_task = self._setup_toolbox.currentWidget().accessibleName()
method = self._tabs_daq_methods.currentWidget().accessibleName() method = self._tabs_daq_methods.currentWidget().accessibleName()
@@ -1992,7 +1961,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
dt_misc = cfg.value(AppCfg.DT_MISC) dt_misc = cfg.value(AppCfg.DT_MISC)
geo = app._geometry geo = app._geometry
verbose=0xff
fn='/tmp/shapepath' fn='/tmp/shapepath'
try: try:
dt=app._deltatau dt=app._deltatau
@@ -2006,12 +1974,9 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
sp=dt._shapepath sp=dt._shapepath
sp.verbose=dt_misc['verbose'] sp.verbose=dt_misc['verbose']
sp.points=kwargs['points'] sp.points=kwargs['points']
#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']=dt_misc['pt2pt_time'] sp.meta['pt2pt_time']=dt_misc['pt2pt_time']
sp.setup_gather() sp.setup_gather()
sp.setup_sync(verbose=verbose&32, timeOfs=0.05) sp.setup_sync(verbose=sp.verbose&0x20, timeOfs=0.05)
try: try:
p=geo._fitPlane p=geo._fitPlane
sp.setup_coord_trf(cz=f'{p[0]:+.18g}X{p[1]:+.18g}Y{p[2]:+.18g}') # reset to shape path system sp.setup_coord_trf(cz=f'{p[0]:+.18g}X{p[1]:+.18g}Y{p[2]:+.18g}') # reset to shape path system
@@ -2037,7 +2002,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
plt.show(block=False) plt.show(block=False)
#plt.show(block=True) #plt.show(block=True)
def esc_run_steps(self, steps, title): def esc_run_steps(self, steps, title, esc_state):
self._esc_state ="busy"
with pg.ProgressDialog(title, 0, len(steps)) as dlg: with pg.ProgressDialog(title, 0, len(steps)) as dlg:
for step in steps: for step in steps:
step() step()
@@ -2045,8 +2011,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
if dlg.wasCanceled(): if dlg.wasCanceled():
QMessageBox.warning(self, "escape steps", "ABORTED" + title) QMessageBox.warning(self, "escape steps", "ABORTED" + title)
break break
self._esc_state = esc_state
#backlight="{'pos_in': -10.0, 'pos_out': 0.0}"
def move_post_tube(self, pos): 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}" # 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}"
@@ -3554,7 +3519,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
self.re_connect_collect_button() self.re_connect_collect_button()
def _OLD_create_escape_toolbar(self): def _OLD_create_escape_toolbar(self):
cont = self._rightmost cont = self._wd_right
w = QGroupBox("Escape") w = QGroupBox("Escape")
layout = QHBoxLayout() layout = QHBoxLayout()
w.setLayout(layout) w.setLayout(layout)
@@ -3725,7 +3690,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
if __name__=="__main__": if __name__=="__main__":
def main(): def main():
from PyQt5.QtCore import QT_VERSION_STR
#_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__} 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}' ) _log.info(f'Version: pyqtgraph:{pg.__version__} epics:{epics.__version__} qt:{QT_VERSION_STR}' )
import argparse, socket import argparse, socket

View File

@@ -16,7 +16,7 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="_wd_hlayout">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@@ -46,7 +46,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QSplitter" name="_main_splitter"> <widget class="QSplitter" name="_wd_splitter">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@@ -65,7 +65,7 @@
<property name="childrenCollapsible"> <property name="childrenCollapsible">
<bool>true</bool> <bool>true</bool>
</property> </property>
<widget class="QTabWidget" name="_left_tabs"> <widget class="QTabWidget" name="_wd_tabs_left">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
@@ -107,81 +107,13 @@
<string>Data Storage</string> <string>Data Storage</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_5">
<item row="2" column="0"> <item row="0" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Prefix</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Folder</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="_label_folder">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Folder where the data will be stored; triple-click to select.</string>
</property>
<property name="statusTip">
<string>Folder where the data will be stored; triple-click to select. Type 'folder' in a console to go here.</string>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Project</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="_le_project"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="_le_prefix"/> <widget class="QLineEdit" name="_le_prefix"/>
</item> </item>
<item row="5" column="0"> <item row="1" column="1">
<widget class="QLabel" name="label"> <widget class="QLineEdit" name="_le_runnumber"/>
<property name="text">
<string>Filenames</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item> </item>
<item row="4" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Run</string> <string>Run</string>
@@ -191,46 +123,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1">
<widget class="QLineEdit" name="_label_actual_prefix">
<property name="frame">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="_label_runnumber">
<property name="frame">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_14"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>P-group</string> <string>Prefix</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QLabel" name="_label_pgroup">
<property name="accessibleName">
<string>pgroup</string>
</property>
<property name="text">
<string>p-group-undefined</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -239,89 +141,7 @@
<property name="title"> <property name="title">
<string>Experimental Parameters</string> <string>Experimental Parameters</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4"/>
<item row="0" column="2">
<widget class="QDoubleSpinBox" name="_dsb_exposure_time">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> s</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Oscillation Step</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Exposure Time</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Blast radius</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="_dsb_blast_radius">
<property name="suffix">
<string> µm</string>
</property>
<property name="maximum">
<double>200.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="_dsb_oscillation_step">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> deg</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
@@ -614,16 +434,16 @@
</attribute> </attribute>
</widget> </widget>
</widget> </widget>
<widget class="QStackedWidget" name="_centerpiece_stack"> <widget class="QStackedWidget" name="_wd_stack">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="microscope_page"/> <widget class="QWidget" name="microscope_page"/>
<widget class="QWidget" name="sample_select"> <widget class="QWidget" name="sample_select">
<layout class="QVBoxLayout" name="verticalLayout"/> <layout class="QVBoxLayout" name="verticalLayout"/>
</widget> </widget>
</widget> </widget>
<widget class="QWidget" name="_rightmost" native="true"> <widget class="QWidget" name="_wd_right" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@@ -664,7 +484,6 @@
<string>&amp;Advanced</string> <string>&amp;Advanced</string>
</property> </property>
<addaction name="actionHome_Fast_Stages"/> <addaction name="actionHome_Fast_Stages"/>
<addaction name="actionUser_Storage"/>
</widget> </widget>
<widget class="QMenu" name="menuSetup"> <widget class="QMenu" name="menuSetup">
<property name="title"> <property name="title">
@@ -718,16 +537,12 @@
</action> </action>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>_le_project</tabstop>
<tabstop>_le_prefix</tabstop> <tabstop>_le_prefix</tabstop>
<tabstop>_dsb_exposure_time</tabstop>
<tabstop>_dsb_oscillation_step</tabstop>
<tabstop>_tabs_daq_methods</tabstop> <tabstop>_tabs_daq_methods</tabstop>
<tabstop>_sb_grid_y_step</tabstop> <tabstop>_sb_grid_y_step</tabstop>
<tabstop>_left_tabs</tabstop> <tabstop>_wd_tabs_left</tabstop>
<tabstop>_label_actual_prefix</tabstop>
<tabstop>_grid_inspect_area</tabstop> <tabstop>_grid_inspect_area</tabstop>
<tabstop>_label_runnumber</tabstop> <tabstop>_le_runnumber</tabstop>
<tabstop>_bt_add_grid</tabstop> <tabstop>_bt_add_grid</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>