mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
add helper for background execution
This commit is contained in:
parent
3287e8c057
commit
a47db0d609
2 changed files with 79 additions and 0 deletions
|
@ -34,6 +34,7 @@ from aqt import gui_hooks
|
|||
from aqt.profiles import ProfileManager as ProfileManagerType
|
||||
from aqt.qt import *
|
||||
from aqt.qt import sip
|
||||
from aqt.taskman import TaskManager
|
||||
from aqt.utils import (
|
||||
askUser,
|
||||
checkInvalidFilename,
|
||||
|
@ -67,6 +68,7 @@ class AnkiQt(QMainWindow):
|
|||
self.state = "startup"
|
||||
self.opts = opts
|
||||
self.col: Optional[_Collection] = None
|
||||
self.taskman = TaskManager()
|
||||
aqt.mw = self
|
||||
self.app = app
|
||||
self.pm = profileManager
|
||||
|
|
77
qt/aqt/taskman.py
Normal file
77
qt/aqt/taskman.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
"""
|
||||
Helper for running tasks on background threads.
|
||||
"""
|
||||
|
||||
from concurrent.futures import Future
|
||||
from concurrent.futures.thread import ThreadPoolExecutor
|
||||
from dataclasses import dataclass
|
||||
from threading import Lock
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
|
||||
|
||||
@dataclass
|
||||
class PendingDoneCallback:
|
||||
callback: Callable[[Any], None]
|
||||
future: Future
|
||||
|
||||
|
||||
class TaskManager(QObject):
|
||||
_results_available = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
QObject.__init__(self)
|
||||
|
||||
self._executor = ThreadPoolExecutor()
|
||||
|
||||
self._pending_callbacks: List[PendingDoneCallback] = []
|
||||
|
||||
self._results_lock = Lock()
|
||||
self._results_available.connect(self._drain_results) # type: ignore
|
||||
|
||||
def run(
|
||||
self,
|
||||
task: Callable,
|
||||
on_done: Optional[Callable],
|
||||
args: Optional[Dict[str, Any]] = None,
|
||||
) -> Future:
|
||||
"""Run task on a background thread, calling on_done on the main thread if provided.
|
||||
|
||||
Args if provided will be passed on as keyword arguments to the task callable."""
|
||||
if args is None:
|
||||
args = {}
|
||||
|
||||
fut = self._executor.submit(task, **args)
|
||||
|
||||
if on_done is not None:
|
||||
|
||||
def done_closure(completed_future: Future) -> None:
|
||||
self._handle_done_callback(completed_future, on_done)
|
||||
|
||||
fut.add_done_callback(done_closure)
|
||||
|
||||
return fut
|
||||
|
||||
def _handle_done_callback(self, future: Future, callback: Callable) -> None:
|
||||
"""When future completes, schedule its callback to run on the main thread."""
|
||||
# add result to the queue
|
||||
with self._results_lock:
|
||||
self._pending_callbacks.append(
|
||||
PendingDoneCallback(callback=callback, future=future)
|
||||
)
|
||||
|
||||
# and tell the main thread to flush the queue
|
||||
self._results_available.emit() # type: ignore
|
||||
|
||||
def _drain_results(self):
|
||||
"""Fires pending callbacks in the main thread."""
|
||||
with self._results_lock:
|
||||
results = self._pending_callbacks
|
||||
self._pending_callbacks = []
|
||||
|
||||
for result in results:
|
||||
result.callback(result.future)
|
Loading…
Reference in a new issue