[help needed] using rust-based enums as qml properties
xzz53 opened this issue · 2 comments
Hi,
I'm a new qmetaobject-rs
user, and I'm trying to use QEnum-deriving enum as a property type. My code compiles, but at runtime qml sees the property type Object
instead of int
. Any hints are welcome. The minimal (broken) example and output are below.
use cstr::cstr;
use qmetaobject::{prelude::*, QMetaType};
#[derive(Copy, Clone, Debug, Eq, PartialEq, QEnum)]
#[repr(C)]
pub enum OpMode {
Generator = 0,
Wav,
I2C,
Spi,
Uart,
}
impl Default for OpMode {
fn default() -> Self {
OpMode::Generator
}
}
impl QMetaType for OpMode {}
#[allow(non_snake_case)]
#[derive(Default, QObject)]
struct Backend {
base: qt_base_class!(trait QObject),
opMode: qt_property!(OpMode; NOTIFY opmode_changed WRITE set_opmode READ get_opmode),
opmode_changed: qt_signal!(),
}
impl Backend {
fn get_opmode(&self) -> OpMode {
println!("get_opmode()");
self.opMode
}
fn set_opmode(&mut self, mode: OpMode) {
println!("set_opmode(): old={:?}, new={:?}", self.opMode, mode);
self.opMode = mode;
}
}
fn main() {
qml_register_enum::<OpMode>(cstr!("Backend"), 1, 0, cstr!("OpMode"));
qml_register_type::<Backend>(cstr!("Backend"), 1, 0, cstr!("Backend"));
let backend = QObjectBox::new(Backend::default());
let mut engine = QmlEngine::new();
engine.set_object_property(QString::from("backend"), backend.pinned());
engine.load_data(
r#"import QtQuick 2.15
import QtQuick.Window 2.15
import Backend 1.0
Window {
id: window
property int testprop
visible: true
width: 450
height: 580
Component.onCompleted: {
console.log(OpMode.Wav)
console.log(backend.OpMode, typeof(backend.OpMode), JSON.stringify(backend.OpMode))
window.testprop = backend.OpMode
console.log(window.testprop)
}
}
"#
.into(),
);
engine.exec();
}
qml: 1
qml: [object Object] object {}
<Unknown File>:17: Error: Cannot assign QObject* to int
Thanks for sharing your code; it helped me progress on a similar enum vs. int related typing issue while trying to set an enum property. When I try to set my property with "my_property = MyEnum.MyVariant" in a callback, I hit ":57: Error: Cannot assign int to TypeId{t:11890792827600742819}"
You ~did declare "property int testprop"... did you already try "property var testprop" or "property backend.OpMode"?
I believe this has to do with enums in C not being types; they're "tags" and have type int. So the type of the enum is conflated with the type of the variant, and enums aren't really first class types. I am still looking for a workaround, but the answer might just be to give up on enums in qt and resort to stringly or intly typed code.
I changed my property that was previously my enum to be an i32:
stateMachineQt: qt_property!(i32; READ getState WRITE setState NOTIFY state_changed),
Then I implemented TryFrom for it following:
https://stackoverflow.com/questions/28028854/how-do-i-match-enum-values-with-an-integer
And I use that to validate the int in the setter with something like:
fn setState(&mut self, new_state: i32) {
QtStateMachineStateEnum::try_from(new_state).unwrap();
self.stateMachineQt = new_state;
}
This alone wouldn't provide much type safety, since I could still use the wrong enum in the QML, but I mitigated it using large random numbers for my enum variants so they ~probably won't collide with the variants of any other enums:
#[derive(Copy, Clone, Debug, Eq, PartialEq, QEnum)]
#[repr(C)]
enum QtStateMachineStateEnum {
Disabled = 13739,
Enabled = 23639,
}