From cc709f675fc0742df98f32fab61569b66312c762 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 5 Mar 2024 16:02:27 +0100 Subject: [PATCH] Python: Add support for invoke_from_event_loop Fixes #420 --- api/python/lib.rs | 13 +++++++++ .../tests/test_invoke_from_event_loop.py | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 api/python/tests/test_invoke_from_event_loop.py diff --git a/api/python/lib.rs b/api/python/lib.rs index 954602d7d5a..5c9e93b8585 100644 --- a/api/python/lib.rs +++ b/api/python/lib.rs @@ -20,6 +20,18 @@ fn quit_event_loop() -> Result<(), errors::PyEventLoopError> { slint_interpreter::quit_event_loop().map_err(|e| e.into()) } +#[pyfunction] +fn invoke_from_event_loop(callable: PyObject) -> Result<(), errors::PyEventLoopError> { + slint_interpreter::invoke_from_event_loop(move || { + Python::with_gil(|py| { + if let Err(err) = callable.call0(py) { + eprintln!("Error invoking python callable from closure invoked via slint::invoke_from_event_loop: {}", err) + } + }) + }) + .map_err(|e| e.into()) +} + use pyo3::prelude::*; #[pymodule] @@ -42,6 +54,7 @@ fn slint(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_function(wrap_pyfunction!(run_event_loop, m)?)?; m.add_function(wrap_pyfunction!(quit_event_loop, m)?)?; + m.add_function(wrap_pyfunction!(invoke_from_event_loop, m)?)?; Ok(()) } diff --git a/api/python/tests/test_invoke_from_event_loop.py b/api/python/tests/test_invoke_from_event_loop.py new file mode 100644 index 00000000000..7f6aa2869cd --- /dev/null +++ b/api/python/tests/test_invoke_from_event_loop.py @@ -0,0 +1,29 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +from slint import slint as native +import threading +from datetime import timedelta + + +def test_threads(): + global was_here + was_here = False + + def invoked_from_event_loop(): + global was_here + was_here = True + native.quit_event_loop() + + def quit(): + native.invoke_from_event_loop(invoked_from_event_loop) + + thr = threading.Thread(target=quit) + native.Timer.single_shot(timedelta(milliseconds=10), lambda: thr.start()) + fallback_timer = native.Timer() + fallback_timer.start(native.TimerMode.Repeated, timedelta( + milliseconds=100), native.quit_event_loop) + native.run_event_loop() + thr.join() + fallback_timer.stop() + assert was_here == True