Skip to main content

qobject

Attribute Macro qobject 

Source
#[qobject]
Expand description

Annotate an impl or mod block to make its struct accessible from QML.

The macro implements a range of traits that enable bridging from Rust to QML. The mechanism is based on the implementation of various traits with some code generated at macro expansion time. You should not implement these traits yourself. As a user, you should only interact with:

This macro makes it possible to declare the following items within the impl block:

  • signals with the qsignal attribute macro
  • invokable functions with the qslot attribute macro
  • struct properties with the qproperty macro

Further, it allows the struct to implement traits to fulfill specific QML purposes. These are called Base traits. The available base traits are:

  • QParserStatus to receive notifications during QML component construction.
  • QListModel to make a struct accessible by QML ListView, QML Repeater, or similar.
  • QTableModel to make a struct accessible by QML TableView.

Only one of those traits can be implemented at the same time.

§Usage

The qobject macro can be applied to a impl block of the target struct. Only a single impl block can be annotated with this macro and all applications of qsignal, qslot and qproperty have to be limited to this block.

Alternatively, it may be applied to the mod block that contains the struct definition and its associated impl blocks.

In order to communicate with QML, the macro creates bridging objects that are attached to the respective structs. Therefore, objects created with qobject should be created with default_with_attached_qobject or expanded with attach_qobject. This is not necessary if the struct is instantiated in QML.

When register is called, the macro creates a QML module whose name matches your Cargo package name. So for a Cargo.toml with

[package]
name = "hello_world"

[dependencies]
qtbridge

the QML file has to contain

import hello_world

§Requirements

A struct using qobject must implement the Default trait. The static function register has to be called at the start of the main function to make this struct instantiable from QML. The macro implements Drop to call detach_qobject, cleaning up the QML parts of the object. You can implement Drop yourself when using the NoDrop option (see below). In that case, detach_qobject has to be called manually.

§Parameters

Parameters to adjust the macro behavior are passed as comma-separated keywords or keyword-value pairs.

Base = BaseTrait

Set the base trait. Must be one of the special_traits and must be implemented for the corresponding struct. Only one base trait can be set per type.

ConvertToCamelCase

Rust uses snake_case for function names, while in QML camelCase is more common. Use this option to convert function names to camelCase when exposed to QML.

NoQmlElement

Do not implement QmlRegister. QmlRegister registers the struct in the QML type system, allowing you to instantiate this type in QML. The NoQmlElement option can be useful to turn off instantiatability within QML or to provide a manual implementation of this trait with better control over naming and versioning.

Singleton

Implement QmlRegister as a singleton. A singleton is accessed from QML as a single shared instance of the type, using the type name as identifier. This is useful for application-wide data, global settings, or service objects.

NoDrop

Do not implement Drop in the macro. This option has to be set when a custom Drop implementation is required. The function detach_qobject has to be called manually to avoid memory leaks.

LinkMe

This option calls register at application without any additional code. The crate Linkme is used for this purpose and needs to be added to Cargo.toml.

§Example

use qtbridge::{QApp, qobject};

#[derive(Default)]
pub struct Counter {
   value: i32,
}

#[qobject(Singleton)]
impl Counter {
    qproperty!("value", Member = value, Notify = value_changed);

    #[qsignal]
    fn value_changed(&mut self);

    #[qslot]
    fn change_value(&mut self, inc: bool) {
        self.value = match inc {
            true => self.value.saturating_add(1),
            false => self.value.saturating_sub(1),
        };
        self.value_changed();
    }
}

const QML_CODE: &str =
r#"
    import QtQuick
    import QtQuick.Controls
    import QtQuick.Layouts
    import qtbridge // must match your cargo package name

    ApplicationWindow {
        visible: true
        title: qsTr("Counter QML app")
        RowLayout {
            anchors.centerIn: parent
            Button {
                text: "-"
                onClicked: Counter.changeValue(false)
            }
            Button {
                text: "+"
                onClicked: Counter.changeValue(true)
            }
        }
    }
"#;

fn main() {
    QApp::new()
        .register::<Counter>()
        .load_qml(QML_CODE.as_bytes())
        .run();
}