QtBridge Rust

repository·dev·Indexed 41 days ago

https://github.com/qt/qtbridge-rust

QtBridge enables building modern Qt Quick user interfaces with a Rust backend without writing C++ code. It uses attribute macros like #[qobject], #[qslot], #[qproperty], and #[qsignal] to expose Rust types, properties, signals, and slots directly to QML. The framework requires Qt 6.10 or higher and provides utilities for QMetaType support and C++ proxy code generation.

Tokens
25.8K
Snippets
62
Records
108
Agent score
54%

What's inside QtBridge

  1. What is the qt-artifacts crate?

    dev

    The qt-artifacts crate is a manifest provider that contains pre-built Qt artifact download URLs and their corresponding SHA256 checksums. It supports Windows, Linux, and macOS.

    Important Note: This crate does not contain the Qt framework itself; it only provides the links and checksums required to download the prebuilt artifacts.

  2. Select and resolve Qt versions with qt-version

    dev

    The qt-version crate is used to select and resolve supported Qt versions in a Rust project using Cargo features.

    Important Note: This crate does not include the Qt framework itself. It only contains links to prebuilt Qt framework artifacts. You must ensure the Qt framework is available on your system or provided via other means. Using this crate does not change the licensing terms of the Qt framework used in your project.

  3. How QtBridge works with QML and Rust

    dev

    QtBridge allows you to build modern Qt Quick user interfaces using a Rust backend without writing C++. You define Rust types and expose them to QML using attribute macros.

    Core Workflow:

    1. Define a Backend: Create a Rust struct to hold your application logic.
    2. Expose to QML: Use the #[qobject] macro on an impl block for that struct. Use #[qslot], #[qproperty], and #[qsignal] to define how the Rust type appears in QML.
    3. Initialize the App: Use QApp to register your types, load your QML files, and run the application.

    Memory Model: All #[qobject] types are held in Rc<RefCell<_>>. This allows the Rust code, the QML engine, and QML components to share ownership. Rust objects are borrowed only for the duration of a signal, slot, or property invocation. This allows Rust code to freely take mutable borrows between QML interactions, provided no call chain is interrupted in a way that causes a RefCell conflict (which results in a panic).

    use qtbridge::{qobject, QApp};
    
    #[derive(Default)]
    pub struct Backend {}
    
    #[qobject(Singleton)]
    impl Backend {
        #[qslot]
        fn say_hello(&self) {
            println!("Hello World!")
        }
    }
    
    fn main() {
        QApp::new()
            .register::<Backend>()
            .load_qml(include_bytes!("qml/Main.qml"))
            .run();
    }
  4. Submit changes to Gerrit

    dev

    When your local patch is ready, follow these steps to submit it for review:

    1. Branching: Create a new feature branch: git checkout -b feature-name.
    2. Commit Policy: Ensure your commit message follows the Commit Policy. You must include a Task-number: or Fixes: entry in the commit footer.
    3. Pushing: Use the specific refspec to push your changes to the dev branch on Gerrit.

    Your change will then appear in Gerrit for review, where you can add reviewers and modify the commit message.

    git add <the files you modified>
    git commit
    git push origin HEAD:refs/for/dev
  5. Build and test the QtBridge Rust workspace

    dev

    The repository is a Rust workspace. By default, cargo build and cargo test only build the core crates and tests. To ensure your changes are valid across the entire project, including example applications, you must run the workspace-wide commands. It is highly recommended to run the full workspace test before submitting any changes to Gerrit.

    # Build core crates and tests only
    cargo build
    cargo test
    
    # Build the full workspace including example apps
    cargo build --workspace
    cargo test --workspace
  6. Configure Gerrit for code review

    dev

    To contribute code, you must use Gerrit instead of GitHub Pull Requests.

    1. Create a Qt Account: Register at https://login.qt.io/register. This account is used for the Bug tracker, Gerrit, and the Wiki.
    2. Accept CLA: You must accept the Contributor License Agreement (CLA).
    3. Gerrit Registration: Follow the instructions on the Setting up Gerrit wiki page (specifically the 'Gerrit registration', 'Local Setup', and 'Configuring Git' sections).
    4. Verify Connection: Test your SSH connection to Gerrit. A successful connection will display a welcome message and confirm you are connected as <name>.
    ssh codereview.qt-project.org
  7. Install QtBridge via Cargo

    dev

    Add qtbridge to your Cargo.toml dependencies. To enable advanced features like automatic type registration or JSON support, use the corresponding feature flags.

    Available Features:

    • linkme: Automatically registers every type annotated with #[qobject] in the QML type system at startup (removes the need for manual .register() calls).
    • serde_json: Allows using serde_json::Value in properties, signals, and slots. These values are exposed to QML as JSON objects.
    [dependencies]
    qtbridge = { version = "*", features = ["linkme", "serde_json"] }
  8. Get the source code and configure git hooks

    dev

    Clone the repository from the official Qt source to automatically include necessary git hooks (such as the commit-msg hook required for Gerrit). After cloning, navigate to the specific language directory and checkout the dev branch.

    Note: The GitHub repository is a read-only mirror. All development and code reviews must happen via Gerrit.

    git clone "https://codereview.qt-project.org/qt/qtbridge-rust" && (cd "qtbridge-rust" && mkdir -p `git rev-parse --git-dir`/hooks/ && curl -Lo `git rev-parse --git-dir`/hooks/commit-msg https://codereview.qt-project.org/tools/hooks/commit-msg && chmod +x `git rev-parse --git-dir`/hooks/commit-msg)
    
    cd qtbridge-<lang>
    git checkout dev
  9. Configure Qt 6 installation for QtBridge

    dev

    QtBridge requires Qt 6.10 or higher and qmake must be in your system PATH. You must also have a C++ toolchain installed.

    Linux (System Package Manager)

    On Fedora 43/44:

    sudo dnf install qt6-qtbase-devel qt6-qtdeclarative-devel qt6-qtbase-private-devel

    On Ubuntu 26.04/Debian Testing:

    sudo apt install -y qt6-base-dev qt6-declarative-dev qt6-base-private-dev

    Setting Environment Variables (Manual/Source Builds)

    If Qt is not in your standard path, you must update your environment variables:

    Linux:

    export PATH=/path/to/qtbase/bin:$PATH
    export LD_LIBRARY_PATH="/path/to/qtbase/lib:$LD_LIBRARY_PATH"

    macOS:

    export PATH=/path/to/qtbase/bin:$PATH
    export DYLD_FRAMEWORK_PATH=/path/to/qtbase/lib:$DYLD_FRAMEWORK_PATH

    Windows (Command Prompt):

    set PATH=%PATH%;C:\Qt\6.10.1\msvc2022_64\bin\

    Windows (PowerShell):

    $env:PATH += ";C:\Qt\6.10.1\msvc2022_64\bin\"
  10. What is a QPropertyMember and how is it used?

    dev

    The QPropertyMember trait defines how a Rust type can be used as a property in a Qt Quick interface. It handles the conversion between Rust types and QVariant for both read (to Qt) and write (from Qt) operations, and manages property equality to determine if a notify signal should be emitted.

    Supported Types

    You do not need to implement this trait manually for standard types. Support is provided for:

    • Primitive numeric types and bool
    • String
    • Vec<T> where T is a primitive or String
    • Rc<RefCell<T>> where T implements QObjectHolder (for QObject-backed properties)
    • Vec<Rc<RefCell<T>>> where T implements QmlElement (for lists of QML elements)

    Note: For custom types, support must be added via CXX/C++ bindings.

    Safety Requirements

    Because many operations involve raw pointers to QObject instances, the following safety rules apply:

    • to_qvariant / to_qvariant_view: For QObject-backed members, the returned QVariant carries an untracked raw QObject*. The caller must consume this variant and begin tracking its state immediately before any garbage collection occurs. Metaobject dispatch is designed to consume these variants within the same call stack to mitigate risk.
    • from_qvariant: When reading a QObject-backed member from a QVariant, the caller must guarantee the variant contains a live QObject of a compatible type. Qt's type-checking during property writes helps uphold this.