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

View File

@@ -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