optimize imports and cleanup code

This commit is contained in:
2024-09-25 12:36:48 +02:00
parent 457ea0e675
commit 3fc4f50c15
16 changed files with 37 additions and 4994 deletions

View File

@@ -1,445 +0,0 @@
import logging
import numpy
import os
import random
import struct
import time
import yaml
#from scipy.misc import bytescale
import zmq
from os.path import join
from pathlib import Path
#import qsingleton #ZAC: orig. code
#from CoordinatesModel import CoordinatesModel #ZAC: orig. code
from app_config import AppCfg #settings, option, appsconf
EMBL_RESULTS_TIMEOUT = "embl/results_timeout"
EMBL_SAVE_FILES = "embl/save_files"
#embl = appsconf["embl"]
#import imageio
import numpy as np
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal, Qt, QObject, pyqtSlot, QPoint
from PyQt5.QtGui import QStandardItemModel, QCursor
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QHeaderView,
QPushButton,
QFormLayout,
QDoubleSpinBox,
QLabel,
QTableWidgetItem,
QLineEdit,
QTableWidget,
QTextEdit,
QProgressBar,
QHBoxLayout,
QSpinBox,
QTabWidget,
QTableView,
QMenu,
QProgressDialog,
)
#import storage
from app_utils import assert_motor_positions
#folders = storage.Folders()
import logging
logger = logging.getLogger(__name__)
class EmblTaskResultTimeoutException(Exception):
pass
ZPOS, FPATH = range(2)
# record separator
RS = 0x1e
#class EmblMessage(QObject, metaclass=qsingleton.Singleton): #ZAC: orig. code
class EmblMessage(QObject):
def __init__(self, parent=None, **kwargs):
super(EmblMessage, self).__init__(parent, **kwargs)
return #ZAC: orig. code
if not embl["enabled"]:
return
context = zmq.Context()
logger.info("Connecting to server...")
self.socket = context.socket(zmq.REQ)
uri = embl["uri"]
logger.info(f"EMBL alignment server at: {uri}")
self.socket.connect(uri)
def make_PAR(self, min: float, max: float, step: float, ppm: int, beam_xy: tuple):
puckid, sampleid = folders._prefix.split("_")
beam_x, beam_y = beam_xy
msg = (
f"PAR{RS:c}{min}{RS:c}{max}{RS:c}{step}{RS:c}{ppm}{RS:c}{beam_x:.0f}{RS:c}{beam_y:.0f}{RS:c}{puckid}{RS:c}{sampleid}"
)
logger.debug(f"PAR = {msg}")
self.socket.send_string(msg)
ans = self.socket.recv_string()
def make_IMG(self, pos: float, img):
width, height = img.shape
msg = f"IMG{RS:c}{pos:.3f}{RS:c}{width}{RS:c}{height}"
logger.info(f"sending img header: {msg}")
self.socket.send_string(msg)
self.socket.recv_string()
logger.info(
f"about to send img shape={img.shape} type={img.dtype} pos = {pos} mm"
)
self.socket.send(numpy.ascontiguousarray(img))
logger.info(f"waiting for recv")
ans = self.socket.recv_string()
logger.info(f"received!")
def finished(self):
self.make_STS("Finished")
def aborted(self):
self.make_STS("Aborted")
def make_STS(self, status):
msg = f"STS{RS:c}{status}"
self.socket.send_string(msg)
ans = self.socket.recv_string()
def make_RES(self):
n = 1
timeout = settings.value(EMBL_RESULTS_TIMEOUT, type=float) + time.time()
result = "failed"
while time.time() < timeout:
time.sleep(2.0)
logger.info(f"#{n}.requesting RES")
self.socket.send_string("RES")
result = self.socket.recv_string()
logger.info(f" RES = {result}")
if "Pending" != result:
break
n += 1
return result
embl_tube = EmblMessage()
class EmblWidget(QWidget):
def __init__(self, parent):
super(EmblWidget, self).__init__(parent)
#parent.pixelsPerMillimeter.connect(self.save_ppm)
#parent.beamCameraCoordinatesChanged.connect(self.save_beam_coordinates)
self.setLayout(QVBoxLayout())
w = QWidget()
self.layout().addWidget(w)
w.setLayout(QFormLayout())
w.layout().setLabelAlignment(Qt.AlignRight)
self._startZ = QDoubleSpinBox()
self._startZ.setValue(0.6)
self._startZ.setSuffix(" mm")
self._startZ.setRange(-5, 5)
self._startZ.setSingleStep(0.1)
self._startZ.setDecimals(3)
self._endZ = QDoubleSpinBox()
self._endZ.setValue(1.4)
self._endZ.setSuffix(" mm")
self._endZ.setRange(-5, 5)
self._endZ.setSingleStep(0.1)
self._endZ.setDecimals(3)
self._stepZ = QDoubleSpinBox()
self._stepZ.setValue(0.050)
self._stepZ.setSuffix(" mm")
self._stepZ.setRange(0.005, 1.000)
self._stepZ.setSingleStep(0.010)
self._stepZ.setDecimals(3)
self._resultsTimeout = QDoubleSpinBox()
self._resultsTimeout.setRange(1., 2000.)
self._resultsTimeout.setValue(60)
self._resultsTimeout.setSuffix(" seconds")
self._resultsTimeout.setDecimals(0)
self._factors = QLineEdit("1 1 1")
but = QPushButton("update results")
but.clicked.connect(self.apply_factors)
w.layout().addRow(QLabel("Start Z"), self._startZ)
w.layout().addRow(QLabel("End Z"), self._endZ)
w.layout().addRow(QLabel("Step"), self._stepZ)
w.layout().addRow(QLabel("Task Result Timeout"), self._resultsTimeout)
w.layout().addRow(QLabel("Factors"), self._factors)
w.layout().addRow(QLabel("Apply Factors"), but)
but = QPushButton("Scan Z")
but.clicked.connect(self.executeScan)
but.setAccessibleName("emblScanZ")
self.layout().addWidget(but)
tabs = QTabWidget(self)
self.layout().addWidget(tabs)
self._table = QTableWidget()
self._table.setColumnCount(2)
self._table.setHorizontalHeaderLabels(("Z (mm)", "File Path"))
self._table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.layout().addWidget(self._table)
tabs.addTab(self._table, "Z Scans")
tabs.setAccessibleName("embl_tabs")
tabs.setObjectName("embl_tabs")
view = self._results_view = QTableView(self)
#view.setModel(CoordinatesModel()) #ZAC: orig. code
view.verticalHeader().show()
view.horizontalHeader().show()
view.setContextMenuPolicy(Qt.CustomContextMenu)
view.customContextMenuRequested.connect(self.results_menu)
tabs.addTab(view, "Results")
self._tabs = tabs
def apply_factors(self):
self.populate_results(self._results)
@pyqtSlot(float)
def save_ppm(self, ppm: float):
self._ppm = int(ppm)
@pyqtSlot(float, float)
def save_beam_coordinates(self, bx: float, by: float):
logger.debug(f"saving beam_xy {bx} & {by}")
self._beam_xy = (int(bx), int(by))
@pyqtSlot(QPoint)
def results_menu(self, at_point):
table = self.sender()
model = table.model()
index = table.indexAt(at_point)
if not index.isValid():
return
coords = model.coords_at(index)
logger.debug(f"coord: {coords}")
menu = QMenu(table)
gotoAction = menu.addAction("Go here")
if menu.exec_(QCursor.pos()) is gotoAction:
logger.info(f"going to: {index.row()} => {coords}")
self.goto_position(coords)
def configure(self, motors, camera, zoom_device):
self._camera = camera
self._zoom_device = zoom_device
self.motors = motors
def executeScan(self):
fx, fy, cx, cz, omega = self.motors
x, y, z = fx.get_position(), fy.get_position(), cz.get_position()
logger.info(f"scan started at gonio: x,y,z = {x}, {y}, {z}")
self._table.setRowCount(0) # clear previous information
zs = self._startZ.value()
ze = self._endZ.value()
step = self._stepZ.value()
scan_config = {
"z-start": zs,
"z-end": ze + step, # inclusive last value
"z-step": step,
"ppm": self._ppm,
"beam_xy": self._beam_xy,
}
self._gonio_at_start = (x, y, ze + step)
base_Z = self.motors[3] # base_Z
camera = self._camera
zvals = np.arange(zs, ze, step)
numZs = len(zvals)
dlg = QProgressDialog(self)
dlg.setWindowModality(Qt.WindowModal)
dlg.setMinimumDuration(0)
dlg.setCancelButton(None)
dlg.setRange(0, 0)
dlg.setLabelText(f"<b>EMBL Z-Scan</b><br/>")
dlg.setAutoClose(True)
dlg.show()
dlg.setValue(random.randint(1, 20))
self._progress_dialog = dlg
self._acq_thread = AcquisitionThread(base_Z, camera, scan_config)
self._acq_thread.image_acquired.connect(self.addImage)
self._acq_thread.acquisition_finished.connect(self.acquisition_finished)
self._acq_thread.results_fetched.connect(self.populate_results)
self._acq_thread.message.connect(
lambda msg: dlg.setLabelText(f"<b>EMBL Z-Scan</b><br/>{msg}")
)
self._acq_thread.start()
def createModel(self, parent):
model = QStandardItemModel(0, 2, parent)
model.setHeaderData(ZPOS, QtCore.Qt.Horizontal, "Z (mm)")
model.setHeaderData(FPATH, QtCore.Qt.Horizontal, "Image Path")
self._model = model
return model
def addImage(self, idx, zpos, fpath):
logger.info("zpos = {}, fpath = {}".format(zpos, fpath))
rows = self._table.rowCount()
# table.setRowCount(rows + 1)
self._table.insertRow(rows)
item = QTableWidgetItem()
item.setText("{:.03f}".format(zpos))
self._table.setItem(rows, ZPOS, item)
item = QTableWidgetItem()
item.setText(str(fpath))
self._table.setItem(rows, FPATH, item)
self._table.resizeColumnsToContents()
def acquisition_finished(self):
self._progress_dialog.reset()
def goto_position(self, coords):
fx, fy, cx, cz, o = self.motors
tx, ty, bb, tz, to = coords
logger.info(f"moving gonio to: {tx}, {ty}, {tz}")
fx.move(tx, wait=True, ignore_limits=True)
fy.move(ty, wait=True, ignore_limits=True)
cz.move(tz, wait=True, ignore_limits=True)
@pyqtSlot(str)
def populate_results(self, results):
"""Results.txt
60.624%363.4026940025185%-768.32%-1052.52
angle between sample and camera, x, y, z
:return:
"""
if "no results" in results:
return
s1, s2, s3 = [int(c) for c in self._factors.text().split()]
self._results = results
self.coords = []
ox, oy, oz = self._gonio_at_start
oz = self._endZ.value()
for n, line in enumerate(results.split("\n")):
# o, z, y, x = [float(n) for n in line.split("%")]
omega, x, y, z = [float(n) for n in line.split("%")]
no, nx, ny, nz = [omega, ox + s1*x, oy + s2*y, oz + s3*z]
self.coords.append([nx, ny, 0, nz, no])
model = self._results_view.model()
model.update_coordinates(self.coords)
self._tabs.setCurrentIndex(1)
class AcquisitionThread(QtCore.QThread):
image_acquired = pyqtSignal(int, float, str)
results_fetched = pyqtSignal(str)
message = pyqtSignal(str)
acquisition_finished = pyqtSignal()
def __init__(self, z_motor, camera, scan_config):
QtCore.QThread.__init__(self)
self._scanconfig = scan_config
logger.debug(f"scanconfig: {scan_config}")
self.z_motor = z_motor
self.camera = camera
def save_params(self, params):
fname = Path(folders.res_folder) / "z_scan" / "parameters.yaml"
try:
with open(fname, "w") as fp:
yaml.dump(params, fp)
except:
logger.warning(f"failed to save z-scan parameter file: {fname}")
def save_image(self, n, z, img):
if not option(EMBL_SAVE_FILES):
return
zp = f"{n:02d}_Z{z:.3f}".replace(".", "p") + ".png"
fname = Path(folders.res_folder) / "z_scan" / zp
try:
imageio.imsave(fname, img)
except:
logger.error(f"failed to write: {fname}")
return f"failed to write image {n}"
return fname
def save_result(self, result):
if not option(EMBL_SAVE_FILES):
return
fname = Path(folders.res_folder) / "z_scan" / "result.txt"
with open(fname, "w") as fp:
fp.write(result)
def make_folders(self):
if not option(EMBL_SAVE_FILES):
return
fold = Path(folders.res_folder) / "z_scan"
fold.mkdir(parents=True, exist_ok=True)
def run(self):
self.make_folders()
cnf = self._scanconfig
zmin, zmax, zstep = cnf["z-start"], cnf["z-end"], cnf["z-step"]
zvals = np.arange(zmin, zmax, zstep)
num = len(zvals)
logger.debug(
f"Z-scan: min/max/step {zmin}/{zmax}/{zstep} ppm={cnf['ppm']} beam_xy={cnf['beam_xy']}"
)
logger.info(f"Z-vals: {', '.join([f'{z:.2f}' for z in zvals])}")
self.save_params(cnf)
embl_tube.make_PAR(zmin, zmax, zstep, cnf["ppm"], cnf["beam_xy"])
for n, z in enumerate(zvals):
idx = n + 1
self.message.emit(f"{idx}/{num} Z ➠ {z:.2f}")
self.z_motor.move(z, ignore_limits=True, wait=True)
assert_motor_positions([(self.z_motor, z, 0.1)], timeout=3.)
self.message.emit(f"{idx}/{num} acquiring")
img = self.camera.get_image()
self.message.emit(f"{idx}/{num} acquired")
img = np.rot90(img, 1)
img = bytescale(img)
fname = self.save_image(idx, z, img)
self.image_acquired.emit(idx, z, str(fname)) # just to populate table
self.message.emit(f"{idx}/{num} sending")
embl_tube.make_IMG(z, img)
self.message.emit(f"{idx}/{num} sent")
self.message.emit(f"acquisition finished")
embl_tube.finished()
self.message.emit(f"waiting for results")
result = embl_tube.make_RES()
self.save_result(result)
self.results_fetched.emit(result)
self.acquisition_finished.emit()

View File

@@ -1,256 +0,0 @@
import sys
import os
import logging
import csv
from PyQt5 import QtCore, QtGui
import numpy as np
logger = logging.getLogger("helical")
from PyQt5.QtWidgets import (
QTableWidget,
QApplication,
QMainWindow,
QTableWidgetItem,
QFileDialog,
QHeaderView,
QAbstractItemView,
QMenu)
from PyQt5.QtCore import Qt, QFileInfo, pyqtSignal, QPoint, pyqtSlot
DATA_ITEM = 1
START_OMEGA_0 = 0
START_OMEGA_120 = 1
START_OMEGA_240 = 2
STOP_OMEGA_0 = 3
STOP_OMEGA_120 = 4
STOP_OMEGA_240 = 5
ROLE_XTAL_START = 1 + Qt.UserRole
ROLE_XTAL_END = 2 + Qt.UserRole
class HelicalTableWidget(QTableWidget):
gonioMoveRequest = pyqtSignal(float, float, float, float, float)
def __init__(self):
super().__init__()
self.check_change = True
self._scanAngularStep = 0.1
self._scanHorizontalCount = 5
self._scanVerticalCount = 10
self._current_xtal = None
self._start_angle = 0.0
self.init_ui()
def startAngle(self) -> float:
return self._start_angle
@pyqtSlot(float)
def setStartAngle(self, angle:float):
self._start_angle = angle
def init_ui(self):
self.setColumnCount(6)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.resizeColumnsToContents()
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setSelectionBehavior(QAbstractItemView.SelectItems)
labels = [
"0\nbx, bz\n(mm)",
"120\nbx, bz\n(mm)",
"240\nbx, bz\n(mm)",
"0\nbx, bz\n(mm)",
"120\nbx, bz\n(mm)",
"240\nbx, bz\n(mm)",
]
self.setHorizontalHeaderLabels(labels)
self.verticalHeader().resizeSections(QHeaderView.ResizeToContents)
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.cellChanged.connect(self.c_current)
self.show()
def display_coords(self, x, y):
row = self.rowAt(y)
col = self.columnAt(x)
coords = self.gonio_coords_at(row, col)
omega = col % 3 * 120
data = coords[omega]
logger.info("gonio at {}, {}: {}".format(row, col, data))
def goto_gonio_position(self, x, y):
row = self.rowAt(y)
col = self.columnAt(x)
coords = self.gonio_coords_at(row, col)
omega = col % 3 * 120
data = coords[omega]
logger.info("move gonio to: {}".format(row, col, data))
self.gonioMoveRequest.emit(*data)
def remove_xtal(self, x, y):
row = self.rowAt(y)
col = self.columnAt(x)
logger.info("rowCount() = {}, removing row: {}".format(self.rowCount(), row))
self.removeRow(row)
if self.rowCount() == 0:
self._current_xtal = None
logger.info("rowCount() = {}".format(self.rowCount()))
def contextMenuEvent(self, event):
logger.info(event.pos())
x = event.pos().x()
y = event.pos().y()
menu = QMenu(self)
gotoAction = menu.addAction("Move Gonio Here")
gotoAction.triggered.connect(lambda b: self.goto_gonio_position(x, y))
removeAction = menu.addAction("Remove this line")
removeAction.triggered.connect(lambda b: self.remove_xtal(x, y))
menu.popup(QtGui.QCursor.pos())
def setScanHorizontalCount(self, count: int):
logger.debug("horizontal count: {}".format(count))
self._scanHorizontalCount = count
def scanHorizontalCount(self) -> int:
return self._scanHorizontalCount
def setScanVerticalCount(self, count: int):
logger.debug("vertical count: {}".format(count))
self._scanVerticalCount = count
def scanVerticalCount(self) -> int:
return self._scanVerticalCount
def setScanAngularStep(self, step: float):
self._scanAngularStep = step
def scanAngularStep(self) -> float:
return self._scanAngularStep
def scanTotalRange(self) -> float:
return self._scanVerticalCount * self._scanHorizontalCount * self._scanAngularStep
def gonio_coords_at(self, row, col):
role = ROLE_XTAL_START if col < 3 else ROLE_XTAL_END
coords = self.item(row, DATA_ITEM).data(role)
return coords
def add_xtal(self):
row = self.rowCount()
self.setRowCount(row + 1)
self._current_xtal = row
print(row)
for n in range(self.columnCount()):
self.setItem(row, n, QTableWidgetItem("unset"))
def set_xtal_start(self, data: list):
"""sets data to start position
data = [fx, fy, bx, bz, omega]
"""
self.set_data_point(ROLE_XTAL_START, data)
def set_xtal_end(self, data: list):
self.set_data_point(ROLE_XTAL_END, data)
def set_data_point(self, role: int, data: list):
if self._current_xtal is None:
self.add_xtal()
row, col = self._current_xtal, DATA_ITEM
try:
coords = self.item(row, DATA_ITEM).data(role)
except:
print("empty coords; initializing...")
coords = None
if coords is None:
coords = {}
fx, fy, bx, bz, omega = data
o = int(omega)
coords[o] = data
print("coords = {}".format(coords))
if role == ROLE_XTAL_END:
col = 3
else:
col = 0
for n in range(3):
omega = n * 120
try:
fx, fy, bx, bz, omega = coords[omega]
info = "{bx:.3f} mm\n{bz:.3f} mm".format(bx=bx, bz=bz)
except:
info = "unset"
# self.item(row, col + n).setData(Qt.DisplayRole, info)
self.item(row, col + n).setText(info)
self.item(row, col + n).setToolTip(info)
self.item(row, DATA_ITEM).setData(role, coords)
def c_current(self):
if self.check_change:
row = self.currentRow()
col = self.currentColumn()
value = self.item(row, col)
# value = value.text()
def load_datapoints(self):
self.check_change = False
path = QFileDialog.getOpenFileName(
self, "Open CSV", os.getenv("HOME"), "CSV(*.csv)"
)
if path[0] != "":
with open(path[0], newline="") as csv_file:
self.setRowCount(0)
self.setColumnCount(10)
my_file = csv.reader(csv_file, delimiter=",", quotechar="|")
for row_data in my_file:
row = self.rowCount()
self.insertRow(row)
if len(row_data) > 10:
self.setColumnCount(len(row_data))
for column, stuff in enumerate(row_data):
item = QTableWidgetItem(stuff)
self.setItem(row, column, item)
self.check_change = True
def get_data(self, as_numpy=False):
"""return a list of tuples with all defined data
[
(is_fiducial(boolean), x, y, gonio_x, gonio_y),...
]
"""
data = []
for row in range(self.rowCount()):
start = self.item(row, DATA_ITEM).data(ROLE_XTAL_START)
end = self.item(row, DATA_ITEM).data(ROLE_XTAL_END)
data.append([start, end])
# if as_numpy:
# data = np.asarray(data)
return data
if __name__ == "__main__":
class Sheet(QMainWindow):
def __init__(self):
super().__init__()
self.form_widget = HelicalTableWidget()
self.setCentralWidget(self.form_widget)
self.show()
app = QApplication(sys.argv)
sheet = Sheet()
sys.exit(app.exec_())

View File

@@ -22,12 +22,6 @@ init:
-ssh $(REMOTE) 'git clone -o psigithub git@git.psi.ch:epics_support_apps/PBTools.git $(APP)/SwissMX/PBTools'
-ssh $(REMOTE) 'git clone -o psigithub git@git.psi.ch:epics_support_apps/ppmac.git $(APP)/SwissMX/PBTools/ppmac'
-rsync -vai simCamImg $(REMOTE):$(APP)/SwissMX/
# -rsync -vai ../../PBTools $(REMOTE):$(APP)/SwissMX/
# -ssh $(REMOTE) 'git clone -o psigithub git@git.psi.ch:epics_support_apps/PBTools.git $(APP)/PBTools'
# ssh $(REMOTE) 'pip3.6 install --user pandas zmq qtawesome'
# ssh $(REMOTE) '/opt/gfa/python-3.8/latest/bin/pip install --user pandas zmq qtawesome'
# ssh $(REMOTE) 'pip3.6 install --prefix /sf/cristallina/applications/SwissMX/module pandas zmq qtawesome'
# ssh $(REMOTE) '/opt/gfa/python-3.8/latest/bin/pip install --prefix /sf/cristallina/applications/SwissMX/module pandas zmq qtawesome'
update:
ssh $(REMOTE) 'cd $(APP)/SwissMX && git fetch psigithub && git reset psigithub/master --hard'

View File

@@ -13,54 +13,17 @@ This contains a Widget to handle FixTargetFrames and fiducials, calculate final
#https://stackoverflow.com/questions/27909658/json-encoder-and-decoder-for-complex-numpy-arrays
# import yaml
# class Dice(tuple):
# def __new__(cls, a, b):
# return tuple.__new__(cls, [a, b])
# def __repr__(self):
# return "Dice(%s,%s)" % self
# d=Dice(3,6)
# print(d)
# print(yaml.dump(d))
# def dice_representer(dumper, data):
# return dumper.represent_scalar(u'!dice', u'%sd%s' % data)
# yaml.add_representer(Dice, dice_representer)
# def dice_constructor(loader, node):
# value = loader.construct_scalar(node)
# a, b = map(int, value.split('d'))
# return Dice(a, b)
# yaml.add_constructor(u'!dice', dice_constructor)
# print(yaml.load("initial hit points: !dice 8d4"))
import logging
_log=logging.getLogger(__name__)
import json, base64 #, os, pickle, yaml
import json, base64
import numpy as np
import pyqtUsrObj as UsrGO
import pyqtgraph as pg
from PyQt5.QtCore import Qt, QFileInfo, pyqtSignal, pyqtSlot
from PyQt5.QtCore import Qt, QFileInfo, pyqtSignal
from PyQt5.QtWidgets import (
QTableWidgetItem,
QFileDialog,
QGroupBox,
QWidget,
QGridLayout,
QTableWidget,
QAbstractItemView,
QItemDelegate,
QVBoxLayout,
QLabel,
QPushButton,
QApplication,
QMainWindow,
QHeaderView,
QLineEdit,
QSpinBox,
QMessageBox,
QAction,
QComboBox,
QCheckBox)
QFileDialog, QWidget, QGridLayout, QItemDelegate, QVBoxLayout, QPushButton, QApplication,
QMainWindow, QLineEdit, QMessageBox, QAction, QComboBox)
class MyJsonEncoder(json.JSONEncoder):
@@ -109,8 +72,6 @@ class MarkerDelegate(QItemDelegate):
return comboBox
def setEditorData(self, editor, index):
# pos = comboBox.findText(index.model().data(index), Qt.MatchExactly)
# comboBox.setCurrentIndex(pos)
editor.setText("{}".format(index.model().data(index)))
def setModelData(self, editor, model, index):
@@ -158,27 +119,11 @@ class WndFixTarget(QWidget):
btnSv.clicked.connect(lambda: self.save_file())
bl.addWidget(btnSv, 0, 1)
# but = QPushButton("Random Data")
# but.clicked.connect(self.generate_random_data)
# bl.addWidget(but, 4, 0)
# self._num_random_points = QSpinBox()
# self._num_random_points.setMinimum(2)
# self._num_random_points.setMaximum(10000)
# self._num_random_points.setValue(10)
# self._num_random_points.setSuffix(" points")
# bl.addWidget(self._num_random_points, 4, 1)
btnDump = QPushButton("dump")
btnDump.clicked.connect(self.dump_data)
bl.addWidget(btnDump, 0, 2)
self._cbType=cb=QComboBox()
#cb.addItem("C")
#cb.addItem("C++")
#cb.addItems(["Fiducial", "FixTarget(12.5x12.5)", "FixTarget(23.0x23.0)", "FixTarget(<param>)", "Grid(<param>)"])
#cb.currentIndexChanged.connect(self.selectionchange)
bl.addWidget(cb, 1,0,1,1)
self._txtParam=param=QLineEdit()
@@ -196,23 +141,7 @@ class WndFixTarget(QWidget):
bl.addWidget(btnFit, 2, 1,1,1)
layout.addWidget(frame)
#self.markersTable.itemSelectionChanged.connect(self.selection_changed)
#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()
#self._yamlFn=fn=os.path.expanduser('~/.config/PSI/SwissMX.yaml')
#self._tree=tree=pg.DataTreeWidget(data=self._data)
self._data=data
self._tree=tree=pg.DataTreeWidget(data=data)
layout.addWidget(tree, stretch=2)

View File

@@ -1,709 +0,0 @@
# coding=utf-8
import logging
import os
from os.path import join
from typing import List
import numpy as np
try:
import pandas as pd
except ImportError as e:
print (f'{e} import failed')
#import transformations as tfs
from PyQt5.QtCore import Qt, QFileInfo, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
QTableWidgetItem,
QFileDialog,
QGroupBox,
QWidget,
QGridLayout,
QTableWidget,
QAbstractItemView,
QItemDelegate,
QVBoxLayout,
QLabel,
QPushButton,
QApplication,
QMainWindow,
QHeaderView,
QLineEdit,
QSpinBox,
QMessageBox,
QAction,
QCheckBox)
logger = logging.getLogger(__name__)
# from coord_library import conversions
#import conversions #ZAC: orig. code
from app_config import AppCfg
#from storage import Folders #ZAC: orig. code
#folders = Folders() #ZAC: orig. code
XY_Coord = List[float]
DATA_ITEM = 1
ORIGINAL_X = 1
ORIGINAL_Y = 2
GONIO_X = 3
GONIO_Y = 4
RoleGoniometerCoord_X = 1 + Qt.UserRole
RoleGoniometerCoord_Y = 2 + Qt.UserRole
RoleCameraCoord_X = 3 + Qt.UserRole
RoleCameraCoord_Y = 4 + Qt.UserRole
RoleOriginalCoord_X = 5 + Qt.UserRole
RoleOriginalCoord_Y = 6 + Qt.UserRole
def sort_cmass_3d(coord):
"""sorts the 3D coordinates (np array 3 columns) with respect to their distane to the center of mass in 3d"""
cm = conversions.find_geo_center(coord)
# print cm
moved_coord = conversions.center_coord(coord, cm)
sorted_moved_coord = conversions.sort_center_3d(moved_coord)
minus_cm = np.multiply(cm, -1)
return conversions.center_coord(sorted_moved_coord, minus_cm)
def sort_cmass(coord):
"""sorts the 2D coordinates (np array 2 columns) with respect to their distane to the center of mass"""
cm = conversions.find_geo_center(coord)
# print cm
moved_coord = conversions.center_coord(coord, cm)
sorted_moved_coord = conversions.sort_center(moved_coord)
minus_cm = np.multiply(cm, -1)
return conversions.center_coord(sorted_moved_coord, minus_cm)
class MarkerDelegate(QItemDelegate):
def createEditor(self, parent, option, index):
comboBox = QLineEdit(parent)
comboBox.editingFinished.connect(self.emitCommitData)
return comboBox
def setEditorData(self, editor, index):
# pos = comboBox.findText(index.model().data(index), Qt.MatchExactly)
# comboBox.setCurrentIndex(pos)
editor.setText("{}".format(index.model().data(index)))
def setModelData(self, editor, model, index):
print("model {}".format(model))
print(
"delegate setData: {}x{} => {}".format(
index.row(), index.column(), editor.text()
)
)
model.setData(index, editor.text())
model.setData(index, float(editor.text()), Qt.UserRole)
def emitCommitData(self):
print("imagedelegate emitting")
self.commitData.emit(self.sender())
class PrelocatedCoordinates(QWidget):
prefixSelected = pyqtSignal(str)
dataFileLoaded = pyqtSignal(str)
prelocatedDataUpdated = pyqtSignal()
markersDeleted = pyqtSignal()
markerAdded = pyqtSignal(bool, list) # emits is_fiducial(boolean), [x, y, cx, cy]
selectedRowChanged = pyqtSignal(int)
moveFastStageRequest = pyqtSignal(float, float)
def __init__(self, parent=None):
super(PrelocatedCoordinates, self).__init__(parent)
self._xtals_transformed = True
self._current_row = None
layout = QVBoxLayout()
self.setLayout(layout)
frame = QWidget()
bl = QGridLayout()
frame.setLayout(bl)
self._label_prefix = QLabel("not set")
self._label_datafile = QLabel("not loaded")
self._label_datafile.setWordWrap(True)
bl.addWidget(QLabel("Prefix"), 0, 0)
bl.addWidget(self._label_prefix, 0, 1)
bl.addWidget(QLabel("Data File"), 1, 0)
bl.addWidget(self._label_datafile, 1, 1)
but1 = QPushButton("Load Datafile")
but1.clicked.connect(lambda: self.loadMarkers(None))
bl.addWidget(but1, 2, 0)
but1 = QPushButton("Save Datafile")
but1.clicked.connect(lambda: self.saveDataAs())
bl.addWidget(but1, 2, 1)
but2 = QPushButton("Clear Data")
but2.clicked.connect(self.clearMarkers)
bl.addWidget(but2, 3, 0)
# but = QPushButton("Random Data")
# but.clicked.connect(self.generate_random_data)
# bl.addWidget(but, 4, 0)
# self._num_random_points = QSpinBox()
# self._num_random_points.setMinimum(2)
# self._num_random_points.setMaximum(10000)
# self._num_random_points.setValue(10)
# self._num_random_points.setSuffix(" points")
# bl.addWidget(self._num_random_points, 4, 1)
but = QPushButton("dump numpy")
but.clicked.connect(self.dump_numpy)
bl.addWidget(but, 5, 0)
# 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.horizontalHeader().setDefaultSectionSize(80)
self.markersTable.setColumnCount(5)
self.markersTable.setHorizontalHeaderLabels(
("Fiducial?", "orig X", "orig Y", "X", "Y")
)
self.markersTable.horizontalHeader().setSectionResizeMode(
0, QHeaderView.ResizeToContents
)
self.markersTable.horizontalHeader().setSectionResizeMode(
1, QHeaderView.ResizeToContents
)
self.markersTable.horizontalHeader().setSectionResizeMode(
2, QHeaderView.ResizeToContents
)
self.markersTable.horizontalHeader().setSectionResizeMode(
3, QHeaderView.ResizeToContents
)
self.markersTable.horizontalHeader().setSectionResizeMode(
4, QHeaderView.ResizeToContents
)
self.markersTable.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.markersTable.itemSelectionChanged.connect(self.selection_changed)
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()
def delete_selected(self):
row = self._current_row
try:
row += 0
except:
logger.warning("select a row first")
QMessageBox.warning(
self,
"Select a marker first",
"You must select a marker first before updating its goniometer position",
)
return
self.markersTable.removeRow(row)
self.prelocatedDataUpdated.emit()
def selection_changed(self):
row = self.markersTable.currentRow()
if row < 0:
return
self._current_row = row
self.selectedRowChanged.emit(row)
logger.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:
logger.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: XY_Coord):
tbl = self.markersTable
row = self._current_row
try:
row += 0
except:
logger.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
logger.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: XY_Coord):
tbl = self.markersTable
row = self._current_row
try:
row += 0
except:
logger.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):
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: List):
"""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 saveDataAs(self, filename=None):
# filename = folders.get_file("prelocated-save.dat")
data_folder = settings.value("folders/last_prelocation_folder")
if filename is None:
filename, _ = QFileDialog.getSaveFileName(
parent=None,
caption="Open CSV data file",
directory=data_folder,
filter="CSV Data Files (*.csv);;All Files (*)",
)
if not filename:
return
settings.setValue("folders/last_prelocation_folder", QFileInfo(filename).canonicalPath())
logger.info("Saving data in {}".format(filename))
data = self.get_data(as_numpy=True)
df = pd.DataFrame(data)
df.to_csv(filename, float_format="%.6f")
def addSingleMarker(self, row, marker):
is_fiducial, origx, origy, gx, gy = marker
logger.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)
def loadMarkers(self, filename=None):
logger = logging.getLogger("preloc.loadMarkers")
#def_folder = join(folders.pgroup_folder, "preloc_sheets") #ZAC: orig. code
def_folder =''
data_folder = settings.value("folders/last_prelocation_folder", def_folder)
if filename is None:
filename, _ = QFileDialog.getOpenFileName(
self,
"Open CSV data file",
data_folder,
"Any file.. (*.txt);;CSV Data Files (*.csv);;All Files (*)",
)
# filename = folders.get_file("preloc-in.csv")
if not filename: # cancelled dialog
return
try:
settings.setValue("folders/last_prelocation_folder", QFileInfo(filename).canonicalPath())
settings.setValue("folders/last_prelocation_sheet", QFileInfo(filename).absolutePath())
except TypeError as e:
pass
self.clearMarkers()
logger.info(f"loading prelocated coords from {filename}")
df = pd.read_csv(filename, comment="#", header=None, delim_whitespace=False, delimiter="[\t,;]")
try:
prefix = QFileInfo(filename).baseName()
except:
prefix = filename.name
filename = prefix
logger.info(f"prefix => {prefix}")
gonio_coords_available = True
for data in df.as_matrix(): # FIXME FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.
row = self.markersTable.rowCount()
self.markersTable.setRowCount(row + 1)
original = np.copy(data)
data = list(data)
if len(data) in [2, 3]:
gonio_coords_available = False
data.extend([0, 0])
if len(data) == 4:
data.insert(0, False) # fiducial flag
elif len(data) == 5: # fiducial already there, convert to bool
data[0] = bool(data[0])
else:
QMessageBox.warning(
self,
"Wrong number of points in data file",
"I was expecting either 2, 3, 4 or 5 data points per line."
"\n\nFailed around a line with: {}".format(list(original)),
)
self.addSingleMarker(row, data)
self._xtals_transformed = False
self.prefixSelected.emit(prefix)
# only emit this signal if goniometer coordinates already read from file
if gonio_coords_available:
logger.debug(f"dataFileLoaded.emit => {filename}")
self.dataFileLoaded.emit(filename)
"""
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()
"""
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.centralWidget = QWidget()
self.markwi = PrelocatedCoordinates(parent=self)
self.setCentralWidget(self.centralWidget)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.markwi)
self.centralWidget.setLayout(mainLayout)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())

View File

@@ -6,14 +6,11 @@
# #yaml is fixed and not altened by program
import logging
import re
_log = logging.getLogger(__name__)
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication, QMainWindow
import json
import json,re
import numpy as np
class MyJsonEncoder(json.JSONEncoder):
@@ -636,206 +633,15 @@ verbose bits:
for i,k in lut:
cld[i].setValue(twk[k].get_val())
#def cb_save(self):
# self._state=p.saveState()
#def cb_restore(self):
# p=self._p
# add=p['Save/Restore functionality', 'Restore State', 'Add missing items']
# rem=p['Save/Restore functionality', 'Restore State', 'Remove extra items']
# p.restoreState(self._state, addChildren=add, removeChildren=rem)
# ----------------------------- OBSOLETE -----------------------------
#inst_folder = Path(__file__).absolute().parent
#config_file = inst_folder / "swissmx.yaml"
#configs = yaml.load(config_file.read_text(),Loader=yaml.FullLoader)
#endstation = configs["configure_for"]
#appsconf = configs[endstation]
#simulated = appsconf.get("simulate", False)
#logger.info(f"configuring for endstation: {endstation.upper()}")
#if simulated:
# logger.warning("SIMULATION is ACTIVE")
#css_file = inst_folder / "swissmx.css"
#def font(name: str) -> str:
# p = Path(__file__).absolute().parent / "fonts" / name
# return str(p)
#def logo(size: int = 0) -> str:
# p = Path(__file__).absolute().parent / "logos" / "logo.png"
# if size:
# p = Path(__file__).absolute().parent / "logos" / f"tell_logo_{size}x{size}.png"
# return str(p)
# def dlg_geometry(self,obj):
# SPN=GenericDialog.Spinner
# app=QApplication.instance()
# cfg=app._cfg
# w, h = map(float,cfg.value(AppCfg.GEO_BEAM_SZ))
# d = GenericDialog.GenericDialog(
# title="geometry",
# message="Enter the size of the beam in microns",
# inputs={
# "bw": ("beam width um" ,w,SPN(w, min=1, max=200, suffix=" \u00B5m"),),
# "bh": ("beam height um",h,SPN(h, min=1, max=200, suffix=" \u00B5m"),),
# },
# )
# if d.exec():
# results = d.results
# _log.info("Updating beamsize to {}".format(results))
# bm_sz= (results["bw"], results["bh"])
# _log.debug("types {}".format(type(w)))
# cfg.setValue(AppCfg.GEO_BEAM_SZ, bm_sz)
# cfg.sync()
# bm=obj._goBeamMarker
# bm.setSize(bm_sz)
# #self._beammark.set_beam_size((w, h))
#
# def dlg_collimator_reference_positions(self):
# SPN=GenericDialog.Spinner
# app=QApplication.instance()
# cfg=app._cfg
# x_out = cfg.value(AppCfg.COL_DX , 0.0,type=float)
# y_out = cfg.value(AppCfg.COL_DY , 0.0,type=float)
# x_in = cfg.value(AppCfg.COL_X_IN, 0.0,type=float)
# y_in = cfg.value(AppCfg.COL_Y_IN, 0.0,type=float)
# d = GenericDialog.GenericDialog(
# title="Collimator configuration",
# message="Enter reference positions for the collimator",
# inputs={
# AppCfg.COL_DX: ("Collimator out deltaX", x_out, SPN(x_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
# AppCfg.COL_DY: ("Collimator out deltaY", y_out, SPN(y_out, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
# AppCfg.COL_X_IN: ("Collimator in X", x_in, SPN(x_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
# AppCfg.COL_Y_IN: ("Collimator in Y", y_in, SPN(y_in, decimals=3, min=-15.9, max=15.9, suffix=" mm"),),
# },
# )
# if d.exec():
# results = d.results
# _log.info("setting collimator reference positions {}".format(results))
# for k, v in results.items():
# cfg.setValue(k, v)
# cfg.sync()
#
# def dlg_backlight_positions(self):
# SPN=GenericDialog.Spinner
# app=QApplication.instance()
# cfg=app._cfg
# p_in = cfg.value(AppCfg.BKLGT_IN,0,type=int)
# p_out = cfg.value(AppCfg.BKLGT_OUT,0,type=int)
# d = GenericDialog.GenericDialog(
# title="Back Light configuration",
# message="Enter reference positions for the backlight",
# inputs={
# AppCfg.BKLGT_IN: ("In position" , p_in , SPN(p_in, min=-30000, max=10), ),
# AppCfg.BKLGT_OUT: ("Out position", p_out, SPN(p_out, min=-1000, max=10), ),
# },
# )
# if d.exec():
# results = d.results
# _log.info("setting back light reference positions {}".format(results))
# for k, v in results.items():
# cfg.setValue(k, v)
# cfg.sync()
#
# def dlg_cryojet_positions(self):
# p_in = settings.value(CRYOJET_NOZZLE_IN, type=float)
# p_out = settings.value(CRYOJET_NOZZLE_OUT, type=float)
# motion_enabled = option(CRYOJET_MOTION_ENABLED)
# d = GenericDialog(
# title="Cryojet Nozzle Configuration",
# message="Enter reference positions for the cryojet nozzle position",
# inputs={
# CRYOJET_NOZZLE_IN: ("In position", p_in, Spinner(p_in, min=3, max=15)),
# CRYOJET_NOZZLE_OUT: ("Out position",p_out,Spinner(p_out, min=-1000, max=10),),
# CRYOJET_MOTION_ENABLED: ("Move Cryojet in Transitions",motion_enabled,Checkbox(motion_enabled, "move cryojet"),),
# },
# )
# if d.exec():
# results = d.results
# _log.info("setting cryojet reference positions {}".format(results))
# for k, v in results.items():
# settings.setValue(k, v)
# settings.sync()
#
# def dlg_deltatau_parameters(self):
# SPN=GenericDialog.Spinner
# CB=GenericDialog.Checkbox
# app=QApplication.instance()
# cfg=app._cfg
# #dt1 = cfg.value(AppCfg.DT_HOST,'SAR-CPPM-EXPMX1')
# dt1 = cfg.value(AppCfg.DT_HOST)
# dt2 = cfg.value(AppCfg.DT_VEL_SCL, 1, type=float)
# dt3 = cfg.option(AppCfg.DT_SHOW_PLOTS)
#
# d = GenericDialog.GenericDialog(
# title="Delta Tau Parameters",
# message="These parameters affect the data collection.",
# inputs={
# AppCfg.DT_HOST:("host name (host[:port:port_gather])", dt1, QLineEdit(),),
# AppCfg.DT_VEL_SCL: ("Velocity Scale (1=optimal, 0=zero vel at target)", dt2,SPN(dt2, min=0, max=1, suffix=""),),
# AppCfg.DT_SHOW_PLOTS: ("show plots after collection", dt3,CB(dt3, "active"),),
# #DELTATAU_SORT_POINTS: ("Sort pointshoot/prelocated coords", b,CB(b, "sort points"),),
# },
# )
# if d.exec():
# results = d.results
# _log.info("setting delta tau parameters {}".format(results))
# for k, v in results.items():
# cfg.setValue(k, v)
# cfg.sync()
#
# def dlg_tell_mount_positions(self):
# AUTODRY_ENABLED = "tell/autodry_enabled"
# AUTODRY_MAXMOUNTS = "tell/autodry_max_number_of_mounts"
# AUTODRY_MAXTIME = "tell/autodry_max_time"
#
# SECS_HOURS = 60 * 60
#
# enabled = option(AUTODRY_ENABLED)
# maxtime = settings.value(AUTODRY_MAXTIME, type=float) / SECS_HOURS
# maxmounts = settings.value(AUTODRY_MAXMOUNTS, type=int)
#
# d = GenericDialog.GenericDialog(
# title="TELL Settings",
# message="These control some features of the TELL sample changer",
# inputs={
# AUTODRY_ENABLED: ("Auto dry", enabled,
# Checkbox(enabled, "enabled")),
# AUTODRY_MAXMOUNTS: ("Max. num. mounts between dry",maxmounts,
# Spinner(maxmounts, decimals=0, min=1, max=100, suffix=" mounts"),),
# AUTODRY_MAXTIME: ("Max. time between dry",maxtime,
# Spinner(maxtime, decimals=1, min=0.5, max=5, suffix=" hours"),),
# },
# )
# if d.exec():
# results = d.results
# _log.info("setting tell parameters {}".format(results))
# for k, v in results.items():
# if k == AUTODRY_MAXTIME:
# v = v * SECS_HOURS
# settings.setValue(k, v)
# settings.sync()
# ----------------------------------------------
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
import sys
from PyQt5 import QtCore
app=QApplication([])
app._cfg=AppCfg()
w=WndParameter(None)
w.show()
## Start Qt event loop unless running in interactive mode or using pyside.
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QApplication.instance().exec_()

View File

@@ -1,9 +1,7 @@
import time
import logging
_log=logging.getLogger(__name__)
import time
from epics.ca import pend_event
class PositionsNotReached(Exception):

View File

@@ -10,9 +10,6 @@ import logging
_log=logging.getLogger(__name__)
import epics
#from app_config import settings
#from app_utils import assert_motor_positions
class Backlight(object):
def __init__(self, prefix: str='SAR-EXPMX:MOT_BLGT',pos={'pos_in':-30500,'pos_out':1000,'pos_diode':-30500}):
if prefix is None:
@@ -44,7 +41,6 @@ class Backlight(object):
m.move(target, ignore_limits=True, wait=wait)
if __name__ == "__main__":
import time,os
import argparse

View File

@@ -41,10 +41,9 @@ Best regards
import logging
_log = logging.getLogger(__name__)
import enum, epics, time
import time, enum, epics
import numpy as np
class Camera(enum.IntEnum):
OFF = 0
RUNNING = 1
@@ -197,10 +196,6 @@ class epics_cam(object):
elif k=='roi':
pv_rxs=self.getPv('REGIONX_START');pv_rxe=self.getPv('REGIONX_END')
pv_rys=self.getPv('REGIONY_START');pv_rye=self.getPv('REGIONY_END')
#pv_rxs.put(v[0], wait=False)
#pv_rxe.put(v[0]+v[2], wait=False)
#pv_rys.put(v[1], wait=False)
#pv_rye.put(v[1]+v[3], wait=False)
param.append((pv_rxs,v[0]))
param.append((pv_rxe,v[0]+v[2]))
param.append((pv_rys,v[1]))
@@ -277,9 +272,6 @@ class epics_cam(object):
xx, yy = np.meshgrid(x, y)
for i in range(t):
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin(yy+.01*i)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+.1*i)**2 + np.sin((1+.1*np.sin(.2*i))*yy+.001*i**2)**2)#+xx*t+yy*t)
#imgSeq[i,:,:] = 100*np.sqrt(np.sin(xx+2*np.sin(i/t*2*np.pi))**2 + np.sin(yy)**2)
px=2*np.sin(i/t*2*np.pi)
fx=1
py=2*np.sin(i/t*2*np.pi)
@@ -323,7 +315,7 @@ class epics_cam(object):
if __name__ == "__main__":
import time, os, PIL.Image, platform, subprocess
import os, PIL.Image, platform, subprocess
import argparse
logging.basicConfig(level=logging.DEBUG,format='%(name)s:%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.getLogger('PIL').setLevel(logging.INFO)
@@ -337,9 +329,6 @@ if __name__ == "__main__":
else: # linux variants
subprocess.call(('xdg-open', file))
parser = argparse.ArgumentParser()
parser.add_argument("--ui", "-u", help="qt test", type=int, default=0)
parser.add_argument("--sim", "-s", help="simulation mode", type=int, default=None)
@@ -355,20 +344,12 @@ if __name__ == "__main__":
else:
os.environ['EPICS_CA_ADDR_LIST'] ='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066'
if not args.ui:
cam = epics_cam(prefix=args.prefix)
#cam._transformation=np.array(((-1,0),(0,1)),dtype=np.uint8) #ZAC: orig. code
if args.prefix is None:
cam.sim_gen(mode=args.sim)
#sz=(2448,2048)
#ctr=(1200,1400)
#sz=(1200,1000)
#cam.set_roi(int(ctr[0]-sz[0]/2),int(ctr[0]+sz[0]/2),int(ctr[1]-sz[1]/2),int(ctr[1]+sz[1]/2))
#cam.set_exposure(3)
#cam.set_binning(1,1)
cam.run()
n = 1
base = "/tmp/image_{:05d}.png"

View File

@@ -1,17 +1,8 @@
import logging
_log=logging.getLogger(__name__)
#from glob import glob
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
#import eventer
#import qsingleton
#from detector_integration_api import DetectorIntegrationClient
#from app_config import settings, appsconf, simulated
#config = appsconf['jungfrau']
#dia_client = DetectorIntegrationClient(config['uri'])
class JungfrauWaiter(QObject):
finished = pyqtSignal()
@@ -22,9 +13,11 @@ class JungfrauWaiter(QObject):
def work(self):
pass
class JungfrauMissingConfiguration(Exception):
pass
class Jungfrau(QObject):
pedestal_selected = pyqtSignal(str)
configured = pyqtSignal()

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,15 @@ if __name__ == "__main__":
logging.getLogger('matplotlib').setLevel(logging.INFO)
if socket.gethostname()=='ganymede':
sys.path.insert(0, os.path.expanduser('~/Documents/prj/SwissFEL/PBTools'))
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../PBSwissMX/python')))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../../PBTools')))
else:
sys.path.insert(0, '/sf/cristallina/applications/SwissMX/PBTools')
sys.path.insert(0, '/sf/cristallina/applications/SwissMX/PBSwissMX/python')
#_log.info(sys.path)
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(base,'PBTools'))
sys.path.insert(0, os.path.join(base,'PBSwissMX/python'))
elif socket.gethostname()!='ganymede':
#TODO: cleanup slic installation location
sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/mx/slic'))
#sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/slic/slic-package'))
sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/slic/slic-package'))
from PyQt5.QtWidgets import (QApplication,)
from app_config import AppCfg #settings, option, toggle_option
@@ -105,12 +103,6 @@ class Deltatau:
class Jungfrau:
def __init__(self,sim=False):
#python /sf/jungfrau/applications/daq_client/daq_client.py -h
#python /sf/jungfrau/applications/daq_client/daq_client.py -p p19739 -t no_beam_test
# -c/sf/cristallina/config/channel_lists/channel_list_bs-e/sf/cristallina/config/channel_lists/channel_list_ca
# -f/sf/cristallina/config/jungfrau/jf_1d5M.json
# --start_pulseid 15382163895 --stop_pulseid 15382163905
#rsync -vai gac-cristall@saresc-cons-03:/sf/jungfrau/applications/daq_client/daq_client.py .
# setup slic parameters
if sim:
self._sim=True

View File

@@ -18,7 +18,7 @@ _log=logging.getLogger(__name__)
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
from PyQt5.QtGui import QPolygon,QPolygonF
from PyQt5.QtGui import QPolygonF
from PyQt5.QtCore import Qt,QPointF,QLineF
from PyQt5.QtWidgets import QMenu
@@ -94,22 +94,12 @@ class Marker(UsrROI):
#widget.signal.connect(slot_function)
#self.sigRegionChangeFinished.connect(self.OnRgnChanged)
#def OnRgnChanged(self, event):
# print(event)
# obj_info(self)
def paint(self, p, *args):
#pg.ROI.paint(self, p, *args)
r=QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.currentPen)
p.translate(r.left(), r.top())
#p.scale(r.width(), r.height()) # -> values x,y 0 to 1
#f=p.font();
#f.setPixelSize(50)
#p.setFont(f)
# p.drawText(0, 0, '{:.0f}x{:.0f}'.format(*tuple(self.size())))
#p.drawText(0, 0, 'Thierry')
p.scale(.01*r.width(), .01*r.height()) # -> values x,y 0 to 100
m=self._mode
@@ -129,7 +119,6 @@ class Marker(UsrROI):
ctr=tuple((self.pos()+self.size()/2)*1000)
sz=tuple(self.size()*1000)
p.drawText(ofx+5, ofy+45, '{:.1f}x{:.1f} um'.format(*sz))
#p.drawText(5, -35, '{:.1f}'.format(ctr[0]))
p.drawText(ofx+5, ofy+55,42,30,Qt.AlignRight, '{:.1f}'.format(ctr[0]))
p.drawText(ofx+55, ofy+65, '{:.1f} um'.format(ctr[1]))
@@ -145,8 +134,6 @@ class Marker(UsrROI):
f=p.font();
f.setPixelSize(10)
p.setFont(f)
#p.drawText(ofx+0, ofy+0, 'FFF')
#p.drawText(ofx+100, ofy+100, 'GGG')
px=tuple(self.pos()+self.size()/2)
p.drawText(ofx+18, ofy+10, 'optical center')
p.drawText(ofx+5, ofy+80,42,30,Qt.AlignRight, '{:.1f}'.format(px[0]))
@@ -184,17 +171,11 @@ class Fiducial(UsrROI):
p.setPen(pg.mkPen(width=1, color=[255, 0, 0]))
p.drawLine(pg.Point(50,10), pg.Point(50, 90))
p.drawLine(pg.Point(10,50), pg.Point(90, 50))
#tr=p.transform()
#tr.setMatrix(tr.m11(), tr.m12(), tr.m13(), tr.m21(), -tr.m22(), tr.m23(), tr.m31(), tr.m32(), tr.m33())
#p.setTransform(tr)
f=p.font()
f.setPixelSize(10)
p.setFont(f)
#p.drawText(24, 20, 'beam marker')
ctr=tuple(self.pos()+self.size()/2)
sz=tuple(self.size())
#print((*ctr,self._z))
#p.drawText(5, 25, 'x{:.4g}\ny{:.4g}\nz{:.4g}'.format(*ctr,self._z))
p.drawText(52, 10,40,40,Qt.TextWordWrap, 'x{:.5g}\ny{:.5g}\nz{:.5g}'.format(*ctr,self._z))
def ctr(self):
@@ -253,11 +234,6 @@ class Grid(UsrROI):
x=i*px
p.drawEllipse(QPointF(x,y), rx, ry)
#p.translate(r.left(), r.top())
#p.scale(r.width(), r.height())
#p.drawEllipse(0, 0, 1, 1)
#p.drawRect(0, 0, 1, 1)
def __repr__(self):
s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, cnt:{self._cnt}, fidSize:{self._fidSz}, param:{self._param})'
return s
@@ -276,7 +252,6 @@ class Grid(UsrROI):
def get_scan_param(self):
'returns scan parameters for scanning with deltatau. the format is as used for shapepath'
scan=1 # snake motion Y fast, X slow (default)
#pos=np.array(self.pos())
cnt=np.array(self._cnt,np.int32)
sz=np.array(self.size())
pitch=sz/cnt
@@ -292,7 +267,6 @@ class Grid(UsrROI):
for i in range(1, cnt[0], 2):
yy[i]=yy[i][::-1]
pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose()*pitch
#pts+=pos
param={'points':pts}
param.update(self._param)
assert(param.get('code_gen',0)==0) # this provides fully x,y motor coordinates
@@ -348,8 +322,6 @@ class Path(UsrROI):
r=self._rect
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.currentPen)
#p.drawEllipse(0, 0, int(sz[0]), int(sz[1]))
#p.drawRect(0, 0, int(sz[0]), int(sz[1]))
p.drawRect(QtCore.QRectF(0, 0, sz[0], sz[1]).normalized())
#obj_info(p)
p.scale(sz[0]/r.width(), sz[1]/r.height())
@@ -470,19 +442,12 @@ class FixTargetFrame(UsrROI):
self.addScaleHandle([1, 1], [0, 0])
self.addScaleHandle([0, 0], [1, 1])
self.addScaleRotateHandle([1, 0], [0, 0])
#self.sigHoverEvent.connect(self.hover)
# def hover(self):
# _log.debug(f'hover {self}')
def paint(self, p, *args):
#pg.ROI.paint(self, p, *args)
sz=self.state['size']
#nx, ny=self._cnt
#px, py=sz[0]/(nx-1), sz[1]/(ny-1)
r=QtCore.QRectF(0, 0, sz[0], sz[1]).normalized()
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.currentPen)
#p.drawRect(0, 0, int(sz[0]), int(sz[1]))
p.drawRect(r)
dscr=self._dscr
@@ -496,8 +461,6 @@ class FixTargetFrame(UsrROI):
ox,oy=g['pos']
px,py=g['pitch']
nx,ny=g['count']
#x0=ox; x1=ox+(ny-1)*py
#y0=oy; y1=oy+(nx-1)*px
x0=ox-.5*px; x1=ox+(nx-.5)*px
y0=oy-.5*py; y1=oy+(ny-.5)*py
for i in range(nx):
@@ -558,10 +521,7 @@ class FixTargetFrame(UsrROI):
scan=1 # snake motion Y fast, X slow (default)
grid=self._dscr['grid']
self._dscr['size']
#pos=np.array(self.pos())
cnt =np.array(grid['count'],np.int32)
#pos =np.array(grid['pos'],np.float64)
#pitch=np.array(grid['pitch'],np.float64)
xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1]))
if scan==0: # snake motion X fast, Y slow
for i in range(1,cnt[1],2):
@@ -592,11 +552,6 @@ class FixTargetFrame(UsrROI):
param={'grid':grid, 'points':pts, 'trf':trf}
param.update(self._param)
# trf*'gridpos' -> motor pos in mm
# {'pos': [2080, 2080], 'pitch': [120, 120], 'count': [162, 162]}
#np.array((0,0,1))*trf grid 0,0 (top left)
#np.array((161,0,1))*trf # grid 161,0 (top right)
#np.array((161,161,1))*trf # grid 161,161 (bottom right)
return param
@@ -645,9 +600,6 @@ if __name__=='__main__':
p.setRenderHint(QtGui.QPainter.Antialiasing)
p.setPen(self.currentPen)
# p.translate(r.left(), r.top())
# p.scale(r.width(), r.height())
# p.drawRect(0, 0, 1, 1)
p.drawRect(r)
tr=p.worldTransform()
obj_info(tr)
@@ -702,15 +654,9 @@ if __name__=='__main__':
def mouse_click_event(event):
#event.pos(): return Point(self.currentItem.mapFromScene(self._scenePos))
pos=event.pos()
scn=event.scenePos() # there is a small black border, that makes the difference
print(f'mouse-> scene pos:{pt2str(scn)} currentItem pos:{pt2str(pos)}')
#img=viImg.mapFromScene(scn)
#roi=viUsrRoi.mapFromScene(scn)
#print(f'mouse-> img:{pt2str(img)} roi:{pt2str(roi)}')
#childTree(vb)
#obj_info(viImg)
m=int(event.modifiers())
o=event.currentItem
if m&Qt.ShiftModifier:
@@ -721,9 +667,6 @@ if __name__=='__main__':
elif m&Qt.ControlModifier:
tr=o.transform()
obj_info(tr)
#tr.setMatrix(tr.m11(),tr.m12(),tr.m13()+100,tr.m21(),tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33())
#tr.setMatrix(tr.m11(),tr.m12(),tr.m13(),tr.m21(),tr.m22(),tr.m23(),tr.m31(),tr.m32()+20,tr.m33())
#tr.setMatrix(tr.m11(),tr.m12(),tr.m13(),tr.m21(),-tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33())
tr.setMatrix(-tr.m11(),tr.m12(),tr.m13(),tr.m21(),-tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33())
obj_info(tr)
o.setTransform(tr)
@@ -774,24 +717,16 @@ if __name__=='__main__':
viImg=pg.ImageItem(arr, border='y')
vb.addItem(viImg)
# Custom ROI for selecting an image region
#viRoi=pg.ROI([20, -50], [60, 40])
#viRoi=TxtROI([20, -50], [60, 40])
#viRoi.addScaleHandle([1, 1], [0, 0])
#viRoi.addScaleHandle([.7, .5], [0, 0])
#vb.addItem(viRoi)
viUsrRoi=Marker([50, 120], [30, 20],mode=0)
vb.addItem(viUsrRoi)
#obj=Marker([250, 220], [30, 20],mode=1)
#vb.addItem(obj)
vi=Grid( (120,-100), (200,150), (30,20),2)
tr=QtGui.QTransform() # prepare ImageItem transformation:
tr.setMatrix(1, -.1, 0,
.2, 1, 0,
10, 10, 1)
vi.setTransform(tr) # assign transform
#vi=Grid( (50,10), (200,150), (6,4))
vb.addItem(vi) #vi= visual item
grp=pg.ItemGroup()
@@ -811,11 +746,6 @@ if __name__=='__main__':
fiducial=np.array(((18, 7), (25, 16), (70, 20)))
path=gen_swissmx_points(ofs=(10, 5), width=200)
vi=Path((120,100),path,fiducial,fidScl)
#tr=QtGui.QTransform() # prepare ImageItem transformation:
#tr.setMatrix(1, 0, 0,
# 0, 1, 0,
# 10, 10, 1)
#vi.setTransform(tr) # assign transform
vb.addItem(vi)
vi=FixTargetFrame((100,300),(100,100),tpl='test')
@@ -833,9 +763,6 @@ if __name__=='__main__':
viRoi=pg.ROI([-200, -200], [100, 80],movable=True, rotatable=True, resizable=True)
viRoi.addFreeHandle(pos=[.7, .5], axes=None, item=None, name=None, index=None) # rechteck , frei beweglich ??? verschwinden anch bewegung
#viRoi.addRotateFreeHandle([.7, .5], [0, 0], axes=None, item=None, name=None, index=None) # kreis ??? verschwinden anch erstem gebrauch
#viRoi.addRotateHandle([.7, .5], [0, 0], item=None, name=None, index=None) # kreis, nur rot
#viRoi.addScaleHandle([.7, .5], [0, 0], axes=None, item=None, name=None, lockAspect=False, index=None) # raute scale x,y
#viRoi.addScaleRotateHandle([0, .5], [1, .5], item=None, name=None, index=None) # kreis
#viRoi.addTranslateHandle([.7, .5], axes=None, item=None, name=None, index=None) #quadrat
vb.addItem(viRoi)
@@ -846,9 +773,6 @@ if __name__=='__main__':
childTree(vb)
w.scene().sigMouseClicked.connect(mouse_click_event)
#viImg.sigImageChanged
#print(vb.pos())
#vb.setPos(50,50)
if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@@ -1,44 +1,11 @@
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QWidget, QSizePolicy, QVBoxLayout, QDoubleSpinBox, QMessageBox
from app_config import AppCfg #option, toggle_option
def toggle_warn(key):
toggle_option(key)
m = f"Option {key} => {option(key)}"
QMessageBox.warning(None, m, m)
from PyQt5.QtWidgets import QWidget, QSizePolicy, QVBoxLayout, QMessageBox
def horiz_spacer():
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
return spacer
def toggle_visibility(widget, state):
if state:
widget.show()
else:
widget.hide()
def get_sep_line():
line = QtWidgets.QFrame()
line.setFixedHeight(2)
line.setFrameShadow(QtWidgets.QFrame.Sunken)
line.setFrameShape(QtWidgets.QFrame.HLine)
return line
def check_font_by_id(id):
for family in QtGui.QFontDatabase.applicationFontFamilies(id):
print(family)
def toggle_groupbox(gbox, state):
widget = gbox.layout().itemAt(0).widget()
widget.setVisible(state)
def add_item_to_toolbox(toolbox, label, widget_list=[]):
block = QWidget()
block.setAccessibleName(label)

View File

@@ -22,7 +22,7 @@ bitmask for simulation:
0x100: Deltatau motion code
"""
import logging
import logging,time,os,sys,socket
class col:
d='\033[0m' # default
r='\033[31m' # red
@@ -74,33 +74,22 @@ class logHandler(logging.StreamHandler):
except Exception:
self.handleError(record)
# logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s',
handlers=[logHandler()])
#logging.getLogger('PyQt5.uic').setLevel(logging.INFO)
#logging.getLogger('requests').setLevel(logging.INFO)
#logging.getLogger('urllib3').setLevel(logging.INFO)
#logging.getLogger('paramiko').setLevel(logging.INFO)
#logging.getLogger('matplotlib').setLevel(logging.INFO)
#logging.getLogger('PIL').setLevel(logging.INFO)
#logging.getLogger('illumination').setLevel(logging.INFO)
#logging.getLogger('zoom').setLevel(logging.INFO)
#logging.getLogger('pbtools.misc.pp_comm').setLevel(logging.INFO)
_log = logging.getLogger("swissmx")
if __name__=="__main__":
import os,sys,socket
if socket.gethostname()=='ganymede':
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../PBSwissMX/python')))
sys.path.insert(0, os.path.abspath(os.path.join(base,'../../PBTools')))
else:
sys.path.insert(0, '/sf/cristallina/applications/SwissMX/PBTools')
sys.path.insert(0, '/sf/cristallina/applications/SwissMX/PBSwissMX/python')
base=os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(base,'PBTools'))
sys.path.insert(0, os.path.join(base,'PBSwissMX/python'))
import time
class timestamp():
def __init__(self):
self.t=time.time()
@@ -110,9 +99,9 @@ class timestamp():
self.t=t
ts=timestamp()
ts.log('Import part 1/8:')
import sys, os, time
import sys, os
import json, re
import random, signal, subprocess
import signal, subprocess
import matplotlib as mpl
import matplotlib.pyplot as plt
@@ -132,22 +121,18 @@ TASK_PRELOCATED = "prelocated"
TASK_HELICAL = "helical"
TASK_EMBL = "embl"
ts.log('Import part 2/8:')
import ModuleFixTarget
import PrelocatedCoordinatesModel # ZAC: orig. code
from EmblModule import EmblWidget #ZAC: orig. code
from HelicalTable import HelicalTableWidget #ZAC: orig. code
ts.log('Import part 3/8:')
import qtawesome
import qutilities
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, pyqtSlot, QSize, QRegExp, pyqtSignal, QObject, QThread, QRectF,QT_VERSION_STR
from PyQt5.QtGui import QKeySequence, QPixmap, QRegExpValidator, QFont
from PyQt5.QtCore import Qt, pyqtSlot, QSize, pyqtSignal, QObject, QRectF,QT_VERSION_STR
from PyQt5.QtGui import QKeySequence, QPixmap
from PyQt5.QtWidgets import (
QAction, QApplication, QDoubleSpinBox, QFileDialog, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
QMessageBox, QPlainTextEdit, QProgressBar, QProgressDialog, QPushButton, QShortcut, QSizePolicy, QSpinBox,
QSplashScreen, QTextBrowser, QToolBox, QVBoxLayout, QWidget,)
QAction, QApplication, QFileDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit,
QMessageBox, QProgressBar, QPushButton, QShortcut, QSizePolicy,
QSplashScreen, QToolBox, QVBoxLayout, QWidget,)
from PyQt5.uic import loadUiType
ts.log('Import part 4/8:')
import pyqtUsrObj as UsrGO
@@ -165,7 +150,6 @@ import app_utils
from app_config import AppCfg,WndParameter #settings, option, toggle_option
import epics
from epics.ca import pend_event
import camera,backlight,zoom,illumination,geometry
ts.log('Import part 7/8:')
import psi_device

67
zoom.py
View File

@@ -8,30 +8,18 @@
'''
'''
import json
import logging
_log=logging.getLogger(__name__)
import os
from PyQt5 import QtWidgets, uic
import PyQt5.QtWidgets as qtw
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import (
QApplication,
QPushButton,
QGroupBox,
QGridLayout,
QLabel,
QDoubleSpinBox,
QSpinBox,
QVBoxLayout,
QHBoxLayout,
QCheckBox,
QApplication, QPushButton, QGroupBox, QGridLayout, QLabel, QDoubleSpinBox, QSpinBox,
QVBoxLayout, QHBoxLayout, QCheckBox,
)
from PyQt5.uic import loadUiType
import backlight, illumination, camera
import epics
from app_config import AppCfg #settings
Ui_Zoom, QWidget = loadUiType(os.path.join(os.path.dirname(__file__),"zoom.ui"))
MIN_ZOOM = 1
@@ -50,22 +38,6 @@ class Zoom(QGroupBox, Ui_Zoom):
def init_settings(self):
app=QApplication.instance()
#cfg=app._cfg
#keys=cfg.allKeys()
#if cfg.ZOOM_BUTTONS in keys:
# buttons=json.loads(cfg.value(cfg.ZOOM_BUTTONS))
#else:
# buttons=((1, "1"),(200, "200"),(400, "400"),(600, "600"),(800, "800"),(1000, "1000"),)
# cfg.setValue(cfg.ZOOM_BUTTONS,json.dumps(buttons))
#self.get_zoom_pv = PV(zoom_api + ":ZOOM-RBV", callback=self.zoom_update_cb)
#self.status_pv = PV(zoom_api + ":ZOOM-STATUS", callback=self.zoom_status_cb)
#try:
# pv=app._zoom.getPv('POS_RB')
#except AttributeError as e:
# _log.debug('Simulated zoom')
#else:
# pv.callbacks ... = self.zoom_update_cb
# check also: pv.clear_auto_monitor() # disconnect PV monitor callback -> program exit faster.
buttons=((1, "1"), (200, "200"), (400, "400"), (600, "600"), (800, "800"), (1000, "1000"),)
zoom=QApplication.instance()._zoom
@@ -80,7 +52,6 @@ class Zoom(QGroupBox, Ui_Zoom):
self._zoom_spinner.setRange(MIN_ZOOM, MAX_ZOOM)
self._zoom_spinner.setValue(current_zoom_value)
self._zoom_spinner.editingFinished.connect(self.move_zoom_a)
# self._zoom_spinner.setSingleStep(SPINNER_SINGLE_STEP)
self._zoom_spinner.setSingleStep(SPINNER_LARGE_STEP)
_box = QWidget()
_box.setLayout(QHBoxLayout())
@@ -178,14 +149,6 @@ class Zoom(QGroupBox, Ui_Zoom):
self.slid.setSuffix(" ms")
grid.addWidget(self.slid, 0, 1)
#grid.addWidget(QLabel("Acq.Mode"), 1, 0)
#cbox = QComboBox()
#for mode in Camera.AcquisitionMode:
# cbox.addItem(mode.name, mode)
#cbox.setCurrentIndex(cam.pv_acqmode.get())
#cbox.currentIndexChanged.connect(self.update_camera_acqmode)
#grid.addWidget(cbox, 1, 1)
lbox.layout().addWidget(grp)
self.slid.editingFinished.connect(lambda: self.update_exposure(self.slid.value()))
@@ -270,21 +233,6 @@ class Zoom(QGroupBox, Ui_Zoom):
self._zoom_spinner.blockSignals(False)
self.zoomChanged.emit(float(value))
# def get_zoom(self) -> int:
# zoom=QApplication.instance()._zoom
# pos = zoom.get()
# _log.debug("get_zoom(epics) => {}".format(pos))
# return pos
# def zoom_update_cb(self, pvname, value, char_value, **kwargs):
# self._zoom_spinner.blockSignals(True)
# self._zoom_spinner.setValue(value)
# self._zoom_spinner.blockSignals(False)
# def zoom_status_cb(self, pvname, value, char_value, **kwargs):
# busy = bool(value)
# self.setDisabled(busy)
class QopticZoom(object):
@@ -336,7 +284,7 @@ class QopticZoom(object):
if __name__ == "__main__":
import time, os, PIL.Image, platform, subprocess
import sys, os
import argparse
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
@@ -344,18 +292,11 @@ if __name__ == "__main__":
parser.add_argument("--sim", "-s", help="simulate all devices", action='store_true')
args = parser.parse_args()
import sys
_log.info('Arguments:{}'.format(args.__dict__))
os.environ['EPICS_CA_ADDR_LIST'] = '129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066'
app = QApplication(sys.argv)
from app_config import appsconf
#from PyQt5 import QtGui
#qtawesome.load_font("material","MaterialIcons-Regular.ttf","MaterialIcons-Regular.json","fonts/",)
#QtGui.QFontDatabase.addApplicationFont("fonts/Inconsolata-Bold.ttf")
#QtGui.QFontDatabase.addApplicationFont("fonts/Baloo-Regular.ttf")
if args.sim:
app._backlight = backlight.Backlight(None)
app._illumination = illumination.IlluminationControl(None)
@@ -367,8 +308,6 @@ if __name__ == "__main__":
app._zoom = QopticZoom()
app._camera = camera.epics_cam()
simulated = appsconf["microscope"]["zoom"].get("simulate", False)
obj=Zoom()