Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Integrate Qml and Rust by building the QMetaObject at compile time.

License

NotificationsYou must be signed in to change notification settings

woboq/qmetaobject-rs

Repository files navigation

Crates.ioDocumentation

A framework empowering everyone to create Qt/QML applications with Rust.It does so by buildingQMetaObjects at compile time, registering QML types (optionally via exposingQQmlExtensionPlugins) and providing idiomatic wrappers.

Objectives

  • Rust procedural macro (custom derive) to generate aQMetaObject at compile time.
  • Bindings for the main Qt types using thecpp! macro from thecpp crate.
  • Users of this crate should not require to type any line of C++ or use another build system beyond cargo.
  • Performance: Avoid any unnecessary conversion or heap allocation.

Presentation Blog Post:https://woboq.com/blog/qmetaobject-from-rust.html

Overview

use cstr::cstr;use qmetaobject::prelude::*;// The `QObject` custom derive macro allows to expose a class to Qt and QML#[derive(QObject,Default)]structGreeter{// Specify the base class with the qt_base_class macrobase:qt_base_class!(traitQObject),// Declare `name` as a property usable from Qtname:qt_property!(QString;NOTIFY name_changed),// Declare a signalname_changed:qt_signal!(),// And even a slotcompute_greetings:qt_method!(fn compute_greetings(&self, verb:String) ->QString{        format!("{} {}", verb,self.name.to_string()).into()})}fnmain(){// Register the `Greeter` struct to QMLqml_register_type::<Greeter>(cstr!("Greeter"),1,0,cstr!("Greeter"));// Create a QML engine from rustletmut engine =QmlEngine::new();// (Here the QML code is inline, but one can also load from a file)    engine.load_data(r#"        import QtQuick 2.6        import QtQuick.Window 2.0        // Import our Rust classes        import Greeter 1.0        Window {            visible: true            // Instantiate the rust struct            Greeter {                id: greeter;                // Set a property                name: "World"            }            Text {                anchors.centerIn: parent                // Call a method                text: greeter.compute_greetings("hello")            }        }    "#.into());    engine.exec();}

Features

  • Create object inheriting from QObject, QQuickItem, QAbstractListModel, QQmlExtensionPlugin, ...
  • Export Qt properties, signals, methods, ...
  • Also support#[derive(QGadget)] (same as Q_GADGET)
  • Create Qt plugin (see examples/qmlextensionplugins)
  • Partial scene graph support

Requires Qt >= 5.8

Cargo features

Cargo provides a way to enable (or disable default) optionalfeatures.

log

By default, Qt's logging system is not initialized, and messages from e.g. QML'sconsole.log don't go anywhere.The "log" feature enables integration withlog crate, the Rust logging facade.

The feature is enabled by default. To activate it, execute the following code as early as possible inmain():

fnmain(){    qmetaobject::log::init_qt_to_rust();// don't forget to set up env_logger or any other logging backend.}

chrono_qdatetime

Enables interoperability ofQDate andQTime with Rustchrono package.

This feature is disabled by default.

webengine

EnablesQtWebEngine functionality. For more details see theexample.

This feature is disabled by default.

What if a wrapper for the Qt C++ API is missing?

It is quite likely that you would like to call a particular Qt function whichis not wrapped by this crate.

In this case, it is always possible to access C++ directly from your rust codeusing thecpp! macro.

We strive to increase coverage of wrapped API, so whenever there is somethingyou need but currently missing, you are welcome to open a feature request onGitHub issues or send a Pull Request right away.

Tutorial: Adding Rust wrappers for Qt C++ API

This section teaches how to make your own crate with new Qt wrappers, and walkthrough a Graph example provided with this repository.

First things first, set up yourCargo.toml andbuild.rs:

  1. Addqttypes to dependencies.Likely, you would just stick to recent versions published oncrates.io.

    [dependencies]qttypes = {version ="0.2",features = ["qtquick" ] }

    Add more Qt modules you need to the features array.Refer toqttypes crate documentation for a full list of supported modules.
    If youabsolutely need latest unreleased changes, use this instead ofversion = "...":

    • path = "../path/to/qmetaobject-rs/qttypes" or
    • git = "https://github.com/woboq/qmetaobject-rs"
  2. Addcpp to dependencies andcpp_build to build-dependencies.You can find up-to-date instructions oncpp documentation page.

    [dependencies]cpp ="0.5"[build-dependencies]cpp_build ="0.5"
  3. Copybuild.rs script fromqmetaobject/build.rs.It will runcpp_build against you package, using environment provided byqttypes/build.rs.

Now, every time you build your package, content ofcpp! macros will becollected in one big C++ file and compiled into a static library which willlater be linked into a final binary. You can find thiscpp_closures.cppfile buried inside Cargo target directory. Understanding its content might beuseful for troubleshooting.

There are two forms ofcpp! macro.

  • The one with double curly{{ braces}} appends its content verbatim tothe C++ file. Use it to#include headers, define C++ structs & classes etc.

  • The other one is for calling expressions at runtime. It is usually writtenwith( parenthesis), it takes[ arguments] list and requires anunsafe marker (either surrounding block or as a first keyword inside).

Order of macros invocations is preserved on a per-file (Rust module) basis;but processing order of files is not guaranteed by the order ofmoddeclarations. So don't assume visibility — make sure to#include everythingneeded on top of every Rust module.

Check outdocumentation ofcpp to read more about howit works internally.

Now that we are all set, let's take a look at the Graph example's code. It islocated inexamples/graph directory.

Before adding wrappers, we put relevant#include lines inside a{{ doublecurly braced}} macro:

cpp!{{    #include <QtQuick/QQuickItem>}}

If you need to include you own local C++ headers, you can do that too! Checkout how main qmetaobject crate includesqmetaobject_rust.hpp header inevery Rust module that needs it.

Next, we declare a custom QObject, just like in theoverview, butthis time it derives fromQQuickItem. Despite its name,#[derive(QObject)]proc-macro can work with more than one base class, as long as it is properlywrapped and implements theQObject trait.

#[derive(Default,QObject)]structGraph{base:qt_base_class!(traitQQuickItem),// ...}

We wish to callQQuickItem::setFlag method which is currently notexposed in the qmetaobject-rs API, so let's call it directly:

implGraph{fnappendSample(&mutself,value:f64){// ...let obj =self.get_cpp_object();cpp!(unsafe[objas"QQuickItem *"]{            obj->setFlag(QQuickItem::ItemHasContents);});// ...}}

Alternatively, we could add a proper method wrapper, and call it withoutunsafe:

#[repr(C)]enumQQuickItemFlag{ItemClipsChildrenToShape =0x01,ItemAcceptsInputMethod =0x02,ItemIsFocusScope =0x04,ItemHasContents =0x08,ItemAcceptsDrops =0x10,}implGraph{fnset_flag(&mutself,flag:QQuickItemFlag){let obj =self.get_cpp_object();assert!(!obj.is_null());cpp!(unsafe[objas"QQuickItem *", flagas"QQuickItem::Flag"]{            obj->setFlag(flag);});}fnappendSample(&mutself,value:f64){// ...self.set_flag(QQuickItemFlag::ItemHasContents);// ...}}

Note that C++ method takes optional second argument, but since optionalarguments are not supported by Rust nor by FFI glue, it is always left out(and defaults totrue) in this case. To improve on this situation, we couldhave added second required argument to Rust function, or implementtwo "overloads" with slightly different names, e.g.set_flag(Flag, bool) &set_flag_on(Flag) orenable_flag(Flag) etc.

Assert for not-null should not be needed if object is guaranteed to beproperly instantiated and initialized before usage. This applies to thefollowing situations:

  • CallQObject::cpp_construct() directly and store the result in immovablememory location;

  • ConstructQObjectPinned instance: any access to pinned object orconversion toQVariant ensures creation of C++ object;

  • Instantiate object as a QML component. They are always properlydefault-initialized by a QML engine before setting any properties orcalling any signals/slots.

And that's it! You have just implemented a new wrapper for a Qt C++ classmethod. Now send us a Pull Request. 🙂

Comparison to Other Projects

The primary goal of this crate is to provide idiomatic Rust bindings for QML.It focuses solely on QML, not QWidgets or any other non-graphical Qt API.The aim is to eliminate the need for users to know or use C++ and other build systems.This crate excels in achieving this goal.

  • CXX-Qt is an ideal solution for incorporating some Rust into an existing C++ project.CXX-Qt is newer than this crate and utilizes Rust features such as attribute macro,which didn't exist when the qmetaobject crate was designed.(Only derive procedural macro were available in stable Rust at that time)

  • TheRust Qt Binding Generatoris another project that aids in integrating Rust logic into an existing C++/Qt project.This project was also developed before Rust had procedural macros, so it uses an external .json file to generate C++ and Rust code.

  • There are also several older crates that attempted to provide Rust binding around the Qt C++ API.Often automatically generated, these bindings are not idiomatic Rust, require unsafe code to use,and are no longer maintained.

  • Slint is a project created by the author of this crate.Slint is not a QML or Qt binding, but a new language inspired from QML, entirely implemented in Rust.It shares the same objective of providing a means to add a UI to a Rust project with idiomatic Rust API, but instead of using QML for the UI, it uses its own language.
    Slint Logo

The qmetaobject crate is currently only being passively maintained as focus has shifted towards developing Slint.You are encouraged to exploreSlint as an exciting, innovative alternative for creating GUI in Rust projects.

Applications built with this crate

About

Integrate Qml and Rust by building the QMetaObject at compile time.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors38


[8]ページ先頭

©2009-2025 Movatter.jp