Enhance HVideoTool with background processing and device detection: implemented a background job system for detection and restoration tasks, updated the UI to display CUDA/CPU status, and improved device diagnostics. Documentation in README and CLAUDE.md reflects these changes.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""A tiny background-job helper so heavy work doesn't freeze the GUI.
|
||||
|
||||
The app is otherwise synchronous, but a single ``detector.detect()`` (CPU YOLO) or
|
||||
a DeepMosaics restore can block the GUI thread for seconds — ``processEvents`` only
|
||||
runs *between* frames, not *inside* one heavy call. So detection and restoration run
|
||||
on a ``QThreadPool`` thread via :class:`Job`; results come back to the GUI through
|
||||
queued Qt signals.
|
||||
|
||||
Contract: the job function ``fn(job)`` runs on a worker thread and may ONLY touch
|
||||
plain data + the engines (no Qt widgets). It reports progress with ``job.progress``/
|
||||
``job.tick`` and checks ``job.cancelled`` to stop early. Its return value is delivered
|
||||
on the GUI thread via the ``done`` signal; raising :class:`Cancelled` is reported as a
|
||||
clean cancel (``done`` with ``None``), any other exception via ``failed``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QObject, QRunnable, Signal
|
||||
|
||||
from ..core.restore.base import Cancelled
|
||||
|
||||
|
||||
class _Signals(QObject):
|
||||
progress = Signal(int, int, str) # done, total, message
|
||||
tick = Signal(object) # incremental payload (delivered on GUI thread)
|
||||
done = Signal(object) # final result (None if cancelled)
|
||||
failed = Signal(str) # error message
|
||||
|
||||
|
||||
class Job(QRunnable):
|
||||
"""Runs ``fn(job)`` on a thread pool, marshaling progress/result to the GUI."""
|
||||
|
||||
def __init__(self, fn: Callable[["Job"], Any]) -> None:
|
||||
super().__init__()
|
||||
self.setAutoDelete(False) # the GUI keeps a reference until `done`/`failed`
|
||||
self.signals = _Signals()
|
||||
self._fn = fn
|
||||
self._cancelled = False
|
||||
|
||||
# -- called from the GUI thread --
|
||||
def cancel(self) -> None:
|
||||
self._cancelled = True
|
||||
|
||||
@property
|
||||
def cancelled(self) -> bool:
|
||||
return self._cancelled
|
||||
|
||||
# -- called from the worker thread by `fn` --
|
||||
def progress(self, done: int, total: int, message: str = "") -> None:
|
||||
self.signals.progress.emit(done, total, message)
|
||||
|
||||
def tick(self, payload: Any) -> None:
|
||||
self.signals.tick.emit(payload)
|
||||
|
||||
# -- thread entry point --
|
||||
def run(self) -> None: # noqa: D401 - QRunnable override
|
||||
try:
|
||||
result = self._fn(self)
|
||||
except Cancelled:
|
||||
self.signals.done.emit(None)
|
||||
except Exception as exc: # noqa: BLE001 - surface engine/model errors to the GUI
|
||||
self.signals.failed.emit(str(exc))
|
||||
else:
|
||||
self.signals.done.emit(result)
|
||||
Reference in New Issue
Block a user