1d0cce402e
For any name not found in rooms data, rely on new nio.HttpClient.get_displayname() function to get and cache it, e.g. for our own name if no room is joined and past events from users who left the room. @futurize now returns PyQtFuture objects, wrapper for the concurrent.futures.Future objects that can be used from QML, to ensure name retrieval does not block the GUI.
80 lines
2.0 KiB
Python
80 lines
2.0 KiB
Python
# Copyright 2019 miruka
|
|
# This file is part of harmonyqml, licensed under GPLv3.
|
|
|
|
import functools
|
|
import logging
|
|
import sys
|
|
import traceback
|
|
from concurrent.futures import Future
|
|
from threading import currentThread
|
|
from typing import Callable, Optional, Union
|
|
|
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
|
|
|
|
|
class PyQtFuture(QObject):
|
|
gotResult = pyqtSignal()
|
|
|
|
def __init__(self, future: Future, parent: QObject) -> None:
|
|
super().__init__(parent)
|
|
self.future = future
|
|
self._result = None
|
|
|
|
self.future.add_done_callback(lambda _: self.gotResult.emit())
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
return "%s(%s)" % (type(self).__name__, repr(self.future))
|
|
|
|
|
|
@pyqtSlot()
|
|
def cancel(self):
|
|
self.future.cancel()
|
|
|
|
|
|
@pyqtProperty(bool)
|
|
def cancelled(self):
|
|
return self.future.cancelled()
|
|
|
|
|
|
@pyqtProperty(bool)
|
|
def running(self):
|
|
return self.future.running()
|
|
|
|
|
|
@pyqtProperty(bool)
|
|
def done(self):
|
|
return self.future.done()
|
|
|
|
|
|
@pyqtSlot(result="QVariant")
|
|
@pyqtSlot(int, result="QVariant")
|
|
@pyqtSlot(float, result="QVariant")
|
|
def result(self, timeout: Optional[Union[int, float]] = None):
|
|
return self.future.result(timeout)
|
|
|
|
|
|
@pyqtProperty("QVariant", notify=gotResult)
|
|
def value(self):
|
|
return self.future.result() if self.done else None
|
|
|
|
|
|
def add_done_callback(self, fn: Callable[[Future], None]) -> None:
|
|
self.future.add_done_callback(fn)
|
|
|
|
|
|
def futurize(func: Callable) -> Callable:
|
|
@functools.wraps(func)
|
|
def wrapper(self, *args, **kwargs) -> PyQtFuture:
|
|
def run_and_catch_errs():
|
|
# Without this, exceptions are silently ignored
|
|
try:
|
|
return func(self, *args, **kwargs)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
logging.error("Exiting %s due to exception.", currentThread())
|
|
sys.exit(1)
|
|
|
|
return PyQtFuture(self.pool.submit(run_and_catch_errs), self)
|
|
return wrapper
|