summaryrefslogtreecommitdiff
path: root/ui/main_window.py
diff options
context:
space:
mode:
authorChristian Kolset <christian.kolset@gmail.com>2026-08-02 00:58:34 -0600
committerChristian Kolset <christian.kolset@gmail.com>2026-08-02 00:58:34 -0600
commitf6b68f1482a6b6491c9163cf7e9b1489aa24d6d0 (patch)
tree9f97b051e73cac1c0e68798da2f00ad96ae09c42 /ui/main_window.py
parente1b951db7714a9496ebf7f861a6741598d4c7e67 (diff)
Consolidate Devices/Channels/Plot into unified ConfigWindow
Replaces three separate floating windows with single tabbed ConfigWindow: - Tab 0: Devices (device cards, add/remove/configure) - Tab 1: Channels (signal table with Dir/IN/OUT badge + pipeline ⚙ per row; derived channels pane) - Tab 2: Plot (layout canvas + pane config) Adds is_output field to ChannelConfig for output channel direction badges. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'ui/main_window.py')
-rw-r--r--ui/main_window.py166
1 files changed, 66 insertions, 100 deletions
diff --git a/ui/main_window.py b/ui/main_window.py
index 76c203d..22b01a0 100644
--- a/ui/main_window.py
+++ b/ui/main_window.py
@@ -105,9 +105,8 @@ from core.profile import Profile, ProfileManager
from ui.control_panel import ControlPanel
from ui.strip_chart import StripChartWidget
from ui.profile_manager_ui import ProfileButton
-from ui.windows.devices_window import DevicesWindow
-from ui.windows.channels_window import ChannelsWindow
-from ui.windows.plot_window import PlotWindow, build_default_layout
+from ui.windows.config_window import ConfigWindow
+from ui.windows.plot_window import build_default_layout
from ui.windows.settings_window import SettingsWindow
from plugins.plugin_manager import PluginManager
@@ -131,9 +130,7 @@ class MainWindow(QMainWindow):
self._settings = load_settings(SettingsWindow._defaults)
self._elapsed = 0
- self._win_devices = None
- self._win_channels = None
- self._win_plot = None
+ self._win_config = None
self._win_settings = None
_plugins_dir = os.path.join(os.path.dirname(os.path.dirname(
@@ -194,19 +191,21 @@ class MainWindow(QMainWindow):
self._view_menu.addAction(self._act_ctrl_panel)
self._view_menu.addSeparator()
- for label, name, opener in [
- ("Devices", "devices", self._open_devices),
- ("Channels", "channels", self._open_channels),
- ("Plot Builder", "plot", self._open_plot),
+ for label, tab_idx in [
+ ("Devices", 0),
+ ("Channels", 1),
+ ("Plot Builder", 2),
]:
act = QAction(label, self); act.setCheckable(True)
act.triggered.connect(
- lambda checked, nm=name, op=opener:
- op() if checked else
- (getattr(self, f"_win_{nm}") and getattr(self, f"_win_{nm}").hide())
+ lambda checked, ti=tab_idx:
+ self._open_config(ti) if checked else
+ (self._win_config and self._win_config.hide())
)
self._view_menu.addAction(act)
- setattr(self, f"_act_{name}", act)
+ self._act_devices = self._view_menu.actions()[-3]
+ self._act_channels = self._view_menu.actions()[-2]
+ self._act_plot = self._view_menu.actions()[-1]
self._view_plugin_sep = self._view_menu.addSeparator()
self._view_plugin_sep.setVisible(False)
@@ -368,8 +367,8 @@ class MainWindow(QMainWindow):
self._plugin_toolbar_actions[plugin.plugin_id] = tb_actions
self._chart.refresh()
- if self._win_plot:
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.refresh_plot()
self._status.setText(f"Plugin enabled: {plugin.name}")
def _uninstall_plugin(self, plugin_id: str):
@@ -404,8 +403,8 @@ class MainWindow(QMainWindow):
self.engine.remove_device(dev_id)
self._chart.refresh()
- if self._win_plot:
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.refresh_plot()
self._status.setText(f"Plugin disabled: {plugin.name}")
def plugin_enable(self, plugin_id: str):
@@ -422,55 +421,29 @@ class MainWindow(QMainWindow):
# ── Window management ─────────────────────────────────────────────────
- def _toggle_win(self, name: str, checked: bool, btn: QPushButton):
- creators = {
- "devices": self._open_devices,
- "channels": self._open_channels,
- "plot": self._open_plot,
- "settings": self._open_settings,
- }
- wins = {
- "devices": "_win_devices",
- "channels": "_win_channels",
- "plot": "_win_plot",
- "settings": "_win_settings",
- }
- if checked:
- creators[name]()
- else:
- win = getattr(self, wins[name], None)
- if win: win.hide()
-
- def _open_devices(self):
- if self._win_devices is None:
- self._win_devices = DevicesWindow(self.registry, self.engine, self)
- self._win_devices.device_added.connect(self._on_device_added)
- self._win_devices.device_removed.connect(self._on_device_removed)
- self._win_devices.device_reconfigured.connect(self._on_device_reconfigured)
- self._win_devices.channel_visibility_changed.connect(self._on_channel_visibility_changed)
- self._win_devices.channel_name_changed.connect(self._on_channel_name_changed)
- self._win_devices.channel_unit_changed.connect(self._on_channel_unit_changed)
- self._win_devices.closed.connect(lambda: self._act_devices.setChecked(False))
- self._show_win(self._win_devices, "right")
-
- def _open_channels(self):
- if self._win_channels is None:
- self._win_channels = ChannelsWindow(self.registry, self.processor, self)
- self._win_channels.derived_changed.connect(self._on_derived_changed)
- self._win_channels.closed.connect(lambda: self._act_channels.setChecked(False))
- self._show_win(self._win_channels, "right")
-
- def _open_plot(self):
- if self._win_plot is None:
- self._win_plot = PlotWindow(self.registry, self.processor,
- self._chart._cfg, self)
- self._win_plot.layout_applied.connect(self._chart.apply_layout)
- self._win_plot.closed.connect(lambda: self._act_plot.setChecked(False))
- self._win_plot.refresh_channels() # sync any derived channels
- else:
- self._win_plot.cfg = self._chart._cfg
- self._win_plot.refresh_channels()
- self._show_win(self._win_plot, "below")
+ def _open_config(self, tab: int = 0):
+ if self._win_config is None:
+ self._win_config = ConfigWindow(
+ self.registry, self.engine, self.processor,
+ self._chart._cfg, self,
+ )
+ self._win_config.device_added.connect(self._on_device_added)
+ self._win_config.device_removed.connect(self._on_device_removed)
+ self._win_config.device_reconfigured.connect(self._on_device_reconfigured)
+ self._win_config.channel_visibility_changed.connect(self._on_channel_visibility_changed)
+ self._win_config.channel_name_changed.connect(self._on_channel_name_changed)
+ self._win_config.channel_unit_changed.connect(self._on_channel_unit_changed)
+ self._win_config.pipeline_changed.connect(lambda: None)
+ self._win_config.derived_changed.connect(self._on_derived_changed)
+ self._win_config.layout_applied.connect(self._chart.apply_layout)
+ self._win_config.closed.connect(self._on_config_closed)
+ self._win_config.tabs.setCurrentIndex(tab)
+ self._show_win(self._win_config, "right")
+
+ def _on_config_closed(self):
+ self._act_devices.setChecked(False)
+ self._act_channels.setChecked(False)
+ self._act_plot.setChecked(False)
def _open_settings(self):
if self._win_settings is None:
@@ -529,13 +502,10 @@ class MainWindow(QMainWindow):
# Clear controls
self._ctrl.clear_widgets()
- # Refresh open windows
- if self._win_devices:
- self._win_devices.refresh()
- if self._win_channels:
- self._win_channels.refresh_derived()
- if self._win_plot:
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.refresh_devices()
+ self._win_config.refresh_derived()
+ self._win_config.refresh_plot()
self._chart.apply_layout(build_default_layout(self.registry, self.processor))
self._clear_history()
@@ -578,55 +548,52 @@ class MainWindow(QMainWindow):
self._chart.apply_layout(plot_cfg)
else:
self._chart.refresh()
- # Refresh open windows
- if self._win_devices:
- self._win_devices.refresh()
- if self._win_channels:
- self._win_channels.refresh_derived()
- if self._win_plot:
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.refresh_devices()
+ self._win_config.refresh_derived()
+ self._win_config.refresh_plot(self._chart._cfg)
self._status.setText(f"Profile loaded: {profile.name}")
# ── Device / signal events ────────────────────────────────────────────
def _on_device_added(self):
self._chart.refresh()
- if self._win_plot: self._win_plot.refresh_channels()
- if self._win_channels: self._win_channels.on_device_added()
+ if self._win_config:
+ self._win_config.on_device_added()
+ self._win_config.refresh_plot()
self._status.setText("Device added.")
def _on_device_removed(self, dev_id: str):
self._chart.refresh()
- if self._win_channels: self._win_channels.on_device_removed(dev_id)
+ if self._win_config:
+ self._win_config.on_device_removed(dev_id)
self._status.setText(f"Device '{dev_id}' removed.")
def _on_device_reconfigured(self, dev_id: str):
self._chart.refresh()
- if self._win_plot: self._win_plot.refresh_channels()
- if self._win_channels: self._win_channels.on_device_reconfigured(dev_id)
+ if self._win_config:
+ self._win_config.on_device_reconfigured(dev_id)
+ self._win_config.refresh_plot()
self._status.setText(f"Device '{dev_id}' reconfigured.")
def _on_channel_visibility_changed(self, dev_id: str, ch_id: str, enabled: bool):
self._chart.on_channel_enabled_changed(dev_id, ch_id, enabled)
- if self._win_channels:
- self._win_channels.on_channel_enabled_changed(dev_id, ch_id, enabled)
- if self._win_plot:
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.on_channel_enabled_changed(dev_id, ch_id, enabled)
+ self._win_config.refresh_plot()
def _on_channel_name_changed(self, dev_id: str, ch_id: str, name: str):
- if self._win_channels:
- self._win_channels.on_channel_name_changed(dev_id, ch_id, name)
+ if self._win_config:
+ self._win_config.on_channel_name_changed(dev_id, ch_id, name)
def _on_channel_unit_changed(self, dev_id: str, ch_id: str, unit: str):
- if self._win_channels:
- self._win_channels.on_channel_unit_changed(dev_id, ch_id, unit)
+ if self._win_config:
+ self._win_config.on_channel_unit_changed(dev_id, ch_id, unit)
def _on_derived_changed(self):
self._chart.refresh()
- if self._win_plot:
- # Ensure plot window works on current chart cfg, then sync derived panes
- self._win_plot.cfg = self._chart._cfg
- self._win_plot.refresh_channels()
+ if self._win_config:
+ self._win_config.refresh_plot(self._chart._cfg)
# ── Run / Log ─────────────────────────────────────────────────────────
@@ -687,8 +654,7 @@ class MainWindow(QMainWindow):
self._time_lbl.setText(f"{h:02d}:{m:02d}:{s:02d}")
def closeEvent(self, event):
- for w in (self._win_devices, self._win_channels,
- self._win_plot, self._win_settings):
+ for w in (self._win_config, self._win_settings):
if w: w.close()
for plugin in list(self._plugin_mgr.get_loaded()):
try: