Skip to content

Commit 0208df4

Browse files
committed
fix: used multiprocessing to fix UI freeze
1 parent da8b81b commit 0208df4

File tree

2 files changed

+79
-246
lines changed

2 files changed

+79
-246
lines changed

openadapt/app/tray.py

Lines changed: 12 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@
2121
from datetime import datetime
2222
from functools import partial
2323
from pprint import pformat
24-
from threading import Event, Thread
24+
from threading import Thread
2525
from typing import Any, Callable
2626
import inspect
2727
import multiprocessing
28-
import time
2928

3029
from pyqttoast import Toast, ToastButtonAlignment, ToastIcon, ToastPosition, ToastPreset
31-
from PySide6.QtCore import QMargins, QObject, QSize, Qt, QThread, QTimer, Signal
30+
from PySide6.QtCore import QMargins, QObject, QSize, Qt, QThread, Signal
3231
from PySide6.QtGui import QAction, QColor, QFont, QIcon, QPixmap
3332
from PySide6.QtWidgets import (
3433
QApplication,
@@ -46,7 +45,6 @@
4645
QVBoxLayout,
4746
QWidget,
4847
)
49-
import requests
5048

5149
from openadapt.app import FPATH, quick_record, stop_record
5250
from openadapt.app.dashboard.run import cleanup as cleanup_dashboard
@@ -112,109 +110,6 @@ def run(self) -> None:
112110
self.data.emit(data)
113111

114112

115-
class DashboardMonitor(QObject):
116-
"""Monitor dashboard initialization."""
117-
118-
ready = Signal()
119-
120-
def __init__(self, app: QApplication = None, port: int = 5173) -> None:
121-
"""Initializes the DashboardMonitor.
122-
123-
Args:
124-
app (QApplication, optional): The QApplication instance. Defaults to None.
125-
port (int, optional): The port number for the dashboard. Defaults to 5173.
126-
127-
Attributes:
128-
stop_flag (Event): An event flag to signal stopping the monitor.
129-
port (int): The port number for the dashboard.
130-
_is_ready (bool): A flag indicating if the monitor is ready.
131-
monitor_thread (QThread or None): The thread for monitoring.
132-
"""
133-
super().__init__()
134-
self.stop_flag = Event()
135-
self.port = port
136-
self._is_ready = False
137-
138-
if app is not None:
139-
self.monitor_thread = QThread()
140-
self.moveToThread(self.monitor_thread)
141-
self.monitor_thread.started.connect(self.monitor_startup)
142-
self.monitor_thread.finished.connect(self.on_thread_finished)
143-
else:
144-
self.monitor_thread = None
145-
146-
print("DEBUG(DashboardMonitor): Signal ready created")
147-
148-
# Connect to our own ready signal to update state
149-
self.ready.connect(self._update_ready_state)
150-
151-
def _update_ready_state(self) -> None:
152-
"""Update internal ready state when signal is emitted."""
153-
self._is_ready = True
154-
print("DEBUG(DashboardMonitor): Ready state updated")
155-
156-
def on_thread_finished(self) -> None:
157-
"""Handle thread finished signal."""
158-
logger.info("Dashboard monitor thread finished")
159-
160-
def monitor_startup(self) -> None:
161-
"""Monitor dashboard startup process."""
162-
logger.info("Starting dashboard monitoring")
163-
start_time = time.time()
164-
try:
165-
while not self.stop_flag.is_set():
166-
try:
167-
elapsed_time = time.time() - start_time
168-
print(
169-
"DEBUG(DashboardMonitor): Checking dashboard. Elapsed:"
170-
f" {elapsed_time:.2f}s"
171-
)
172-
173-
response = requests.get(f"http://localhost:{self.port}", timeout=1)
174-
if response.status_code == 200:
175-
logger.info("Dashboard is ready!")
176-
177-
# Emit signal in main thread
178-
QTimer.singleShot(0, self.on_dashboard_ready)
179-
break
180-
except requests.RequestException as e:
181-
logger.debug(f"Connection attempt failed: {e}")
182-
time.sleep(0.5)
183-
184-
if time.time() - start_time > 30:
185-
logger.warning("Monitoring timeout")
186-
break
187-
finally:
188-
self.monitor_thread.quit()
189-
190-
def on_dashboard_ready(self) -> None:
191-
"""Handle dashboard being ready."""
192-
try:
193-
self.ready.emit()
194-
logger.info("Emitting ready signal")
195-
except Exception as e:
196-
logger.error(f"Error emitting signal: {e}")
197-
198-
def check_ready_state(self) -> None:
199-
"""Check if dashboard is ready and re-emit if necessary."""
200-
if self._is_ready:
201-
QTimer.singleShot(0, self.on_dashboard_ready)
202-
203-
def stop(self) -> None:
204-
"""Stop monitoring and cleanup thread."""
205-
logger.info("Stopping dashboard monitor")
206-
self.stop_flag.set()
207-
208-
if self.monitor_thread and self.monitor_thread.isRunning():
209-
try:
210-
self.monitor_thread.quit()
211-
if not self.monitor_thread.wait(1000):
212-
logger.warning("Dashboard monitor thread did not stop cleanly")
213-
self.monitor_thread.terminate()
214-
except Exception as e:
215-
logger.error(f"Error stopping dashboard monitor thread: {e}")
216-
217-
218113
class SystemTrayIcon:
219114
"""System tray icon for OpenAdapt."""
220115

@@ -225,13 +120,11 @@ class SystemTrayIcon:
225120
# storing actions is required to prevent garbage collection
226121
recording_actions = {"visualize": [], "replay": []}
227122

228-
def __init__(self, app: QApplication = None) -> None:
123+
def __init__(self) -> None:
229124
"""Initialize the system tray icon."""
230-
if app is None:
125+
self.app = QApplication.instance()
126+
if not self.app:
231127
self.app = QApplication([])
232-
else:
233-
self.app = app
234-
235128
if sys.platform == "darwin":
236129
# hide Dock icon while allowing focus on dialogs
237130
# (must come after QApplication())
@@ -613,36 +506,13 @@ def populate_menu(self, menu: QMenu, action: Callable, action_type: str) -> None
613506

614507
def launch_dashboard(self) -> None:
615508
"""Launch the web dashboard."""
616-
try:
617-
if self.dashboard_thread:
618-
if is_running_from_executable():
619-
return
620-
cleanup_dashboard()
621-
self.dashboard_thread.join()
622-
623-
# Start dashboard
624-
self.dashboard_thread = run_dashboard()
625-
self.dashboard_thread.start()
626-
logger.info("Dashboard thread started")
627-
628-
# Initialize dashboard monitor
629-
self.dashboard_monitor = DashboardMonitor(app=self.app)
630-
631-
# Connect ready signal BEFORE starting monitoring
632-
self.dashboard_monitor.ready.connect(
633-
self.on_dashboard_ready, Qt.ConnectionType.QueuedConnection
634-
)
635-
636-
# Start monitoring
637-
self.dashboard_monitor.monitor_startup()
638-
logger.info("Dashboard monitor started")
639-
640-
except Exception as e:
641-
logger.error(f"Launch dashboard error: {e}")
642-
643-
def on_dashboard_ready(self) -> None:
644-
"""Handle dashboard being ready."""
645-
logger.info("Dashboard is ready - performing final setup")
509+
if self.dashboard_thread:
510+
if is_running_from_executable():
511+
return
512+
cleanup_dashboard()
513+
self.dashboard_thread.join()
514+
self.dashboard_thread = run_dashboard()
515+
self.dashboard_thread.start()
646516

647517
def run(self) -> None:
648518
"""Run the system tray icon."""

0 commit comments

Comments
 (0)