From 16c5c816f4ff6d85cea6f21a5c69c3287eef0dfa Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Sat, 22 Jul 2023 16:59:28 -0400 Subject: [PATCH 01/19] Minimal working version. Needs refactor --- gui/control_main.py | 26 +++++++++----------------- gui/vector.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 gui/vector.py diff --git a/gui/control_main.py b/gui/control_main.py index 78bebe80..0a0f8ceb 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -49,6 +49,7 @@ UserScreenDialog, ) from gui.raster import RasterCell, RasterGroup +from gui.vector import VectorMarker from QPeriodicTable import QPeriodicTable from threads import RaddoseThread, VideoThread @@ -4293,7 +4294,7 @@ def transform_vector_coords(self, prev_coords, current_raw_coords): } def getVectorObject( - self, prevVectorPoint=None, gonioCoords=None, pen=None, brush=None + self, prevVectorPoint=None, gonioCoords=None, pen=None, brush=None, pointName=None ): """Creates and returns a vector start or end point @@ -4321,20 +4322,12 @@ def getVectorObject( markWidth = 10 # TODO: Place vecMarker in such a way that it matches any arbitrary gonioCoords given to this function # currently vecMarker will be placed at the center of the sample cam - vecMarker = self.scene.addEllipse( - self.centerMarker.x() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - - (markWidth / 2.0) - - 1 - + self.centerMarkerCharOffsetY, - markWidth, - markWidth, - pen, - brush, - ) + marker_x = self.centerMarker.x() - (markWidth / 2.0) - 1 + self.centerMarkerCharOffsetX + marker_y = self.centerMarker.y() - (markWidth / 2.0) - 1 + self.centerMarkerCharOffsetY + + vecMarker = VectorMarker(marker_x, marker_y, markWidth, markWidth, pen=pen, + brush=brush, parent=self, pointName=pointName) + self.scene.addItem(vecMarker) if not gonioCoords: gonioCoords = { "x": self.sampx_pv.get(), @@ -4377,7 +4370,7 @@ def setVectorPointCB(self, pointName): brush = QtGui.QBrush(QtCore.Qt.red) else: brush = QtGui.QBrush(QtCore.Qt.blue) - point = self.getVectorObject(prevVectorPoint=point, brush=brush) + point = self.getVectorObject(prevVectorPoint=point, brush=brush, pointName=pointName) setattr(self, pointName, point) if self.vectorStart and self.vectorEnd: self.drawVector() @@ -4404,7 +4397,6 @@ def drawVector(self): + self.centerMarkerCharOffsetY, pen, ) - self.vecLine.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) def clearVectorCB(self): if self.vectorStart: diff --git a/gui/vector.py b/gui/vector.py new file mode 100644 index 00000000..2ac20486 --- /dev/null +++ b/gui/vector.py @@ -0,0 +1,44 @@ +from qtpy import QtWidgets, QtGui, QtCore +import daq_utils + +class VectorMarker(QtWidgets.QGraphicsEllipseItem): + def __init__(self, *args, **kwargs): + brush = kwargs.pop('brush', QtGui.QBrush(QtCore.Qt.blue)) + pen = kwargs.pop('pen', QtGui.QPen(QtCore.Qt.blue)) + self.parent = kwargs.pop('parent', None) + self.pointName = kwargs.pop('pointName', None) + super().__init__(*args, **kwargs) + self.setBrush(brush) + self.setPen(pen) + self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable) + self.setFlag(QtWidgets.QGraphicsEllipseItem.ItemSendsGeometryChanges) + + + def itemChange(self, change, value): + if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged: + if self.parent and self.parent.vecLine: + self.parent.scene.removeItem(self.parent.vecLine) + self.parent.drawVector() + return super().itemChange(change, value) + + + def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: + print("New position:", self.pos()) + if self.parent: + micron_x = self.parent.screenXPixels2microns(self.pos().x()) + micron_y = self.parent.screenYPixels2microns(self.pos().y()) + if self.pointName and getattr(self.parent, self.pointName): + omega = self.parent.omegaRBV_pv.get() + point = getattr(self.parent, self.pointName) + gonio_offset_x, gonio_offset_y, gonio_offset_z, omega = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) + gonioCoords = {"x": self.parent.sampx_pv.get() + gonio_offset_x, + "y": self.parent.sampy_pv.get() + gonio_offset_y, + "z": self.parent.sampz_pv.get() + gonio_offset_z, + "omega": omega} + vectorCoords = self.parent.transform_vector_coords( + point["coords"], gonioCoords + ) + point["coords"] = vectorCoords + point["gonioCoords"] = gonioCoords + + return super().mouseReleaseEvent(event) \ No newline at end of file From b1c63f2c9933f6d1b190c5bf310e69815e01f286 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Sun, 23 Jul 2023 18:45:15 -0400 Subject: [PATCH 02/19] Created a vector object class: - Seperates from ControlMain - Majority of ControlMain dependencies removed --- gui/control_main.py | 272 +++++++------------------------------- gui/vector.py | 316 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 344 insertions(+), 244 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 0a0f8ceb..b71029ff 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -49,7 +49,7 @@ UserScreenDialog, ) from gui.raster import RasterCell, RasterGroup -from gui.vector import VectorMarker +from gui.vector import VectorMarker, VectorWidget from QPeriodicTable import QPeriodicTable from threads import RaddoseThread, VideoThread @@ -157,8 +157,6 @@ def __init__(self): self.popupMessage.setModal(False) self.groupName = "skinner" self.scannerType = getBlConfig("scannerType") - self.vectorStart = None - self.vectorEnd = None self.centerMarkerCharSize = 20 self.centerMarkerCharOffsetX = 12 self.centerMarkerCharOffsetY = 18 @@ -206,6 +204,7 @@ def __init__(self): self.raddoseTimer.timeout.connect(self.spawnRaddoseThread) self.createSampleTab() + self.vector_widget = VectorWidget(main_window=self) self.initCallbacks() if self.scannerType != "PI": @@ -824,11 +823,11 @@ def createSampleTab(self): setVectorStartButton = QtWidgets.QPushButton("Vector\nStart") setVectorStartButton.setStyleSheet("background-color: blue") setVectorStartButton.clicked.connect( - lambda: self.setVectorPointCB("vectorStart") + lambda: self.setVectorPointCB("vector_start") ) setVectorEndButton = QtWidgets.QPushButton("Vector\nEnd") setVectorEndButton.setStyleSheet("background-color: red") - setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vectorEnd")) + setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vector_end")) self.vecLine = None vectorFPPLabel = QtWidgets.QLabel("Number of Wedges") self.vectorFPP_ledit = QtWidgets.QLineEdit("1") @@ -1540,7 +1539,7 @@ def adjustGraphics4ZoomChange(self, fov): self.processSampMove(self.sampx_pv.get(), "x") self.processSampMove(self.sampy_pv.get(), "y") self.processSampMove(self.sampz_pv.get(), "z") - if self.vectorStart != None: + if self.vector_widget.vector_start != None: self.processSampMove(self.sampx_pv.get(), "x") self.processSampMove(self.sampy_pv.get(), "y") self.processSampMove(self.sampz_pv.get(), "z") @@ -1969,55 +1968,8 @@ def processSampMove(self, posRBV, motID): newY = self.calculateNewYCoordPos(startYX, startYY) raster["graphicsItem"].setPos(raster["graphicsItem"].x(), newY) - self.vectorStart = self.updatePoint(self.vectorStart, posRBV, motID) - self.vectorEnd = self.updatePoint(self.vectorEnd, posRBV, motID) - if self.vectorStart != None and self.vectorEnd != None: - self.vecLine.setLine( - self.vectorStart["graphicsitem"].x() - + self.vectorStart["centerCursorX"] - + self.centerMarkerCharOffsetX, - self.vectorStart["graphicsitem"].y() - + self.vectorStart["centerCursorY"] - + self.centerMarkerCharOffsetY, - self.vectorEnd["graphicsitem"].x() - + self.vectorStart["centerCursorX"] - + self.centerMarkerCharOffsetX, - self.vectorEnd["graphicsitem"].y() - + self.vectorStart["centerCursorY"] - + self.centerMarkerCharOffsetY, - ) - - def updatePoint(self, point, posRBV, motID): - """Updates a point on the screen - - Updates the position of a point (e.g. self.vectorStart) drawn on the screen based on - which motor was moved (motID) using gonio position (posRBV) - """ - if point is None: - return point - centerMarkerOffsetX = point["centerCursorX"] - self.centerMarker.x() - centerMarkerOffsetY = point["centerCursorY"] - self.centerMarker.y() - startYY = point["coords"]["z"] - startYX = point["coords"]["y"] - startX = point["coords"]["x"] - - if motID == "omega": - newY = self.calculateNewYCoordPos(startYX, startYY) - point["graphicsitem"].setPos( - point["graphicsitem"].x(), newY - centerMarkerOffsetY - ) - if motID == "x": - delta = startX - posRBV - newX = float(self.screenXmicrons2pixels(delta)) - point["graphicsitem"].setPos( - newX - centerMarkerOffsetX, point["graphicsitem"].y() - ) - if motID == "y" or motID == "z": - newY = self.calculateNewYCoordPos(startYX, startYY) - point["graphicsitem"].setPos( - point["graphicsitem"].x(), newY - centerMarkerOffsetY - ) - return point + offset = (self.centerMarkerCharOffsetX, self.centerMarkerCharOffsetY) + self.vector_widget.update_vector(posRBV, motID, self.centerMarker.pos(), offset) def queueEnScanCB(self): self.protoComboBox.setCurrentIndex(self.protoComboBox.findText(str("eScan"))) @@ -2303,27 +2255,22 @@ def rasterStepChanged(self, text): self.beamHeight_ledit.setText(text) def updateVectorLengthAndSpeed(self): - x_vec_end = self.vectorEnd["coords"]["x"] - y_vec_end = self.vectorEnd["coords"]["y"] - z_vec_end = self.vectorEnd["coords"]["z"] - x_vec_start = self.vectorStart["coords"]["x"] - y_vec_start = self.vectorStart["coords"]["y"] - z_vec_start = self.vectorStart["coords"]["z"] - x_vec = x_vec_end - x_vec_start - y_vec = y_vec_end - y_vec_start - z_vec = z_vec_end - z_vec_start - trans_total = math.sqrt(x_vec**2 + y_vec**2 + z_vec**2) - if daq_utils.beamline == "nyx": - trans_total *= 1000 - self.vecLenLabelOutput.setText(str(int(trans_total))) - totalExpTime = ( - float(self.osc_end_ledit.text()) / float(self.osc_range_ledit.text()) - ) * float( - self.exp_time_ledit.text() - ) # (range/inc)*exptime - speed = trans_total / totalExpTime - self.vecSpeedLabelOutput.setText(str(int(speed))) - return x_vec, y_vec, z_vec, trans_total + osc_end = float(self.osc_end_ledit.text()) + osc_range = float(self.osc_range_ledit.text()) + exposure_time = float(self.exp_time_ledit.text()) + + ( + x_vec, + y_vec, + z_vec, + vector_length, + vector_speed, + ) = self.vector_widget.get_length_and_speed( + osc_end=osc_end, osc_range=osc_range, exposure_time=exposure_time + ) + self.vecLenLabelOutput.setText(str(int(vector_length))) + self.vecSpeedLabelOutput.setText(str(int(vector_speed))) + return x_vec, y_vec, z_vec, vector_length def totalExpChanged(self, text): if text == "oscEnd" and daq_utils.beamline == "fmx": @@ -4150,8 +4097,8 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): x_vec, y_vec, z_vec, trans_total = self.updateVectorLengthAndSpeed() framesPerPoint = int(self.vectorFPP_ledit.text()) vectorParams = { - "vecStart": self.vectorStart["coords"], - "vecEnd": self.vectorEnd["coords"], + "vecStart": self.vector_widget.vector_start["coords"], + "vecEnd": self.vector_widget.vector_end["coords"], "x_vec": x_vec, "y_vec": y_vec, "z_vec": z_vec, @@ -4160,10 +4107,10 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): } reqObj["vectorParams"] = vectorParams except Exception as e: - if self.vectorStart == None: + if self.vector_widget.vector_start == None: self.popupServerMessage("Vector start must be defined.") return - elif self.vectorEnd == None: + elif self.vector_widget.vector_end == None: self.popupServerMessage("Vector end must be defined.") return logger.error("Exception while getting vector parameters: %s" % e) @@ -4252,164 +4199,47 @@ def removePuckCB(self): dewarPos, ok = DewarDialog.getDewarPos(parent=self, action="remove") self.timerSample.start(SAMPLE_TIMER_DELAY) - def transform_vector_coords(self, prev_coords, current_raw_coords): - """Updates y and z co-ordinates of vector points when they are moved - - This function tweaks the y and z co-ordinates such that when a vector start or - end point is adjusted in the 2-D plane of the screen, it maintains the points' location - in the 3rd dimension perpendicular to the screen - - Args: - prev_coords: Dictionary with x,y and z co-ordinates of the previous location of the sample - current_raw_coords: Dictionary with x, y and z co-ordinates of the sample derived from the goniometer - PVs - omega: Omega of the Goniometer (usually RBV) - - Returns: - A dictionary mapping x, y and z to tweaked coordinates - """ - - # Transform z from prev point and y from current point to lab coordinate system - _, _, zLabPrev, _ = daq_utils.gonio2lab( - prev_coords["x"], - prev_coords["y"], - prev_coords["z"], - current_raw_coords["omega"], - ) - _, yLabCurrent, _, _ = daq_utils.gonio2lab( - current_raw_coords["x"], - current_raw_coords["y"], - current_raw_coords["z"], - current_raw_coords["omega"], - ) - - # Take y co-ordinate from current point and z-coordinate from prev point and transform back to gonio co-ordinates - _, yTweakedCurrent, zTweakedCurrent, _ = daq_utils.lab2gonio( - prev_coords["x"], yLabCurrent, zLabPrev, current_raw_coords["omega"] - ) - return { - "x": current_raw_coords["x"], - "y": yTweakedCurrent, - "z": zTweakedCurrent, - } - - def getVectorObject( - self, prevVectorPoint=None, gonioCoords=None, pen=None, brush=None, pointName=None - ): - """Creates and returns a vector start or end point - - Places a start or end vector marker wherever the crosshair is located in - the sample camera view and returns a dictionary of metadata related to that point - - Args: - prevVectorPoint: Dictionary of metadata related to a point being adjusted. For example, - a previously placed vectorStart point is moved, its old position is used to determine - its new co-ordinates in 3D space - gonioCoords: Dictionary of gonio coordinates. If not provided will retrieve current PV values - pen: QPen object that defines the color of the point's outline - brush: QBrush object that defines the color of the point's fill color - Returns: - A dict mapping the following keys - "coords": A dictionary of tweaked x, y and z positions of the Goniometer - "raw_coords": A dictionary of x, y, z co-ordinates obtained from the Goniometer PVs - "graphicsitem": Qt object referring to the marker on the sample camera - "centerCursorX" and "centerCursorY": Location of the center marker when this marker was placed - """ - if not pen: - pen = QtGui.QPen(QtCore.Qt.blue) - if not brush: - brush = QtGui.QBrush(QtCore.Qt.blue) - markWidth = 10 - # TODO: Place vecMarker in such a way that it matches any arbitrary gonioCoords given to this function - # currently vecMarker will be placed at the center of the sample cam - marker_x = self.centerMarker.x() - (markWidth / 2.0) - 1 + self.centerMarkerCharOffsetX - marker_y = self.centerMarker.y() - (markWidth / 2.0) - 1 + self.centerMarkerCharOffsetY - - vecMarker = VectorMarker(marker_x, marker_y, markWidth, markWidth, pen=pen, - brush=brush, parent=self, pointName=pointName) - self.scene.addItem(vecMarker) - if not gonioCoords: - gonioCoords = { - "x": self.sampx_pv.get(), - "y": self.sampy_pv.get(), - "z": self.sampz_pv.get(), - "omega": self.omegaRBV_pv.get(), - } - if prevVectorPoint: - vectorCoords = self.transform_vector_coords( - prevVectorPoint["coords"], gonioCoords - ) - else: - vectorCoords = { - k: v for k, v in gonioCoords.items() if k in ["x", "y", "z"] - } - return { - "coords": vectorCoords, - "gonioCoords": gonioCoords, - "graphicsitem": vecMarker, - "centerCursorX": self.centerMarker.x(), - "centerCursorY": self.centerMarker.y(), - } - def setVectorPointCB(self, pointName): """Callback function to update a vector point Callback to remove or add the appropriate vector start or end point on the sample camera. - Calls getVectorObject to generate metadata related to this point. Draws a vector line if + Calls getObject to generate metadata related to this point. Draws a vector line if both start and end is defined Args: - point: Point to be placed (Either vectorStart or vectorEnd) + pointName: Name of the point to be placed (Either "vectorStart" or "vectorEnd") """ - point = getattr(self, pointName) - if point: - self.scene.removeItem(point["graphicsitem"]) - if self.vecLine: - self.scene.removeItem(self.vecLine) - if pointName == "vectorEnd": - brush = QtGui.QBrush(QtCore.Qt.red) - else: - brush = QtGui.QBrush(QtCore.Qt.blue) - point = self.getVectorObject(prevVectorPoint=point, brush=brush, pointName=pointName) - setattr(self, pointName, point) - if self.vectorStart and self.vectorEnd: - self.drawVector() + gonio_coords = { + "x": self.sampx_pv.get(), + "y": self.sampy_pv.get(), + "z": self.sampz_pv.get(), + "omega": self.omegaRBV_pv.get(), + } + center_x = self.centerMarker.x() + self.centerMarkerCharOffsetX + center_y = self.centerMarker.y() + self.centerMarkerCharOffsetY + self.vector_widget.set_vector_point( + point_name=pointName, + scene=self.scene, + gonio_coords=gonio_coords, + center=(center_x, center_y), + ) def drawVector(self): - pen = QtGui.QPen(QtCore.Qt.blue) try: self.updateVectorLengthAndSpeed() except: pass self.protoVectorRadio.setChecked(True) - self.vecLine = self.scene.addLine( - self.centerMarker.x() - + self.vectorStart["graphicsitem"].x() - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - + self.vectorStart["graphicsitem"].y() - + self.centerMarkerCharOffsetY, - self.centerMarker.x() - + self.vectorEnd["graphicsitem"].x() - + self.centerMarkerCharOffsetX, - self.centerMarker.y() - + self.vectorEnd["graphicsitem"].y() - + self.centerMarkerCharOffsetY, - pen, - ) + center_x = self.centerMarker.x() + self.centerMarkerCharOffsetX + center_y = self.centerMarker.y() + self.centerMarkerCharOffsetY + center = (center_x, center_y) + + self.vector_widget.draw_vector(center, self.scene) def clearVectorCB(self): - if self.vectorStart: - self.scene.removeItem(self.vectorStart["graphicsitem"]) - self.vectorStart = None - if self.vectorEnd: - self.scene.removeItem(self.vectorEnd["graphicsitem"]) - self.vectorEnd = None - self.vecLenLabelOutput.setText("---") - self.vecSpeedLabelOutput.setText("---") - if self.vecLine: - self.scene.removeItem(self.vecLine) - self.vecLine = None + self.vector_widget.clear_vector(self.scene) + self.vecLenLabelOutput.setText("---") + self.vecSpeedLabelOutput.setText("---") def puckToDewarCB(self): while 1: diff --git a/gui/vector.py b/gui/vector.py index 2ac20486..d48976d5 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -1,44 +1,314 @@ +import typing from qtpy import QtWidgets, QtGui, QtCore +import numpy as np import daq_utils +if typing.TYPE_CHECKING: + from gui.control_main import ControlMain + + class VectorMarker(QtWidgets.QGraphicsEllipseItem): def __init__(self, *args, **kwargs): - brush = kwargs.pop('brush', QtGui.QBrush(QtCore.Qt.blue)) - pen = kwargs.pop('pen', QtGui.QPen(QtCore.Qt.blue)) - self.parent = kwargs.pop('parent', None) - self.pointName = kwargs.pop('pointName', None) + self.blue_color = QtCore.Qt.GlobalColor.blue + brush = kwargs.pop("brush", QtGui.QBrush()) + pen = kwargs.pop("pen", QtGui.QPen(self.blue_color)) + self.parent = kwargs.pop("parent", None) + self.point_name = kwargs.pop("point_name", None) + self.coords = kwargs.pop("coords") + self.gonio_coords = kwargs.pop("gonio_coords") + self.center_marker = kwargs.pop("center_marker") super().__init__(*args, **kwargs) self.setBrush(brush) self.setPen(pen) - self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable) - self.setFlag(QtWidgets.QGraphicsEllipseItem.ItemSendsGeometryChanges) - - + self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable) + self.setFlag( + QtWidgets.QGraphicsEllipseItem.GraphicsItemFlag.ItemSendsGeometryChanges + ) + def itemChange(self, change, value): - if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged: + if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: if self.parent and self.parent.vecLine: self.parent.scene.removeItem(self.parent.vecLine) self.parent.drawVector() return super().itemChange(change, value) - - + def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: print("New position:", self.pos()) if self.parent: micron_x = self.parent.screenXPixels2microns(self.pos().x()) micron_y = self.parent.screenYPixels2microns(self.pos().y()) - if self.pointName and getattr(self.parent, self.pointName): + if self.point_name and getattr(self.parent, self.point_name): omega = self.parent.omegaRBV_pv.get() - point = getattr(self.parent, self.pointName) - gonio_offset_x, gonio_offset_y, gonio_offset_z, omega = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) - gonioCoords = {"x": self.parent.sampx_pv.get() + gonio_offset_x, - "y": self.parent.sampy_pv.get() + gonio_offset_y, - "z": self.parent.sampz_pv.get() + gonio_offset_z, - "omega": omega} + point: VectorMarker = getattr(self.parent, self.point_name) + ( + gonio_offset_x, + gonio_offset_y, + gonio_offset_z, + omega, + ) = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) + gonio_coords = { + "x": self.parent.sampx_pv.get() + gonio_offset_x, + "y": self.parent.sampy_pv.get() + gonio_offset_y, + "z": self.parent.sampz_pv.get() + gonio_offset_z, + "omega": omega, + } vectorCoords = self.parent.transform_vector_coords( - point["coords"], gonioCoords - ) - point["coords"] = vectorCoords - point["gonioCoords"] = gonioCoords + point.coords, gonio_coords + ) + point.coords = vectorCoords + point.gonio_coords = gonio_coords + + return super().mouseReleaseEvent(event) + + +class VectorWidget(QtWidgets.QWidget): + def __init__( + self, + parent: QtWidgets.QWidget | None = None, + flags=..., + scene: QtWidgets.QGraphicsScene = ..., + main_window: ControlMain = ..., + ) -> None: + super().__init__(parent, flags) + self.main_window = main_window + self.vector_start: "None | VectorMarker" = None + self.vector_end: "None | VectorMarker" = None + self.vector_line: "None | QtWidgets.QGraphicsLineItem" = None + self.blue_color = QtCore.Qt.GlobalColor.blue + self.red_color = QtCore.Qt.GlobalColor.red + + def update_point( + self, + point: VectorMarker, + pos_rbv: int, + mot_id: str, + center_marker: QtCore.QPointF, + ): + """Updates a point on the screen + + Updates the position of a point (e.g. self.vector_start) drawn on the screen based on + which motor was moved (motID) using gonio position (posRBV) + """ + if point is None: + return point + centerMarkerOffsetX = point.center_marker.x() - center_marker.x() + centerMarkerOffsetY = point.center_marker.y() - center_marker.y() + startYY = point.coords["z"] + startYX = point.coords["y"] + startX = point.coords["x"] + + if mot_id == "omega": + newY = self.main_window.calculateNewYCoordPos(startYX, startYY) + point.setPos(point.x(), newY - centerMarkerOffsetY) + if mot_id == "x": + delta = startX - pos_rbv + newX = float(self.main_window.screenXmicrons2pixels(delta)) + point.setPos(newX - centerMarkerOffsetX, point.y()) + if mot_id == "y" or mot_id == "z": + newY = self.main_window.calculateNewYCoordPos(startYX, startYY) + point.setPos(point.x(), newY - centerMarkerOffsetY) + return point + + def update_vector( + self, + pos_rbv: int, + mot_id: str, + center_marker: QtCore.QPointF, + offset: tuple[int, int], + ): + if ( + self.vector_start is not None + and self.vector_end is not None + and self.vector_line is not None + ): + self.vector_start = self.update_point( + self.vector_start, pos_rbv, mot_id, center_marker + ) + self.vector_end = self.update_point( + self.vector_end, pos_rbv, mot_id, center_marker + ) + self.vector_line.setLine( + self.vector_start.x() + self.vector_start.center_marker.x() + offset[0], + self.vector_start.y() + self.vector_start.center_marker.y() + offset[1], + self.vector_end.x() + self.vector_start.center_marker.x() + offset[0], + self.vector_end.y() + self.vector_start.center_marker.y() + offset[1], + ) + + def get_length(self) -> tuple[int, int, int, np.floating[typing.Any]]: + trans_total = np.floating(0.0) + x_vec = y_vec = z_vec = 0 + + if self.vector_start and self.vector_end: + vec_end = np.array(list(map(self.vector_end.coords.get, ("x", "y", "z")))) + vec_start = np.array( + list(map(self.vector_start.coords.get, ("x", "y", "z"))) + ) + + vec_diff = vec_end - vec_start + trans_total = np.linalg.norm(vec_diff) + if daq_utils.beamline == "nyx": + trans_total *= 1000 + + return x_vec, y_vec, z_vec, trans_total + + def get_length_and_speed( + self, osc_end: float, osc_range: float, exposure_time: float + ) -> tuple[int, int, int, np.floating[typing.Any], np.floating[typing.Any]]: + total_exposure_time: float = (osc_end / osc_range) * exposure_time + x_vec, y_vec, z_vec, vector_length = self.get_length() + speed = vector_length / total_exposure_time + + return x_vec, y_vec, z_vec, vector_length, speed + + def set_vector_point( + self, + point_name: str, + scene: QtWidgets.QGraphicsScene, + gonio_coords: "dict[str, typing.Any]", + center: "tuple[float, float]", + ): + point = getattr(self, point_name) + if point: + scene.removeItem(point["graphicsitem"]) + if self.vector_line: + scene.removeItem(self.vector_line) + if point_name == "vector_end": + brush = QtGui.QBrush(self.red_color) + else: + brush = QtGui.QBrush(self.blue_color) + point = self.setup_vector_object( + gonio_coords, + center, + prev_vector_point=point, + brush=brush, + point_name=point_name, + ) + setattr(self, point_name, point) + scene.addItem(point) + if self.vector_start and self.vector_end: + self.draw_vector(center, scene) + + def draw_vector(self, center: tuple[float, float], scene: QtWidgets.QGraphicsScene): + pen = QtGui.QPen(self.blue_color) + + if self.vector_start is not None and self.vector_end is not None: + self.vecLine = scene.addLine( + center[0] + self.vector_start.x(), + center[1] + self.vector_start.y(), + center[0] + self.vector_end.x(), + center[1] + self.vector_end.y(), + pen, + ) + + def setup_vector_object( + self, + gonio_coords: "dict[str, typing.Any]", + center: "tuple[float, float]", + prev_vector_point: "None | VectorMarker" = None, + pen: "None | QtGui.QPen" = None, + brush: "None | QtGui.QBrush" = None, + point_name: "None | str" = None, + ) -> VectorMarker: + """Creates and returns a vector start or end point + + Places a start or end vector marker wherever the crosshair is located in + the sample camera view and returns a dictionary of metadata related to that point + + Args: + prev_vector_point: Dictionary of metadata related to a point being adjusted. For example, + a previously placed vector_start point is moved, its old position is used to determine + its new co-ordinates in 3D space + gonio_coords: Dictionary of gonio coordinates. If not provided will retrieve current PV values + pen: QPen object that defines the color of the point's outline + brush: QBrush object that defines the color of the point's fill color + Returns: + A VectorMarker along with the following metadata + "coords": A dictionary of tweaked x, y and z positions of the Goniometer + "gonio_coords": A dictionary of x, y, z co-ordinates obtained from the Goniometer PVs + "center_marker": Location of the center marker when this marker was placed + """ + if not pen: + pen = QtGui.QPen(self.blue_color) + if not brush: + brush = QtGui.QBrush(self.blue_color) + markWidth = 10 + marker_x = center[0] - (markWidth / 2.0) - 1 + marker_y = center[1] - (markWidth / 2.0) - 1 + + if prev_vector_point: + vectorCoords = self.transform_vector_coords( + prev_vector_point.coords, gonio_coords + ) + else: + vectorCoords = { + k: v for k, v in gonio_coords.items() if k in ["x", "y", "z"] + } + + vecMarker = VectorMarker( + marker_x, + marker_y, + markWidth, + markWidth, + pen=pen, + brush=brush, + parent=self, + point_name=point_name, + coords=vectorCoords, + gonio_coords=gonio_coords, + center_marker=self.main_window.centerMarker, + ) + return vecMarker + + def clear_vector(self, scene: QtWidgets.QGraphicsScene): + if self.vector_start: + scene.removeItem(self.vector_start) + self.vector_start = None + if self.vector_end: + scene.removeItem(self.vector_end) + self.vector_end = None + if self.vecLine: + scene.removeItem(self.vecLine) + self.vecLine = None + + def transform_vector_coords( + self, prev_coords: "dict[str, float]", current_raw_coords: "dict[str, float]" + ): + """Updates y and z co-ordinates of vector points when they are moved + + This function tweaks the y and z co-ordinates such that when a vector start or + end point is adjusted in the 2-D plane of the screen, it maintains the points' location + in the 3rd dimension perpendicular to the screen + + Args: + prev_coords: Dictionary with x,y and z co-ordinates of the previous location of the sample + current_raw_coords: Dictionary with x, y and z co-ordinates of the sample derived from the goniometer + PVs + omega: Omega of the Goniometer (usually RBV) + + Returns: + A dictionary mapping x, y and z to tweaked coordinates + """ + + # Transform z from prev point and y from current point to lab coordinate system + _, _, zLabPrev, _ = daq_utils.gonio2lab( + prev_coords["x"], + prev_coords["y"], + prev_coords["z"], + current_raw_coords["omega"], + ) + _, yLabCurrent, _, _ = daq_utils.gonio2lab( + current_raw_coords["x"], + current_raw_coords["y"], + current_raw_coords["z"], + current_raw_coords["omega"], + ) - return super().mouseReleaseEvent(event) \ No newline at end of file + # Take y co-ordinate from current point and z-coordinate from prev point and transform back to gonio co-ordinates + _, yTweakedCurrent, zTweakedCurrent, _ = daq_utils.lab2gonio( + prev_coords["x"], yLabCurrent, zLabPrev, current_raw_coords["omega"] + ) + return { + "x": current_raw_coords["x"], + "y": yTweakedCurrent, + "z": zTweakedCurrent, + } From d1ea3cff951c62247cf1117b0d54ff13f0ff40b4 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Sun, 23 Jul 2023 19:14:59 -0400 Subject: [PATCH 03/19] Fixes to make vector drag work --- gui/control_main.py | 2 +- gui/vector.py | 46 ++++++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index b71029ff..13916ddb 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -203,8 +203,8 @@ def __init__(self): self.raddoseTimer.setInterval(1000) self.raddoseTimer.timeout.connect(self.spawnRaddoseThread) - self.createSampleTab() self.vector_widget = VectorWidget(main_window=self) + self.createSampleTab() self.initCallbacks() if self.scannerType != "PI": diff --git a/gui/vector.py b/gui/vector.py index d48976d5..069f1aac 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -12,7 +12,7 @@ def __init__(self, *args, **kwargs): self.blue_color = QtCore.Qt.GlobalColor.blue brush = kwargs.pop("brush", QtGui.QBrush()) pen = kwargs.pop("pen", QtGui.QPen(self.blue_color)) - self.parent = kwargs.pop("parent", None) + self.parent: VectorWidget = kwargs.pop("parent", None) self.point_name = kwargs.pop("point_name", None) self.coords = kwargs.pop("coords") self.gonio_coords = kwargs.pop("gonio_coords") @@ -27,18 +27,18 @@ def __init__(self, *args, **kwargs): def itemChange(self, change, value): if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: - if self.parent and self.parent.vecLine: - self.parent.scene.removeItem(self.parent.vecLine) - self.parent.drawVector() + if self.parent and self.parent.vector_line: + self.parent.main_window.scene.removeItem(self.parent.vector_line) + self.parent.main_window.drawVector() return super().itemChange(change, value) def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: print("New position:", self.pos()) if self.parent: - micron_x = self.parent.screenXPixels2microns(self.pos().x()) - micron_y = self.parent.screenYPixels2microns(self.pos().y()) + micron_x = self.parent.main_window.screenXPixels2microns(self.pos().x()) + micron_y = self.parent.main_window.screenYPixels2microns(self.pos().y()) if self.point_name and getattr(self.parent, self.point_name): - omega = self.parent.omegaRBV_pv.get() + omega = self.parent.main_window.omegaRBV_pv.get() point: VectorMarker = getattr(self.parent, self.point_name) ( gonio_offset_x, @@ -47,9 +47,9 @@ def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: omega, ) = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) gonio_coords = { - "x": self.parent.sampx_pv.get() + gonio_offset_x, - "y": self.parent.sampy_pv.get() + gonio_offset_y, - "z": self.parent.sampz_pv.get() + gonio_offset_z, + "x": self.parent.main_window.sampx_pv.get() + gonio_offset_x, + "y": self.parent.main_window.sampy_pv.get() + gonio_offset_y, + "z": self.parent.main_window.sampz_pv.get() + gonio_offset_z, "omega": omega, } vectorCoords = self.parent.transform_vector_coords( @@ -64,12 +64,10 @@ def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: class VectorWidget(QtWidgets.QWidget): def __init__( self, - parent: QtWidgets.QWidget | None = None, - flags=..., - scene: QtWidgets.QGraphicsScene = ..., - main_window: ControlMain = ..., + main_window: "ControlMain", + parent: "QtWidgets.QWidget | None" = None, ) -> None: - super().__init__(parent, flags) + super().__init__(parent) self.main_window = main_window self.vector_start: "None | VectorMarker" = None self.vector_end: "None | VectorMarker" = None @@ -114,7 +112,7 @@ def update_vector( pos_rbv: int, mot_id: str, center_marker: QtCore.QPointF, - offset: tuple[int, int], + offset: "tuple[int, int]", ): if ( self.vector_start is not None @@ -134,8 +132,8 @@ def update_vector( self.vector_end.y() + self.vector_start.center_marker.y() + offset[1], ) - def get_length(self) -> tuple[int, int, int, np.floating[typing.Any]]: - trans_total = np.floating(0.0) + def get_length(self) -> "tuple[int, int, int, np.floating[typing.Any]]": + trans_total = 0.0 x_vec = y_vec = z_vec = 0 if self.vector_start and self.vector_end: @@ -153,7 +151,7 @@ def get_length(self) -> tuple[int, int, int, np.floating[typing.Any]]: def get_length_and_speed( self, osc_end: float, osc_range: float, exposure_time: float - ) -> tuple[int, int, int, np.floating[typing.Any], np.floating[typing.Any]]: + ) -> "tuple[int, int, int, np.floating[typing.Any], np.floating[typing.Any]]": total_exposure_time: float = (osc_end / osc_range) * exposure_time x_vec, y_vec, z_vec, vector_length = self.get_length() speed = vector_length / total_exposure_time @@ -188,11 +186,11 @@ def set_vector_point( if self.vector_start and self.vector_end: self.draw_vector(center, scene) - def draw_vector(self, center: tuple[float, float], scene: QtWidgets.QGraphicsScene): + def draw_vector(self, center: "tuple[float, float]", scene: QtWidgets.QGraphicsScene): pen = QtGui.QPen(self.blue_color) if self.vector_start is not None and self.vector_end is not None: - self.vecLine = scene.addLine( + self.vector_line = scene.addLine( center[0] + self.vector_start.x(), center[1] + self.vector_start.y(), center[0] + self.vector_end.x(), @@ -266,9 +264,9 @@ def clear_vector(self, scene: QtWidgets.QGraphicsScene): if self.vector_end: scene.removeItem(self.vector_end) self.vector_end = None - if self.vecLine: - scene.removeItem(self.vecLine) - self.vecLine = None + if self.vector_line: + scene.removeItem(self.vector_line) + self.vector_line = None def transform_vector_coords( self, prev_coords: "dict[str, float]", current_raw_coords: "dict[str, float]" From d3886ad8682e806e469ffac7de5b36d9b849c3b9 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Sun, 23 Jul 2023 20:16:04 -0400 Subject: [PATCH 04/19] Added signals to vector marker to handle changes --- gui/vector.py | 68 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index 069f1aac..d738c35c 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -8,6 +8,9 @@ class VectorMarker(QtWidgets.QGraphicsEllipseItem): + marker_pos_changed = QtCore.Signal(object) + marker_dropped = QtCore.Signal(object) + def __init__(self, *args, **kwargs): self.blue_color = QtCore.Qt.GlobalColor.blue brush = kwargs.pop("brush", QtGui.QBrush()) @@ -27,37 +30,12 @@ def __init__(self, *args, **kwargs): def itemChange(self, change, value): if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: - if self.parent and self.parent.vector_line: - self.parent.main_window.scene.removeItem(self.parent.vector_line) - self.parent.main_window.drawVector() + self.marker_pos_changed.emit(value) return super().itemChange(change, value) def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: print("New position:", self.pos()) - if self.parent: - micron_x = self.parent.main_window.screenXPixels2microns(self.pos().x()) - micron_y = self.parent.main_window.screenYPixels2microns(self.pos().y()) - if self.point_name and getattr(self.parent, self.point_name): - omega = self.parent.main_window.omegaRBV_pv.get() - point: VectorMarker = getattr(self.parent, self.point_name) - ( - gonio_offset_x, - gonio_offset_y, - gonio_offset_z, - omega, - ) = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) - gonio_coords = { - "x": self.parent.main_window.sampx_pv.get() + gonio_offset_x, - "y": self.parent.main_window.sampy_pv.get() + gonio_offset_y, - "z": self.parent.main_window.sampz_pv.get() + gonio_offset_z, - "omega": omega, - } - vectorCoords = self.parent.transform_vector_coords( - point.coords, gonio_coords - ) - point.coords = vectorCoords - point.gonio_coords = gonio_coords - + self.marker_dropped.emit(self) return super().mouseReleaseEvent(event) @@ -186,7 +164,9 @@ def set_vector_point( if self.vector_start and self.vector_end: self.draw_vector(center, scene) - def draw_vector(self, center: "tuple[float, float]", scene: QtWidgets.QGraphicsScene): + def draw_vector( + self, center: "tuple[float, float]", scene: QtWidgets.QGraphicsScene + ): pen = QtGui.QPen(self.blue_color) if self.vector_start is not None and self.vector_end is not None: @@ -255,8 +235,40 @@ def setup_vector_object( gonio_coords=gonio_coords, center_marker=self.main_window.centerMarker, ) + vecMarker.marker_dropped.connect(self.update_marker_position) + vecMarker.marker_pos_changed.connect(self.update_vector_position) return vecMarker + def update_vector_position(self, value): + if self.vector_line: + self.main_window.scene.removeItem(self.vector_line) + self.main_window.drawVector() + + def update_marker_position(self, point: VectorMarker): + # First convert the distance moved by the point from pixels to microns + micron_x = self.main_window.screenXPixels2microns(point.pos().x()) + micron_y = self.main_window.screenYPixels2microns(point.pos().y()) + omega = self.main_window.omegaRBV_pv.get() + + # Then translate the delta from microns in the lab co-ordinate system to gonio + ( + gonio_offset_x, + gonio_offset_y, + gonio_offset_z, + omega, + ) = daq_utils.lab2gonio(micron_x, -micron_y, 0, omega) + + # Then add the delta to the current gonio co-ordinates + gonio_coords = { + "x": self.main_window.sampx_pv.get() + gonio_offset_x, + "y": self.main_window.sampy_pv.get() + gonio_offset_y, + "z": self.main_window.sampz_pv.get() + gonio_offset_z, + "omega": omega, + } + vectorCoords = self.transform_vector_coords(point.coords, gonio_coords) + point.coords = vectorCoords + point.gonio_coords = gonio_coords + def clear_vector(self, scene: QtWidgets.QGraphicsScene): if self.vector_start: scene.removeItem(self.vector_start) From 9ae3a204612a4f742392426faa7712df9adfb948 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Sun, 23 Jul 2023 20:45:09 -0400 Subject: [PATCH 05/19] Added signal fixes and mouse hover cursor changes --- gui/vector.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index d738c35c..d6cc5ab3 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -6,11 +6,13 @@ if typing.TYPE_CHECKING: from gui.control_main import ControlMain - -class VectorMarker(QtWidgets.QGraphicsEllipseItem): +class VectorMarkerSignals(QtCore.QObject): marker_pos_changed = QtCore.Signal(object) marker_dropped = QtCore.Signal(object) + +class VectorMarker(QtWidgets.QGraphicsEllipseItem): + def __init__(self, *args, **kwargs): self.blue_color = QtCore.Qt.GlobalColor.blue brush = kwargs.pop("brush", QtGui.QBrush()) @@ -27,16 +29,31 @@ def __init__(self, *args, **kwargs): self.setFlag( QtWidgets.QGraphicsEllipseItem.GraphicsItemFlag.ItemSendsGeometryChanges ) + self.signals = VectorMarkerSignals() def itemChange(self, change, value): if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: - self.marker_pos_changed.emit(value) + self.signals.marker_pos_changed.emit(value) return super().itemChange(change, value) def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: print("New position:", self.pos()) - self.marker_dropped.emit(self) + self.signals.marker_dropped.emit(self) return super().mouseReleaseEvent(event) + + def hoverEnterEvent(self, event): + cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) + self.setCursor(cursor) + super().hoverEnterEvent(event) + + def hoverLeaveEvent(self, event): + self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) + super().hoverLeaveEvent(event) + + def mousePressEvent(self, event: "QGraphicsSceneMouseEvent") -> None: + cursor = QtGui.QCursor(QtCore.Qt.ClosedHandCursor) + self.setCursor(cursor) + super().mousePressEvent(event) class VectorWidget(QtWidgets.QWidget): @@ -235,8 +252,8 @@ def setup_vector_object( gonio_coords=gonio_coords, center_marker=self.main_window.centerMarker, ) - vecMarker.marker_dropped.connect(self.update_marker_position) - vecMarker.marker_pos_changed.connect(self.update_vector_position) + vecMarker.signals.marker_dropped.connect(self.update_marker_position) + vecMarker.signals.marker_pos_changed.connect(self.update_vector_position) return vecMarker def update_vector_position(self, value): From 19892d46ebd1bc71df9d1663dcc4e1992f63486a Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Wed, 26 Jul 2023 13:23:30 -0400 Subject: [PATCH 06/19] Fixed issue where the point variable is accessed like a dict --- gui/vector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/vector.py b/gui/vector.py index d6cc5ab3..2dd64b1d 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -162,7 +162,7 @@ def set_vector_point( ): point = getattr(self, point_name) if point: - scene.removeItem(point["graphicsitem"]) + scene.removeItem(point) if self.vector_line: scene.removeItem(self.vector_line) if point_name == "vector_end": From bc7a05de14678f0ca01978487a9c439b5a490315 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 26 Jul 2023 16:56:14 -0400 Subject: [PATCH 07/19] Added alternate cursor in case open hand doesnt exist --- gui/vector.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index 2dd64b1d..21f8d268 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -6,13 +6,13 @@ if typing.TYPE_CHECKING: from gui.control_main import ControlMain + class VectorMarkerSignals(QtCore.QObject): marker_pos_changed = QtCore.Signal(object) marker_dropped = QtCore.Signal(object) class VectorMarker(QtWidgets.QGraphicsEllipseItem): - def __init__(self, *args, **kwargs): self.blue_color = QtCore.Qt.GlobalColor.blue brush = kwargs.pop("brush", QtGui.QBrush()) @@ -40,10 +40,12 @@ def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: print("New position:", self.pos()) self.signals.marker_dropped.emit(self) return super().mouseReleaseEvent(event) - + def hoverEnterEvent(self, event): cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) self.setCursor(cursor) + if self.cursor.shape() != cursor: + self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor) super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): From 7c6bc4779244c83c5f31e9dbd10b1c1e4802d5c8 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Wed, 26 Jul 2023 17:12:32 -0400 Subject: [PATCH 08/19] Added set accept on hover --- gui/vector.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index 21f8d268..654fc268 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -30,6 +30,7 @@ def __init__(self, *args, **kwargs): QtWidgets.QGraphicsEllipseItem.GraphicsItemFlag.ItemSendsGeometryChanges ) self.signals = VectorMarkerSignals() + self.setAcceptHoverEvents(True) def itemChange(self, change, value): if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: @@ -37,15 +38,14 @@ def itemChange(self, change, value): return super().itemChange(change, value) def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: - print("New position:", self.pos()) + cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) + self.setCursor(cursor) self.signals.marker_dropped.emit(self) return super().mouseReleaseEvent(event) def hoverEnterEvent(self, event): cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) self.setCursor(cursor) - if self.cursor.shape() != cursor: - self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor) super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): @@ -180,6 +180,7 @@ def set_vector_point( ) setattr(self, point_name, point) scene.addItem(point) + point.setZValue(2.0) if self.vector_start and self.vector_end: self.draw_vector(center, scene) From 46cb653d6cab370c76d865ee7a18a7d75e39a035 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Tue, 1 Aug 2023 14:05:14 -0400 Subject: [PATCH 09/19] Cleaned up and modularized raster drawing --- gui/control_main.py | 237 ++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 138 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 13916ddb..1da51bb2 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3276,35 +3276,25 @@ def definePolyRaster( logger.error("bad value for beam width or beam height") self.popupServerMessage("bad value for beam width or beam height") return + rasterDef = { + "rasterType": "normal", + "beamWidth": beamWidth, + "beamHeight": beamHeight, + "status": RasterStatus.NEW.value, + "x": self.sampx_pv.get(), + "y": self.sampy_pv.get(), + "z": self.sampz_pv.get(), + "omega": self.omega_pv.get(), + "stepsize": stepsize, + "rowDefs": [], + } # just storing step as microns, not using here if self.scannerType == "PI": - rasterDef = { - "rasterType": "normal", - "beamWidth": beamWidth, - "beamHeight": beamHeight, - "status": RasterStatus.NEW.value, - "x": self.sampx_pv.get() + self.sampFineX_pv.get(), - "y": self.sampy_pv.get() + self.sampFineY_pv.get(), - "z": self.sampz_pv.get() + self.sampFineZ_pv.get(), - "omega": self.omega_pv.get(), - "stepsize": stepsize, - "rowDefs": [], - } # just storing step as microns, not using her - else: - rasterDef = { - "rasterType": "normal", - "beamWidth": beamWidth, - "beamHeight": beamHeight, - "status": RasterStatus.NEW.value, - "x": self.sampx_pv.get(), - "y": self.sampy_pv.get(), - "z": self.sampz_pv.get(), - "omega": self.omega_pv.get(), - "stepsize": stepsize, - "rowDefs": [], - } # just storing step as microns, not using here - numsteps_h = int( - raster_w / stepsizeXPix - ) # raster_w = width,goes to numsteps horizonatl + rasterDef["x"] += self.sampFineX_pv.get() + rasterDef["y"] += self.sampFineY_pv.get() + rasterDef["z"] += self.sampFineZ_pv.get() + + # raster_w = width,goes to numsteps horizonatl + numsteps_h = int(raster_w / stepsizeXPix) numsteps_v = int(raster_h / stepsizeYPix) if numsteps_h == 2: numsteps_h = 1 # fix slop in user single line attempt @@ -3315,66 +3305,44 @@ def definePolyRaster( rasterDef["numCells"] = numsteps_h * numsteps_v point_offset_x = -(numsteps_h * stepsizeXPix) / 2 point_offset_y = -(numsteps_v * stepsizeYPix) / 2 + + vertical_raster = False + direction = numsteps_v if (numsteps_h == 1) or ( numsteps_v > numsteps_h and getBlConfig("vertRasterOn") - ): # vertical raster - for i in range(numsteps_h): - rowCellCount = 0 - for j in range(numsteps_v): - newCellX = point_x + (i * stepsizeXPix) + point_offset_x - newCellY = point_y + (j * stepsizeYPix) + point_offset_y - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - rowCellCount = rowCellCount + 1 - if ( - rowCellCount != 0 - ): # test for no points in this row of the bounding rect are in the poly? - vectorStartX = self.screenXPixels2microns( - rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX - ) - vectorEndX = vectorStartX - vectorStartY = self.screenYPixels2microns( - rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY - ) - vectorEndY = vectorStartY + self.screenYPixels2microns( - rowCellCount * stepsizeYPix - ) - newRowDef = { - "start": {"x": vectorStartX, "y": vectorStartY}, - "end": {"x": vectorEndX, "y": vectorEndY}, - "numsteps": rowCellCount, - } - rasterDef["rowDefs"].append(newRowDef) - else: # horizontal raster - for i in range(numsteps_v): - rowCellCount = 0 - for j in range(numsteps_h): - newCellX = point_x + (j * stepsizeXPix) + point_offset_x - newCellY = point_y + (i * stepsizeYPix) + point_offset_y - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - rowCellCount = rowCellCount + 1 - if ( - rowCellCount != 0 - ): # testing for no points in this row of the bounding rect are in the poly? - vectorStartX = self.screenXPixels2microns( - rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX - ) - vectorEndX = vectorStartX + self.screenXPixels2microns( - rowCellCount * stepsizeXPix - ) # this looks better - vectorStartY = self.screenYPixels2microns( - rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY - ) - vectorEndY = vectorStartY - newRowDef = { - "start": {"x": vectorStartX, "y": vectorStartY}, - "end": {"x": vectorEndX, "y": vectorEndY}, - "numsteps": rowCellCount, - } - rasterDef["rowDefs"].append(newRowDef) + ): + vertical_raster = True + direction = numsteps_h + + for i in range(direction): + rowCellCount = 0 + rowStartX = point_x + point_offset_x + rowStartY = point_y + point_offset_y + if vertical_raster: + rowStartX += i * stepsizeXPix + rowCellCount = numsteps_v + else: + rowStartY += i * stepsizeYPix + rowCellCount = numsteps_h + vectorStartX = self.screenXPixels2microns( + rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX + ) + vectorStartY = self.screenYPixels2microns( + rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY + ) + + vectorEndX = vectorStartX + vectorEndY = vectorStartY + if vertical_raster: + vectorEndY += self.screenYPixels2microns(rowCellCount * stepsizeYPix) + else: + vectorEndX += self.screenXPixels2microns(rowCellCount * stepsizeXPix) + newRowDef = { + "start": {"x": vectorStartX, "y": vectorStartY}, + "end": {"x": vectorEndX, "y": vectorEndY}, + "numsteps": rowCellCount, + } + rasterDef["rowDefs"].append(newRowDef) setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) setBlConfig("rasterDefaultTrans", float(self.transmission_ledit.text())) @@ -3389,6 +3357,49 @@ def rasterIsDrawn(self, rasterReq): return True return False + def draw_raster(self, raster_def: "dict[str, Any]", raster_dir: str) -> RasterGroup: + """ + This method is only concerned with creating the raster graphics + + Arguments: + raster_def : dict containing start and end points in microns for each row of the raster + + Returns: + raster_group : Raster graphics object of type RasterGroup + """ + stepsizeX = self.screenXmicrons2pixels(raster_def["stepsize"]) + stepsizeY = self.screenYmicrons2pixels(raster_def["stepsize"]) + pen = QtGui.QPen(QtCore.Qt.red) + newRasterCellList = [] + offset = { + "x": self.centerMarker.x() + self.centerMarkerCharOffsetX, + "y": self.centerMarker.y() + self.centerMarkerCharOffsetY, + } + for i in range(len(raster_def["rowDefs"])): + newCellX = ( + self.screenXmicrons2pixels(raster_def["rowDefs"][i]["start"]["x"]) + + offset["x"] + ) + newCellY = ( + self.screenYmicrons2pixels(raster_def["rowDefs"][i]["start"]["y"]) + + offset["y"] + ) + for j in range(raster_def["rowDefs"][i]["numsteps"]): + if raster_dir == "horizontal": + newCellX += j * stepsizeX + else: + newCellY += j * stepsizeY + + newCell = RasterCell( + int(newCellX), int(newCellY), stepsizeX, stepsizeY, self + ) + newRasterCellList.append(newCell) + newCell.setPen(pen) + newItemGroup = RasterGroup(self) + for i in range(len(newRasterCellList)): + newItemGroup.addToGroup(newRasterCellList[i]) + return newItemGroup + def drawPolyRaster( self, rasterReq, x=-1, y=-1, z=-1 ): # rasterDef in microns,offset from center, need to convert to pixels to draw, mainly this is for displaying autoRasters, but also called in zoom change @@ -3396,11 +3407,6 @@ def drawPolyRaster( rasterDef = rasterReq["request_obj"]["rasterDef"] except KeyError: return - beamSize = self.screenXmicrons2pixels(rasterDef["beamWidth"]) - stepsizeX = self.screenXmicrons2pixels(rasterDef["stepsize"]) - stepsizeY = self.screenYmicrons2pixels(rasterDef["stepsize"]) - pen = QtGui.QPen(QtCore.Qt.red) - newRasterCellList = [] try: if ( rasterDef["rowDefs"][0]["start"]["y"] @@ -3411,54 +3417,9 @@ def drawPolyRaster( rasterDir = "vertical" except IndexError: return - for i in range(len(rasterDef["rowDefs"])): - rowCellCount = 0 - for j in range(rasterDef["rowDefs"][i]["numsteps"]): - if rasterDir == "horizontal": - newCellX = ( - self.screenXmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["x"] - ) - + (j * stepsizeX) - + self.centerMarker.x() - + self.centerMarkerCharOffsetX - ) - newCellY = ( - self.screenYmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["y"] - ) - + self.centerMarker.y() - + self.centerMarkerCharOffsetY - ) - else: - newCellX = ( - self.screenXmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["x"] - ) - + self.centerMarker.x() - + self.centerMarkerCharOffsetX - ) - newCellY = ( - self.screenYmicrons2pixels( - rasterDef["rowDefs"][i]["start"]["y"] - ) - + (j * stepsizeY) - + self.centerMarker.y() - + self.centerMarkerCharOffsetY - ) - if rowCellCount == 0: # start of a new row - rowStartX = newCellX - rowStartY = newCellY - newCellX = int(newCellX) - newCellY = int(newCellY) - newCell = RasterCell(newCellX, newCellY, stepsizeX, stepsizeY, self) - newRasterCellList.append(newCell) - newCell.setPen(pen) - rowCellCount = rowCellCount + 1 # really just for test of new row - newItemGroup = RasterGroup(self) + newItemGroup = self.draw_raster(rasterDef, rasterDir) self.scene.addItem(newItemGroup) - for i in range(len(newRasterCellList)): - newItemGroup.addToGroup(newRasterCellList[i]) + newRasterGraphicsDesc = { "uid": rasterReq["uid"], "coords": { From ca71e5b4dbdd5ff03e4560d72d0bddea1a39e788 Mon Sep 17 00:00:00 2001 From: vshekar1 Date: Tue, 1 Aug 2023 15:08:56 -0400 Subject: [PATCH 10/19] Fixed issue with adding vector data --- gui/control_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 1da51bb2..e9e54fc7 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -4058,8 +4058,8 @@ def addSampleRequestCB(self, rasterDef=None, selectedSampleID=None): x_vec, y_vec, z_vec, trans_total = self.updateVectorLengthAndSpeed() framesPerPoint = int(self.vectorFPP_ledit.text()) vectorParams = { - "vecStart": self.vector_widget.vector_start["coords"], - "vecEnd": self.vector_widget.vector_end["coords"], + "vecStart": self.vector_widget.vector_start.coords, + "vecEnd": self.vector_widget.vector_end.coords, "x_vec": x_vec, "y_vec": y_vec, "z_vec": z_vec, From a572102fd396e56849179750396dd294de069525 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 5 Jun 2024 18:32:33 -0400 Subject: [PATCH 11/19] Fixed raster drawing --- gui/control_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index e7f393dc..3924f540 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3359,9 +3359,9 @@ def draw_raster(self, raster_def: "dict[str, Any]", raster_dir: str) -> RasterGr ) for j in range(raster_def["rowDefs"][i]["numsteps"]): if raster_dir == "horizontal": - newCellX += j * stepsizeX + newCellX += stepsizeX else: - newCellY += j * stepsizeY + newCellY += stepsizeY newCell = RasterCell( int(newCellX), int(newCellY), stepsizeX, stepsizeY, self From 070822fe1d6148deeb48bc753c7cee1614d14ef6 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 5 Jun 2024 20:33:08 -0400 Subject: [PATCH 12/19] Added full vector button --- gui/control_main.py | 23 +++++++++++++++++------ gui/vector.py | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 3924f540..9169476d 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -836,6 +836,9 @@ def createSampleTab(self): setVectorEndButton = QtWidgets.QPushButton("Vector\nEnd") setVectorEndButton.setStyleSheet("background-color: red") setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vector_end")) + + setVectorButton = QtWidgets.QPushButton("Set\nVector") + setVectorButton.clicked.connect(lambda: self.setVectorPointCB("full_vector")) self.vecLine = None vectorFPPLabel = QtWidgets.QLabel("Number of Wedges") self.vectorFPP_ledit = QtWidgets.QLineEdit("1") @@ -846,6 +849,7 @@ def createSampleTab(self): self.vecSpeedLabelOutput = QtWidgets.QLabel("---") hBoxVectorLayout1.addWidget(setVectorStartButton) hBoxVectorLayout1.addWidget(setVectorEndButton) + hBoxVectorLayout1.addWidget(setVectorButton) hBoxVectorLayout1.addWidget(vectorFPPLabel) hBoxVectorLayout1.addWidget(self.vectorFPP_ledit) hBoxVectorLayout1.addWidget(vecLenLabel) @@ -4157,12 +4161,19 @@ def setVectorPointCB(self, pointName): } center_x = self.centerMarker.x() + self.centerMarkerCharOffsetX center_y = self.centerMarker.y() + self.centerMarkerCharOffsetY - self.vector_widget.set_vector_point( - point_name=pointName, - scene=self.scene, - gonio_coords=gonio_coords, - center=(center_x, center_y), - ) + if pointName == "full_vector": + self.vector_widget.set_vector( + scene=self.scene, + gonio_coords=gonio_coords, + center=(center_x, center_y), + ) + else: + self.vector_widget.set_vector_point( + point_name=pointName, + scene=self.scene, + gonio_coords=gonio_coords, + center=(center_x, center_y), + ) def drawVector(self): try: diff --git a/gui/vector.py b/gui/vector.py index 654fc268..609d1744 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -154,6 +154,24 @@ def get_length_and_speed( speed = vector_length / total_exposure_time return x_vec, y_vec, z_vec, vector_length, speed + + def set_vector(self, scene: QtWidgets.QGraphicsScene, + gonio_coords: "dict[str, typing.Any]", + center: "tuple[float, float]"): + gonio_coords_start = { + "x": gonio_coords["x"] - 20, + "y": gonio_coords["y"], + "z": gonio_coords["z"], + "omega": gonio_coords["omega"], + } + gonio_coords_end = { + "x": gonio_coords["x"] + 20, + "y": gonio_coords["y"], + "z": gonio_coords["z"], + "omega": gonio_coords["omega"], + } + self.set_vector_point("vector_start", scene, gonio_coords_start, center) + self.set_vector_point("vector_end", scene, gonio_coords_end, center) def set_vector_point( self, From 4a7f6adba326be155de71314129b4b39c8382de9 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Wed, 5 Jun 2024 21:05:44 -0400 Subject: [PATCH 13/19] Added processSampMove to update vector node positions --- gui/control_main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/control_main.py b/gui/control_main.py index 9169476d..9712caa2 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -4167,6 +4167,9 @@ def setVectorPointCB(self, pointName): gonio_coords=gonio_coords, center=(center_x, center_y), ) + self.processSampMove(self.sampx_pv.get(), "x") + self.processSampMove(self.sampy_pv.get(), "y") + self.processSampMove(self.sampz_pv.get(), "z") else: self.vector_widget.set_vector_point( point_name=pointName, From 8fd68a9ccc3b8e152acb4b03ce3d575f29e8f42e Mon Sep 17 00:00:00 2001 From: Shekar V Date: Thu, 6 Jun 2024 11:11:39 -0400 Subject: [PATCH 14/19] Added quick vector widgets and fixed position updates --- gui/control_main.py | 26 +++++++++++++++++++------- gui/vector.py | 25 ++++++++++++++++--------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 9712caa2..6d628ba2 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -837,8 +837,6 @@ def createSampleTab(self): setVectorEndButton.setStyleSheet("background-color: red") setVectorEndButton.clicked.connect(lambda: self.setVectorPointCB("vector_end")) - setVectorButton = QtWidgets.QPushButton("Set\nVector") - setVectorButton.clicked.connect(lambda: self.setVectorPointCB("full_vector")) self.vecLine = None vectorFPPLabel = QtWidgets.QLabel("Number of Wedges") self.vectorFPP_ledit = QtWidgets.QLineEdit("1") @@ -849,14 +847,27 @@ def createSampleTab(self): self.vecSpeedLabelOutput = QtWidgets.QLabel("---") hBoxVectorLayout1.addWidget(setVectorStartButton) hBoxVectorLayout1.addWidget(setVectorEndButton) - hBoxVectorLayout1.addWidget(setVectorButton) hBoxVectorLayout1.addWidget(vectorFPPLabel) hBoxVectorLayout1.addWidget(self.vectorFPP_ledit) hBoxVectorLayout1.addWidget(vecLenLabel) hBoxVectorLayout1.addWidget(self.vecLenLabelOutput) hBoxVectorLayout1.addWidget(vecSpeedLabel) hBoxVectorLayout1.addWidget(self.vecSpeedLabelOutput) - self.vectorParamsFrame.setLayout(hBoxVectorLayout1) + vector_widgets_layout = QtWidgets.QVBoxLayout() + vector_widgets_layout.addLayout(hBoxVectorLayout1) + hBoxVectorLayout2 = QtWidgets.QHBoxLayout() + setVectorButton = QtWidgets.QPushButton("Set Quick\nVector") + setVectorButton.clicked.connect(lambda: self.setVectorPointCB("full_vector")) + vector_length_label = QtWidgets.QLabel("Quick vector length (microns)") + self.vector_length_ledit = QtWidgets.QLineEdit("40") + self.vector_length_ledit.setValidator(QIntValidator(self)) + + hBoxVectorLayout2.addWidget(setVectorButton) + hBoxVectorLayout2.addWidget(vector_length_label) + hBoxVectorLayout2.addWidget(self.vector_length_ledit) + + vector_widgets_layout.addLayout(hBoxVectorLayout2) + self.vectorParamsFrame.setLayout(vector_widgets_layout) vBoxColParams1.addLayout(hBoxColParams1) vBoxColParams1.addLayout(hBoxColParams2) vBoxColParams1.addLayout(hBoxColParams25) @@ -4166,10 +4177,8 @@ def setVectorPointCB(self, pointName): scene=self.scene, gonio_coords=gonio_coords, center=(center_x, center_y), + length=int(self.vector_length_ledit.text()) ) - self.processSampMove(self.sampx_pv.get(), "x") - self.processSampMove(self.sampy_pv.get(), "y") - self.processSampMove(self.sampz_pv.get(), "z") else: self.vector_widget.set_vector_point( point_name=pointName, @@ -4177,6 +4186,9 @@ def setVectorPointCB(self, pointName): gonio_coords=gonio_coords, center=(center_x, center_y), ) + self.processSampMove(self.sampx_pv.get(), "x") + self.processSampMove(self.sampy_pv.get(), "y") + self.processSampMove(self.sampz_pv.get(), "z") def drawVector(self): try: diff --git a/gui/vector.py b/gui/vector.py index 609d1744..694e56ba 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -1,6 +1,8 @@ import typing -from qtpy import QtWidgets, QtGui, QtCore + import numpy as np +from qtpy import QtCore, QtGui, QtWidgets + import daq_utils if typing.TYPE_CHECKING: @@ -111,17 +113,19 @@ def update_vector( center_marker: QtCore.QPointF, offset: "tuple[int, int]", ): - if ( - self.vector_start is not None - and self.vector_end is not None - and self.vector_line is not None - ): + if self.vector_start is not None: self.vector_start = self.update_point( self.vector_start, pos_rbv, mot_id, center_marker ) + if self.vector_end is not None: self.vector_end = self.update_point( self.vector_end, pos_rbv, mot_id, center_marker ) + if ( + self.vector_start is not None + and self.vector_end is not None + and self.vector_line is not None + ): self.vector_line.setLine( self.vector_start.x() + self.vector_start.center_marker.x() + offset[0], self.vector_start.y() + self.vector_start.center_marker.y() + offset[1], @@ -157,15 +161,16 @@ def get_length_and_speed( def set_vector(self, scene: QtWidgets.QGraphicsScene, gonio_coords: "dict[str, typing.Any]", - center: "tuple[float, float]"): + center: "tuple[float, float]", length=40): + offset = int(length/2) gonio_coords_start = { - "x": gonio_coords["x"] - 20, + "x": gonio_coords["x"] - offset, "y": gonio_coords["y"], "z": gonio_coords["z"], "omega": gonio_coords["omega"], } gonio_coords_end = { - "x": gonio_coords["x"] + 20, + "x": gonio_coords["x"] + offset, "y": gonio_coords["y"], "z": gonio_coords["z"], "omega": gonio_coords["omega"], @@ -360,3 +365,5 @@ def transform_vector_coords( "y": yTweakedCurrent, "z": zTweakedCurrent, } + "z": zTweakedCurrent, + } From e6be1792813ffd67030e6fb97fb75e0cf13861cb Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 7 Jun 2024 02:20:04 -0400 Subject: [PATCH 15/19] Fixed syntax error --- gui/vector.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index 694e56ba..68a388db 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -365,5 +365,3 @@ def transform_vector_coords( "y": yTweakedCurrent, "z": zTweakedCurrent, } - "z": zTweakedCurrent, - } From 6429a68eea0b7a9edaf949a1d39ac63514c162f3 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 14 Jun 2024 09:36:57 -0400 Subject: [PATCH 16/19] Added typing info --- gui/vector.py | 52 +++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/gui/vector.py b/gui/vector.py index 68a388db..9f062345 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -15,7 +15,7 @@ class VectorMarkerSignals(QtCore.QObject): class VectorMarker(QtWidgets.QGraphicsEllipseItem): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: self.blue_color = QtCore.Qt.GlobalColor.blue brush = kwargs.pop("brush", QtGui.QBrush()) pen = kwargs.pop("pen", QtGui.QPen(self.blue_color)) @@ -34,28 +34,28 @@ def __init__(self, *args, **kwargs): self.signals = VectorMarkerSignals() self.setAcceptHoverEvents(True) - def itemChange(self, change, value): + def itemChange(self, change, value) -> typing.Any: if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged: self.signals.marker_pos_changed.emit(value) return super().itemChange(change, value) def mouseReleaseEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: - cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) + cursor = QtGui.QCursor(QtCore.Qt.CursorShape.OpenHandCursor) self.setCursor(cursor) self.signals.marker_dropped.emit(self) return super().mouseReleaseEvent(event) - def hoverEnterEvent(self, event): - cursor = QtGui.QCursor(QtCore.Qt.OpenHandCursor) + def hoverEnterEvent(self, event) -> None: + cursor = QtGui.QCursor(QtCore.Qt.CursorShape.OpenHandCursor) self.setCursor(cursor) super().hoverEnterEvent(event) - def hoverLeaveEvent(self, event): - self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) + def hoverLeaveEvent(self, event) -> None: + self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor)) super().hoverLeaveEvent(event) - def mousePressEvent(self, event: "QGraphicsSceneMouseEvent") -> None: - cursor = QtGui.QCursor(QtCore.Qt.ClosedHandCursor) + def mousePressEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: + cursor = QtGui.QCursor(QtCore.Qt.CursorShape.ClosedHandCursor) self.setCursor(cursor) super().mousePressEvent(event) @@ -80,7 +80,7 @@ def update_point( pos_rbv: int, mot_id: str, center_marker: QtCore.QPointF, - ): + ) -> VectorMarker: """Updates a point on the screen Updates the position of a point (e.g. self.vector_start) drawn on the screen based on @@ -112,7 +112,7 @@ def update_vector( mot_id: str, center_marker: QtCore.QPointF, offset: "tuple[int, int]", - ): + ) -> None: if self.vector_start is not None: self.vector_start = self.update_point( self.vector_start, pos_rbv, mot_id, center_marker @@ -133,7 +133,7 @@ def update_vector( self.vector_end.y() + self.vector_start.center_marker.y() + offset[1], ) - def get_length(self) -> "tuple[int, int, int, np.floating[typing.Any]]": + def get_length(self) -> "tuple[int, int, int, np.floating[typing.Any] | float]": trans_total = 0.0 x_vec = y_vec = z_vec = 0 @@ -152,17 +152,21 @@ def get_length(self) -> "tuple[int, int, int, np.floating[typing.Any]]": def get_length_and_speed( self, osc_end: float, osc_range: float, exposure_time: float - ) -> "tuple[int, int, int, np.floating[typing.Any], np.floating[typing.Any]]": + ) -> "tuple[int, int, int, np.floating[typing.Any] | float, np.floating[typing.Any] | float]": total_exposure_time: float = (osc_end / osc_range) * exposure_time x_vec, y_vec, z_vec, vector_length = self.get_length() speed = vector_length / total_exposure_time return x_vec, y_vec, z_vec, vector_length, speed - - def set_vector(self, scene: QtWidgets.QGraphicsScene, - gonio_coords: "dict[str, typing.Any]", - center: "tuple[float, float]", length=40): - offset = int(length/2) + + def set_vector( + self, + scene: QtWidgets.QGraphicsScene, + gonio_coords: "dict[str, typing.Any]", + center: "tuple[float, float]", + length=40, + ) -> None: + offset = int(length / 2) gonio_coords_start = { "x": gonio_coords["x"] - offset, "y": gonio_coords["y"], @@ -184,7 +188,7 @@ def set_vector_point( scene: QtWidgets.QGraphicsScene, gonio_coords: "dict[str, typing.Any]", center: "tuple[float, float]", - ): + ) -> None: point = getattr(self, point_name) if point: scene.removeItem(point) @@ -209,7 +213,7 @@ def set_vector_point( def draw_vector( self, center: "tuple[float, float]", scene: QtWidgets.QGraphicsScene - ): + ) -> None: pen = QtGui.QPen(self.blue_color) if self.vector_start is not None and self.vector_end is not None: @@ -282,12 +286,12 @@ def setup_vector_object( vecMarker.signals.marker_pos_changed.connect(self.update_vector_position) return vecMarker - def update_vector_position(self, value): + def update_vector_position(self, value) -> None: if self.vector_line: self.main_window.scene.removeItem(self.vector_line) self.main_window.drawVector() - def update_marker_position(self, point: VectorMarker): + def update_marker_position(self, point: VectorMarker) -> None: # First convert the distance moved by the point from pixels to microns micron_x = self.main_window.screenXPixels2microns(point.pos().x()) micron_y = self.main_window.screenYPixels2microns(point.pos().y()) @@ -312,7 +316,7 @@ def update_marker_position(self, point: VectorMarker): point.coords = vectorCoords point.gonio_coords = gonio_coords - def clear_vector(self, scene: QtWidgets.QGraphicsScene): + def clear_vector(self, scene: QtWidgets.QGraphicsScene) -> None: if self.vector_start: scene.removeItem(self.vector_start) self.vector_start = None @@ -325,7 +329,7 @@ def clear_vector(self, scene: QtWidgets.QGraphicsScene): def transform_vector_coords( self, prev_coords: "dict[str, float]", current_raw_coords: "dict[str, float]" - ): + ) -> dict[str, float]: """Updates y and z co-ordinates of vector points when they are moved This function tweaks the y and z co-ordinates such that when a vector start or From 5383c17388bcfe538373f25e7a6d6b587b10f0b1 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Fri, 14 Jun 2024 09:37:21 -0400 Subject: [PATCH 17/19] Added letters to start and end nodes --- gui/vector.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gui/vector.py b/gui/vector.py index 9f062345..e1d63e22 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -59,6 +59,16 @@ def mousePressEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None: self.setCursor(cursor) super().mousePressEvent(event) + def paint(self, painter, option, widget): + super().paint(painter, option, widget) + rect = self.rect() + painter.setPen(QtGui.QPen(QtCore.Qt.GlobalColor.black)) + if self.point_name == "vector_start": + text = "S" + else: + text = "E" + painter.drawText(rect, QtCore.Qt.AlignmentFlag.AlignCenter, text) + class VectorWidget(QtWidgets.QWidget): def __init__( From ddef424102d2add9ccd42e994bd5be73d61efda1 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 24 Jun 2024 10:52:55 -0400 Subject: [PATCH 18/19] Hide vector nodes when shift is held - Raster explore dialog is not focused when displayed - Update vector length and speed when a node is moved --- gui/control_main.py | 14 ++++++++++---- gui/dialog/raster_explore.py | 1 + gui/vector.py | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 6d628ba2..bbad77d5 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -247,6 +247,16 @@ def __init__(self): self.XRFInfoDict = self.parseXRFTable() # I don't like this # self.dewarTree.refreshTreeDewarView() + def eventFilter(self, obj, event): + # Event filter to hide vector nodes when shift is held. This is to allow the user to see + # the raster hidden by large vector nodes + if event.type() == QtCore.QEvent.KeyPress and event.key() == QtCore.Qt.Key.Key_Shift: + self.vector_widget.hide_nodes() + elif event.type() == QtCore.QEvent.KeyRelease and event.key() == QtCore.Qt.Key.Key_Shift: + self.vector_widget.show_nodes() + return QtWidgets.QWidget.eventFilter(self, obj, event) + + def setGuiValues(self, values): for item, value in values.items(): logger.info("resetting %s to %s" % (item, value)) @@ -4191,10 +4201,6 @@ def setVectorPointCB(self, pointName): self.processSampMove(self.sampz_pv.get(), "z") def drawVector(self): - try: - self.updateVectorLengthAndSpeed() - except: - pass self.protoVectorRadio.setChecked(True) center_x = self.centerMarker.x() + self.centerMarkerCharOffsetX center_y = self.centerMarker.y() + self.centerMarkerCharOffsetY diff --git a/gui/dialog/raster_explore.py b/gui/dialog/raster_explore.py index e86f79e0..f0f221c8 100644 --- a/gui/dialog/raster_explore.py +++ b/gui/dialog/raster_explore.py @@ -39,6 +39,7 @@ def __init__(self): vBoxParams1.addLayout(hBoxParams3) vBoxParams1.addWidget(self.buttons) self.setLayout(vBoxParams1) + self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowDoesNotAcceptFocus) def setSpotCount(self, val): self.spotCount_ledit.setText(str(val)) diff --git a/gui/vector.py b/gui/vector.py index e1d63e22..391ddfc8 100644 --- a/gui/vector.py +++ b/gui/vector.py @@ -326,6 +326,9 @@ def update_marker_position(self, point: VectorMarker) -> None: point.coords = vectorCoords point.gonio_coords = gonio_coords + # Update vector length and speed in the GUI after any node moves + self.main_window.updateVectorLengthAndSpeed() + def clear_vector(self, scene: QtWidgets.QGraphicsScene) -> None: if self.vector_start: scene.removeItem(self.vector_start) @@ -379,3 +382,15 @@ def transform_vector_coords( "y": yTweakedCurrent, "z": zTweakedCurrent, } + + def hide_nodes(self): + if self.vector_start: + self.vector_start.setVisible(False) + if self.vector_end: + self.vector_end.setVisible(False) + + def show_nodes(self): + if self.vector_start: + self.vector_start.setVisible(True) + if self.vector_end: + self.vector_end.setVisible(True) From 14d5e19c52efff4fd4111e834a77994eab86d027 Mon Sep 17 00:00:00 2001 From: Shekar V Date: Mon, 24 Jun 2024 11:18:52 -0400 Subject: [PATCH 19/19] Reverting raster changes --- gui/control_main.py | 237 ++++++++++++++++++++++++++------------------ 1 file changed, 138 insertions(+), 99 deletions(-) diff --git a/gui/control_main.py b/gui/control_main.py index 96b0d5a8..2a0dca44 100644 --- a/gui/control_main.py +++ b/gui/control_main.py @@ -3309,25 +3309,35 @@ def definePolyRaster( logger.error("bad value for beam width or beam height") self.popupServerMessage("bad value for beam width or beam height") return - rasterDef = { - "rasterType": "normal", - "beamWidth": beamWidth, - "beamHeight": beamHeight, - "status": RasterStatus.NEW.value, - "x": self.sampx_pv.get(), - "y": self.sampy_pv.get(), - "z": self.sampz_pv.get(), - "omega": self.omega_pv.get(), - "stepsize": stepsize, - "rowDefs": [], - } # just storing step as microns, not using here if self.scannerType == "PI": - rasterDef["x"] += self.sampFineX_pv.get() - rasterDef["y"] += self.sampFineY_pv.get() - rasterDef["z"] += self.sampFineZ_pv.get() - - # raster_w = width,goes to numsteps horizonatl - numsteps_h = int(raster_w / stepsizeXPix) + rasterDef = { + "rasterType": "normal", + "beamWidth": beamWidth, + "beamHeight": beamHeight, + "status": RasterStatus.NEW.value, + "x": self.sampx_pv.get() + self.sampFineX_pv.get(), + "y": self.sampy_pv.get() + self.sampFineY_pv.get(), + "z": self.sampz_pv.get() + self.sampFineZ_pv.get(), + "omega": self.omega_pv.get(), + "stepsize": stepsize, + "rowDefs": [], + } # just storing step as microns, not using her + else: + rasterDef = { + "rasterType": "normal", + "beamWidth": beamWidth, + "beamHeight": beamHeight, + "status": RasterStatus.NEW.value, + "x": self.sampx_pv.get(), + "y": self.sampy_pv.get(), + "z": self.sampz_pv.get(), + "omega": self.omega_pv.get(), + "stepsize": stepsize, + "rowDefs": [], + } # just storing step as microns, not using here + numsteps_h = int( + raster_w / stepsizeXPix + ) # raster_w = width,goes to numsteps horizonatl numsteps_v = int(raster_h / stepsizeYPix) if numsteps_h == 2: numsteps_h = 1 # fix slop in user single line attempt @@ -3338,44 +3348,66 @@ def definePolyRaster( rasterDef["numCells"] = numsteps_h * numsteps_v point_offset_x = -(numsteps_h * stepsizeXPix) / 2 point_offset_y = -(numsteps_v * stepsizeYPix) / 2 - - vertical_raster = False - direction = numsteps_v if (numsteps_h == 1) or ( numsteps_v > numsteps_h and getBlConfig("vertRasterOn") - ): - vertical_raster = True - direction = numsteps_h - - for i in range(direction): - rowCellCount = 0 - rowStartX = point_x + point_offset_x - rowStartY = point_y + point_offset_y - if vertical_raster: - rowStartX += i * stepsizeXPix - rowCellCount = numsteps_v - else: - rowStartY += i * stepsizeYPix - rowCellCount = numsteps_h - vectorStartX = self.screenXPixels2microns( - rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX - ) - vectorStartY = self.screenYPixels2microns( - rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY - ) - - vectorEndX = vectorStartX - vectorEndY = vectorStartY - if vertical_raster: - vectorEndY += self.screenYPixels2microns(rowCellCount * stepsizeYPix) - else: - vectorEndX += self.screenXPixels2microns(rowCellCount * stepsizeXPix) - newRowDef = { - "start": {"x": vectorStartX, "y": vectorStartY}, - "end": {"x": vectorEndX, "y": vectorEndY}, - "numsteps": rowCellCount, - } - rasterDef["rowDefs"].append(newRowDef) + ): # vertical raster + for i in range(numsteps_h): + rowCellCount = 0 + for j in range(numsteps_v): + newCellX = point_x + (i * stepsizeXPix) + point_offset_x + newCellY = point_y + (j * stepsizeYPix) + point_offset_y + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + rowCellCount = rowCellCount + 1 + if ( + rowCellCount != 0 + ): # test for no points in this row of the bounding rect are in the poly? + vectorStartX = self.screenXPixels2microns( + rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX + ) + vectorEndX = vectorStartX + vectorStartY = self.screenYPixels2microns( + rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY + ) + vectorEndY = vectorStartY + self.screenYPixels2microns( + rowCellCount * stepsizeYPix + ) + newRowDef = { + "start": {"x": vectorStartX, "y": vectorStartY}, + "end": {"x": vectorEndX, "y": vectorEndY}, + "numsteps": rowCellCount, + } + rasterDef["rowDefs"].append(newRowDef) + else: # horizontal raster + for i in range(numsteps_v): + rowCellCount = 0 + for j in range(numsteps_h): + newCellX = point_x + (j * stepsizeXPix) + point_offset_x + newCellY = point_y + (i * stepsizeYPix) + point_offset_y + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + rowCellCount = rowCellCount + 1 + if ( + rowCellCount != 0 + ): # testing for no points in this row of the bounding rect are in the poly? + vectorStartX = self.screenXPixels2microns( + rowStartX - self.centerMarker.x() - self.centerMarkerCharOffsetX + ) + vectorEndX = vectorStartX + self.screenXPixels2microns( + rowCellCount * stepsizeXPix + ) # this looks better + vectorStartY = self.screenYPixels2microns( + rowStartY - self.centerMarker.y() - self.centerMarkerCharOffsetY + ) + vectorEndY = vectorStartY + newRowDef = { + "start": {"x": vectorStartX, "y": vectorStartY}, + "end": {"x": vectorEndX, "y": vectorEndY}, + "numsteps": rowCellCount, + } + rasterDef["rowDefs"].append(newRowDef) setBlConfig("rasterDefaultWidth", float(self.osc_range_ledit.text())) setBlConfig("rasterDefaultTime", float(self.exp_time_ledit.text())) setBlConfig("rasterDefaultTrans", float(self.transmission_ledit.text())) @@ -3390,49 +3422,6 @@ def rasterIsDrawn(self, rasterReq): return True return False - def draw_raster(self, raster_def: "dict[str, Any]", raster_dir: str) -> RasterGroup: - """ - This method is only concerned with creating the raster graphics - - Arguments: - raster_def : dict containing start and end points in microns for each row of the raster - - Returns: - raster_group : Raster graphics object of type RasterGroup - """ - stepsizeX = self.screenXmicrons2pixels(raster_def["stepsize"]) - stepsizeY = self.screenYmicrons2pixels(raster_def["stepsize"]) - pen = QtGui.QPen(QtCore.Qt.red) - newRasterCellList = [] - offset = { - "x": self.centerMarker.x() + self.centerMarkerCharOffsetX, - "y": self.centerMarker.y() + self.centerMarkerCharOffsetY, - } - for i in range(len(raster_def["rowDefs"])): - newCellX = ( - self.screenXmicrons2pixels(raster_def["rowDefs"][i]["start"]["x"]) - + offset["x"] - ) - newCellY = ( - self.screenYmicrons2pixels(raster_def["rowDefs"][i]["start"]["y"]) - + offset["y"] - ) - for j in range(raster_def["rowDefs"][i]["numsteps"]): - if raster_dir == "horizontal": - newCellX += stepsizeX - else: - newCellY += stepsizeY - - newCell = RasterCell( - int(newCellX), int(newCellY), stepsizeX, stepsizeY, self - ) - newRasterCellList.append(newCell) - newCell.setPen(pen) - newItemGroup = RasterGroup(self) - for i in range(len(newRasterCellList)): - newItemGroup.addToGroup(newRasterCellList[i]) - return newItemGroup - def drawPolyRaster( self, rasterReq, x=-1, y=-1, z=-1 ): # rasterDef in microns,offset from center, need to convert to pixels to draw, mainly this is for displaying autoRasters, but also called in zoom change @@ -3440,6 +3429,11 @@ def drawPolyRaster( rasterDef = rasterReq["request_obj"]["rasterDef"] except KeyError: return + beamSize = self.screenXmicrons2pixels(rasterDef["beamWidth"]) + stepsizeX = self.screenXmicrons2pixels(rasterDef["stepsize"]) + stepsizeY = self.screenYmicrons2pixels(rasterDef["stepsize"]) + pen = QtGui.QPen(QtCore.Qt.red) + newRasterCellList = [] try: if ( rasterDef["rowDefs"][0]["start"]["y"] @@ -3450,9 +3444,54 @@ def drawPolyRaster( rasterDir = "vertical" except IndexError: return - newItemGroup = self.draw_raster(rasterDef, rasterDir) + for i in range(len(rasterDef["rowDefs"])): + rowCellCount = 0 + for j in range(rasterDef["rowDefs"][i]["numsteps"]): + if rasterDir == "horizontal": + newCellX = ( + self.screenXmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["x"] + ) + + (j * stepsizeX) + + self.centerMarker.x() + + self.centerMarkerCharOffsetX + ) + newCellY = ( + self.screenYmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["y"] + ) + + self.centerMarker.y() + + self.centerMarkerCharOffsetY + ) + else: + newCellX = ( + self.screenXmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["x"] + ) + + self.centerMarker.x() + + self.centerMarkerCharOffsetX + ) + newCellY = ( + self.screenYmicrons2pixels( + rasterDef["rowDefs"][i]["start"]["y"] + ) + + (j * stepsizeY) + + self.centerMarker.y() + + self.centerMarkerCharOffsetY + ) + if rowCellCount == 0: # start of a new row + rowStartX = newCellX + rowStartY = newCellY + newCellX = int(newCellX) + newCellY = int(newCellY) + newCell = RasterCell(newCellX, newCellY, stepsizeX, stepsizeY, self) + newRasterCellList.append(newCell) + newCell.setPen(pen) + rowCellCount = rowCellCount + 1 # really just for test of new row + newItemGroup = RasterGroup(self) self.scene.addItem(newItemGroup) - + for i in range(len(newRasterCellList)): + newItemGroup.addToGroup(newRasterCellList[i]) newRasterGraphicsDesc = { "uid": rasterReq["uid"], "coords": {