Disconnect log signals on exit.

If we don't do this, the uvicorn server may log its shutdown after the
Qt application has closed, and the signal this attempts to emit may not
be valid. Disconnect the log signals when the application exits to
prevent that.

There's actually another solution that I thought would be better, but I
couldn't get it to work:
https://www.pyinstaller.org/en/stable/feature-notes.html#automatic-hiding-and-minimization-of-console-window-under-windows
describes a way to have pyinstaller hide or minimize the console rather
than disabling it entirely. I was never really fond of getting rid of
the console window in the first place, but it did bother some users. If
we could get the hide or minimize option working, that'd probably avoid
bothering users, but also make the logs much easier to find, get us out
of the trouble of maintaining our own log viewer, and fix the problem
mentioned in the comment I add here (the log window only works if
there's only one in memory log handler).

Another option would be ditching our log window and instead just having
that menu item open the log file or directory in whatever program the OS
defaults to (probably notepad). It would still have the quirk of maybe
needing to open more than one location, since logging is use
configurable.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/3278.
This commit is contained in:
Dan Albert 2023-12-02 13:17:13 -08:00
parent 88591fd18c
commit 5ee3afeddb
3 changed files with 31 additions and 8 deletions

View File

@ -1,5 +1,8 @@
from __future__ import annotations
import logging
import typing
from collections.abc import Iterator
LogHook = typing.Callable[[str], None]
@ -15,6 +18,16 @@ class HookableInMemoryHandler(logging.Handler):
self._log = ""
self._hook = None
@staticmethod
def iter_registered_handlers(
logger: logging.Logger | None = None,
) -> Iterator[HookableInMemoryHandler]:
if logger is None:
logger = logging.getLogger()
for handler in logger.handlers:
if isinstance(handler, HookableInMemoryHandler):
yield handler
@property
def log(self) -> str:
return self._log

View File

@ -27,6 +27,7 @@ from game.theater import ControlPoint, MissionTarget, TheaterGroundObject
from game.turnstate import TurnState
from qt_ui import liberation_install
from qt_ui.dialogs import Dialog
from qt_ui.logging_handler import HookableInMemoryHandler
from qt_ui.models import GameModel
from qt_ui.simcontroller import SimController
from qt_ui.uiflags import UiFlags
@ -576,6 +577,10 @@ class QLiberationWindow(QMainWindow):
self._cp_dialog = QBaseMenu2(None, cp, self.game_model)
self._cp_dialog.show()
def _disconnect_log_signals(self) -> None:
for handler in HookableInMemoryHandler.iter_registered_handlers():
handler.clearHook()
def _qsettings(self) -> QSettings:
return QSettings("DCS Liberation", "Qt UI")
@ -597,6 +602,7 @@ class QLiberationWindow(QMainWindow):
QMessageBox.Yes | QMessageBox.No,
)
if result == QMessageBox.Yes:
self._disconnect_log_signals()
self._save_window_geometry()
super().closeEvent(event)
self.dialog = None

View File

@ -1,14 +1,13 @@
import logging
import typing
from PySide6.QtCore import Signal
from PySide6.QtGui import QTextCursor, QIcon
from PySide6.QtWidgets import (
QDialog,
QPlainTextEdit,
QVBoxLayout,
QPushButton,
)
from PySide6.QtGui import QTextCursor, QIcon
from qt_ui.logging_handler import HookableInMemoryHandler
@ -50,12 +49,17 @@ class QLogsWindow(QDialog):
self.appendLogSignal.connect(self.appendLog)
try:
# This assumes that there's never more than one in memory handler. We don't
# configure more than one by default, but logging is customizable with
# resources/logging.yaml. If someone adds a second in-memory handler, only
# the first one (in arbitrary order) will be shown.
self._logging_handler = next(
HookableInMemoryHandler.iter_registered_handlers()
)
except StopIteration:
self._logging_handler = None
logger = logging.getLogger()
for handler in logger.handlers:
if isinstance(handler, HookableInMemoryHandler):
self._logging_handler = handler
break
if self._logging_handler is not None:
self.textbox.setPlainText(self._logging_handler.log)
self.textbox.moveCursor(QTextCursor.End)