cxx-qt icon indicating copy to clipboard operation
cxx-qt copied to clipboard

Rust bindings for QQmlEngine & QQmlApplicationEngine

Open Be-ing opened this issue 2 years ago • 8 comments

This could allow for writing QML applications in Rust without downstream application developers needing to write C++

Be-ing avatar Mar 14 '22 20:03 Be-ing

So this goes against the current goals and focus of this tool, of not exposing intimate details about Qt to Rust and limiting scope to only plugins and modules which allows a safe and more idiomatic API. If you want to wrap the whole of the C++ Qt API there are other solutions which already exist for this purpose. Also if you want a Rust-only app then there are other toolkits which will be better suited (eg Slint UI), as even if you wrap the whole of the Qt API in Rust, it still requires the developer to have Qt knowledge.

Maybe eventually I could see something like the following code, but for the short/medium term this is won't fix. And i'm also not sure how useful this would be, as you still would likely want to perform connections / register other types etc. You still need a C++ compiler setup and the CMake side still needs configuring etc.

#[cxx_qt(app(resources = ["qml.qrc"], main_qml_file = "qrc:/qml/main.qml"))]
fn main() {
    println!("Hello world");
}

or

fn main() {
    cxx_qt::App::new("app_id")
        .resources(vec!("qml.qrc"))
        .main_qml_file("qrc:/qml/main.qml")
        .build()
        .unwrap()
}

(inspired by tokio)

ahayzen-kdab avatar Mar 15 '22 10:03 ahayzen-kdab

So this goes against the current goals and focus of this tool, of not exposing intimate details about Qt to Rust and limiting scope to only plugins and modules which allows a safe and more idiomatic API. If you want to wrap the whole of the C++ Qt API there are other solutions which already exist for this purpose.

This won't require wrapping the whole C++ API of Qt, just enough to launch a QML application.

Also if you want a Rust-only app then there are other toolkits which will be better suited (eg Slint UI), as even if you wrap the whole of the Qt API in Rust, it still requires the developer to have Qt knowledge.

Slint is great! I'm using it for my own application. And there are other Rust GUI libraries as well. But none of them are nearly as mature as Qt and likely won't be for years to come. I agree that I probably wouldn't recommend using cxx-qt for a primarily Rust application to a developer who doesn't have prior Qt experience. But there are lots of developers with Qt experience. :)

i'm also not sure how useful this would be, as you still would likely want to perform connections / register other types etc.

Yes, there likely would be a bit more than this desired, but far from the full API of Qt.

You still need a C++ compiler setup and the CMake side still needs configuring etc.

A C++ compiler is required of course, but I think it could be feasible to do the build with Cargo without requiring CMake.

I think it would be valuable to have a single Qt binding crate that could be used both within C++ applications and within Rust applications. This could enable long term, step-by-step migration of existing Qt applications to primarily Rust. Also, I think it could attract substantially more contributors, especially if it is usable together with KDE Frameworks. I am curious about @Ayush1325's thoughts on that last point.

Be-ing avatar Mar 15 '22 14:03 Be-ing

I think it would be valuable to have a single Qt binding crate that could be used both within C++ applications and within Rust applications. This could enable long term, step-by-step migration of existing Qt applications to primarily Rust. Also, I think it could attract substantially more contributors, especially if it is usable together with KDE Frameworks. I am curious about @Ayush1325's thoughts on that last point.

Yes, I agree with this point. The problem with using Rust just for modules/plugins is that it will only be attractive to a very small set of people. For most small to medium-sized projects, it simply isn't worth it to use 2 languages/build systems, and thus attracting new developers will be difficult.

However, I also agree that allowing the writing of Qt applications in Rust isn't nearly as simple as just having wrappers for QQmlEngine and QQmlApplicationEngine. I am a collaborator in qmetaobject project and I can say that adding support for completely writing applications in Rust will take a lot more work, especially since it would be best if things like registering QObjects, etc is done at compile time.

Since qmetaobject already exists, I am more interested in the cxx-qt-lib crate and see if that can replace qttypes crate, then qmetaobject and cxx-qt could theoretically be used together. Then the bindings like kconfig-rs can rely on cxx-qt-lib and be used with both projects.

In short, for now, I think it is fine if cxx-qt focuses on modules/plugins and qmetaobject focuses on writing the whole application in Rust.

Ayush1325 avatar Mar 15 '22 14:03 Ayush1325

Since qmetaobject already exists, I am more interested in the cxx-qt-lib crate and see if that can replace qttypes crate, then qmetaobject and cxx-qt could theoretically be used together. Then the bindings like kconfig-rs can rely on cxx-qt-lib and be used with both projects.

To be clear, you would like to use cxx-qt-lib in qmetaobject? I didn't propose that before because I figure it would take a large effort, maybe as much work as it would to fill in the gaps to make cxx-qt usable for primarily Rust applications.

Be-ing avatar Mar 15 '22 14:03 Be-ing

To be clear, you would like to use cxx-qt-lib in qmetaobject? I didn't propose that before because I figure it would take a large effort, maybe as much work as it would fill in the gaps to make cxx-qt usable for primarily Rust applications.

Well, I can't say anything for sure right now but I will probably start contributing to cxx-qt-lib once #105 is merged to first bring it to feature parity with qttypes. After that, I will try to see if it would be possible to replace qttypes with it. The feature parity part will take quite a while though so there's that. And I am not the maintainer of qmetaobject so I can't promise if everyone will agree to replace qttypes with cxx-qt-lib.

However, in my personal opinion, if we ignore the work to bring cxx-qt-lib up to feature parity with qttypes, then the amount of work it should take to replace qttypes with it should be less than having to add Rust application support in cxx-qt.

From my rough estimate, to add Rust application support to cxx-qt, we will need the following:

  1. Remove reliance on CMake. Maybe have a separate crate for applications that links to the qt stuff without CMake. It will also need to check the qt versions and platforms to work things out.
  2. Add support to register objects/singletons to QML at compile time. This is done using macros in qmetaobject and I am guessing we will need to do something similar here. Will also include support for connecting signals and slots.
  3. Since CMake is absent, the moc stuff, especially generating QMetaObject at compile time will need to be done from Rust.
  4. QRC support. Currently, qmetaobject has a qrc! macro for this.
  5. Support for QGadget, QEnum, etc. (Dunno if they already exist)

Basically, we will need to rewrite a lot of stuff that already exists in qmetaobject.

The work can be accelerated if someone is willing to mentor a project for GSoC from here (Of course I would love to participate as a student). I think someone mentioned in #kde-soc channel that I should see if someone would be willing to mentor something in cxx-qt as a KDE project in GSoC when I asked about a Rust project in KDE.

Ayush1325 avatar Mar 15 '22 15:03 Ayush1325

Interesting... @ogoffart, @ratijas, @rubdos, what are your thoughts on this?

Be-ing avatar Mar 15 '22 17:03 Be-ing

From what I understand, this is the difference in philosophy between the two project. qmetaobject aim at just providing a idiomatic Rust API to use QML without having to care about C++ or the C++ API of Qt.

ogoffart avatar Mar 15 '22 17:03 ogoffart

In #221 I got Cargo to run the entire build for an example QML application without needing CMake.

I made a helper crate for build.rs scripts called qt-build, which is in this repository now. This finds and links Qt and its dependencies using a regex to parse the .prl files that qmake uses. It also gets the header include paths for Qt. We have CI showing this works on Linux, macOS, and Windows, with Qt5 and Qt6, with static and dynamic linking, though there is still an issue with rpaths not getting set with dynamic linking due to that feature existing in Cargo yet (#224). The qt-build crate has helper methods for invoking moc and rcc, the output of which can then be compiled with cc, cpp_build, or cxx_build. qt-build does not compile any C++ on its own; I wrote it with the intention that it could also be used by qttypes and Whisperfish.

Now the cxx-qt-builder crate generates C++ code from the Rust module with the #[cxx_qt::bridge] attribute, uses qt-build to link Qt and run moc on the generated code, and compiles it with the cc crate. To get the whole executable building with Cargo, I wrote a small .cpp file which gets compiled by cc that has an extern "C" function invoked from the Rust executable's main function. This little .cpp file takes care of creating the QQmlApplicationEngine, initializing the Qt resource system, and registering the QML types. With this approach it is feasible to write most of an application in Rust with only ~25 lines of C++ code. Making Rust bindings for QGuiApplication, QQmlApplicationEngine, and qmlRegisterType is not needed. It would still be nice to be able to do this entirely from Rust without needing to manually write any C++ code. So, for me that's a low priority compared to other improvements for cxx-qt. Though if someone else wants to contribute those bindings that would be nice!

Be-ing avatar Aug 26 '22 22:08 Be-ing

It would still be nice to be able to do this entirely from Rust without needing to manually write any C++ code.

This is now implemented! Since https://github.com/KDAB/cxx-qt/pull/428 was merged yesterday, QML types and QRC resources can be automatically registered at build time. So now a QML application can be written entirely in Rust and built only with Cargo without needing CMake.

Be-ing avatar Feb 09 '23 22:02 Be-ing