{"id":13439694,"url":"https://github.com/woboq/qmetaobject-rs","last_synced_at":"2025-04-10T11:32:34.439Z","repository":{"id":32697002,"uuid":"136326040","full_name":"woboq/qmetaobject-rs","owner":"woboq","description":"Integrate Qml and Rust by building the QMetaObject at compile time.","archived":false,"fork":false,"pushed_at":"2025-01-18T07:58:40.000Z","size":965,"stargazers_count":672,"open_issues_count":91,"forks_count":91,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-03T06:42:12.590Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/woboq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-06T12:32:41.000Z","updated_at":"2025-04-02T13:02:34.000Z","dependencies_parsed_at":"2022-07-14T07:00:31.606Z","dependency_job_id":"c0edb1f0-c694-4fce-a876-35bb355ee938","html_url":"https://github.com/woboq/qmetaobject-rs","commit_stats":{"total_commits":588,"total_committers":49,"mean_commits":12.0,"dds":0.6173469387755102,"last_synced_commit":"b08463a2e83ced7bfeecef4a02f5812dc6be02c2"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woboq%2Fqmetaobject-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woboq%2Fqmetaobject-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woboq%2Fqmetaobject-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/woboq%2Fqmetaobject-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/woboq","download_url":"https://codeload.github.com/woboq/qmetaobject-rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248208679,"owners_count":21065203,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:16.321Z","updated_at":"2025-04-10T11:32:33.680Z","avatar_url":"https://github.com/woboq.png","language":"Rust","funding_links":[],"categories":["Libraries","库 Libraries","库","Binding"],"sub_categories":["GUI","GUI GUI","图形用户界面 GUI"],"readme":"# QMetaObject crate for Rust\n\n[![Crates.io](https://img.shields.io/crates/v/qmetaobject.svg)](https://crates.io/crates/qmetaobject)\n[![Documentation](https://docs.rs/qmetaobject/badge.svg)](https://docs.rs/qmetaobject/)\n\nA framework empowering everyone to create Qt/QML applications with Rust.\nIt does so by building `QMetaObject`s at compile time, registering QML types (optionally via exposing `QQmlExtensionPlugin`s) and providing idiomatic wrappers.\n\n## Objectives\n\n - Rust procedural macro (custom derive) to generate a `QMetaObject` at compile time.\n - Bindings for the main Qt types using the `cpp!` macro from the [`cpp`](https://crates.io/crates/cpp) crate.\n - Users of this crate should not require to type any line of C++ or use another build system beyond cargo.\n - Performance: Avoid any unnecessary conversion or heap allocation.\n\nPresentation Blog Post: https://woboq.com/blog/qmetaobject-from-rust.html\n\n## Overview\n\n```rust\nuse cstr::cstr;\nuse qmetaobject::prelude::*;\n\n// The `QObject` custom derive macro allows to expose a class to Qt and QML\n#[derive(QObject, Default)]\nstruct Greeter {\n    // Specify the base class with the qt_base_class macro\n    base: qt_base_class!(trait QObject),\n    // Declare `name` as a property usable from Qt\n    name: qt_property!(QString; NOTIFY name_changed),\n    // Declare a signal\n    name_changed: qt_signal!(),\n    // And even a slot\n    compute_greetings: qt_method!(fn compute_greetings(\u0026self, verb: String) -\u003e QString {\n        format!(\"{} {}\", verb, self.name.to_string()).into()\n    })\n}\n\nfn main() {\n    // Register the `Greeter` struct to QML\n    qml_register_type::\u003cGreeter\u003e(cstr!(\"Greeter\"), 1, 0, cstr!(\"Greeter\"));\n    // Create a QML engine from rust\n    let mut engine = QmlEngine::new();\n    // (Here the QML code is inline, but one can also load from a file)\n    engine.load_data(r#\"\n        import QtQuick 2.6\n        import QtQuick.Window 2.0\n        // Import our Rust classes\n        import Greeter 1.0\n\n        Window {\n            visible: true\n            // Instantiate the rust struct\n            Greeter {\n                id: greeter;\n                // Set a property\n                name: \"World\"\n            }\n            Text {\n                anchors.centerIn: parent\n                // Call a method\n                text: greeter.compute_greetings(\"hello\")\n            }\n        }\n    \"#.into());\n    engine.exec();\n}\n```\n\n## Features\n\n - Create object inheriting from QObject, QQuickItem, QAbstractListModel, QQmlExtensionPlugin, ...\n - Export Qt properties, signals, methods, ...\n - Also support `#[derive(QGadget)]` (same as Q_GADGET)\n - Create Qt plugin (see examples/qmlextensionplugins)\n - Partial scene graph support\n\nRequires Qt \u003e= 5.8\n\n## Cargo features\n\nCargo provides a way to enable (or disable default) optional [features](https://doc.rust-lang.org/cargo/reference/features.html).\n\n### `log`\n\nBy default, Qt's logging system is not initialized, and messages from e.g. QML's `console.log` don't go anywhere.\nThe \"log\" feature enables integration with [`log`](https://crates.io/crates/log) crate, the Rust logging facade.\n\nThe feature is enabled by default. To activate it, execute the following code as early as possible in `main()`:\n\n```rust\nfn main() {\n    qmetaobject::log::init_qt_to_rust();\n    // don't forget to set up env_logger or any other logging backend.\n}\n```\n\n### `chrono_qdatetime`\n\nEnables interoperability of `QDate` and `QTime` with Rust [`chrono`](https://crates.io/crates/chrono) package.\n\nThis feature is disabled by default.\n\n### `webengine`\n\nEnables `QtWebEngine` functionality. For more details see the [example](./examples/webengine).\n\nThis feature is disabled by default.\n\n## What if a wrapper for the Qt C++ API is missing?\n\nIt is quite likely that you would like to call a particular Qt function which\nis not wrapped by this crate.\n\nIn this case, it is always possible to access C++ directly from your rust code\nusing the `cpp!` macro.\n\nWe strive to increase coverage of wrapped API, so whenever there is something\nyou need but currently missing, you are welcome to open a feature request on\nGitHub issues or send a Pull Request right away.\n\n### Tutorial: Adding Rust wrappers for Qt C++ API\n\nThis section teaches how to make your own crate with new Qt wrappers, and walk\nthrough a Graph example provided with this repository.\n\nFirst things first, set up your _Cargo.toml_ and _build.rs_:\n\n1. Add `qttypes` to dependencies.\n   Likely, you would just stick to recent versions published on [crates.io](versions).\n   ```toml\n   [dependencies]\n   qttypes = { version = \"0.2\", features = [ \"qtquick\" ] }\n   ```\n   Add more Qt modules you need to the features array.\n   Refer to [qttypes crate documentation](docs.qttypes) for a full list of supported modules.\n   \u003cbr/\u003e\n   If you _absolutely need_ latest unreleased changes, use this instead of `version = \"...\"`:\n    * `path = \"../path/to/qmetaobject-rs/qttypes\"` or\n    * `git = \"https://github.com/woboq/qmetaobject-rs\"`\n\n2. Add `cpp` to dependencies and `cpp_build` to build-dependencies.\n   You can find up-to-date instructions on [`cpp` documentation](https://docs.rs/cpp) page.\n   ```toml\n   [dependencies]\n   cpp = \"0.5\"\n\n   [build-dependencies]\n   cpp_build = \"0.5\"\n   ```\n\n3. Copy _build.rs_ script from [_qmetaobject/build.rs_](./qmetaobject/build.rs).\n   It will run `cpp_build` against you package, using environment provided by\n   [_qttypes/build.rs_](./qttypes/build.rs).\n\nNow, every time you build your package, content of `cpp!` macros will be\ncollected in one big C++ file and compiled into a static library which will\nlater be linked into a final binary. You can find this _cpp_closures.cpp_\nfile buried inside Cargo target directory. Understanding its content might be\nuseful for troubleshooting.\n\nThere are two forms of `cpp!` macro.\n\n* The one with double curly `{{` braces `}}` appends its content verbatim to\n  the C++ file. Use it to `#include` headers, define C++ structs \u0026 classes etc.\n\n* The other one is for calling expressions at runtime. It is usually written\n  with `(` parenthesis `)`, it takes `[` arguments `]` list and requires an\n  `unsafe` marker (either surrounding block or as a first keyword inside).\n\nOrder of macros invocations is preserved on a per-file (Rust module) basis;\nbut processing order of files is not guaranteed by the order of `mod`\ndeclarations. So don't assume visibility — make sure to `#include` everything\nneeded on top of every Rust module.\n\nCheck out [documentation of `cpp`](https://docs.rs/cpp) to read more about how\nit works internally.\n\nNow that we are all set, let's take a look at the Graph example's code. It is\nlocated in [_examples/graph_](./examples/graph) directory.\n\nBefore adding wrappers, we put relevant `#include` lines inside a `{{` double\ncurly braced `}}` macro:\n\n```rust\ncpp! {{\n    #include \u003cQtQuick/QQuickItem\u003e\n}}\n```\n\nIf you need to include you own local C++ headers, you can do that too! Check\nout how main qmetaobject crate includes _qmetaobject_rust.hpp_ header in\nevery Rust module that needs it.\n\nNext, we declare a custom QObject, just like in the [overview](#overview), but\nthis time it derives from `QQuickItem`. Despite its name, `#[derive(QObject)]`\nproc-macro can work with more than one base class, as long as it is properly\nwrapped and implements the [`QObject`](trait.QObject) trait.\n\n```rust\n#[derive(Default, QObject)]\nstruct Graph {\n    base: qt_base_class!(trait QQuickItem),\n\n    // ...\n}\n```\n\nWe wish to call [`QQuickItem::setFlag`] method which is currently not\nexposed in the qmetaobject-rs API, so let's call it directly:\n\n```rust\nimpl Graph {\n    fn appendSample(\u0026mut self, value: f64) {\n        // ...\n        let obj = self.get_cpp_object();\n        cpp!(unsafe [obj as \"QQuickItem *\"] {\n            obj-\u003esetFlag(QQuickItem::ItemHasContents);\n        });\n        // ...\n    }\n}\n```\n\nAlternatively, we could add a proper method wrapper, and call it without `unsafe`:\n\n```rust\n#[repr(C)]\nenum QQuickItemFlag {\n    ItemClipsChildrenToShape = 0x01,\n    ItemAcceptsInputMethod = 0x02,\n    ItemIsFocusScope = 0x04,\n    ItemHasContents = 0x08,\n    ItemAcceptsDrops = 0x10,\n}\n\nimpl Graph {\n    fn set_flag(\u0026mut self, flag: QQuickItemFlag) {\n        let obj = self.get_cpp_object();\n        assert!(!obj.is_null());\n        cpp!(unsafe [obj as \"QQuickItem *\", flag as \"QQuickItem::Flag\"] {\n            obj-\u003esetFlag(flag);\n        });\n    }\n\n    fn appendSample(\u0026mut self, value: f64) {\n        // ...\n        self.set_flag(QQuickItemFlag::ItemHasContents);\n        // ...\n    }\n}\n```\n\nNote that C++ method takes optional second argument, but since optional\narguments are not supported by Rust nor by FFI glue, it is always left out\n(and defaults to `true`) in this case. To improve on this situation, we could\nhave added second required argument to Rust function, or implement\ntwo \"overloads\" with slightly different names, e.g. `set_flag(Flag, bool)` \u0026\n`set_flag_on(Flag)` or `enable_flag(Flag)` etc.\n\nAssert for not-null should not be needed if object is guaranteed to be\nproperly instantiated and initialized before usage. This applies to the\nfollowing situations:\n\n- Call [`QObject::cpp_construct()`] directly and store the result in immovable\n  memory location;\n\n- Construct [`QObjectPinned`] instance: any access to pinned object or\n  conversion to [`QVariant`] ensures creation of C++ object;\n\n- Instantiate object as a QML component. They are always properly\n  default-initialized by a QML engine before setting any properties or\n  calling any signals/slots.\n\nAnd that's it! You have just implemented a new wrapper for a Qt C++ class\nmethod. Now send us a Pull Request. 🙂\n\n[versions]: https://crates.io/crates/qmetaobject/versions\n[trait.QObject]: https://docs.rs/qmetaobject/latest/qmetaobject/trait.QObject.html\n[`QQuickItem::setFlag`]: https://doc.qt.io/qt-5/qquickitem.html#setFlag\n[`QObject::cpp_construct()`]: https://docs.rs/qmetaobject/latest/qmetaobject/trait.QObject.html#tymethod.cpp_construct\n[`QObjectPinned`]: https://docs.rs/qmetaobject/latest/qmetaobject/struct.QObjectPinned.html\n[`QVariant`]: https://docs.rs/qmetaobject/latest/qmetaobject/struct.QVariant.html\n[docs.qttypes]: https://docs.rs/qttypes/latest/qttypes/#cargo-features\n\n## Comparison to Other Projects\n\nThe primary goal of this crate is to provide idiomatic Rust bindings for QML.\nIt focuses solely on QML, not QWidgets or any other non-graphical Qt API.\nThe aim is to eliminate the need for users to know or use C++ and other build systems.\nThis crate excels in achieving this goal.\n\n* **[CXX-Qt](https://github.com/KDAB/cxx-qt/)** is an ideal solution for incorporating some Rust into an existing C++ project.\n  CXX-Qt is newer than this crate and utilizes Rust features such as attribute macro,\n  which didn't exist when the qmetaobject crate was designed.\n  (Only derive procedural macro were available in stable Rust at that time)\n\n* The **[Rust Qt Binding Generator](https://invent.kde.org/sdk/rust-qt-binding-generator)**\n  is another project that aids in integrating Rust logic into an existing C++/Qt project.\n  This project was also developed before Rust had procedural macros, so it uses an external .json file to generate C++ and Rust code.\n\n* There are also several older crates that attempted to provide Rust binding around the Qt C++ API.\n  Often automatically generated, these bindings are not idiomatic Rust, require unsafe code to use,\n  and are no longer maintained.\n\n* **[Slint](https://slint.rs)** is a project created by the author of this crate.\n  Slint is not a QML or Qt binding, but a new language inspired from QML, entirely implemented in Rust.\n  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.\\\n  [![Slint Logo](https://slint.dev/logo/slint-logo-simple-light.svg)](https://slint.rs)\n\nThe qmetaobject crate is currently only being passively maintained as focus has shifted towards developing Slint.\nYou are encouraged to explore [Slint](https://slint.rs) as an exciting, innovative alternative for creating GUI in Rust projects.\n\n## Applications built with this crate\n\n * https://github.com/gyroflow/gyroflow\n   ![Gyroflow screenshot](https://github.com/gyroflow/gyroflow/blob/master/resources/screenshot.jpg)\n\n* https://github.com/kalaksi/lightkeeper\n  ![LightKeeper screenshot](https://github.com/kalaksi/lightkeeper/blob/master/doc/images/LightkeeperRM-overview.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoboq%2Fqmetaobject-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwoboq%2Fqmetaobject-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwoboq%2Fqmetaobject-rs/lists"}