Skip to content

Commit 855cd0d

Browse files
committed
Python: Add support for invoke_from_event_loop
Fixes #4203
1 parent 7ada390 commit 855cd0d

File tree

3 files changed

+47
-0
lines changed

3 files changed

+47
-0
lines changed

api/python/slint/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ fn set_xdg_app_id(app_id: String) -> Result<(), errors::PyPlatformError> {
6363
slint_interpreter::set_xdg_app_id(app_id).map_err(|e| e.into())
6464
}
6565

66+
#[gen_stub_pyfunction]
67+
#[pyfunction]
68+
fn invoke_from_event_loop(callable: Py<PyAny>) -> Result<(), errors::PyEventLoopError> {
69+
slint_interpreter::invoke_from_event_loop(move || {
70+
Python::attach(|py| {
71+
if let Err(err) = callable.call0(py) {
72+
eprintln!("Error invoking python callable from closure invoked via slint::invoke_from_event_loop: {}", err)
73+
}
74+
})
75+
})
76+
.map_err(|e| e.into())
77+
}
78+
6679
use pyo3::prelude::*;
6780

6881
#[pymodule]
@@ -90,6 +103,7 @@ fn slint(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
90103
m.add_function(wrap_pyfunction!(run_event_loop, m)?)?;
91104
m.add_function(wrap_pyfunction!(quit_event_loop, m)?)?;
92105
m.add_function(wrap_pyfunction!(set_xdg_app_id, m)?)?;
106+
m.add_function(wrap_pyfunction!(invoke_from_event_loop, m)?)?;
93107

94108
Ok(())
95109
}

api/python/slint/slint/slint.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class Timer:
143143
def restart(self) -> None: ...
144144

145145
def set_xdg_app_id(app_id: str) -> None: ...
146+
def invoke_from_event_loop(callable: typing.Callable[[], None]) -> None: ...
146147
def run_event_loop() -> None: ...
147148
def quit_event_loop() -> None: ...
148149

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright © SixtyFPS GmbH <[email protected]>
2+
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3+
4+
from slint import slint as native
5+
import threading
6+
from datetime import timedelta
7+
8+
was_here = False
9+
10+
11+
def test_threads() -> None:
12+
global was_here
13+
was_here = False
14+
15+
def invoked_from_event_loop() -> None:
16+
global was_here
17+
was_here = True
18+
native.quit_event_loop()
19+
20+
def quit() -> None:
21+
native.invoke_from_event_loop(invoked_from_event_loop)
22+
23+
thr = threading.Thread(target=quit)
24+
native.Timer.single_shot(timedelta(milliseconds=10), lambda: thr.start())
25+
fallback_timer = native.Timer()
26+
fallback_timer.start(
27+
native.TimerMode.Repeated, timedelta(milliseconds=100), native.quit_event_loop
28+
)
29+
native.run_event_loop()
30+
thr.join()
31+
fallback_timer.stop()
32+
assert was_here

0 commit comments

Comments
 (0)