Feat: GUI updated and using process manager - #170
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HWQ3GnHuE2uY2yfqKchc6U
There was a problem hiding this comment.
Good catch, for linux we should be using killpg because we already start processes in their own process group. No complaints!
There was a problem hiding this comment.
Not sure why the microscope hosts are hardcoded into the shared... otherwise looks fine
| ) | ||
|
|
||
|
|
||
| DIGITAL_TWIN_HOST = 'localhost' |
|
|
||
|
|
||
| DIGITAL_TWIN_HOST = 'localhost' | ||
| SPECTRA300_HOST = '10.46.217.241' |
There was a problem hiding this comment.
I think this logic (along with that in run_servers) can be consolidated into a single helper function in ProcessManager
There was a problem hiding this comment.
Good, but same comment as in run_mcp
Addresses review feedback on ff77a76: - mcp_gui.py/server_gui.py launched their subprocess via a lightweight ManagedCommand that only sent a single SIGTERM with no wait/escalation, so orphaned children could survive the Stop button. ManagedCommand now delegates to ProcessManager.start_process/stop_process for killpg + bounded wait + SIGKILL escalation, and window close also shuts the managed process down. - Consolidated the duplicated SIGTERM/SIGHUP/SIGBREAK-to-KeyboardInterrupt boilerplate in run_mcp.py and run_servers.py into a single install_shutdown_signal_handler() helper in ProcessManager. - Added on_output callback support to ProcessManager.start_process so the GUI can stream live output while still using the shared process lifecycle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fix: route GUI Stop button and startup scripts through ProcessManager
There was a problem hiding this comment.
Pull request overview
Updates the Asyncroscopy startup GUIs and startup scripts to route process lifecycle through the shared ProcessManager, aiming to ensure the Stop action terminates full subprocess trees and to modernize the GUI layout/styling.
Changes:
- Refactors startup GUIs to launch/stop commands via
ProcessManager(plus new shared UI components/theme helpers). - Adds
install_shutdown_signal_handler()so SIGTERM/SIGHUP/SIGBREAK unwind launcher scripts viaKeyboardInterruptand triggerProcessManagercleanup. - Extends
ProcessManagerto optionally stream stdout/stderr lines to a callback, with new/updated tests.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_startup_guis.py | Adds tests for new shared GUI helpers and ManagedCommand delegating stop/shutdown. |
| tests/test_process_manager.py | Adds tests for output streaming callback and shutdown signal handler behavior. |
| startup_scripts/run_servers.py | Switches to shared shutdown signal handler installation. |
| startup_scripts/run_mcp.py | Installs shared shutdown signal handler at startup. |
| startup_guis/shared.py | Major shared GUI refactor: theming, components, instrument discovery, and ManagedCommand now uses ProcessManager. |
| startup_guis/server_gui.py | Updates server GUI layout/components and uses new shared helpers + managed command shutdown on close. |
| startup_guis/qt_compat.py | Extends Qt compatibility exports/constants used by new shared components. |
| startup_guis/mcp_gui.py | Updates MCP GUI layout/components; adds instrument preset picker + tool-count badge parsing. |
| asyncroscopy/utils/process_manager.py | Adds output streaming callback support; improves stale PID cleanup and adds shutdown signal handler helper. |
| asyncroscopy/mcp/mcp_server.py | Emits a “MCP ready: … tool(s)” line to support GUI tool-count badge. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| command=command, | ||
| env=env, | ||
| stderr=subprocess.STDOUT, | ||
| on_output=self.output_ready.emit, |
| for line in iter(stream.readline, b""): | ||
| buf.append(line.decode(errors="replace").rstrip()) | ||
| text = line.decode(errors="replace").rstrip() | ||
| buf.append(text) | ||
| if on_line is not None: | ||
| on_line(text) |
| tango = data.get('tango') | ||
| if not isinstance(tango, dict) or 'host' not in tango or 'port' not in tango: | ||
| continue | ||
| label = (data.get('instrument') or {}).get('description') or path.stem |
Updated GUIs by ensuring that it utilizes the process manager because the stop button wasn't killing all subprocess and improved design (increasing terminal sizing, removing duplicate buttons, etc.).