mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
Merge branch 'cSAX-feedback'
This commit is contained in:
@ -506,6 +506,44 @@
|
||||
<string>Coordinates</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Entries Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_mode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Individual</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Start/Stop</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidget_coordinates">
|
||||
<property name="selectionMode">
|
||||
@ -524,6 +562,11 @@
|
||||
<string>Show</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Tag</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
@ -534,33 +577,56 @@
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Tag</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_exportCSV">
|
||||
<property name="text">
|
||||
<string>Export CSV</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_importCSV">
|
||||
<property name="text">
|
||||
<string>Import CSV</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_help">
|
||||
<property name="text">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="pushButton_exportCSV">
|
||||
<property name="text">
|
||||
<string>Export CSV</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_resize_auto">
|
||||
<property name="text">
|
||||
<string>Resize Auto</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="pushButton_importCSV">
|
||||
<property name="text">
|
||||
<string>Import CSV</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="pushButton_resize_table">
|
||||
<property name="text">
|
||||
<string>Resize Table</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="pushButton_help">
|
||||
<property name="text">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="pushButton_duplicate">
|
||||
<property name="text">
|
||||
<string>Duplicate Last Entry</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -23,6 +23,7 @@ from PyQt5.QtWidgets import (
|
||||
)
|
||||
from PyQt5.QtWidgets import QShortcut
|
||||
from pyqtgraph.Qt import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from bec_lib.core import MessageEndpoints, BECMessage
|
||||
from bec_widgets.qt_utils import DoubleValidationDelegate
|
||||
@ -84,6 +85,10 @@ class MotorApp(QWidget):
|
||||
self.init_ui()
|
||||
self.tag_N = 1 # position label for saved coordinates
|
||||
|
||||
# State tracking for entries
|
||||
self.last_selected_index = -1
|
||||
self.is_next_entry_end = False
|
||||
|
||||
# Get all motors available
|
||||
self.motor_thread.retrieve_all_motors() # TODO link to combobox that it always refresh
|
||||
|
||||
@ -410,8 +415,9 @@ class MotorApp(QWidget):
|
||||
"""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)
|
||||
self.update_table_header()
|
||||
# self.tableWidget_coordinates.setItemDelegateForColumn(3, self.double_delegate) #TODO check where to delegate
|
||||
# self.tableWidget_coordinates.setItemDelegateForColumn(4, self.double_delegate)
|
||||
|
||||
# Signals
|
||||
self.tableWidget_coordinates.itemChanged.connect(self.update_saved_coordinates)
|
||||
@ -425,8 +431,19 @@ class MotorApp(QWidget):
|
||||
lambda: self.load_table_from_csv(self.tableWidget_coordinates, precision=self.precision)
|
||||
)
|
||||
|
||||
self.pushButton_resize_table.clicked.connect(
|
||||
lambda: self.resizeTable(self.tableWidget_coordinates)
|
||||
)
|
||||
|
||||
self.pushButton_duplicate.clicked.connect(
|
||||
lambda: self.duplicate_last_row(self.tableWidget_coordinates)
|
||||
)
|
||||
|
||||
self.pushButton_help.clicked.connect(self.show_help_dialog)
|
||||
|
||||
# Mode switch
|
||||
self.comboBox_mode.currentIndexChanged.connect(self.update_table_header)
|
||||
|
||||
def init_ui(self) -> None:
|
||||
"""Setup all ui elements"""
|
||||
|
||||
@ -534,11 +551,58 @@ class MotorApp(QWidget):
|
||||
self.toolButton_up.setShortcut("")
|
||||
self.toolButton_down.setShortcut("")
|
||||
|
||||
def update_table_header(self):
|
||||
current_index = self.comboBox_mode.currentIndex()
|
||||
|
||||
if self.tableWidget_coordinates.rowCount() > 0:
|
||||
msgBox = QMessageBox()
|
||||
msgBox.setIcon(QMessageBox.Warning)
|
||||
msgBox.setText(
|
||||
"Switching modes will delete all table entries. Do you want to continue?"
|
||||
)
|
||||
msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
|
||||
returnValue = msgBox.exec()
|
||||
|
||||
if returnValue == QMessageBox.Cancel:
|
||||
self.comboBox_mode.blockSignals(True) # Block signals
|
||||
self.comboBox_mode.setCurrentIndex(self.last_selected_index)
|
||||
self.comboBox_mode.blockSignals(False) # Unblock signals
|
||||
return
|
||||
|
||||
self.tableWidget_coordinates.setRowCount(0) # Wipe table
|
||||
|
||||
if current_index == 0: # 'individual' is selected
|
||||
self.tableWidget_coordinates.setColumnCount(5)
|
||||
self.tableWidget_coordinates.setHorizontalHeaderLabels(
|
||||
["Move", "Show", "Tag", "X", "Y"]
|
||||
)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(3, self.double_delegate)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(4, self.double_delegate)
|
||||
|
||||
elif current_index == 1: # 'start/stop' is selected
|
||||
self.tableWidget_coordinates.setColumnCount(7)
|
||||
self.tableWidget_coordinates.setHorizontalHeaderLabels(
|
||||
["Move", "Show", "Tag", "X [start]", "Y [start]", "X [end]", "Y [end]"]
|
||||
)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(3, self.double_delegate)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(4, self.double_delegate)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(5, self.double_delegate)
|
||||
self.tableWidget_coordinates.setItemDelegateForColumn(6, self.double_delegate)
|
||||
|
||||
self.last_selected_index = current_index # Save the last selected index
|
||||
|
||||
def generate_table_coordinate(
|
||||
self, table: QtWidgets.QTableWidget, coordinates: tuple, tag: str = None, precision: int = 0
|
||||
) -> None:
|
||||
current_row_count = table.rowCount()
|
||||
table.setRowCount(current_row_count + 1)
|
||||
current_index = self.comboBox_mode.currentIndex()
|
||||
# current_col_count = table.columnCount()
|
||||
|
||||
if current_index == 1 and self.is_next_entry_end:
|
||||
target_row = table.rowCount() - 1 # Last row
|
||||
else:
|
||||
new_row_count = table.rowCount() + 1
|
||||
table.setRowCount(new_row_count)
|
||||
target_row = new_row_count - 1 # New row
|
||||
|
||||
# Create QDoubleValidator
|
||||
validator = QDoubleValidator()
|
||||
@ -552,8 +616,8 @@ class MotorApp(QWidget):
|
||||
lambda state, widget=checkBox: self.toggle_point_visibility(state, widget)
|
||||
)
|
||||
|
||||
table.setItem(current_row_count, 4, QtWidgets.QTableWidgetItem(str(tag)))
|
||||
table.setCellWidget(current_row_count, 1, checkBox)
|
||||
table.setItem(target_row, 2, QtWidgets.QTableWidgetItem(str(tag)))
|
||||
table.setCellWidget(target_row, 1, checkBox)
|
||||
|
||||
# Apply validator to x and y coordinate QTableWidgetItem
|
||||
item_x = QtWidgets.QTableWidgetItem(str(f"{coordinates[0]:.{precision}f}"))
|
||||
@ -561,16 +625,22 @@ class MotorApp(QWidget):
|
||||
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}"))
|
||||
)
|
||||
if current_index == 1:
|
||||
col_index = 7
|
||||
if self.is_next_entry_end:
|
||||
table.setItem(target_row, 5, item_x)
|
||||
table.setItem(target_row, 6, item_y)
|
||||
else:
|
||||
table.setItem(target_row, 3, item_x)
|
||||
table.setItem(target_row, 4, item_y)
|
||||
self.is_next_entry_end = not self.is_next_entry_end
|
||||
else:
|
||||
col_index = 5
|
||||
table.setItem(target_row, 3, item_x)
|
||||
table.setItem(target_row, 4, item_y)
|
||||
|
||||
table.setCellWidget(current_row_count, 0, button)
|
||||
|
||||
button.clicked.connect(partial(self.move_to_row_coordinates, table, current_row_count))
|
||||
table.setCellWidget(target_row, 0, button)
|
||||
button.clicked.connect(partial(self.move_to_row_coordinates, table, target_row))
|
||||
|
||||
brushes = [
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
@ -579,21 +649,20 @@ class MotorApp(QWidget):
|
||||
|
||||
# Adding extra columns
|
||||
if self.extra_columns:
|
||||
col_index = 5 # Starting index for extra columns
|
||||
table.setColumnCount(col_index + len(self.extra_columns))
|
||||
for col_dict in self.extra_columns:
|
||||
for col_name, default_value in col_dict.items():
|
||||
if current_row_count == 0:
|
||||
if target_row == 0 or (current_index == 1 and not self.is_next_entry_end):
|
||||
item = QtWidgets.QTableWidgetItem(str(default_value))
|
||||
else:
|
||||
prev_item = table.item(current_row_count - 1, col_index)
|
||||
prev_item = table.item(target_row - 1, col_index)
|
||||
item_text = prev_item.text() if prev_item else ""
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
|
||||
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
||||
table.setItem(current_row_count, col_index, item)
|
||||
table.setItem(target_row, col_index, item)
|
||||
|
||||
if current_row_count == 0:
|
||||
if target_row == 0 or (current_index == 1 and not self.is_next_entry_end):
|
||||
table.setHorizontalHeaderItem(
|
||||
col_index, QtWidgets.QTableWidgetItem(col_name)
|
||||
)
|
||||
@ -602,17 +671,54 @@ class MotorApp(QWidget):
|
||||
|
||||
self.saved_motor_map.setData(pos=self.saved_motor_positions, brush=brushes)
|
||||
|
||||
self.align_table_center(table)
|
||||
|
||||
if self.checkBox_resize_auto.isChecked():
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
def duplicate_last_row(self, table: QtWidgets.QTableWidget) -> None:
|
||||
last_row = table.rowCount() - 1
|
||||
if last_row == -1:
|
||||
return
|
||||
|
||||
table.setRowCount(last_row + 2)
|
||||
new_row = last_row + 1
|
||||
|
||||
for col in range(table.columnCount()):
|
||||
cell_widget = table.cellWidget(last_row, col)
|
||||
cell_item = table.item(last_row, col)
|
||||
|
||||
if isinstance(cell_widget, QtWidgets.QCheckBox):
|
||||
new_checkbox = QtWidgets.QCheckBox()
|
||||
new_checkbox.setChecked(cell_widget.isChecked())
|
||||
table.setCellWidget(new_row, col, new_checkbox)
|
||||
|
||||
elif isinstance(cell_widget, QtWidgets.QPushButton):
|
||||
new_button = QtWidgets.QPushButton(cell_widget.text())
|
||||
new_button.clicked.connect(partial(self.move_to_row_coordinates, table, new_row))
|
||||
table.setCellWidget(new_row, col, new_button)
|
||||
|
||||
elif cell_item:
|
||||
new_item = QtWidgets.QTableWidgetItem(cell_item.text())
|
||||
new_item.setFlags(cell_item.flags())
|
||||
table.setItem(new_row, col, new_item)
|
||||
|
||||
self.align_table_center(table)
|
||||
|
||||
if self.checkBox_resize_auto.isChecked():
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
@staticmethod
|
||||
def align_table_center(table: QtWidgets.QTableWidget) -> None:
|
||||
for row in range(table.rowCount()):
|
||||
for col in range(table.columnCount()):
|
||||
item = table.item(row, col)
|
||||
if item:
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
def move_to_row_coordinates(self, table, row):
|
||||
x = float(table.item(row, 2).text())
|
||||
y = float(table.item(row, 3).text())
|
||||
x = float(table.item(row, 3).text())
|
||||
y = float(table.item(row, 4).text())
|
||||
self.move_motor_absolute(x, y)
|
||||
|
||||
def toggle_point_visibility(self, state, checkBox_widget):
|
||||
@ -654,13 +760,13 @@ class MotorApp(QWidget):
|
||||
|
||||
for row in range(rows):
|
||||
x = (
|
||||
float(self.tableWidget_coordinates.item(row, 2).text())
|
||||
if self.tableWidget_coordinates.item(row, 2) is not None
|
||||
float(self.tableWidget_coordinates.item(row, 3).text())
|
||||
if self.tableWidget_coordinates.item(row, 3) is not None
|
||||
else None
|
||||
)
|
||||
y = (
|
||||
float(self.tableWidget_coordinates.item(row, 3).text())
|
||||
if self.tableWidget_coordinates.item(row, 3) is not None
|
||||
float(self.tableWidget_coordinates.item(row, 4).text())
|
||||
if self.tableWidget_coordinates.item(row, 4) is not None
|
||||
else None
|
||||
)
|
||||
|
||||
@ -690,6 +796,11 @@ class MotorApp(QWidget):
|
||||
self.saved_motor_positions = np.delete(self.saved_motor_positions, row_index, axis=0)
|
||||
del self.saved_point_visibility[row_index]
|
||||
|
||||
# If in 'start/stop' mode, check if only the 'start' coordinates are present in the row being deleted
|
||||
if self.comboBox_mode.currentIndex() == 1:
|
||||
if self.tableWidget_coordinates.item(row_index, 5) is None:
|
||||
self.is_next_entry_end = False
|
||||
|
||||
# Update the plot
|
||||
brushes = [
|
||||
pg.mkBrush(255, 165, 0, 255) if visible else pg.mkBrush(255, 165, 0, 0)
|
||||
@ -708,6 +819,9 @@ class MotorApp(QWidget):
|
||||
partial(self.move_to_row_coordinates, self.tableWidget_coordinates, row)
|
||||
)
|
||||
|
||||
def resizeTable(self, table):
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
def export_table_to_csv(self, table: QtWidgets.QTableWidget):
|
||||
options = QFileDialog.Options()
|
||||
filePath, _ = QFileDialog.getSaveFileName(
|
||||
@ -791,7 +905,8 @@ class MotorApp(QWidget):
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
table.setItem(current_row, col + 2, item)
|
||||
|
||||
table.resizeColumnsToContents()
|
||||
if self.checkBox_resize_auto.isChecked():
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
def save_absolute_coordinates(self):
|
||||
self.generate_table_coordinate(
|
||||
|
Reference in New Issue
Block a user