worker.py 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. # worker.py
  2. from __future__ import annotations
  3. from typing import Any, Callable
  4. from PyQt6.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
  5. class Worker(QObject):
  6. finished = pyqtSignal(object)
  7. failed = pyqtSignal(str)
  8. def __init__(self, func: Callable[[], Any]) -> None:
  9. super().__init__()
  10. self._func = func
  11. @pyqtSlot()
  12. def run(self) -> None:
  13. try:
  14. result = self._func()
  15. self.finished.emit(result)
  16. except Exception as e:
  17. self.failed.emit(str(e))
  18. def run_in_thread(parent: QObject, func: Callable[[], Any], on_ok, on_err) -> None:
  19. """
  20. Запустить func() в отдельном QThread.
  21. on_ok(result) вызовется в GUI-потоке (через сигнал).
  22. """
  23. thread = QThread(parent)
  24. worker = Worker(func)
  25. worker.moveToThread(thread)
  26. # Keep references alive until thread finishes.
  27. # Without this, local vars may be garbage-collected early and the job never starts.
  28. active = getattr(parent, "_active_threads", None)
  29. if active is None:
  30. active = set()
  31. setattr(parent, "_active_threads", active)
  32. active.add((thread, worker))
  33. thread.started.connect(worker.run)
  34. worker.finished.connect(on_ok)
  35. worker.failed.connect(on_err)
  36. worker.finished.connect(thread.quit)
  37. worker.failed.connect(thread.quit)
  38. worker.finished.connect(worker.deleteLater)
  39. worker.failed.connect(worker.deleteLater)
  40. def _cleanup() -> None:
  41. try:
  42. active.discard((thread, worker))
  43. except Exception:
  44. pass
  45. thread.finished.connect(_cleanup)
  46. thread.finished.connect(thread.deleteLater)
  47. thread.start()