Implement cancelling python coros from QML
This was needed to implement the cancel button featue on the login screen
This commit is contained in:
		| @@ -54,7 +54,7 @@ class App: | ||||
|         return asyncio.run_coroutine_threadsafe(coro, self.loop) | ||||
|  | ||||
|  | ||||
|     def _call_coro(self, coro: Coroutine, uuid: str) -> None: | ||||
|     def _call_coro(self, coro: Coroutine, uuid: str) -> Future: | ||||
|         def on_done(future: Future) -> None: | ||||
|             result = exception = trace = None | ||||
|  | ||||
| @@ -66,21 +66,23 @@ class App: | ||||
|  | ||||
|             CoroutineDone(uuid, result, exception, trace) | ||||
|  | ||||
|         self.run_in_loop(coro).add_done_callback(on_done) | ||||
|         future = self.run_in_loop(coro) | ||||
|         future.add_done_callback(on_done) | ||||
|         return future | ||||
|  | ||||
|  | ||||
|     def call_backend_coro(self, name: str, uuid: str, args: Sequence[str] = (), | ||||
|                          ) -> None: | ||||
|         self._call_coro(attrgetter(name)(self.backend)(*args), uuid) | ||||
|                          ) -> Future: | ||||
|         return self._call_coro(attrgetter(name)(self.backend)(*args), uuid) | ||||
|  | ||||
|  | ||||
|     def call_client_coro(self, | ||||
|                          account_id: str, | ||||
|                          name:       str, | ||||
|                          uuid:       str, | ||||
|                          args:       Sequence[str] = ()) -> None: | ||||
|                          args:       Sequence[str] = ()) -> Future: | ||||
|         client = self.backend.clients[account_id] | ||||
|         self._call_coro(attrgetter(name)(client)(*args), uuid) | ||||
|         return self._call_coro(attrgetter(name)(client)(*args), uuid) | ||||
|  | ||||
|  | ||||
|     def pdb(self, additional_data: Sequence = ()) -> None: | ||||
|   | ||||
| @@ -31,7 +31,7 @@ HBox { | ||||
|  | ||||
|             signInTimeout.restart() | ||||
|  | ||||
|             py.callCoro("login_client", args, userId => { | ||||
|             loginFuture = py.callCoro("login_client", args, userId => { | ||||
|                 signInTimeout.stop() | ||||
|                 errorMessage.text = "" | ||||
|                 button.loading    = false | ||||
| @@ -48,9 +48,14 @@ HBox { | ||||
|                 ) | ||||
|  | ||||
|             }, type => { | ||||
|                 if (type === "CancelledError") return | ||||
|  | ||||
|                 signInTimeout.stop() | ||||
|  | ||||
|                 if (type === "CancelledError") { | ||||
|                     loginFuture    = null | ||||
|                     button.loading = false | ||||
|                     return | ||||
|                 } | ||||
|  | ||||
|                 let txt = qsTr("Invalid request or login type") | ||||
|  | ||||
|                 if (type === "MatrixForbidden") | ||||
| @@ -64,11 +69,14 @@ HBox { | ||||
|             }) | ||||
|         }, | ||||
|  | ||||
|         cancel: button => {} | ||||
|         cancel: button => { if (loginFuture) loginFuture.cancel() } | ||||
|     }) | ||||
|  | ||||
|  | ||||
|     property var loginFuture: null | ||||
|  | ||||
|     property string signInWith: "username" | ||||
|  | ||||
|     readonly property bool canSignIn: | ||||
|         serverField.text && idField.text && passwordField.text && | ||||
|         ! serverField.error | ||||
|   | ||||
| @@ -1,15 +1,40 @@ | ||||
| import QtQuick 2.12 | ||||
| import QtQuick.Controls 2.12 | ||||
| import io.thp.pyotherside 1.5 | ||||
| import "event_handlers.js" as EventHandlers | ||||
|  | ||||
| Python { | ||||
|     id: py | ||||
|  | ||||
|  | ||||
|     property bool ready: false | ||||
|     property bool startupAnyAccountsSaved: false | ||||
|     property var pendingCoroutines: ({}) | ||||
|  | ||||
|  | ||||
|     function newQmlFuture() { | ||||
|         return { | ||||
|             _pyFuture: null, | ||||
|  | ||||
|             get pyFuture() { return this._pyFuture }, | ||||
|  | ||||
|             set pyFuture(value) { | ||||
|                 this._pyFuture = value | ||||
|                 if (this.cancelPending) this.cancel() | ||||
|             }, | ||||
|  | ||||
|             cancelPending: false, | ||||
|  | ||||
|             cancel: function() { | ||||
|                 if (! this.pyFuture) { | ||||
|                     this.cancelPending = true | ||||
|                     return | ||||
|                 } | ||||
|  | ||||
|                 py.call(py.getattr(this.pyFuture, "cancel")) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function setattr(obj, attr, value, callback=null) { | ||||
|         py.call(py.getattr(obj, "__setattr__"), [attr, value], callback) | ||||
|     } | ||||
| @@ -22,27 +47,43 @@ Python { | ||||
|         let uuid = name + "." + CppUtils.uuid() | ||||
|  | ||||
|         pendingCoroutines[uuid] = {onSuccess, onError} | ||||
|         call("APP.call_backend_coro", [name, uuid, args]) | ||||
|  | ||||
|         let qmlFuture = py.newQmlFuture() | ||||
|  | ||||
|         call("APP.call_backend_coro", [name, uuid, args], pyFuture => { | ||||
|             qmlFuture.pyFuture = pyFuture | ||||
|         }) | ||||
|  | ||||
|         return qmlFuture | ||||
|     } | ||||
|  | ||||
|     function callClientCoro( | ||||
|         accountId, name, args=[], onSuccess=null, onError=null | ||||
|     ) { | ||||
|         let qmlFuture = py.newQmlFuture() | ||||
|  | ||||
|         callCoro("wait_until_client_exists", [accountId], () => { | ||||
|             let uuid = accountId + "." + name + "." + CppUtils.uuid() | ||||
|  | ||||
|             pendingCoroutines[uuid] = {onSuccess, onError} | ||||
|             call("APP.call_client_coro", [accountId, name, uuid, args]) | ||||
|  | ||||
|             let call_args = [accountId, name, uuid, args] | ||||
|  | ||||
|             call("APP.call_client_coro", call_args, pyFuture => { | ||||
|                 qmlFuture.pyFuture = pyFuture | ||||
|             }) | ||||
|         }) | ||||
|  | ||||
|         return qmlFuture | ||||
|     } | ||||
|  | ||||
|     function saveConfig(backend_attribute, data, callback=null) { | ||||
|         if (! py.ready) { return }  // config not loaded yet | ||||
|         callCoro(backend_attribute + ".write", [data], callback) | ||||
|         return callCoro(backend_attribute + ".write", [data], callback) | ||||
|     } | ||||
|  | ||||
|     function loadSettings(callback=null) { | ||||
|         callCoro("load_settings", [], ([settings, uiState, theme]) => { | ||||
|         return callCoro("load_settings", [], ([settings, uiState, theme]) => { | ||||
|             window.settings = settings | ||||
|             window.uiState  = uiState | ||||
|             window.theme    = Qt.createQmlObject(theme, window, "theme") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	