diff options
Diffstat (limited to 'ui')
25 files changed, 196 insertions, 64 deletions
diff --git a/ui/__pycache__/__init__.cpython-312.pyc b/ui/__pycache__/__init__.cpython-312.pyc Binary files differindex fb20d59..8dc1c2f 100644 --- a/ui/__pycache__/__init__.cpython-312.pyc +++ b/ui/__pycache__/__init__.cpython-312.pyc diff --git a/ui/__pycache__/__init__.cpython-314.pyc b/ui/__pycache__/__init__.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..15d71d0 --- /dev/null +++ b/ui/__pycache__/__init__.cpython-314.pyc diff --git a/ui/__pycache__/add_device_dialog.cpython-312.pyc b/ui/__pycache__/add_device_dialog.cpython-312.pyc Binary files differindex 8af1f9c..e9aab22 100644 --- a/ui/__pycache__/add_device_dialog.cpython-312.pyc +++ b/ui/__pycache__/add_device_dialog.cpython-312.pyc diff --git a/ui/__pycache__/config_dialog.cpython-312.pyc b/ui/__pycache__/config_dialog.cpython-312.pyc Binary files differindex 5a051dc..cbc9cf0 100644 --- a/ui/__pycache__/config_dialog.cpython-312.pyc +++ b/ui/__pycache__/config_dialog.cpython-312.pyc diff --git a/ui/__pycache__/control_editor.cpython-312.pyc b/ui/__pycache__/control_editor.cpython-312.pyc Binary files differindex d12d50f..42855d5 100644 --- a/ui/__pycache__/control_editor.cpython-312.pyc +++ b/ui/__pycache__/control_editor.cpython-312.pyc diff --git a/ui/__pycache__/control_editor.cpython-314.pyc b/ui/__pycache__/control_editor.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..aaea0ee --- /dev/null +++ b/ui/__pycache__/control_editor.cpython-314.pyc diff --git a/ui/__pycache__/control_panel.cpython-312.pyc b/ui/__pycache__/control_panel.cpython-312.pyc Binary files differindex 6d020f5..8761466 100644 --- a/ui/__pycache__/control_panel.cpython-312.pyc +++ b/ui/__pycache__/control_panel.cpython-312.pyc diff --git a/ui/__pycache__/control_panel.cpython-314.pyc b/ui/__pycache__/control_panel.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..79f9084 --- /dev/null +++ b/ui/__pycache__/control_panel.cpython-314.pyc diff --git a/ui/__pycache__/main_window.cpython-312.pyc b/ui/__pycache__/main_window.cpython-312.pyc Binary files differindex b528ac2..a290571 100644 --- a/ui/__pycache__/main_window.cpython-312.pyc +++ b/ui/__pycache__/main_window.cpython-312.pyc diff --git a/ui/__pycache__/main_window.cpython-314.pyc b/ui/__pycache__/main_window.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..acef085 --- /dev/null +++ b/ui/__pycache__/main_window.cpython-314.pyc diff --git a/ui/__pycache__/profile_manager_ui.cpython-312.pyc b/ui/__pycache__/profile_manager_ui.cpython-312.pyc Binary files differindex c9fbb4a..97cbe3a 100644 --- a/ui/__pycache__/profile_manager_ui.cpython-312.pyc +++ b/ui/__pycache__/profile_manager_ui.cpython-312.pyc diff --git a/ui/__pycache__/profile_manager_ui.cpython-314.pyc b/ui/__pycache__/profile_manager_ui.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..ea12b21 --- /dev/null +++ b/ui/__pycache__/profile_manager_ui.cpython-314.pyc diff --git a/ui/__pycache__/strip_chart.cpython-312.pyc b/ui/__pycache__/strip_chart.cpython-312.pyc Binary files differindex 47dbea1..ff41c1a 100644 --- a/ui/__pycache__/strip_chart.cpython-312.pyc +++ b/ui/__pycache__/strip_chart.cpython-312.pyc diff --git a/ui/__pycache__/strip_chart.cpython-314.pyc b/ui/__pycache__/strip_chart.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..e56015f --- /dev/null +++ b/ui/__pycache__/strip_chart.cpython-314.pyc diff --git a/ui/add_device_dialog.py b/ui/add_device_dialog.py index 03ece63..50f234d 100644 --- a/ui/add_device_dialog.py +++ b/ui/add_device_dialog.py @@ -36,6 +36,150 @@ class PortScanThread(QThread): self.ports_found.emit(ArduinoLayer.list_ports()) +# ── Background scan threads ─────────────────────────────────────────────────── + +class NIScanThread(QThread): + devices_found = pyqtSignal(list) + + def run(self): + try: + import nidaqmx # type: ignore + devs = [(d.name, d.product_type) for d in nidaqmx.system.System().devices] + except Exception: + devs = [] + self.devices_found.emit(devs) + + +# ── Reusable port scanner widget ───────────────────────────────────────────── + +class PortScanGroup(QGroupBox): + """Scan-and-click widget that fills a target QLineEdit with the chosen port.""" + + def __init__(self, target_edit: QLineEdit): + super().__init__("Available Serial Ports") + self._target = target_edit + self._scanner = None + + lay = QVBoxLayout(self) + lay.setSpacing(4) + + top = QHBoxLayout() + self._scan_btn = QPushButton("🔍 Scan Ports") + self._scan_btn.setObjectName("addTraceBtn") + self._scan_btn.clicked.connect(self._scan) + self._status = QLabel("Click Scan to detect ports") + self._status.setObjectName("traceSource") + top.addWidget(self._scan_btn) + top.addWidget(self._status, 1) + lay.addLayout(top) + + self._list = QListWidget() + self._list.setMaximumHeight(100) + self._list.setObjectName("portList") + self._list.itemClicked.connect(self._on_select) + self._list.setToolTip("Click a port to select it") + lay.addWidget(self._list) + + hint = QLabel("↑ Click a port above to fill the Port field") + hint.setObjectName("traceSource") + lay.addWidget(hint) + + def _scan(self): + self._scan_btn.setEnabled(False) + self._status.setText("Scanning…") + self._list.clear() + self._scanner = PortScanThread() + self._scanner.ports_found.connect(self._on_found) + self._scanner.start() + + def _on_found(self, ports): + self._scan_btn.setEnabled(True) + self._list.clear() + if not ports: + self._status.setText("No ports found") + item = QListWidgetItem(" No serial ports detected") + item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsSelectable) + self._list.addItem(item) + else: + self._status.setText(f"{len(ports)} port(s) found") + for device, desc in ports: + label = f" {device}" + if desc and desc != device: + label += f" — {desc}" + item = QListWidgetItem(label) + item.setData(Qt.ItemDataRole.UserRole, device) + self._list.addItem(item) + + def _on_select(self, item: QListWidgetItem): + port = item.data(Qt.ItemDataRole.UserRole) + if port: + self._target.setText(port) + + +class NIScanGroup(QGroupBox): + """Scan-and-click widget that fills a target QLineEdit with the chosen NI device.""" + + def __init__(self, target_edit: QLineEdit): + super().__init__("Available NI Devices") + self._target = target_edit + self._scanner = None + + lay = QVBoxLayout(self) + lay.setSpacing(4) + + top = QHBoxLayout() + self._scan_btn = QPushButton("🔍 Scan NI Devices") + self._scan_btn.setObjectName("addTraceBtn") + self._scan_btn.clicked.connect(self._scan) + self._status = QLabel("Click Scan to detect NI devices") + self._status.setObjectName("traceSource") + top.addWidget(self._scan_btn) + top.addWidget(self._status, 1) + lay.addLayout(top) + + self._list = QListWidget() + self._list.setMaximumHeight(100) + self._list.setObjectName("portList") + self._list.itemClicked.connect(self._on_select) + self._list.setToolTip("Click a device to select it") + lay.addWidget(self._list) + + hint = QLabel("↑ Click a device above to fill the NI Device field") + hint.setObjectName("traceSource") + lay.addWidget(hint) + + def _scan(self): + self._scan_btn.setEnabled(False) + self._status.setText("Scanning…") + self._list.clear() + self._scanner = NIScanThread() + self._scanner.devices_found.connect(self._on_found) + self._scanner.start() + + def _on_found(self, devices): + self._scan_btn.setEnabled(True) + self._list.clear() + if not devices: + self._status.setText("No NI devices found") + item = QListWidgetItem(" No NI devices detected") + item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsSelectable) + self._list.addItem(item) + else: + self._status.setText(f"{len(devices)} device(s) found") + for name, product in devices: + label = f" {name}" + if product: + label += f" — {product}" + item = QListWidgetItem(label) + item.setData(Qt.ItemDataRole.UserRole, name) + self._list.addItem(item) + + def _on_select(self, item: QListWidgetItem): + name = item.data(Qt.ItemDataRole.UserRole) + if name: + self._target.setText(name) + + # ── Per-type config panels ──────────────────────────────────────────────────── class AnalogInputPanel(QWidget): @@ -73,7 +217,6 @@ class AnalogInputPanel(QWidget): self.max_v_spin.setSuffix(" V") lay.addRow("Max Voltage:", self.max_v_spin) - # Arduino port (shown always, relevant for arduino backend) self.ard_port_edit = QLineEdit("") self.ard_port_edit.setPlaceholderText("e.g. COM3 or /dev/ttyUSB0") lay.addRow("Arduino Port:", self.ard_port_edit) @@ -85,9 +228,34 @@ class AnalogInputPanel(QWidget): # Simulate self.sim_chk = QCheckBox("Simulation mode") - self.sim_chk.setChecked(True) + self.sim_chk.setChecked(False) lay.addRow(self.sim_chk) + self._ni_scan_grp = NIScanGroup(self.ni_device_edit) + lay.addRow(self._ni_scan_grp) + + self._ard_scan_grp = PortScanGroup(self.ard_port_edit) + lay.addRow(self._ard_scan_grp) + + self._ni_widgets = [self.ni_device_edit, self.min_v_spin, self.max_v_spin, self._ni_scan_grp] + self._ard_widgets = [self.ard_port_edit, self.ard_baud_cb, self._ard_scan_grp] + self.backend_cb.currentTextChanged.connect(self._on_backend_changed) + self._on_backend_changed(self.backend_cb.currentText()) + + def _on_backend_changed(self, backend: str): + ni = backend == "nidaqmx" + lay = self.layout() + for w in self._ni_widgets: + w.setVisible(ni) + lbl = lay.labelForField(w) + if lbl: + lbl.setVisible(ni) + for w in self._ard_widgets: + w.setVisible(not ni) + lbl = lay.labelForField(w) + if lbl: + lbl.setVisible(not ni) + def build_device(self, device_id: str) -> AnalogInputDevice: return AnalogInputDevice( device_id=device_id, @@ -132,9 +300,32 @@ class DigitalIOPanel(QWidget): lay.addRow("Arduino Port:", self.ard_port_edit) self.sim_chk = QCheckBox("Simulation mode") - self.sim_chk.setChecked(True) + self.sim_chk.setChecked(False) lay.addRow(self.sim_chk) + self._ni_scan_grp = NIScanGroup(self.ni_device_edit) + lay.addRow(self._ni_scan_grp) + + self._ard_scan_grp = PortScanGroup(self.ard_port_edit) + lay.addRow(self._ard_scan_grp) + + self.backend_cb.currentTextChanged.connect(self._on_backend_changed) + self._on_backend_changed(self.backend_cb.currentText()) + + def _on_backend_changed(self, backend: str): + ni = backend == "nidaqmx" + lay = self.layout() + for w, show in [ + (self.ni_device_edit, ni), + (self._ni_scan_grp, ni), + (self.ard_port_edit, not ni), + (self._ard_scan_grp, not ni), + ]: + w.setVisible(show) + lbl = lay.labelForField(w) + if lbl: + lbl.setVisible(show) + def build_device(self, device_id: str) -> DigitalIODevice: return DigitalIODevice( device_id=device_id, @@ -177,70 +368,11 @@ class SerialPanel(QWidget): form.addRow("Parse Format:", self.fmt_cb) self.sim_chk = QCheckBox("Simulation mode") - self.sim_chk.setChecked(True) + self.sim_chk.setChecked(False) form.addRow(self.sim_chk) lay.addLayout(form) - - # Port scanner - scan_grp = QGroupBox("Available Serial Ports") - scan_lay = QVBoxLayout(scan_grp) - scan_lay.setSpacing(4) - - scan_top = QHBoxLayout() - self._scan_btn = QPushButton("🔍 Scan Ports") - self._scan_btn.setObjectName("addTraceBtn") - self._scan_btn.clicked.connect(self._scan_ports) - self._scan_status = QLabel("Click Scan to detect ports") - self._scan_status.setObjectName("traceSource") - scan_top.addWidget(self._scan_btn) - scan_top.addWidget(self._scan_status, 1) - scan_lay.addLayout(scan_top) - - self._port_list = QListWidget() - self._port_list.setMaximumHeight(100) - self._port_list.setObjectName("portList") - self._port_list.itemClicked.connect(self._on_port_selected) - self._port_list.setToolTip("Click a port to select it") - scan_lay.addWidget(self._port_list) - - hint = QLabel("↑ Click a port above to fill the Port field") - hint.setObjectName("traceSource") - scan_lay.addWidget(hint) - lay.addWidget(scan_grp) - - self._scanner = None - - def _scan_ports(self): - self._scan_btn.setEnabled(False) - self._scan_status.setText("Scanning…") - self._port_list.clear() - self._scanner = PortScanThread() - self._scanner.ports_found.connect(self._on_ports_found) - self._scanner.start() - - def _on_ports_found(self, ports): - self._scan_btn.setEnabled(True) - self._port_list.clear() - if not ports: - self._scan_status.setText("No ports found") - item = QListWidgetItem(" No serial ports detected") - item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsSelectable) - self._port_list.addItem(item) - else: - self._scan_status.setText(f"{len(ports)} port(s) found") - for device, desc in ports: - label = f" {device}" - if desc and desc != device: - label += f" — {desc}" - item = QListWidgetItem(label) - item.setData(Qt.ItemDataRole.UserRole, device) - self._port_list.addItem(item) - - def _on_port_selected(self, item: QListWidgetItem): - port = item.data(Qt.ItemDataRole.UserRole) - if port: - self.port_edit.setText(port) + lay.addWidget(PortScanGroup(self.port_edit)) def build_device(self, device_id: str) -> SerialDevice: return SerialDevice( diff --git a/ui/windows/__pycache__/__init__.cpython-312.pyc b/ui/windows/__pycache__/__init__.cpython-312.pyc Binary files differindex 37c4a45..66495c5 100644 --- a/ui/windows/__pycache__/__init__.cpython-312.pyc +++ b/ui/windows/__pycache__/__init__.cpython-312.pyc diff --git a/ui/windows/__pycache__/__init__.cpython-314.pyc b/ui/windows/__pycache__/__init__.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..873ff5b --- /dev/null +++ b/ui/windows/__pycache__/__init__.cpython-314.pyc diff --git a/ui/windows/__pycache__/devices_window.cpython-312.pyc b/ui/windows/__pycache__/devices_window.cpython-312.pyc Binary files differindex ca01f31..601221d 100644 --- a/ui/windows/__pycache__/devices_window.cpython-312.pyc +++ b/ui/windows/__pycache__/devices_window.cpython-312.pyc diff --git a/ui/windows/__pycache__/devices_window.cpython-314.pyc b/ui/windows/__pycache__/devices_window.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..e1a4868 --- /dev/null +++ b/ui/windows/__pycache__/devices_window.cpython-314.pyc diff --git a/ui/windows/__pycache__/plot_window.cpython-312.pyc b/ui/windows/__pycache__/plot_window.cpython-312.pyc Binary files differindex c2e3745..288e026 100644 --- a/ui/windows/__pycache__/plot_window.cpython-312.pyc +++ b/ui/windows/__pycache__/plot_window.cpython-312.pyc diff --git a/ui/windows/__pycache__/plot_window.cpython-314.pyc b/ui/windows/__pycache__/plot_window.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..30b0a23 --- /dev/null +++ b/ui/windows/__pycache__/plot_window.cpython-314.pyc diff --git a/ui/windows/__pycache__/settings_window.cpython-312.pyc b/ui/windows/__pycache__/settings_window.cpython-312.pyc Binary files differindex 72f96e1..d596487 100644 --- a/ui/windows/__pycache__/settings_window.cpython-312.pyc +++ b/ui/windows/__pycache__/settings_window.cpython-312.pyc diff --git a/ui/windows/__pycache__/settings_window.cpython-314.pyc b/ui/windows/__pycache__/settings_window.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..cc9691b --- /dev/null +++ b/ui/windows/__pycache__/settings_window.cpython-314.pyc diff --git a/ui/windows/__pycache__/signals_window.cpython-312.pyc b/ui/windows/__pycache__/signals_window.cpython-312.pyc Binary files differindex 0526a85..1f6a3db 100644 --- a/ui/windows/__pycache__/signals_window.cpython-312.pyc +++ b/ui/windows/__pycache__/signals_window.cpython-312.pyc diff --git a/ui/windows/__pycache__/signals_window.cpython-314.pyc b/ui/windows/__pycache__/signals_window.cpython-314.pyc Binary files differnew file mode 100644 index 0000000..224e31e --- /dev/null +++ b/ui/windows/__pycache__/signals_window.cpython-314.pyc |
