diff --git a/CustomROI.py b/CustomROI.py
deleted file mode 100644
index 20cf3f4..0000000
--- a/CustomROI.py
+++ /dev/null
@@ -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))
diff --git a/GenericDialog.py b/GenericDialog.py
deleted file mode 100644
index cafb121..0000000
--- a/GenericDialog.py
+++ /dev/null
@@ -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_())
-
diff --git a/ModuleFixTarget.py b/ModuleFixTarget.py
index 45869e7..08bc213 100644
--- a/ModuleFixTarget.py
+++ b/ModuleFixTarget.py
@@ -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
diff --git a/app_config.py b/app_config.py
index 02d2cf7..701e08f 100644
--- a/app_config.py
+++ b/app_config.py
@@ -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"
diff --git a/deltatau.py b/deltatau.py
index b35f1dd..bdb42a0 100644
--- a/deltatau.py
+++ b/deltatau.py
@@ -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
diff --git a/swissmx.py b/swissmx.py
index 6062fd6..1d0b0f4 100755
--- a/swissmx.py
+++ b/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
diff --git a/swissmx.ui b/swissmx.ui
index 0bd26df..cffa71a 100644
--- a/swissmx.ui
+++ b/swissmx.ui
@@ -16,7 +16,7 @@
0
-
+
0
@@ -46,7 +46,7 @@
0
-
-
+
0
@@ -65,7 +65,7 @@
true
-
+
0
@@ -107,81 +107,13 @@
Data Storage
-
-
-
-
- Prefix
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- Folder
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Folder where the data will be stored; triple-click to select.
-
-
- Folder where the data will be stored; triple-click to select. Type 'folder' in a console to go here.
-
-
- TextLabel
-
-
- Qt::PlainText
-
-
- true
-
-
- Qt::TextSelectableByMouse
-
-
-
- -
-
-
- Project
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- -
+
-
- -
-
-
- Filenames
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
+
-
+
- -
+
-
Run
@@ -191,46 +123,16 @@
- -
-
-
- false
-
-
- true
-
-
-
- -
-
-
- false
-
-
- true
-
-
-
-
-
+
- P-group
+ Prefix
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
-
- pgroup
-
-
- p-group-undefined
-
-
-
@@ -239,89 +141,7 @@
Experimental Parameters
-
- -
-
-
-
- 0
- 0
-
-
-
- s
-
-
- 3
-
-
- 0.010000000000000
-
-
- 1000.000000000000000
-
-
- 0.100000000000000
-
-
- 0.100000000000000
-
-
-
- -
-
-
- Oscillation Step
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- Exposure Time
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- Blast radius
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- µm
-
-
- 200.000000000000000
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- deg
-
-
-
-
+
-
@@ -614,16 +434,16 @@
-
+
- 1
+ 0
-
+
0
@@ -664,7 +484,6 @@
&Advanced
-
- _le_project
_le_prefix
- _dsb_exposure_time
- _dsb_oscillation_step
_tabs_daq_methods
_sb_grid_y_step
- _left_tabs
- _label_actual_prefix
+ _wd_tabs_left
_grid_inspect_area
- _label_runnumber
+ _le_runnumber
_bt_add_grid