cleanup
This commit is contained in:
595
CustomROI.py
595
CustomROI.py
@@ -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))
|
||||
204
GenericDialog.py
204
GenericDialog.py
@@ -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_())
|
||||
|
||||
@@ -195,63 +195,20 @@ class WndFixTarget(QWidget):
|
||||
#btnDelAll.clicked.connect(lambda x: _log.warning("TODO: IMPLEMENT") )
|
||||
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)
|
||||
self.markersTable = QTableWidget()
|
||||
self.markersTable.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.markersTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.markersTable.setItemDelegate(MarkerDelegate(self))
|
||||
#self.markersTable.itemSelectionChanged.connect(self.selection_changed)
|
||||
#self.markersTable.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
|
||||
# self.markersTable.horizontalHeader().setDefaultSectionSize(80)
|
||||
self.markersTable.setColumnCount(5)
|
||||
self.markersTable.setHorizontalHeaderLabels(("Fiducial?", "orig X", "orig Y", "X", "Y"))
|
||||
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)
|
||||
#deleteRowAction = QAction("Delete this row", self)
|
||||
#deleteRowAction.triggered.connect(self.delete_selected)
|
||||
#self.markersTable.addAction(deleteRowAction)
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
#layout.addWidget(self.markersTable, stretch=2)
|
||||
#self.connect_all_signals()
|
||||
|
||||
#self._yamlFn=fn=os.path.expanduser('~/.config/PSI/SwissMX.yaml')
|
||||
|
||||
@@ -259,15 +216,74 @@ class WndFixTarget(QWidget):
|
||||
self._data=data
|
||||
self._tree=tree=pg.DataTreeWidget(data=data)
|
||||
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):
|
||||
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:
|
||||
filename, _ = QFileDialog.getOpenFileName(self,"Load data file",None,
|
||||
"json files (*.json);;all files (*)",)
|
||||
@@ -282,14 +298,7 @@ class WndFixTarget(QWidget):
|
||||
data=json.load(f,object_hook=MyJsonDecoder)
|
||||
else:
|
||||
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)
|
||||
try:
|
||||
wnd=app._mainWnd
|
||||
@@ -399,6 +408,14 @@ class WndFixTarget(QWidget):
|
||||
# pickle.dump(self._data, f)
|
||||
#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):
|
||||
row = self._current_row
|
||||
try:
|
||||
@@ -418,337 +435,6 @@ class WndFixTarget(QWidget):
|
||||
self.selectedRowChanged.emit(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__":
|
||||
import sys
|
||||
|
||||
|
||||
@@ -12,13 +12,9 @@ _log = logging.getLogger(__name__)
|
||||
|
||||
from PyQt5 import QtCore, QtGui
|
||||
from PyQt5.QtCore import QSettings
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow #QLineEdit, QWidget, QGridLayout, QLabel
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow
|
||||
import json
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
import GenericDialog
|
||||
|
||||
#from pyqtgraph.Qt import QtCore, QtGui
|
||||
|
||||
class MyJsonEncoder(json.JSONEncoder):
|
||||
""" Special json encoder for numpy types """
|
||||
@@ -60,26 +56,20 @@ class AppCfg(QSettings):
|
||||
DT_MISC="deltatau/miscellaneous"
|
||||
# ---------- OBSOLETE ??? ----------
|
||||
#ZOOM_BUTTONS="sample_viewing/zoom_buttons"
|
||||
|
||||
SKIP_ESCAPE_TRANSITIONS_IF_SAFE="escape/skip_transitions_if_safe"
|
||||
|
||||
CRYOJET_MOTION_ENABLED="cryojet/motion_enabled"
|
||||
CRYOJET_NOZZLE_OUT="cryojet/nozzle_out"
|
||||
CRYOJET_NOZZLE_IN="cryojet/nozzle_in"
|
||||
|
||||
#SKIP_ESCAPE_TRANSITIONS_IF_SAFE="escape/skip_transitions_if_safe"
|
||||
#CRYOJET_MOTION_ENABLED="cryojet/motion_enabled"
|
||||
#CRYOJET_NOZZLE_OUT="cryojet/nozzle_out"
|
||||
#CRYOJET_NOZZLE_IN="cryojet/nozzle_in"
|
||||
#DELTATAU_HOST="deltatau/host"
|
||||
#DELTATAU_SHOW_PLOTS="deltatau/show_plots"
|
||||
DELTATAU_OMEGACOS="deltatau/omegacos"
|
||||
DELTATAU_SORT_POINTS="deltatau/sort_points"
|
||||
#DELTATAU_OMEGACOS="deltatau/omegacos"
|
||||
#DELTATAU_SORT_POINTS="deltatau/sort_points"
|
||||
#DELTATAU_VELOCITY_SCALE="deltatau/velocity_scale"
|
||||
|
||||
CAMERA_TRANSFORMATIONS="camera/transformations"
|
||||
CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm"
|
||||
|
||||
EXPERIMENT_PGROUP="experiment/pgroup"
|
||||
EXPERIMENT_UID="experiment/uid"
|
||||
|
||||
ACTIVATE_PULSE_PICKER="scanning/activate_pulse_picker"
|
||||
#CAMERA_TRANSFORMATIONS="camera/transformations"
|
||||
#CAMERA_ZOOM_TO_PPM="camera/zoom_to_ppm"
|
||||
#EXPERIMENT_PGROUP="experiment/pgroup"
|
||||
#EXPERIMENT_UID="experiment/uid"
|
||||
#ACTIVATE_PULSE_PICKER="scanning/activate_pulse_picker"
|
||||
|
||||
def __init__(self):
|
||||
super(AppCfg, self).__init__("PSI", "SwissMX")
|
||||
@@ -220,6 +210,8 @@ from pyqtgraph.parametertree import Parameter, ParameterTree
|
||||
class WndParameter(QMainWindow):
|
||||
def __init__(self, parent=None):
|
||||
super(WndParameter, self).__init__(parent)
|
||||
|
||||
self.setWindowTitle('SwissMX Preferences')
|
||||
app=QApplication.instance()
|
||||
cfg=app._cfg
|
||||
#GBL_FLD_SCR_SHOT="global/folder_screenshot"
|
||||
|
||||
@@ -35,7 +35,7 @@ class Deltatau:
|
||||
try:
|
||||
self._comm=comm=PPComm(**param,timeout=2.0)
|
||||
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}')
|
||||
self._comm=comm=None
|
||||
self._gather=gather=None
|
||||
|
||||
222
swissmx.py
222
swissmx.py
@@ -45,20 +45,10 @@ ts=timestamp()
|
||||
ts.log('Import part 1/8:')
|
||||
import sys, os, time
|
||||
import json, re
|
||||
import random, signal,threading
|
||||
|
||||
import random, signal, subprocess
|
||||
import matplotlib as mpl
|
||||
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_SETUP_GEOMETRY_CALIB = "geometry_calib"
|
||||
TASK_SETUP_PPM_CALIBRATION_TBOX = "ppm_calibration_tbox"
|
||||
@@ -78,17 +68,12 @@ import ModuleFixTarget
|
||||
import PrelocatedCoordinatesModel # ZAC: orig. code
|
||||
from EmblModule import EmblWidget #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:')
|
||||
import qtawesome
|
||||
import qutilities
|
||||
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.QtWidgets import (
|
||||
QAction, QApplication, QDoubleSpinBox, QFileDialog, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
|
||||
@@ -96,20 +81,11 @@ from PyQt5.QtWidgets import (
|
||||
QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,)
|
||||
from PyQt5.uic import loadUiType
|
||||
ts.log('Import part 4/8:')
|
||||
import CustomROI as CstROI
|
||||
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.SmaractMotorTweak import SmaractMotorTweak
|
||||
from epics_widgets.SimMotorTweak import SimMotorTweak
|
||||
ts.log('Import part 5/8:')
|
||||
#import matplotlib as mpl
|
||||
#import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
np.set_printoptions(suppress=True,linewidth=196)
|
||||
import pyqtgraph as pg
|
||||
@@ -128,47 +104,24 @@ import deltatau
|
||||
import detector
|
||||
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():
|
||||
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()
|
||||
@@ -177,7 +130,6 @@ def sigint_handler(*args):
|
||||
class AcquisitionAbortedException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Sequencer(QObject):
|
||||
finished = pyqtSignal()
|
||||
timeoutExpired = pyqtSignal()
|
||||
@@ -260,13 +212,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
QtGui.QFontDatabase.addApplicationFont("fonts/Inconsolata-Bold.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.create_escape_toolbar()
|
||||
@@ -333,13 +278,13 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
self.init_actions()
|
||||
#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._centerpiece_stack.setCurrentIndex(0)
|
||||
self._centerpiece_stack.currentChanged.connect(self.cb_update_center_widget)
|
||||
self._OLD_init_validators()
|
||||
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._OLD_wire_storage()
|
||||
|
||||
self.cb_update_center_widget(0) # start camera updater
|
||||
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)]
|
||||
else:
|
||||
sz=(500, 1200)
|
||||
self._main_splitter.setSizes(sz)
|
||||
self._wd_splitter.setSizes(sz)
|
||||
|
||||
#if AppCfg.BEAM_MARKER_POSITIONS in keys:
|
||||
# self._beam_markers = s.value(AppCfg.BEAM_MARKER_POSITIONS)
|
||||
@@ -523,11 +468,11 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
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)
|
||||
#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)
|
||||
@@ -575,9 +520,9 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
self.toolBar.widgetForAction(action).setAccessibleName("action_DataCollection")
|
||||
|
||||
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.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.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)
|
||||
|
||||
# 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.")
|
||||
@@ -631,14 +582,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
#action.triggered.connect(cfg.dlg_deltatau_parameters)
|
||||
#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.setToolTip("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')
|
||||
cfg.setValue(cfg.WINDOW_GEOMETRY, self.saveGeometry())
|
||||
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())
|
||||
_log.info('save settings')
|
||||
#QMainWindow.closeEvent(self, event)
|
||||
@@ -755,19 +698,13 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
self.setStyleSheet(sheet.read())
|
||||
|
||||
def setup_sliders(self):
|
||||
cont = self._rightmost
|
||||
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)
|
||||
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.init_settings()
|
||||
@@ -1506,46 +1443,44 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def cb_esc_sample_exchange(self):
|
||||
app=QApplication.instance()
|
||||
self._esc_state ="busy"
|
||||
steps = []
|
||||
#if option(CRYOJET_MOTION_ENABLED):
|
||||
# steps.append(lambda: self.move_cryojet_nozzle("out"))
|
||||
steps.extend(
|
||||
[
|
||||
steps=[
|
||||
lambda:self.move_detector("out"),
|
||||
lambda: self.move_post_tube("out"),
|
||||
lambda: app._backlight.move("out", wait=True),
|
||||
lambda: app._backlight.move("out"),
|
||||
lambda: self.move_collimator("out"),
|
||||
]
|
||||
)
|
||||
self.esc_run_steps(steps, "Transitioning to Sample Exchange")
|
||||
self._esc_state ="ManualSampleExchange"
|
||||
self.esc_run_steps(steps, "Transitioning to Sample Exchange","ManualSampleExchange")
|
||||
|
||||
def cb_esc_sample_alignment(self):
|
||||
app=QApplication.instance()
|
||||
self._esc_state ="busy"
|
||||
steps = [
|
||||
# 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):
|
||||
# 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")
|
||||
self._esc_state ="SampleAlignment"
|
||||
#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()
|
||||
self._esc_state ="busy"
|
||||
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")
|
||||
self._esc_state ="DataCollection"
|
||||
self.esc_run_steps(steps, "Transitioning to Data Collection","DataCollection")
|
||||
|
||||
def cb_really_quit(self):
|
||||
"""called when user Ctrl-Q the app"""
|
||||
@@ -1555,9 +1490,43 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def cb_deltatau_home_faststages(self):
|
||||
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")
|
||||
|
||||
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):
|
||||
try:
|
||||
tc=self._testCode
|
||||
@@ -1602,8 +1571,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
#print(vb.childGroup.childItems())
|
||||
pass
|
||||
|
||||
def prepare_left_tabs(self):
|
||||
tabs = self._left_tabs
|
||||
def prepare_wd_tabs_left(self):
|
||||
tabs = self._wd_tabs_left
|
||||
tabs.currentChanged.connect(self.cb_switch_task)
|
||||
|
||||
setup_tab = self._tab_setup
|
||||
@@ -1832,8 +1801,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
|
||||
def cb_switch_task(self, index=0):
|
||||
stack = self._centerpiece_stack
|
||||
task = self._left_tabs.currentWidget().accessibleName()
|
||||
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()
|
||||
|
||||
@@ -1992,7 +1961,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
dt_misc = cfg.value(AppCfg.DT_MISC)
|
||||
|
||||
geo = app._geometry
|
||||
verbose=0xff
|
||||
fn='/tmp/shapepath'
|
||||
try:
|
||||
dt=app._deltatau
|
||||
@@ -2006,12 +1974,9 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
sp=dt._shapepath
|
||||
sp.verbose=dt_misc['verbose']
|
||||
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.setup_gather()
|
||||
sp.setup_sync(verbose=verbose&32, timeOfs=0.05)
|
||||
sp.setup_sync(verbose=sp.verbose&0x20, timeOfs=0.05)
|
||||
try:
|
||||
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
|
||||
@@ -2037,7 +2002,8 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
plt.show(block=False)
|
||||
#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:
|
||||
for step in steps:
|
||||
step()
|
||||
@@ -2045,8 +2011,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
if dlg.wasCanceled():
|
||||
QMessageBox.warning(self, "escape steps", "ABORTED" + title)
|
||||
break
|
||||
|
||||
#backlight="{'pos_in': -10.0, 'pos_out': 0.0}"
|
||||
self._esc_state = esc_state
|
||||
|
||||
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}"
|
||||
@@ -3554,7 +3519,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
self.re_connect_collect_button()
|
||||
|
||||
def _OLD_create_escape_toolbar(self):
|
||||
cont = self._rightmost
|
||||
cont = self._wd_right
|
||||
w = QGroupBox("Escape")
|
||||
layout = QHBoxLayout()
|
||||
w.setLayout(layout)
|
||||
@@ -3725,7 +3690,6 @@ class WndSwissMx(QMainWindow, Ui_MainWindow):
|
||||
|
||||
if __name__=="__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__} epics:{epics.__version__} qt:{QT_VERSION_STR}' )
|
||||
import argparse, socket
|
||||
|
||||
215
swissmx.ui
215
swissmx.ui
@@ -16,7 +16,7 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="_wd_hlayout">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -46,7 +46,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="_main_splitter">
|
||||
<widget class="QSplitter" name="_wd_splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -65,7 +65,7 @@
|
||||
<property name="childrenCollapsible">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QTabWidget" name="_left_tabs">
|
||||
<widget class="QTabWidget" name="_wd_tabs_left">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -107,81 +107,13 @@
|
||||
<string>Data Storage</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="2" column="0">
|
||||
<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">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="_le_prefix"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Filenames</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="_le_runnumber"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
@@ -191,46 +123,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>P-group</string>
|
||||
<string>Prefix</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -239,89 +141,7 @@
|
||||
<property name="title">
|
||||
<string>Experimental Parameters</string>
|
||||
</property>
|
||||
<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>
|
||||
<layout class="QGridLayout" name="gridLayout_4"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -614,16 +434,16 @@
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QStackedWidget" name="_centerpiece_stack">
|
||||
<widget class="QStackedWidget" name="_wd_stack">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="microscope_page"/>
|
||||
<widget class="QWidget" name="sample_select">
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="_rightmost" native="true">
|
||||
<widget class="QWidget" name="_wd_right" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -664,7 +484,6 @@
|
||||
<string>&Advanced</string>
|
||||
</property>
|
||||
<addaction name="actionHome_Fast_Stages"/>
|
||||
<addaction name="actionUser_Storage"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuSetup">
|
||||
<property name="title">
|
||||
@@ -718,16 +537,12 @@
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>_le_project</tabstop>
|
||||
<tabstop>_le_prefix</tabstop>
|
||||
<tabstop>_dsb_exposure_time</tabstop>
|
||||
<tabstop>_dsb_oscillation_step</tabstop>
|
||||
<tabstop>_tabs_daq_methods</tabstop>
|
||||
<tabstop>_sb_grid_y_step</tabstop>
|
||||
<tabstop>_left_tabs</tabstop>
|
||||
<tabstop>_label_actual_prefix</tabstop>
|
||||
<tabstop>_wd_tabs_left</tabstop>
|
||||
<tabstop>_grid_inspect_area</tabstop>
|
||||
<tabstop>_label_runnumber</tabstop>
|
||||
<tabstop>_le_runnumber</tabstop>
|
||||
<tabstop>_bt_add_grid</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
||||
Reference in New Issue
Block a user