mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
Merge branch 'motor_csax_features_request'
This commit is contained in:
@ -6,10 +6,16 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1219</width>
|
||||
<height>668</height>
|
||||
<width>1409</width>
|
||||
<height>748</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1409</width>
|
||||
<height>748</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Motor Controller</string>
|
||||
</property>
|
||||
@ -29,9 +35,21 @@
|
||||
<height>471</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6" stretch="1,1,1,0,1">
|
||||
<property name="spacing">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="motorSelection">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>261</width>
|
||||
<height>145</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Motor Selection</string>
|
||||
</property>
|
||||
@ -71,6 +89,9 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
@ -81,6 +102,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="motorControl">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>261</width>
|
||||
<height>339</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Motor Relative</string>
|
||||
</property>
|
||||
@ -93,36 +120,48 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<widget class="QCheckBox" name="checkBox_same_xy">
|
||||
<property name="text">
|
||||
<string>Step [X] = Step [Y]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="step_grid">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_step_y">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>111</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step [Y]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>111</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Decimal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_precision">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinBox_step">
|
||||
<widget class="QDoubleSpinBox" name="spinBox_step_x">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
@ -140,10 +179,67 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_step_x">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>111</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step [X]</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinBox_step_y">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="spinBox_precision">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>19</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<layout class="QGridLayout" name="direction_grid">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
@ -273,20 +369,36 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>13</height>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="motorControl_absolute">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>261</width>
|
||||
<height>195</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Move Absolute</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_save_with_go">
|
||||
<property name="text">
|
||||
<string>Save position with Go</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="1">
|
||||
|
@ -1,22 +1,23 @@
|
||||
import os
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
|
||||
import numpy as np
|
||||
from enum import Enum
|
||||
import pyqtgraph as pg
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtCore import QThread, pyqtSlot
|
||||
from PyQt5.QtCore import QThread, pyqtSlot, QPoint
|
||||
from PyQt5.QtCore import pyqtSignal, Qt
|
||||
from PyQt5.QtWidgets import QApplication, QWidget
|
||||
from PyQt5.QtGui import QDoubleValidator
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget
|
||||
from PyQt5.QtWidgets import QShortcut
|
||||
from pyqtgraph.Qt import QtWidgets, uic, QtCore
|
||||
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import QShortcut
|
||||
|
||||
from bec_lib.core import MessageEndpoints, BECMessage
|
||||
from bec_widgets.qt_utils import DoubleValidationDelegate
|
||||
|
||||
|
||||
# TODO - General features
|
||||
# - updating motor precision
|
||||
# - put motor status (moving, stopped, etc)
|
||||
# - add mouse interactions with the plot -> click to select coordinates, double click to move?
|
||||
# - adjust right click actions
|
||||
@ -197,10 +198,16 @@ class MotorApp(QWidget):
|
||||
self.enable_motor_controls(False)
|
||||
target_coordinates = (x, y)
|
||||
self.motor_thread.move_to_coordinates(target_coordinates)
|
||||
if self.checkBox_save_with_go.isChecked():
|
||||
self.save_absolute_coordinates()
|
||||
|
||||
def move_motor_relative(self, motor, value: float) -> None:
|
||||
def move_motor_relative(self, motor, axis: str, direction: int) -> None:
|
||||
self.enable_motor_controls(False)
|
||||
self.motor_thread.move_relative(motor, value)
|
||||
if axis == "x":
|
||||
step = direction * self.spinBox_step_x.value()
|
||||
elif axis == "y":
|
||||
step = direction * self.spinBox_step_y.value()
|
||||
self.motor_thread.move_relative(motor, step)
|
||||
|
||||
def update_plot_setting(self, max_points, num_dim_points, scatter_size):
|
||||
self.max_points = max_points
|
||||
@ -223,17 +230,8 @@ class MotorApp(QWidget):
|
||||
self.spinBox_precision.setValue(self.precision)
|
||||
self.update_precision(self.precision)
|
||||
|
||||
def init_ui(self) -> None:
|
||||
"""Setup all ui elements"""
|
||||
# TODO can be separated to multiple functions
|
||||
|
||||
# Set default parameters
|
||||
self.set_from_config()
|
||||
|
||||
##########################
|
||||
# 2D Plot
|
||||
##########################
|
||||
|
||||
def init_ui_plot_elements(self) -> None:
|
||||
"""Initialize the plot elements"""
|
||||
self.label_coorditanes = self.glw.addLabel(f"Motor position: (X, Y)", row=0, col=0)
|
||||
self.plot_map = self.glw.addPlot(row=1, col=0)
|
||||
self.limit_map = pg.ImageItem()
|
||||
@ -255,28 +253,23 @@ class MotorApp(QWidget):
|
||||
self.plot_map.addItem(self.saved_motor_map)
|
||||
self.plot_map.showGrid(x=True, y=True)
|
||||
|
||||
##########################
|
||||
# Motor General setting
|
||||
##########################
|
||||
def init_ui_motor_control(self) -> None:
|
||||
"""Initialize the motor control elements"""
|
||||
|
||||
# # TODO make function to update precision
|
||||
# self.precision = 2 # self.spinBox_precision.value() # Define the decimal precision
|
||||
|
||||
##########################
|
||||
# Motor movements signals
|
||||
##########################
|
||||
# Connect CheckBox stateChanged signal to sync function
|
||||
self.checkBox_same_xy.stateChanged.connect(
|
||||
lambda: self.sync_step_sizes(self.spinBox_step_x, self.spinBox_step_y)
|
||||
)
|
||||
|
||||
self.toolButton_right.clicked.connect(
|
||||
lambda: self.move_motor_relative(self.motor_x, self.spinBox_step.value())
|
||||
lambda: self.move_motor_relative(self.motor_x, "x", 1)
|
||||
)
|
||||
self.toolButton_left.clicked.connect(
|
||||
lambda: self.move_motor_relative(self.motor_x, -self.spinBox_step.value())
|
||||
)
|
||||
self.toolButton_up.clicked.connect(
|
||||
lambda: self.move_motor_relative(self.motor_y, self.spinBox_step.value())
|
||||
lambda: self.move_motor_relative(self.motor_x, "x", -1)
|
||||
)
|
||||
self.toolButton_up.clicked.connect(lambda: self.move_motor_relative(self.motor_y, "y", 1))
|
||||
self.toolButton_down.clicked.connect(
|
||||
lambda: self.move_motor_relative(self.motor_y, -self.spinBox_step.value())
|
||||
lambda: self.move_motor_relative(self.motor_y, "y", -1)
|
||||
)
|
||||
|
||||
# Switch between key shortcuts active
|
||||
@ -290,36 +283,18 @@ class MotorApp(QWidget):
|
||||
)
|
||||
)
|
||||
|
||||
# Go absolute button
|
||||
self.pushButton_go_absolute.clicked.connect(self.save_absolute_coordinates)
|
||||
self.pushButton_go_absolute.setShortcut("Ctrl+G")
|
||||
self.pushButton_go_absolute.setToolTip("Ctrl+G")
|
||||
|
||||
# Set absolute coordinates
|
||||
self.pushButton_set.clicked.connect(self.save_absolute_coordinates)
|
||||
self.pushButton_set.setShortcut("Ctrl+D")
|
||||
self.pushButton_set.setToolTip("Ctrl+D")
|
||||
|
||||
# Save Current coordinates
|
||||
self.pushButton_save.clicked.connect(self.save_current_coordinates)
|
||||
self.pushButton_save.setShortcut("Ctrl+S")
|
||||
self.pushButton_save.setToolTip("Ctrl+S")
|
||||
|
||||
# Stop Button
|
||||
self.pushButton_stop.clicked.connect(self.motor_thread.stop_movement)
|
||||
self.pushButton_stop.setShortcut("Ctrl+X")
|
||||
self.pushButton_stop.setToolTip("Ctrl+X")
|
||||
|
||||
# Enable/Disable GUI
|
||||
self.motor_thread.move_finished.connect(lambda: self.enable_motor_controls(True))
|
||||
|
||||
# Update precision
|
||||
# Precision update
|
||||
self.spinBox_precision.valueChanged.connect(lambda x: self.update_precision(x))
|
||||
|
||||
##########################
|
||||
# Motor Configs
|
||||
##########################
|
||||
|
||||
# SpinBoxes - Motor Limits #TODO make spinboxes own limits updated, currently is [-1000, 1000]
|
||||
def init_ui_motor_configs(self) -> None:
|
||||
"""Limit and plot spinBoxes"""
|
||||
|
||||
# SpinBoxes change color to yellow before updated, limits are updated with update button
|
||||
self.spinBox_x_min.valueChanged.connect(lambda: self.param_changed(self.spinBox_x_min))
|
||||
@ -338,7 +313,7 @@ class MotorApp(QWidget):
|
||||
lambda: self.param_changed(self.spinBox_scatter_size)
|
||||
)
|
||||
|
||||
# Config updates
|
||||
# Limit Update
|
||||
self.pushButton_updateLimits.clicked.connect(
|
||||
lambda: self.update_all_motor_limits(
|
||||
x_limit=[self.spinBox_x_min.value(), self.spinBox_x_max.value()],
|
||||
@ -346,6 +321,7 @@ class MotorApp(QWidget):
|
||||
)
|
||||
)
|
||||
|
||||
# Plot Update
|
||||
self.pushButton_update_config.clicked.connect(
|
||||
lambda: self.update_plot_setting(
|
||||
max_points=self.spinBox_max_points.value(),
|
||||
@ -354,12 +330,15 @@ class MotorApp(QWidget):
|
||||
)
|
||||
)
|
||||
|
||||
# TODO map with floats as well -> or decide system for higher precision
|
||||
self.pushButton_enableGUI.clicked.connect(lambda: self.enable_motor_controls(True))
|
||||
|
||||
def init_ui_motor_connections(self) -> None:
|
||||
# Signal from motor thread to update coordinates
|
||||
self.motor_thread.coordinates_updated.connect(
|
||||
lambda x, y: self.update_image_map(round(x, self.precision), round(y, self.precision))
|
||||
)
|
||||
|
||||
# Motor connections
|
||||
# Motor connections button
|
||||
self.pushButton_connecMotors.clicked.connect(
|
||||
lambda: self.connect_motor(
|
||||
self.comboBox_motor_x.currentText(), self.comboBox_motor_y.currentText()
|
||||
@ -372,7 +351,8 @@ class MotorApp(QWidget):
|
||||
self.motorControl_absolute.setEnabled(False)
|
||||
self.tabWidget_tables.setTabEnabled(1, False)
|
||||
|
||||
# Keyboard shortcuts
|
||||
def init_keyboard_shortcuts(self) -> None:
|
||||
"""Initialize the keyboard shortcuts"""
|
||||
|
||||
# Delete table entry
|
||||
delete_shortcut = QShortcut(QKeySequence("Delete"), self)
|
||||
@ -380,13 +360,58 @@ class MotorApp(QWidget):
|
||||
delete_shortcut.activated.connect(self.delete_selected_row)
|
||||
backspace_shortcut.activated.connect(self.delete_selected_row)
|
||||
|
||||
# Increase/decrease step
|
||||
increase_shortcut = QShortcut(QKeySequence("Ctrl+A"), self)
|
||||
decrease_shortcut = QShortcut(QKeySequence("Ctrl+Z"), self)
|
||||
increase_shortcut.activated.connect(self.increase_step)
|
||||
decrease_shortcut.activated.connect(self.decrease_step)
|
||||
# Increase/decrease step size for X motor
|
||||
increase_x_shortcut = QShortcut(QKeySequence("Ctrl+A"), self)
|
||||
decrease_x_shortcut = QShortcut(QKeySequence("Ctrl+Z"), self)
|
||||
increase_x_shortcut.activated.connect(lambda: self.change_step_size(self.spinBox_step_x, 2))
|
||||
decrease_x_shortcut.activated.connect(
|
||||
lambda: self.change_step_size(self.spinBox_step_x, 0.5)
|
||||
)
|
||||
|
||||
self.pushButton_enableGUI.clicked.connect(lambda: self.enable_motor_controls(True))
|
||||
# Increase/decrease step size for Y motor
|
||||
increase_y_shortcut = QShortcut(QKeySequence("Alt+A"), self)
|
||||
decrease_y_shortcut = QShortcut(QKeySequence("Alt+Z"), self)
|
||||
increase_y_shortcut.activated.connect(lambda: self.change_step_size(self.spinBox_step_y, 2))
|
||||
decrease_y_shortcut.activated.connect(
|
||||
lambda: self.change_step_size(self.spinBox_step_y, 0.5)
|
||||
)
|
||||
|
||||
# Go absolute button
|
||||
self.pushButton_go_absolute.setShortcut("Ctrl+G")
|
||||
self.pushButton_go_absolute.setToolTip("Ctrl+G")
|
||||
|
||||
# Set absolute coordinates
|
||||
self.pushButton_set.setShortcut("Ctrl+D")
|
||||
self.pushButton_set.setToolTip("Ctrl+D")
|
||||
|
||||
# Save Current coordinates
|
||||
self.pushButton_save.setShortcut("Ctrl+S")
|
||||
self.pushButton_save.setToolTip("Ctrl+S")
|
||||
|
||||
# Stop Button
|
||||
self.pushButton_stop.setShortcut("Ctrl+X")
|
||||
self.pushButton_stop.setToolTip("Ctrl+X")
|
||||
|
||||
def init_ui_table(self) -> None:
|
||||
"""Initialize the table validators for x and y coordinates and table signals"""
|
||||
# Validators
|
||||
self.double_delegate = DoubleValidationDelegate(self.tableWidget_coordinates)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(2, self.double_delegate)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(3, self.double_delegate)
|
||||
|
||||
# Signals
|
||||
self.tableWidget_coordinates.itemChanged.connect(self.update_saved_coordinates)
|
||||
|
||||
def init_ui(self) -> None:
|
||||
"""Setup all ui elements"""
|
||||
|
||||
self.set_from_config() # Set default parameters
|
||||
self.init_ui_plot_elements() # 2D Plot
|
||||
self.init_ui_motor_control() # Motor Controls
|
||||
self.init_ui_motor_configs() # Motor Configs
|
||||
self.init_ui_motor_connections() # Motor Connections
|
||||
self.init_keyboard_shortcuts() # Keyboard Shortcuts
|
||||
self.init_ui_table() # Table validators for x and y coordinates
|
||||
|
||||
def init_motor_map(self):
|
||||
# Get motor limits
|
||||
@ -490,43 +515,37 @@ class MotorApp(QWidget):
|
||||
current_row_count = table.rowCount()
|
||||
table.setRowCount(current_row_count + 1)
|
||||
|
||||
# Create QDoubleValidator
|
||||
validator = QDoubleValidator()
|
||||
validator.setDecimals(precision) # TODO not sure if necessary
|
||||
|
||||
checkBox = QtWidgets.QCheckBox()
|
||||
checkBox.setChecked(True)
|
||||
button = QtWidgets.QPushButton("Go")
|
||||
|
||||
# Connect checkBox state change to toggle visibility
|
||||
checkBox.stateChanged.connect(
|
||||
lambda state, coord=coordinates: self.toggle_point_visibility(state, coord)
|
||||
lambda state, widget=checkBox: self.toggle_point_visibility(state, widget)
|
||||
)
|
||||
|
||||
table.setItem(current_row_count, 0, QtWidgets.QTableWidgetItem(str(tag)))
|
||||
table.setCellWidget(current_row_count, 1, checkBox)
|
||||
|
||||
# Apply validator to x and y coordinate QTableWidgetItem
|
||||
item_x = QtWidgets.QTableWidgetItem(str(f"{coordinates[0]:.{precision}f}"))
|
||||
item_y = QtWidgets.QTableWidgetItem(str(f"{coordinates[1]:.{precision}f}"))
|
||||
item_x.setFlags(item_x.flags() | Qt.ItemIsEditable)
|
||||
item_y.setFlags(item_y.flags() | Qt.ItemIsEditable)
|
||||
|
||||
table.setItem(
|
||||
current_row_count, 2, QtWidgets.QTableWidgetItem(str(f"{coordinates[0]:.{precision}f}"))
|
||||
)
|
||||
table.setItem(
|
||||
current_row_count, 3, QtWidgets.QTableWidgetItem(str(f"{coordinates[1]:.{precision}f}"))
|
||||
)
|
||||
|
||||
table.setCellWidget(current_row_count, 4, button)
|
||||
|
||||
# Hook signals of table
|
||||
button.clicked.connect(
|
||||
lambda: self.move_motor_absolute(
|
||||
float(table.item(current_row_count, 2).text()),
|
||||
float(table.item(current_row_count, 3).text()),
|
||||
)
|
||||
)
|
||||
|
||||
# Add point to scatter plot
|
||||
# Add a True value to saved_point_visibility list when a new point is added.
|
||||
self.saved_point_visibility.append(True)
|
||||
|
||||
# Update the scatter plot to maintain the visibility of existing points
|
||||
new_pos = np.array(coordinates)
|
||||
if self.saved_motor_positions.size == 0:
|
||||
self.saved_motor_positions = np.array([new_pos])
|
||||
else:
|
||||
self.saved_motor_positions = np.vstack((self.saved_motor_positions, new_pos))
|
||||
button.clicked.connect(partial(self.move_to_row_coordinates, table, current_row_count))
|
||||
|
||||
brushes = [
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
@ -537,32 +556,104 @@ class MotorApp(QWidget):
|
||||
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
def toggle_point_visibility(self, state, coord):
|
||||
index = np.where((self.saved_motor_positions == coord).all(axis=1))[0][0]
|
||||
self.saved_point_visibility[index] = state == Qt.Checked
|
||||
def move_to_row_coordinates(self, table, row):
|
||||
x = float(table.item(row, 2).text())
|
||||
y = float(table.item(row, 3).text())
|
||||
self.move_motor_absolute(x, y)
|
||||
|
||||
def toggle_point_visibility(self, state, checkBox_widget):
|
||||
parent = checkBox_widget.parent()
|
||||
while not isinstance(parent, QTableWidget):
|
||||
parent = parent.parent()
|
||||
|
||||
table = parent
|
||||
|
||||
pos = checkBox_widget.pos()
|
||||
item = table.indexAt(pos)
|
||||
row_index = item.row()
|
||||
|
||||
# print(f"Row {row_index} visibility changed to {state == Qt.Checked}")
|
||||
|
||||
self.saved_point_visibility[row_index] = state == Qt.Checked
|
||||
|
||||
# Generate brushes based on visibility state
|
||||
brushes = [
|
||||
pg.mkBrush(255, 0, 0, 255) if visible else pg.mkBrush(255, 0, 0, 0)
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
for visible in self.saved_point_visibility
|
||||
]
|
||||
|
||||
# brushed_rgb = [brush.color().getRgb() for brush in brushes]
|
||||
|
||||
# print(f"Poinst: {self.saved_motor_positions}")
|
||||
# print(f"Brushes: {brushed_rgb}")
|
||||
|
||||
self.saved_motor_map.setData(pos=self.saved_motor_positions, brush=brushes)
|
||||
|
||||
def update_saved_coordinates(self):
|
||||
"""
|
||||
Update the saved coordinates and replot them.
|
||||
"""
|
||||
rows = self.tableWidget_coordinates.rowCount()
|
||||
# Initialize an empty array to hold new coordinates
|
||||
new_saved_positions = np.empty((0, 2))
|
||||
new_visibility = []
|
||||
|
||||
for row in range(rows):
|
||||
x = (
|
||||
float(self.tableWidget_coordinates.item(row, 2).text())
|
||||
if self.tableWidget_coordinates.item(row, 2) is not None
|
||||
else None
|
||||
)
|
||||
y = (
|
||||
float(self.tableWidget_coordinates.item(row, 3).text())
|
||||
if self.tableWidget_coordinates.item(row, 3) is not None
|
||||
else None
|
||||
)
|
||||
|
||||
# Only add the point if both x and y are not None
|
||||
if x is not None and y is not None:
|
||||
new_saved_positions = np.vstack((new_saved_positions, [x, y]))
|
||||
checkbox = self.tableWidget_coordinates.cellWidget(row, 1)
|
||||
new_visibility.append(checkbox.isChecked())
|
||||
|
||||
# Update saved positions and visibility
|
||||
self.saved_motor_positions = new_saved_positions
|
||||
self.saved_point_visibility = new_visibility
|
||||
|
||||
# Replot saved positions based on new data
|
||||
brushes = [
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
for visible in self.saved_point_visibility
|
||||
]
|
||||
self.saved_motor_map.setData(pos=self.saved_motor_positions, brush=brushes)
|
||||
|
||||
def delete_selected_row(self):
|
||||
selected_rows = self.tableWidget_coordinates.selectionModel().selectedRows()
|
||||
for row in reversed(selected_rows):
|
||||
row_index = row.row()
|
||||
rows_to_delete = [row.row() for row in selected_rows]
|
||||
rows_to_delete.sort(reverse=True) # Sort in descending order
|
||||
|
||||
for row_index in rows_to_delete:
|
||||
self.saved_motor_positions = np.delete(self.saved_motor_positions, row_index, axis=0)
|
||||
del self.saved_point_visibility[row_index] # Update this line
|
||||
del self.saved_point_visibility[row_index]
|
||||
|
||||
# Update the plot
|
||||
brushes = [
|
||||
pg.mkBrush(255, 0, 0, 255) if visible else pg.mkBrush(255, 0, 0, 0)
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
for visible in self.saved_point_visibility
|
||||
] # Regenerate brushes
|
||||
self.saved_motor_map.setData(
|
||||
pos=self.saved_motor_positions, brush=brushes
|
||||
) # Update this line
|
||||
]
|
||||
self.saved_motor_map.setData(pos=self.saved_motor_positions, brush=brushes)
|
||||
|
||||
# Remove the row from the table
|
||||
self.tableWidget_coordinates.removeRow(row_index)
|
||||
|
||||
# Update the 'Go' buttons
|
||||
for row in range(self.tableWidget_coordinates.rowCount()):
|
||||
button = self.tableWidget_coordinates.cellWidget(row, 4)
|
||||
button.clicked.disconnect()
|
||||
button.clicked.connect(
|
||||
partial(self.move_to_row_coordinates, self.tableWidget_coordinates, row)
|
||||
)
|
||||
|
||||
def save_absolute_coordinates(self):
|
||||
self.generate_table_coordinate(
|
||||
self.tableWidget_coordinates,
|
||||
@ -585,21 +676,20 @@ class MotorApp(QWidget):
|
||||
|
||||
def update_precision(self, precision: int):
|
||||
self.precision = precision
|
||||
self.spinBox_step.setDecimals(self.precision)
|
||||
self.spinBox_step_x.setDecimals(self.precision)
|
||||
self.spinBox_step_y.setDecimals(self.precision)
|
||||
self.spinBox_absolute_x.setDecimals(self.precision)
|
||||
self.spinBox_absolute_y.setDecimals(self.precision)
|
||||
|
||||
def increase_step(self):
|
||||
old_step = self.spinBox_step.value()
|
||||
new_step = old_step * 2
|
||||
def change_step_size(self, spinBox: QtWidgets.QDoubleSpinBox, factor: float) -> None:
|
||||
old_step = spinBox.value()
|
||||
new_step = old_step * factor
|
||||
spinBox.setValue(new_step)
|
||||
|
||||
self.spinBox_step.setValue(new_step)
|
||||
|
||||
def decrease_step(self):
|
||||
old_step = self.spinBox_step.value()
|
||||
new_step = old_step / 2
|
||||
|
||||
self.spinBox_step.setValue(new_step)
|
||||
def sync_step_sizes(self, spinBox1, spinBox2):
|
||||
if self.checkBox_same_xy.isChecked():
|
||||
value = spinBox1.value()
|
||||
spinBox2.setValue(value)
|
||||
|
||||
@staticmethod
|
||||
def param_changed(ui_element):
|
||||
@ -827,7 +917,7 @@ if __name__ == "__main__":
|
||||
|
||||
from bec_lib import BECClient
|
||||
|
||||
from bec_lib.core import ServiceConfig, RedisConnector
|
||||
from bec_lib.core import ServiceConfig
|
||||
|
||||
parser = argparse.ArgumentParser(description="Motor App")
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
from .crosshair import Crosshair
|
||||
from .colors import Colors
|
||||
from .validator_delegate import DoubleValidationDelegate
|
||||
|
10
bec_widgets/qt_utils/validator_delegate.py
Normal file
10
bec_widgets/qt_utils/validator_delegate.py
Normal file
@ -0,0 +1,10 @@
|
||||
from PyQt5.QtGui import QDoubleValidator
|
||||
from PyQt5.QtWidgets import QStyledItemDelegate, QLineEdit
|
||||
|
||||
|
||||
class DoubleValidationDelegate(QStyledItemDelegate):
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = QLineEdit(parent)
|
||||
validator = QDoubleValidator()
|
||||
editor.setValidator(validator)
|
||||
return editor
|
Reference in New Issue
Block a user