Run in the background and open when MTGA starts
- Closing the window hides it to the system tray; tracking and history recording continue. Quit from the tray, the menu or Ctrl+Q. - Show the window when MTGA starts a new Player.log, detected with OS file notifications on the log folder (QFileSystemWatcher), not polling. - Single instance over a local socket: launching again shows the running window (or stays hidden with --background). - Menu options for background mode, show-on-launch and start at login (XDG autostart on Linux, HKCU Run key on Windows); --background flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f3c7c13d0
commit
c4533d12c0
4 files changed
+404
-4
No files matched your search
@@ -8,6 +8,7 @@ from pathlib import Path
|
||||
from PySide6.QtCore import QSettings, Qt, QThread, QTimer, Signal
|
||||
from PySide6.QtGui import QAction, QFont, QFontMetrics, QKeySequence
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QButtonGroup,
|
||||
QFileDialog,
|
||||
QFrame,
|
||||
@@ -19,12 +20,14 @@ from PySide6.QtWidgets import (
|
||||
QPushButton,
|
||||
QSizePolicy,
|
||||
QStackedWidget,
|
||||
QSystemTrayIcon,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from .. import paths
|
||||
from ..background import LaunchWatcher, autostart_enabled, set_autostart
|
||||
from ..carddb import CardDB
|
||||
from ..gamestate import MatchTracker
|
||||
from ..history import HistoryRecorder, HistoryStore, import_log, replay_game
|
||||
@@ -213,6 +216,9 @@ class MainWindow(QMainWindow):
|
||||
self.clock_timer.start()
|
||||
|
||||
self.resize(1400, 880)
|
||||
self._quitting = False
|
||||
self.launch_watcher: LaunchWatcher | None = None
|
||||
self._setup_tray()
|
||||
self.history_view.set_games(history.games())
|
||||
self.show_view(int(self._settings.value("view2", MATCH, type=int)))
|
||||
if log_path is not None:
|
||||
@@ -235,9 +241,26 @@ class MainWindow(QMainWindow):
|
||||
clear_act = menu.addAction("Clear image cache")
|
||||
clear_act.triggered.connect(self._clear_cache)
|
||||
menu.addSeparator()
|
||||
for label, key, default, tip in (
|
||||
("Keep running in the background when closed", "background/close_to_tray", True,
|
||||
"Closing the window hides it; games keep being recorded. Quit from the tray or this menu."),
|
||||
("Show the window when MTGA starts", "background/show_on_launch", True,
|
||||
"Brings the window up when MTGA starts a new log."),
|
||||
):
|
||||
act = menu.addAction(label)
|
||||
act.setCheckable(True)
|
||||
act.setChecked(self._settings.value(key, default, type=bool))
|
||||
act.setToolTip(tip)
|
||||
act.toggled.connect(lambda on, k=key: self._settings.setValue(k, on))
|
||||
login = menu.addAction("Start at login (in the background)")
|
||||
login.setCheckable(True)
|
||||
login.setChecked(autostart_enabled())
|
||||
login.toggled.connect(self._set_autostart)
|
||||
menu.setToolTipsVisible(True)
|
||||
menu.addSeparator()
|
||||
quit_act = QAction("Quit", self)
|
||||
quit_act.setShortcut(QKeySequence.StandardKey.Quit)
|
||||
quit_act.triggered.connect(self.close)
|
||||
quit_act.triggered.connect(self.quit_app)
|
||||
self.addAction(quit_act) # keep the shortcut active while the menu is closed
|
||||
unpreview = QAction("Stop previewing", self)
|
||||
unpreview.setShortcut(QKeySequence(Qt.Key.Key_Escape))
|
||||
@@ -310,6 +333,12 @@ class MainWindow(QMainWindow):
|
||||
self.follower.status.connect(self.status_log.setText)
|
||||
self.follower.reset.connect(self._reset_tracker)
|
||||
self.follower.start()
|
||||
if self.launch_watcher is not None:
|
||||
self.launch_watcher.deleteLater()
|
||||
self.launch_watcher = None
|
||||
if not self.replaying:
|
||||
self.launch_watcher = LaunchWatcher(path, self)
|
||||
self.launch_watcher.launched.connect(self._on_mtga_launched)
|
||||
|
||||
def _reset_tracker(self) -> None:
|
||||
self.tracker = MatchTracker(card_lookup=self.cards)
|
||||
@@ -369,9 +398,68 @@ class MainWindow(QMainWindow):
|
||||
self.follower = None
|
||||
self.images.shutdown()
|
||||
|
||||
# -- background ------------------------------------------------------------
|
||||
|
||||
def _setup_tray(self) -> None:
|
||||
self.tray: QSystemTrayIcon | None = None
|
||||
if not QSystemTrayIcon.isSystemTrayAvailable():
|
||||
return
|
||||
self.tray = QSystemTrayIcon(QApplication.windowIcon(), self)
|
||||
self.tray.setToolTip("MTGA Assistant")
|
||||
menu = QMenu(self)
|
||||
menu.addAction("Show window").triggered.connect(self.bring_to_front)
|
||||
menu.addAction("Hide window").triggered.connect(self.hide)
|
||||
menu.addSeparator()
|
||||
menu.addAction("Quit").triggered.connect(self.quit_app)
|
||||
self.tray.setContextMenu(menu)
|
||||
self.tray.activated.connect(self._on_tray_activated)
|
||||
self.tray.show()
|
||||
|
||||
def _on_tray_activated(self, reason: QSystemTrayIcon.ActivationReason) -> None:
|
||||
if reason == QSystemTrayIcon.ActivationReason.Trigger:
|
||||
if self.isVisible():
|
||||
self.hide()
|
||||
else:
|
||||
self.bring_to_front()
|
||||
|
||||
def bring_to_front(self) -> None:
|
||||
if self.isMinimized():
|
||||
self.showNormal()
|
||||
self.show()
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
|
||||
def _on_mtga_launched(self) -> None:
|
||||
if self._settings.value("background/show_on_launch", True, type=bool):
|
||||
self.bring_to_front()
|
||||
|
||||
def _set_autostart(self, enabled: bool) -> None:
|
||||
try:
|
||||
set_autostart(enabled)
|
||||
except OSError as e:
|
||||
QMessageBox.warning(self, "Start at login", f"Couldn't change the login item: {e}")
|
||||
|
||||
def quit_app(self) -> None:
|
||||
self._quitting = True
|
||||
QApplication.quit()
|
||||
|
||||
def closeEvent(self, event) -> None:
|
||||
self.shutdown()
|
||||
keep = self._settings.value("background/close_to_tray", True, type=bool)
|
||||
if keep and not self.replaying and not self._quitting:
|
||||
event.ignore()
|
||||
self.hide()
|
||||
if self.tray is not None and not self._settings.value("background/told", False, type=bool):
|
||||
self.tray.showMessage("MTGA Assistant", "Still recording games in the background. "
|
||||
"Use the tray icon to show the window or quit.",
|
||||
QApplication.windowIcon(), 6000)
|
||||
self._settings.setValue("background/told", True)
|
||||
return
|
||||
super().closeEvent(event)
|
||||
QApplication.quit() # not running in the background: closing the window quits
|
||||
|
||||
def showEvent(self, event) -> None:
|
||||
super().showEvent(event)
|
||||
self._force_refresh() # rendering is skipped while hidden
|
||||
|
||||
# -- rendering -------------------------------------------------------------
|
||||
|
||||
@@ -391,6 +479,8 @@ class MainWindow(QMainWindow):
|
||||
self.match_view.tick(self.shown_tracker, self.now_ms())
|
||||
|
||||
def refresh(self) -> None:
|
||||
if not self.isVisible():
|
||||
return # tracking continues; the views catch up when shown
|
||||
t = self.shown_tracker
|
||||
index = self.stack.currentIndex()
|
||||
key = (id(t), t.version)
|
||||
|
||||
Reference in new issue
Block a user