mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 02:39:28 +00:00
Add support for timers and run/quit_event_loop
This commit is contained in:
parent
ebaecbcb7e
commit
33a1d07226
5 changed files with 148 additions and 1 deletions
|
@ -24,8 +24,9 @@ i-slint-backend-testing = { version = "=1.4.0", path="../../internal/backends/te
|
||||||
i-slint-renderer-skia = { version = "=1.4.0", path="../../internal/renderers/skia", optional = true, features = ["x11", "wayland"] }
|
i-slint-renderer-skia = { version = "=1.4.0", path="../../internal/renderers/skia", optional = true, features = ["x11", "wayland"] }
|
||||||
i-slint-core = { version = "=1.4.0", path="../../internal/core", features = ["ffi"] }
|
i-slint-core = { version = "=1.4.0", path="../../internal/core", features = ["ffi"] }
|
||||||
slint-interpreter = { workspace = true, features = ["default", "display-diagnostics", "internal"] }
|
slint-interpreter = { workspace = true, features = ["default", "display-diagnostics", "internal"] }
|
||||||
pyo3 = { version = "0.20.0", features = ["extension-module", "indexmap"] }
|
pyo3 = { version = "0.20.0", features = ["extension-module", "indexmap", "chrono"] }
|
||||||
indexmap = { version = "2.1.0" }
|
indexmap = { version = "2.1.0" }
|
||||||
|
chrono = "0.4"
|
||||||
spin_on = "0.1"
|
spin_on = "0.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -45,6 +45,20 @@ impl From<slint_interpreter::PlatformError> for PyPlatformError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PyEventLoopError(pub slint_interpreter::EventLoopError);
|
||||||
|
|
||||||
|
impl From<PyEventLoopError> for PyErr {
|
||||||
|
fn from(err: PyEventLoopError) -> Self {
|
||||||
|
pyo3::exceptions::PyRuntimeError::new_err(err.0.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<slint_interpreter::EventLoopError> for PyEventLoopError {
|
||||||
|
fn from(err: slint_interpreter::EventLoopError) -> Self {
|
||||||
|
Self(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PyInvokeError(pub slint_interpreter::InvokeError);
|
pub struct PyInvokeError(pub slint_interpreter::InvokeError);
|
||||||
|
|
||||||
impl From<PyInvokeError> for PyErr {
|
impl From<PyInvokeError> for PyErr {
|
||||||
|
|
|
@ -4,16 +4,37 @@
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
use interpreter::{ComponentCompiler, PyDiagnostic, PyDiagnosticLevel, PyValueType};
|
use interpreter::{ComponentCompiler, PyDiagnostic, PyDiagnosticLevel, PyValueType};
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod timer;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn run_event_loop() -> Result<(), errors::PyPlatformError> {
|
||||||
|
slint_interpreter::run_event_loop().map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyfunction]
|
||||||
|
fn quit_event_loop() -> Result<(), errors::PyEventLoopError> {
|
||||||
|
slint_interpreter::quit_event_loop().map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
fn slint(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
fn slint(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
|
i_slint_backend_selector::with_platform(|_b| {
|
||||||
|
// Nothing to do, just make sure a backend was created
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|e| errors::PyPlatformError(e))?;
|
||||||
|
|
||||||
m.add_class::<ComponentCompiler>()?;
|
m.add_class::<ComponentCompiler>()?;
|
||||||
m.add_class::<PyValueType>()?;
|
m.add_class::<PyValueType>()?;
|
||||||
m.add_class::<PyDiagnosticLevel>()?;
|
m.add_class::<PyDiagnosticLevel>()?;
|
||||||
m.add_class::<PyDiagnostic>()?;
|
m.add_class::<PyDiagnostic>()?;
|
||||||
|
m.add_class::<timer::PyTimerMode>()?;
|
||||||
|
m.add_class::<timer::PyTimer>()?;
|
||||||
|
m.add_function(wrap_pyfunction!(run_event_loop, m)?)?;
|
||||||
|
m.add_function(wrap_pyfunction!(quit_event_loop, m)?)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
26
api/python/tests/test_timers.py
Normal file
26
api/python/tests/test_timers.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import slint
|
||||||
|
from slint import ValueType
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
def test_timer():
|
||||||
|
global counter
|
||||||
|
counter = 0
|
||||||
|
def quit_after_two_invocations():
|
||||||
|
global counter
|
||||||
|
counter = counter + 1
|
||||||
|
if counter >= 2:
|
||||||
|
slint.quit_event_loop()
|
||||||
|
|
||||||
|
test_timer = slint.Timer()
|
||||||
|
test_timer.start(slint.TimerMode.Repeated, timedelta(milliseconds=100), quit_after_two_invocations)
|
||||||
|
slint.run_event_loop()
|
||||||
|
test_timer.stop()
|
||||||
|
assert(counter == 2)
|
||||||
|
|
||||||
|
def test_single_shot():
|
||||||
|
slint.Timer.single_shot(timedelta(milliseconds=100), slint.quit_event_loop)
|
||||||
|
slint.run_event_loop()
|
85
api/python/timer.rs
Normal file
85
api/python/timer.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[pyclass(name = "TimerMode")]
|
||||||
|
pub enum PyTimerMode {
|
||||||
|
/// A SingleShot timer is fired only once.
|
||||||
|
SingleShot,
|
||||||
|
/// A Repeated timer is fired repeatedly until it is stopped or dropped.
|
||||||
|
Repeated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PyTimerMode> for i_slint_core::timers::TimerMode {
|
||||||
|
fn from(value: PyTimerMode) -> Self {
|
||||||
|
match value {
|
||||||
|
PyTimerMode::SingleShot => i_slint_core::timers::TimerMode::SingleShot,
|
||||||
|
PyTimerMode::Repeated => i_slint_core::timers::TimerMode::Repeated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass(name = "Timer")]
|
||||||
|
pub struct PyTimer {
|
||||||
|
timer: i_slint_core::timers::Timer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyTimer {
|
||||||
|
#[new]
|
||||||
|
fn py_new() -> Self {
|
||||||
|
PyTimer { timer: Default::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(
|
||||||
|
&self,
|
||||||
|
mode: PyTimerMode,
|
||||||
|
interval: chrono::Duration,
|
||||||
|
callback: PyObject,
|
||||||
|
) -> PyResult<()> {
|
||||||
|
let interval = interval
|
||||||
|
.to_std()
|
||||||
|
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
|
||||||
|
self.timer.start(mode.into(), interval, move || {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
callback.call0(py).expect("unexpected failure running python timer callback");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[staticmethod]
|
||||||
|
fn single_shot(duration: chrono::Duration, callback: PyObject) -> PyResult<()> {
|
||||||
|
let duration = duration
|
||||||
|
.to_std()
|
||||||
|
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
|
||||||
|
i_slint_core::timers::Timer::single_shot(duration, move || {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
callback.call0(py).expect("unexpected failure running python timer callback");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&self) {
|
||||||
|
self.timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restart(&self) {
|
||||||
|
self.timer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn running(&self) -> bool {
|
||||||
|
self.timer.running()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_interval(&self, interval: chrono::Duration) -> PyResult<()> {
|
||||||
|
let interval = interval
|
||||||
|
.to_std()
|
||||||
|
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
|
||||||
|
self.timer.set_interval(interval);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue