Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[help needed] using rust-based enums as qml properties #264

Open
xzz53 opened this issue Mar 17, 2022 · 2 comments
Open

[help needed] using rust-based enums as qml properties #264

xzz53 opened this issue Mar 17, 2022 · 2 comments

Comments

@xzz53
Copy link

xzz53 commented Mar 17, 2022

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
@andrew-otiv
Copy link

andrew-otiv commented Aug 22, 2023

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.

@andrew-otiv
Copy link

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,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants