mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-20 03:50:00 +00:00
Merge branch 'master' of github.com:slint-ui/slint
This commit is contained in:
commit
2b129b87c4
9 changed files with 118 additions and 30 deletions
|
|
@ -229,7 +229,7 @@ The types used for properties in the Slint Language each translate to specific t
|
||||||
| `physical_length` | `float` | |
|
| `physical_length` | `float` | |
|
||||||
| `duration` | `float` | The number of milliseconds |
|
| `duration` | `float` | The number of milliseconds |
|
||||||
| `angle` | `float` | The angle in degrees |
|
| `angle` | `float` | The angle in degrees |
|
||||||
| structure | `dict` | Structures are mapped to Python dictionaries where each structure field is an item. |
|
| structure | `dict`/`Struct` | When reading, structures are mapped to data classes, when writing dicts are also accepted. |
|
||||||
| array | `slint.Model` | |
|
| array | `slint.Model` | |
|
||||||
|
|
||||||
### Arrays and Models
|
### Arrays and Models
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ fn slint(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
m.add_class::<brush::PyColor>()?;
|
m.add_class::<brush::PyColor>()?;
|
||||||
m.add_class::<brush::PyBrush>()?;
|
m.add_class::<brush::PyBrush>()?;
|
||||||
m.add_class::<models::PyModelBase>()?;
|
m.add_class::<models::PyModelBase>()?;
|
||||||
|
m.add_class::<value::PyStruct>()?;
|
||||||
m.add_function(wrap_pyfunction!(run_event_loop, m)?)?;
|
m.add_function(wrap_pyfunction!(run_event_loop, m)?)?;
|
||||||
m.add_function(wrap_pyfunction!(quit_event_loop, m)?)?;
|
m.add_function(wrap_pyfunction!(quit_event_loop, m)?)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,11 +178,15 @@ def _build_class(compdef):
|
||||||
for global_name in compdef.globals:
|
for global_name in compdef.globals:
|
||||||
global_class = _build_global_class(compdef, global_name)
|
global_class = _build_global_class(compdef, global_name)
|
||||||
|
|
||||||
def global_getter(self):
|
def mk_global(global_class):
|
||||||
wrapper = global_class()
|
def global_getter(self):
|
||||||
setattr(wrapper, "__instance__", self.__instance__)
|
wrapper = global_class()
|
||||||
return wrapper
|
setattr(wrapper, "__instance__", self.__instance__)
|
||||||
properties_and_callbacks[global_name] = property(global_getter)
|
return wrapper
|
||||||
|
|
||||||
|
return property(global_getter)
|
||||||
|
|
||||||
|
properties_and_callbacks[global_name] = mk_global(global_class)
|
||||||
|
|
||||||
return type("SlintClassWrapper", (Component,), properties_and_callbacks)
|
return type("SlintClassWrapper", (Component,), properties_and_callbacks)
|
||||||
|
|
||||||
|
|
@ -277,3 +281,4 @@ ListModel = models.ListModel
|
||||||
Model = models.Model
|
Model = models.Model
|
||||||
Timer = native.Timer
|
Timer = native.Timer
|
||||||
TimerMode = native.TimerMode
|
TimerMode = native.TimerMode
|
||||||
|
Struct = native.PyStruct
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ def test_property_access():
|
||||||
export struct MyStruct {
|
export struct MyStruct {
|
||||||
title: string,
|
title: string,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
|
dash-prop: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
export component Test {
|
export component Test {
|
||||||
|
|
@ -36,6 +37,7 @@ def test_property_access():
|
||||||
in property <MyStruct> structprop: {
|
in property <MyStruct> structprop: {
|
||||||
title: "builtin",
|
title: "builtin",
|
||||||
finished: true,
|
finished: true,
|
||||||
|
dash-prop: true,
|
||||||
};
|
};
|
||||||
in property <image> imageprop: @image-url("../../../examples/printerdemo/ui/images/cat.jpg");
|
in property <image> imageprop: @image-url("../../../examples/printerdemo/ui/images/cat.jpg");
|
||||||
|
|
||||||
|
|
@ -75,11 +77,16 @@ def test_property_access():
|
||||||
instance.set_property("boolprop", 0)
|
instance.set_property("boolprop", 0)
|
||||||
|
|
||||||
structval = instance.get_property("structprop")
|
structval = instance.get_property("structprop")
|
||||||
assert isinstance(structval, dict)
|
assert isinstance(structval, native.PyStruct)
|
||||||
assert structval == {'title': 'builtin', 'finished': True}
|
assert structval.title == "builtin"
|
||||||
instance.set_property("structprop", {'title': 'new', 'finished': False})
|
assert structval.finished == True
|
||||||
assert instance.get_property("structprop") == {
|
assert structval.dash_prop == True
|
||||||
'title': 'new', 'finished': False}
|
instance.set_property(
|
||||||
|
"structprop", {'title': 'new', 'finished': False, 'dash_prop': False})
|
||||||
|
structval = instance.get_property("structprop")
|
||||||
|
assert structval.title == "new"
|
||||||
|
assert structval.finished == False
|
||||||
|
assert structval.dash_prop == False
|
||||||
|
|
||||||
imageval = instance.get_property("imageprop")
|
imageval = instance.get_property("imageprop")
|
||||||
assert imageval.width == 320
|
assert imageval.width == 320
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ def test_load_file_wrapper():
|
||||||
|
|
||||||
assert instance.MyGlobal.minus_one(100) == 99
|
assert instance.MyGlobal.minus_one(100) == 99
|
||||||
|
|
||||||
|
assert instance.SecondGlobal.second == "second"
|
||||||
|
|
||||||
del instance
|
del instance
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ export global MyGlobal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export global SecondGlobal {
|
||||||
|
out property <string> second: "second";
|
||||||
|
}
|
||||||
|
|
||||||
export component App inherits Window {
|
export component App inherits Window {
|
||||||
in-out property <string> hello: "World";
|
in-out property <string> hello: "World";
|
||||||
callback say-hello(string) -> string;
|
callback say-hello(string) -> string;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{IntoPyDict, PyDict};
|
use pyo3::types::PyDict;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct PyValue(pub slint_interpreter::Value);
|
pub struct PyValue(pub slint_interpreter::Value);
|
||||||
struct PyValueRef<'a>(&'a slint_interpreter::Value);
|
struct PyValueRef<'a>(&'a slint_interpreter::Value);
|
||||||
|
|
@ -41,11 +43,9 @@ impl<'a> ToPyObject for PyValueRef<'a> {
|
||||||
crate::models::PyModelShared::rust_into_js_model(model)
|
crate::models::PyModelShared::rust_into_js_model(model)
|
||||||
.unwrap_or_else(|| crate::models::ReadOnlyRustModel::from(model).into_py(py))
|
.unwrap_or_else(|| crate::models::ReadOnlyRustModel::from(model).into_py(py))
|
||||||
}
|
}
|
||||||
slint_interpreter::Value::Struct(structval) => structval
|
slint_interpreter::Value::Struct(structval) => {
|
||||||
.iter()
|
PyStruct { data: structval.clone() }.into_py(py)
|
||||||
.map(|(name, val)| (name.to_string().into_py(py), PyValueRef(val).into_py(py)))
|
}
|
||||||
.into_py_dict_bound(py)
|
|
||||||
.into_py(py),
|
|
||||||
slint_interpreter::Value::Brush(brush) => {
|
slint_interpreter::Value::Brush(brush) => {
|
||||||
crate::brush::PyBrush::from(brush.clone()).into_py(py)
|
crate::brush::PyBrush::from(brush.clone()).into_py(py)
|
||||||
}
|
}
|
||||||
|
|
@ -90,13 +90,18 @@ impl FromPyObject<'_> for PyValue {
|
||||||
ob.extract::<PyRef<'_, crate::models::ReadOnlyRustModel>>()
|
ob.extract::<PyRef<'_, crate::models::ReadOnlyRustModel>>()
|
||||||
.map(|rustmodel| slint_interpreter::Value::Model(rustmodel.0.clone()))
|
.map(|rustmodel| slint_interpreter::Value::Model(rustmodel.0.clone()))
|
||||||
})
|
})
|
||||||
|
.or_else(|_| {
|
||||||
|
ob.extract::<PyRef<'_, PyStruct>>().and_then(|pystruct| {
|
||||||
|
Ok(slint_interpreter::Value::Struct(pystruct.data.clone()))
|
||||||
|
})
|
||||||
|
})
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
ob.extract::<&PyDict>().and_then(|dict| {
|
ob.extract::<&PyDict>().and_then(|dict| {
|
||||||
let dict_items: Result<Vec<(String, slint_interpreter::Value)>, PyErr> = dict
|
let dict_items: Result<Vec<(String, slint_interpreter::Value)>, PyErr> = dict
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, pyval)| {
|
.map(|(name, pyval)| {
|
||||||
let name = name.extract::<&str>()?.to_string();
|
let name = name.extract::<&str>()?.to_string();
|
||||||
let slintval: PyValue = pyval.extract()?;
|
let slintval = PyValue::extract(pyval)?;
|
||||||
Ok((name, slintval.0))
|
Ok((name, slintval.0))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<(_, _)>, PyErr>>();
|
.collect::<Result<Vec<(_, _)>, PyErr>>();
|
||||||
|
|
@ -114,3 +119,64 @@ impl From<slint_interpreter::Value> for PyValue {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pyclass(subclass, unsendable)]
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct PyStruct {
|
||||||
|
data: slint_interpreter::Struct,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyStruct {
|
||||||
|
#[new]
|
||||||
|
fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __getattr__(&self, key: &str) -> PyResult<PyValue> {
|
||||||
|
self.data.get_field(key).map_or_else(
|
||||||
|
|| {
|
||||||
|
Err(pyo3::exceptions::PyAttributeError::new_err(format!(
|
||||||
|
"Python: No such field {key} on PyStruct"
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
|value| Ok(value.clone().into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn __setattr__(&mut self, py: Python<'_>, key: String, value: PyObject) -> PyResult<()> {
|
||||||
|
let pv: PyValue = value.extract(py)?;
|
||||||
|
self.data.set_field(key, pv.0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __iter__(slf: PyRef<'_, Self>) -> PyStructFieldIterator {
|
||||||
|
PyStructFieldIterator {
|
||||||
|
inner: slf
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
|
.map(|(name, val)| (name.to_string(), val.clone()))
|
||||||
|
.collect::<HashMap<_, _>>()
|
||||||
|
.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __copy__(&self) -> Self {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass(unsendable)]
|
||||||
|
struct PyStructFieldIterator {
|
||||||
|
inner: std::collections::hash_map::IntoIter<String, slint_interpreter::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyStructFieldIterator {
|
||||||
|
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<(String, PyValue)> {
|
||||||
|
slf.inner.next().map(|(name, val)| (name, PyValue(val)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from datetime import timedelta, datetime
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import itertools
|
import itertools
|
||||||
|
import copy
|
||||||
import slint
|
import slint
|
||||||
from slint import Color, ListModel, Timer, TimerMode
|
from slint import Color, ListModel, Timer, TimerMode
|
||||||
|
|
||||||
|
|
@ -14,31 +15,32 @@ class MainWindow(slint.loader.memory.MainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
initial_tiles = self.memory_tiles
|
initial_tiles = self.memory_tiles
|
||||||
tiles = ListModel(itertools.chain(initial_tiles, initial_tiles))
|
tiles = ListModel(itertools.chain(
|
||||||
|
map(copy.copy, initial_tiles), map(copy.copy, initial_tiles)))
|
||||||
random.shuffle(tiles)
|
random.shuffle(tiles)
|
||||||
self.memory_tiles = tiles
|
self.memory_tiles = tiles
|
||||||
|
|
||||||
@slint.callback
|
@slint.callback
|
||||||
def check_if_pair_solved(self):
|
def check_if_pair_solved(self):
|
||||||
flipped_tiles = [(index, tile) for index, tile in enumerate(
|
flipped_tiles = [(index, copy.copy(tile)) for index, tile in enumerate(
|
||||||
self.memory_tiles) if tile["image-visible"] and not tile["solved"]]
|
self.memory_tiles) if tile.image_visible and not tile.solved]
|
||||||
if len(flipped_tiles) == 2:
|
if len(flipped_tiles) == 2:
|
||||||
tile1_index, tile1 = flipped_tiles[0]
|
tile1_index, tile1 = flipped_tiles[0]
|
||||||
tile2_index, tile2 = flipped_tiles[1]
|
tile2_index, tile2 = flipped_tiles[1]
|
||||||
is_pair_solved = tile1["image"].path == tile2["image"].path
|
is_pair_solved = tile1.image.path == tile2.image.path
|
||||||
if is_pair_solved:
|
if is_pair_solved:
|
||||||
tile1["solved"] = True
|
tile1.solved = True
|
||||||
self.memory_tiles[tile1_index] = tile1
|
self.memory_tiles[tile1_index] = tile1
|
||||||
tile2["solved"] = True
|
tile2.solved = True
|
||||||
self.memory_tiles[tile2_index] = tile2
|
self.memory_tiles[tile2_index] = tile2
|
||||||
else:
|
else:
|
||||||
self.disable_tiles = True
|
self.disable_tiles = True
|
||||||
|
|
||||||
def reenable_tiles():
|
def reenable_tiles():
|
||||||
self.disable_tiles = False
|
self.disable_tiles = False
|
||||||
tile1["image-visible"] = False
|
tile1.image_visible = False
|
||||||
self.memory_tiles[tile1_index] = tile1
|
self.memory_tiles[tile1_index] = tile1
|
||||||
tile2["image-visible"] = False
|
tile2.image_visible = False
|
||||||
self.memory_tiles[tile2_index] = tile2
|
self.memory_tiles[tile2_index] = tile2
|
||||||
|
|
||||||
Timer.single_shot(timedelta(seconds=1), reenable_tiles)
|
Timer.single_shot(timedelta(seconds=1), reenable_tiles)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from slint import Color, ListModel, Timer, TimerMode
|
||||||
import slint
|
import slint
|
||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
import os
|
import os
|
||||||
|
import copy
|
||||||
import sys
|
import sys
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
|
|
@ -48,13 +49,13 @@ class MainWindow(slint.loader.ui.printerdemo.MainWindow):
|
||||||
def update_jobs(self):
|
def update_jobs(self):
|
||||||
if len(self.printer_queue) <= 0:
|
if len(self.printer_queue) <= 0:
|
||||||
return
|
return
|
||||||
top_item = self.printer_queue[0]
|
top_item = copy.copy(self.printer_queue[0])
|
||||||
top_item["progress"] += 1
|
top_item.progress += 1
|
||||||
if top_item["progress"] >= 100:
|
if top_item.progress >= 100:
|
||||||
del self.printer_queue[0]
|
del self.printer_queue[0]
|
||||||
if len(self.printer_queue) == 0:
|
if len(self.printer_queue) == 0:
|
||||||
return
|
return
|
||||||
top_item = self.printer_queue[0]
|
top_item = copy.copy(self.printer_queue[0])
|
||||||
self.printer_queue[0] = top_item
|
self.printer_queue[0] = top_item
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue