mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-27 20:42:25 +00:00
Add support for mapping image properties
This exposes a slint.Image class, which has a load_from_path class method as well as size/width/height properties. cc #4202
This commit is contained in:
parent
9aa931f1f8
commit
93efd74e24
6 changed files with 97 additions and 3 deletions
|
@ -86,3 +86,17 @@ impl From<slint_interpreter::SetCallbackError> for PySetCallbackError {
|
||||||
Self(err)
|
Self(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PyLoadImageError(pub slint_interpreter::LoadImageError);
|
||||||
|
|
||||||
|
impl From<PyLoadImageError> for PyErr {
|
||||||
|
fn from(err: PyLoadImageError) -> Self {
|
||||||
|
pyo3::exceptions::PyRuntimeError::new_err(err.0.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<slint_interpreter::LoadImageError> for PyLoadImageError {
|
||||||
|
fn from(err: slint_interpreter::LoadImageError) -> Self {
|
||||||
|
Self(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
api/python/image.rs
Normal file
55
api/python/image.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// 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::*;
|
||||||
|
|
||||||
|
#[pyclass(unsendable)]
|
||||||
|
pub struct PyImage {
|
||||||
|
pub image: slint_interpreter::Image,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl PyImage {
|
||||||
|
#[new]
|
||||||
|
fn py_new() -> PyResult<Self> {
|
||||||
|
Ok(Self { image: Default::default() })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn size(&self) -> PyResult<(u32, u32)> {
|
||||||
|
Ok(self.image.size().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn width(&self) -> PyResult<u32> {
|
||||||
|
Ok(self.image.size().width)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn height(&self) -> PyResult<u32> {
|
||||||
|
Ok(self.image.size().height)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[getter]
|
||||||
|
fn path(&self) -> PyResult<Option<&std::path::Path>> {
|
||||||
|
Ok(self.image.path())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[staticmethod]
|
||||||
|
fn load_from_path(path: std::path::PathBuf) -> Result<Self, crate::errors::PyLoadImageError> {
|
||||||
|
let image = slint_interpreter::Image::load_from_path(&path)?;
|
||||||
|
Ok(Self { image })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[staticmethod]
|
||||||
|
fn load_from_svg_data(data: &[u8]) -> Result<Self, crate::errors::PyLoadImageError> {
|
||||||
|
let image = slint_interpreter::Image::load_from_svg_data(data)?;
|
||||||
|
Ok(Self { image })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&slint_interpreter::Image> for PyImage {
|
||||||
|
fn from(image: &slint_interpreter::Image) -> Self {
|
||||||
|
Self { image: image.clone() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
mod image;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
use interpreter::{ComponentCompiler, PyDiagnostic, PyDiagnosticLevel, PyValueType};
|
use interpreter::{ComponentCompiler, PyDiagnostic, PyDiagnosticLevel, PyValueType};
|
||||||
mod errors;
|
mod errors;
|
||||||
|
@ -28,6 +29,7 @@ fn slint(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
.map_err(|e| errors::PyPlatformError(e))?;
|
.map_err(|e| errors::PyPlatformError(e))?;
|
||||||
|
|
||||||
m.add_class::<ComponentCompiler>()?;
|
m.add_class::<ComponentCompiler>()?;
|
||||||
|
m.add_class::<image::PyImage>()?;
|
||||||
m.add_class::<PyValueType>()?;
|
m.add_class::<PyValueType>()?;
|
||||||
m.add_class::<PyDiagnosticLevel>()?;
|
m.add_class::<PyDiagnosticLevel>()?;
|
||||||
m.add_class::<PyDiagnostic>()?;
|
m.add_class::<PyDiagnostic>()?;
|
||||||
|
|
|
@ -8,3 +8,5 @@ def load_file(path):
|
||||||
compdef = compiler.build_from_path(path)
|
compdef = compiler.build_from_path(path)
|
||||||
instance = compdef.create()
|
instance = compdef.create()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
Image = native.PyImage
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from slint import slint as native
|
from slint import slint as native
|
||||||
from slint.slint import ValueType;
|
from slint.slint import ValueType, PyImage;
|
||||||
|
import os
|
||||||
|
|
||||||
def test_property_access():
|
def test_property_access():
|
||||||
compiler = native.ComponentCompiler()
|
compiler = native.ComponentCompiler()
|
||||||
|
@ -32,10 +33,11 @@ def test_property_access():
|
||||||
title: "builtin",
|
title: "builtin",
|
||||||
finished: true,
|
finished: true,
|
||||||
};
|
};
|
||||||
|
in property <image> imageprop: @image-url("../../../examples/printerdemo/ui/images/cat.jpg");
|
||||||
|
|
||||||
callback test-callback();
|
callback test-callback();
|
||||||
}
|
}
|
||||||
""", "")
|
""", os.path.join(os.path.dirname(__file__), "main.slint"))
|
||||||
assert compdef != None
|
assert compdef != None
|
||||||
|
|
||||||
instance = compdef.create()
|
instance = compdef.create()
|
||||||
|
@ -74,6 +76,19 @@ def test_property_access():
|
||||||
instance.set_property("structprop", {'title': 'new', 'finished': False})
|
instance.set_property("structprop", {'title': 'new', 'finished': False})
|
||||||
assert instance.get_property("structprop") == {'title': 'new', 'finished': False}
|
assert instance.get_property("structprop") == {'title': 'new', 'finished': False}
|
||||||
|
|
||||||
|
imageval = instance.get_property("imageprop")
|
||||||
|
assert imageval.width == 320
|
||||||
|
assert imageval.height == 480
|
||||||
|
assert "cat.jpg" in imageval.path
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError, match="The image cannot be loaded"):
|
||||||
|
PyImage.load_from_path("non-existent.png")
|
||||||
|
|
||||||
|
instance.set_property("imageprop", PyImage.load_from_path(os.path.join(os.path.dirname(__file__), "../../../examples/iot-dashboard/images/humidity.png")))
|
||||||
|
imageval = instance.get_property("imageprop")
|
||||||
|
assert imageval.size == (36, 36)
|
||||||
|
assert "humidity.png" in imageval.path
|
||||||
|
|
||||||
with pytest.raises(TypeError, match="'int' object cannot be converted to 'PyString'"):
|
with pytest.raises(TypeError, match="'int' object cannot be converted to 'PyString'"):
|
||||||
instance.set_property("structprop", {42: 'wrong'})
|
instance.set_property("structprop", {42: 'wrong'})
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,9 @@ impl<'a> ToPyObject for PyValueRef<'a> {
|
||||||
slint_interpreter::Value::Number(num) => num.into_py(py),
|
slint_interpreter::Value::Number(num) => num.into_py(py),
|
||||||
slint_interpreter::Value::String(str) => str.into_py(py),
|
slint_interpreter::Value::String(str) => str.into_py(py),
|
||||||
slint_interpreter::Value::Bool(b) => b.into_py(py),
|
slint_interpreter::Value::Bool(b) => b.into_py(py),
|
||||||
slint_interpreter::Value::Image(_) => todo!(),
|
slint_interpreter::Value::Image(image) => {
|
||||||
|
crate::image::PyImage::from(image).into_py(py)
|
||||||
|
}
|
||||||
slint_interpreter::Value::Model(_) => todo!(),
|
slint_interpreter::Value::Model(_) => todo!(),
|
||||||
slint_interpreter::Value::Struct(structval) => structval
|
slint_interpreter::Value::Struct(structval) => structval
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -60,6 +62,10 @@ impl FromPyObject<'_> for PyValue {
|
||||||
ob.extract::<&'_ str>().map(|s| slint_interpreter::Value::String(s.into()))
|
ob.extract::<&'_ str>().map(|s| slint_interpreter::Value::String(s.into()))
|
||||||
})
|
})
|
||||||
.or_else(|_| ob.extract::<f64>().map(|num| slint_interpreter::Value::Number(num)))
|
.or_else(|_| ob.extract::<f64>().map(|num| slint_interpreter::Value::Number(num)))
|
||||||
|
.or_else(|_| {
|
||||||
|
ob.extract::<PyRef<'_, crate::image::PyImage>>()
|
||||||
|
.map(|pyimg| slint_interpreter::Value::Image(pyimg.image.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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue