diff --git a/bec_widgets/examples/modular_app/___init__.py b/bec_widgets/examples/modular_app/___init__.py new file mode 100644 index 00000000..68ba0263 --- /dev/null +++ b/bec_widgets/examples/modular_app/___init__.py @@ -0,0 +1,85 @@ +from PyQt5.QtWidgets import ( + QApplication, + QMainWindow, + QVBoxLayout, + QWidget, + QTabWidget, + QPushButton, + QDialog, + QListView, + QStandardItemModel, + QStandardItem, +) +from bec_widgets.widgets.device_monitor import BECDeviceMonitor +from bec_widgets.bec_dispatcher import bec_dispatcher + + +class ConfigDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("Configuration") + self.layout = QVBoxLayout(self) + self.listView = QListView(self) + self.layout.addWidget(self.listView) + self.okButton = QPushButton("OK", self) + self.layout.addWidget(self.okButton) + self.okButton.clicked.connect(self.accept) + + def populate_device_list(self, device_names): + model = QStandardItemModel() + for device_name in device_names: + item = QStandardItem(device_name) + item.setCheckable(True) + model.appendRow(item) + self.listView.setModel(model) + + +class PlotTabWidget(QTabWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.addTabButton = QPushButton("Add Tab", self) + self.addTabButton.clicked.connect(self.add_new_tab) + self.setCornerWidget(self.addTabButton) + + def add_new_tab(self): + config_dialog = ConfigDialog(self) + config_dialog.populate_device_list(bec_dispatcher.client.device_manager.devices.keys()) + if config_dialog.exec_(): + selected_devices = [ + config_dialog.listView.model().item(i).text() + for i in range(config_dialog.listView.model().rowCount()) + if config_dialog.listView.model().item(i).checkState() + ] + # Assuming device_config is a function that generates a config based on selected devices + config = device_config(selected_devices) + bec_device_monitor = BECDeviceMonitor(parent=self, config=config) + self.addTab(bec_device_monitor, f"Tab {self.count() + 1}") + + +class ModularApp(QMainWindow): + def __init__(self, client=None, parent=None): + super(ModularApp, self).__init__(parent) + self.client = bec_dispatcher.client if client is None else client + self.init_ui() + + def init_ui(self): + self.tabWidget = PlotTabWidget(self) + self.setCentralWidget(self.tabWidget) + + +def device_config(selected_devices): + # Generate a configuration based on the selected devices + # This is a placeholder and should be replaced with your actual logic + pass + + +if __name__ == "__main__": + client = bec_dispatcher.client + client.start() + + app = QApplication([]) + modularApp = ModularApp(client=client) + + window = modularApp + window.show() + app.exec_() diff --git a/bec_widgets/examples/modular_app/modular_app.py b/bec_widgets/examples/modular_app/modular_app.py index f2353ba5..e933b4d0 100644 --- a/bec_widgets/examples/modular_app/modular_app.py +++ b/bec_widgets/examples/modular_app/modular_app.py @@ -1,7 +1,9 @@ import os + from PyQt5 import uic from PyQt5.QtWidgets import QMainWindow, QApplication, QVBoxLayout -from bec_widgets.widgets.device_monitor import BECDeviceMonitor + +from bec_widgets.widgets.monitor.device_monitor import BECDeviceMonitor config_1 = { "plot_settings": { diff --git a/bec_widgets/widgets/monitor/__init__.py b/bec_widgets/widgets/monitor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bec_widgets/widgets/monitor/config_dialog.py b/bec_widgets/widgets/monitor/config_dialog.py new file mode 100644 index 00000000..8b8e732f --- /dev/null +++ b/bec_widgets/widgets/monitor/config_dialog.py @@ -0,0 +1,108 @@ +from PyQt5 import uic +from PyQt5.QtWidgets import ( + QApplication, + QWidget, + QVBoxLayout, + QLineEdit, + QLabel, + QGroupBox, + QHBoxLayout, + QPushButton, + QTableWidgetItem, +) + +Ui_Form, BaseClass = uic.loadUiType("config_dialog.ui") +Tab_Ui_Form, Tab_BaseClass = uic.loadUiType("tab_template.ui") + + +class MainApp(QWidget, Ui_Form): + def __init__(self): + super().__init__() + self.setupUi(self) + + self.signal_count = 1 # TODO decide if useful + + self.tab_ui_objects = [] # Create a list to hold the Tab_Ui_Form objects + + # Connect the buttons + self.pushButton_add_new_plot.clicked.connect(self.add_new_plot) + self.pushButton_ok.clicked.connect(self.get_configuration) + + self.add_new_plot() # add initial first plot tab + + def add_new_plot(self): + # Set tabs + new_tab_widget = QWidget() + new_tab = Tab_Ui_Form() + new_tab.setupUi(new_tab_widget) + + # Tab Signals + new_tab.pushButton_y_new.clicked.connect( + lambda: self.add_new_signal(new_tab.tableWidget_y_signals) + ) + new_tab_name = f"Plot {self.tabWidget_plots.count() + 1}" + self.tabWidget_plots.addTab( + new_tab_widget, new_tab_name + ) # Add the new QWidget as a new tab + self.tab_ui_objects.append(new_tab) # Append the Tab_Ui_Form object to the list + + def add_new_signal(self, tableWidget_y_signals): + row_position = tableWidget_y_signals.rowCount() + tableWidget_y_signals.insertRow(row_position) + tableWidget_y_signals.setItem(row_position, 0, QTableWidgetItem("")) + tableWidget_y_signals.setItem(row_position, 1, QTableWidgetItem("")) + + def get_configuration(self): + config = { + "plot_settings": { + "background_color": "black", + "num_columns": self.spinBox.value(), + "colormap": self.comboBox_2.currentText(), + "scan_types": self.comboBox.currentText() == "Enabled", + }, + "plot_data": [], + } + + for index in range(self.tabWidget_plots.count()): + tab = self.tabWidget_plots.widget(index) + ui_object = self.tab_ui_objects[index] + table = ui_object.tableWidget_y_signals + signals = [ + { + "name": self.safe_text(table.item(row, 0)), + "entry": self.safe_text(table.item(row, 1)), + } + for row in range(table.rowCount()) + ] + + plot_config = { + "plot_name": self.safe_text(ui_object.lineEdit_plot_title), + "x": { + "label": self.safe_text(ui_object.lineEdit_x_label), + "signals": [ + { + "name": self.safe_text(ui_object.lineEdit_x_name), + "entry": self.safe_text(ui_object.lineEdit_x_entry), + } + ], + }, + "y": { + "label": self.safe_text(ui_object.lineEdit_y_label), + "signals": signals, + }, + } + config["plot_data"].append(plot_config) + + print(config) + return config + + @staticmethod + def safe_text(line_edit): + return "" if line_edit is None else line_edit.text() + + +if __name__ == "__main__": + app = QApplication([]) + main_app = MainApp() + main_app.show() + app.exec_() diff --git a/bec_widgets/widgets/monitor/config_dialog.ui b/bec_widgets/widgets/monitor/config_dialog.ui new file mode 100644 index 00000000..b7fc6f59 --- /dev/null +++ b/bec_widgets/widgets/monitor/config_dialog.ui @@ -0,0 +1,160 @@ + + + Form + + + + 0 + 0 + 558 + 769 + + + + Plot Configuration + + + + + + + + Plot Layout Settings + + + + + + + + + Scan Types + + + + + + + Default Color Palette + + + + + + + + magma + + + + + viridis + + + + + reds + + + + + + + + Number of Columns + + + + + + + Qt::Vertical + + + + + + + + Disabled + + + + + Enabled + + + + + + + + Add New Plot + + + + + + + + + + Configuration + + + + + + Import + + + + + + + Export + + + + + + + + + + + + -1 + + + + + + + + + Cancel + + + + + + + Apply + + + + + + + OK + + + + + + + + + + diff --git a/bec_widgets/widgets/device_monitor.py b/bec_widgets/widgets/monitor/device_monitor.py similarity index 100% rename from bec_widgets/widgets/device_monitor.py rename to bec_widgets/widgets/monitor/device_monitor.py diff --git a/bec_widgets/widgets/monitor/tab_template.ui b/bec_widgets/widgets/monitor/tab_template.ui new file mode 100644 index 00000000..498febce --- /dev/null +++ b/bec_widgets/widgets/monitor/tab_template.ui @@ -0,0 +1,169 @@ + + + Form + + + + 0 + 0 + 506 + 592 + + + + Form + + + + + + General + + + + + + X Label + + + + + + + Qt::Horizontal + + + + + + + + + + Y Label + + + + + + + + + + + + + Plot Title + + + + + + + Qt::Vertical + + + + + + + + + + + + X Axis + + + + + + Name: + + + + + + + + + + Qt::Vertical + + + + + + + Entry: + + + + + + + + + + + + + + 1 + + + + + Name + + + + + Entries + + + + + Color + + + + + + + + + + Remove Current Plot + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add New Signal + + + + + + + + + + + +