Re: [Interest] Problems with qt_add_qml_module
Hi, allow me to follow up on my own mail. I have made some progress through exploring (and reverse-engineering), and I would like to refine my questions to refocus the discussion. But perhaps it's best if I briefly describe my misunderstandings first, as that may help others as well. The first argument to the `qt_add_qml_module()` function is *not* a new target to be defined by the call. Rather, it is to be defined prior - typically as a library - and it is considered to be the "backing target". Furthermore, the `SOURCES` argument isn't a list of files that are to be compiled into a new artefact (library or executable). Rather, it's a list of files to be inspected by some Qt tools to extract types that are to be exported through the newly defined module / plugin. (As such, the files listed in `SOURCES` are typically also listed as sources to the backing target assumed to be defined via the usual `add_library()`, `target_link_libraries()`, etc.) It is also worth noting that for the introspection only headers are needed, i.e. the `SOURCES` argument to `at_add_qml_module()` only needs to list headers that define QML types (those that use the `QML_ELEMENT`, typically). Is this description accurate so far ? There are a few caveats I observed: The call to `qt_add_qml_module()` has to occur in the same CMakeLists.txt file as the associated backing target definition (e.g. `add_library()`), or else make complains about missing symbols. Further, the `OUTPUT_DIRECTORY` argument to `qt_add_qml_module()` seems to be required (at least if more than one QML modules are defined). It is probably assumed that the structure of the QML modules matches the structure of the underlying C++ libraries. That is not quite the case for me. I want to create a set of QML modules whose types come from a variety of C++ libraries, without a simple one-to-one mapping between C++ library and QML module. One thing that bothers me in particular is that The classes that I want to reflect into QML are supposed to contain the `QML_ELEMENT` macro, which I think is a bit intrusive. I would prefer if these classes wouldn't have to know about QML at all, i.e. all the meta-object infrastructure would be added separately (as with the `qmlRegisterType<>()` business). That would allow me to more clearly separate backend from frontend. All that being said, am I on the right track, or am I still fundamentally misunderstanding how to use QML with Qt6 ? Thanks, On Tue, Oct 24, 2023 at 2:50 PM Stefan Seefeld wrote: > > > On Tue, Oct 24, 2023 at 10:47 AM Ulf Hermann wrote: > >> > In our existing code we call `qmlRegisterType<...>(...)` in our regular >> > C++ code (e.g., some shared libraries that the main application links >> to). >> > I thought that, to be able to use those types in stand-alone QML code, >> > I'd need to move these calls into plugins, or at least have some QML >> > extension module link to these libraries so they can see these types. >> > What am I missing ? >> >> You are getting this fundamentally wrong. When using qt_add_qml_module >> you do not have to call any qmlRegisterType at all. qmltyperegistrar and >> CMake do all of this behind the scenes and relieve you from the pain of >> maintaining your plugins, type registration calls, qmltypes files, >> versions, metaobject revisions and all the other cruft you have to do in >> Qt5. You can easily have modules that contain both QML files and >> C++-based types now, too. >> > > That sounds great. It would be nice if this was documented a little more > clearly, though, as the existing docs are a little light and obscure. > How does this mechanism figure out what types should be registered / > exported and under what names ? > As I described in my previous mail, we have a great number of libraries, > defining many classes. Some of them derive from `QObject`, > and a few of those need to be made available to QML. > I expect to eventually create a number of QML extension modules (following > the modular structure of my application), so how can I express which > headers to extract the types from, and how can I define the names I want > to give to those types in QML ? > > Thanks, > > -- > > ...ich hab' noch einen Koffer in Berlin... > > -- ...ich hab' noch einen Koffer in Berlin... ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
Hi Nils, thanks a lot for your elaborate answer. That's very helpful indeed. I still have a couple of fundamental questions (see my reply to Ulf), but this gives some good hints. In fact I think the best way forward might be to set up a little toy project that has the desired structure, and which lets me explore the various aspects that are still unclear (e.g. C++ -> QML type name mapping etc.) Thanks, On Tue, Oct 24, 2023 at 11:22 AM Nils Jeisecke wrote: > Hi, > > On Tue, Oct 24, 2023 at 4:35 PM Stefan Seefeld > wrote: > > So it sounds like I still don't understand what `qt_add_qml_module` is > doing. > > AFAIK it builds a bunch of cmake custom commands that use moc for c++ > and some other tooling for qml files to extract all the type > information. This then is the basis to the qmlsc, qmltc and plugin > code generators (qmlRegisterType etc.). All the generated stuff is > added to the backing target. > > I've used these steps to modernize the code base of existing projects: > > 1. Port the Qml code to use proper modules. This can be partially > automated (https://github.com/njeisecke/qml-migration). > > 2. Move existing C++ code into proper qml modules, use QML_ELEMENT, > QML_SINGLETON etc., remove qmlRegisterType and fix the imports in the > qml files. This means quite some work and usually also comes with some > restructuring of the code base. > > 3. Replace all context properties by QML_SINGLETON classes providing > all previous context properties as typed class properties. > > 4. Fix all qmllint warnings. Your code will work but qmlsc won't > compile it to C++. There are still lots of places in Qml that make > typing and type checking hard (e.g. Loader.item can be anything) the > (undocumented) "as" operator is a great help here. Also inline > components are somewhat neglected as types by qmllint. > > 5. The hardest part: Refactor all Qml code to eliminate all > "unqualified access". There is a reason why this check is not enabled > in qmllint by default ;-) The scope lookup rules were a major design > fault in Qml, leading to uncheckable, uncompilable and hard to > maintain code. > > It's quite an achievement to now have tooling in Qt 6.6 that allows > this iterative approach to overcome deficiencies inherently built into > the Qml DNA. Kudos to the Qml team. > > You will finally be able to use qmllint for comprehensive type > checking of the qml/c++ code base which is a huge win for code quality > and maintenance. > > Nils > -- ...ich hab' noch einen Koffer in Berlin... ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
On Tue, Oct 24, 2023 at 10:47 AM Ulf Hermann wrote: > > In our existing code we call `qmlRegisterType<...>(...)` in our regular > > C++ code (e.g., some shared libraries that the main application links > to). > > I thought that, to be able to use those types in stand-alone QML code, > > I'd need to move these calls into plugins, or at least have some QML > > extension module link to these libraries so they can see these types. > > What am I missing ? > > You are getting this fundamentally wrong. When using qt_add_qml_module > you do not have to call any qmlRegisterType at all. qmltyperegistrar and > CMake do all of this behind the scenes and relieve you from the pain of > maintaining your plugins, type registration calls, qmltypes files, > versions, metaobject revisions and all the other cruft you have to do in > Qt5. You can easily have modules that contain both QML files and > C++-based types now, too. > That sounds great. It would be nice if this was documented a little more clearly, though, as the existing docs are a little light and obscure. How does this mechanism figure out what types should be registered / exported and under what names ? As I described in my previous mail, we have a great number of libraries, defining many classes. Some of them derive from `QObject`, and a few of those need to be made available to QML. I expect to eventually create a number of QML extension modules (following the modular structure of my application), so how can I express which headers to extract the types from, and how can I define the names I want to give to those types in QML ? Thanks, -- ...ich hab' noch einen Koffer in Berlin... ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] QEvent::Quit vs. QEvent::Close
Then, it means it's a bug, because it's set always. At least in Qt 6.4.2. On 10/24/2023 12:15 AM, Volker Hilsheimer via Interest wrote: They should; the window system event is handled as a spontaneous event and should reach Q(Gui)Application::event with the spontaneous flag set; a call to Q(Core)Application::quit sends (or posts, depending on the thread) the event as a synthetic event to the qApp instance, so the spontaneous flag will not be set. Volker ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] QEvent::Quit vs. QEvent::Close
Hello, No, it returns true always. On 10/23/2023 11:30 PM, Hamish Moffatt via Interest wrote: On 24/10/23 06:10, Alexander Dyagilev wrote: I would prefer for an additional event to be used when the user triggers Quit. E.g. something like QEvent::QuitByUser. For now, it seems I will have to involve some additional ugly logics (something like setting a global variable) More code more complexity for such a simple situation. Not good, IMO. Does QEvent::spontaneous() tell you anything about the two different quit events? Hamish ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
Hi, On Tue, Oct 24, 2023 at 4:35 PM Stefan Seefeld wrote: > So it sounds like I still don't understand what `qt_add_qml_module` is doing. AFAIK it builds a bunch of cmake custom commands that use moc for c++ and some other tooling for qml files to extract all the type information. This then is the basis to the qmlsc, qmltc and plugin code generators (qmlRegisterType etc.). All the generated stuff is added to the backing target. I've used these steps to modernize the code base of existing projects: 1. Port the Qml code to use proper modules. This can be partially automated (https://github.com/njeisecke/qml-migration). 2. Move existing C++ code into proper qml modules, use QML_ELEMENT, QML_SINGLETON etc., remove qmlRegisterType and fix the imports in the qml files. This means quite some work and usually also comes with some restructuring of the code base. 3. Replace all context properties by QML_SINGLETON classes providing all previous context properties as typed class properties. 4. Fix all qmllint warnings. Your code will work but qmlsc won't compile it to C++. There are still lots of places in Qml that make typing and type checking hard (e.g. Loader.item can be anything) the (undocumented) "as" operator is a great help here. Also inline components are somewhat neglected as types by qmllint. 5. The hardest part: Refactor all Qml code to eliminate all "unqualified access". There is a reason why this check is not enabled in qmllint by default ;-) The scope lookup rules were a major design fault in Qml, leading to uncheckable, uncompilable and hard to maintain code. It's quite an achievement to now have tooling in Qt 6.6 that allows this iterative approach to overcome deficiencies inherently built into the Qml DNA. Kudos to the Qml team. You will finally be able to use qmllint for comprehensive type checking of the qml/c++ code base which is a huge win for code quality and maintenance. Nils ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
In our existing code we call `qmlRegisterType<...>(...)` in our regular C++ code (e.g., some shared libraries that the main application links to). I thought that, to be able to use those types in stand-alone QML code, I'd need to move these calls into plugins, or at least have some QML extension module link to these libraries so they can see these types. What am I missing ? You are getting this fundamentally wrong. When using qt_add_qml_module you do not have to call any qmlRegisterType at all. qmltyperegistrar and CMake do all of this behind the scenes and relieve you from the pain of maintaining your plugins, type registration calls, qmltypes files, versions, metaobject revisions and all the other cruft you have to do in Qt5. You can easily have modules that contain both QML files and C++-based types now, too. All you need to do is make sure the resulting modules end up in an import path that's visible to your application. The easiest way to do this is place them in directories named by the module URIs (with dots replaced by directory separators) directly below the application directory. That is, no directory named "qml" or other shenanigans between the modules and the application. ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
On Tue, Oct 24, 2023 at 10:11 AM Ulf Hermann wrote: > > Most of the context would be injected into the QML runtime from C++ via > > calls to `setContextProperty()`, as well as by defining additional QML > > modules from C++ (via `qmlRegister...()`). > > This is where your architecture fails. By using setContextProperty() and > procedurally registering types, you > > a, still make your types depend on the context (due to context > properties). You still won't be able to use them in standalone tools. > > b, prevent QML tooling such as qmllint, qmlcachegen, qmlls to do > anything useful with your QML code since they cannot see through your > procedural type registrations or your context properties. > This is precisely what I'm saying, and why I want to define my types in an extension module. > > Instead of this: > > 1. Use singletons or object properties instead of context properties. > There are also initial properties you can pass to QQmlComponent and > QQmlApplicationEngine if that helps. > > 2. Use QML_ELEMENT and friends as well as qt_add_qml_module in CMake to > define your types declaratively so that tooling can see them. > Yes !!! Thank you for confirming that I'm on the right track ! :-) > This way you obtain truly re-usable QML modules you can test in > isolation. You also won't need to implement your own plugins or fight > with the linker since qmltyperegistrar will generate the registration > code and the hacky symbol references for you. This is the whole point of > qt_add_qml_module. If you don't want qmltyperegistrar to do its work, > there is little point in using qt_add_qml_module at all. > So it sounds like I still don't understand what `qt_add_qml_module` is doing. In our existing code we call `qmlRegisterType<...>(...)` in our regular C++ code (e.g., some shared libraries that the main application links to). I thought that, to be able to use those types in stand-alone QML code, I'd need to move these calls into plugins, or at least have some QML extension module link to these libraries so they can see these types. What am I missing ? Thanks, -- ...ich hab' noch einen Koffer in Berlin... ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
Most of the context would be injected into the QML runtime from C++ via calls to `setContextProperty()`, as well as by defining additional QML modules from C++ (via `qmlRegister...()`). This is where your architecture fails. By using setContextProperty() and procedurally registering types, you a, still make your types depend on the context (due to context properties). You still won't be able to use them in standalone tools. b, prevent QML tooling such as qmllint, qmlcachegen, qmlls to do anything useful with your QML code since they cannot see through your procedural type registrations or your context properties. Instead of this: 1. Use singletons or object properties instead of context properties. There are also initial properties you can pass to QQmlComponent and QQmlApplicationEngine if that helps. 2. Use QML_ELEMENT and friends as well as qt_add_qml_module in CMake to define your types declaratively so that tooling can see them. This way you obtain truly re-usable QML modules you can test in isolation. You also won't need to implement your own plugins or fight with the linker since qmltyperegistrar will generate the registration code and the hacky symbol references for you. This is the whole point of qt_add_qml_module. If you don't want qmltyperegistrar to do its work, there is little point in using qt_add_qml_module at all. ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
Hi Ulf, thank you for following up ! Yes, it's entirely possible I'm missing something fundamental, trying to approach a problem from the wrong side, so let me give some context: We are developing an application that's written mostly in C++ with a GUI done in QML. The application architecture is fairly complex, so we tried to modularize it as much as possible. At the heart is an application "skeleton" that would set up the basic services (including a QML Application Engine), into which additional components are loaded. The skeleton app would load a `main.qml` file that defines a few Loader instances, which would eventually load additional QML items at runtime. Most of the context would be injected into the QML runtime from C++ via calls to `setContextProperty()`, as well as by defining additional QML modules from C++ (via `qmlRegister...()`). So far we use C++ unit tests that would load particular QML items individually and then run some checks from C++. The above setup implies that the QML modules are very dependent on the C++ context, and as a result, cannot be loaded by stand-alone QML tools, including and in particular things like `qmltestrunner`. Thus, I would like to define some QML "extension modules" such that I can load them into QML as standalone tools (prototypes, unit tests, etc.). Does the above make sense ? Is this an appropriate use of extension modules ? Are there other ways to do what I want ? Now let me follow up to some technical details: Now, the specific exceptions are run time style selection for > QtQuick.Controls and image providers. If you are affected by those, you > should set NO_GENERATE_PLUGIN_SOURCE, NO_PLUGIN_OPTIONAL, and explicitly > specify PLUGIN_TARGET and CLASS_NAME. Then you can use target_sources on > the generated plugin target to add your own implementation of the > specified plugin class. Be aware that you need to manually reference > some symbols from the backing library in order to prevent the linker > from "optimizing" the dependency away. > I tried that, but the linking of my plugin library failed due to unresolved symbols. I tried to specify additional libraries using `target_link_libraries()` which resulted in the cmake error I reported earlier. Thanks, -- ...ich hab' noch einen Koffer in Berlin... ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
[Interest] some challenges setting window mask from QML
I set about implementing a type that allows a user to set the window mask (aka wayland input region, i.e. non-rectangular windows) from QML. I ended up with a qml Item that works like this: |Window| |{ | | WaylandInputRegion {| | item: theMenu | | }| | Rectangle {| | id: theMenu| | radius:50| | //etc pp | | } | |}| | | behind the scenes it uses grabToImage, then QBitMap::fromImage, QRegion(QBitmap&) and finally window()->handle()->setMask() I also added a fast path for when item is null to use the WaylandInputRegion itself as a simple rectangular region. However, the current implementation has the following issues that I would appreciate input for: using an external item as the bitmap source requires us to track changes in that item including its scene position to correctly reflect that in the window mask. I haven't found a way to do that so I rely on explicit calls to updateRegion() from qml code at known points when animations stop. Also for items anchored to the windows contentItem, position is invalid as the contentItem geometry appears to be 0x0, 0,0 for some time after start. Florian -- Florian Hänel Geschäftsführer echtzeit.solutions GmbH Handelsregistergericht München HRB 231056 Ust-ID DE310379807 ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] How to avoid qml engine repeatedly copying and garbage collecting qstringlists?
Actually, funnily enough, Qt.fontfamilies is an order of magnitude faster than custom property wrapping the result of ::families. Dunno why. 5s vs 500 ms for the sue case Still, checking on .lncludes(name) in javascript is seemingly also an order of magnitude slower than sending family name to c++ and prematurely exiting from there. 500 ms vs <50ms In 5.15 Qt.fontFamilies() is a JavaScript method on the 'Qt' member of the JavaScript global object. It looks like this: ReturnedValue QtObject::method_fontFamilies(const FunctionObject *b, const Value *, const Value *, int argc) { QV4::Scope scope(b); if (argc != 0) THROW_GENERIC_ERROR("Qt.fontFamilies(): Invalid arguments"); return scope.engine->fromVariant(QVariant(QQml_guiProvider()->fontFamilies())); } So, it wraps the QStringList into a QVariant and the QVaroamt into a JS object. I'm really surprised that the wrapping is faster than anything else, but I'm not surprised that access to its internals is slow. In Qt6, this is a Q_INVOKABLE C++ method on the 'Qt' singleton. It returns the QStringList as-is and lets the QML engine do with it whatever it needs to do. If this is a particularly hot code path, what you might try is create a QJSValue that holds a JS array of font families in C++ and then expose that instead of the QStringList. This way you tightly control the JavaScript wrapping of the list. In turn, you lose any type information. A QJSValue can be anything. However, in Qt5 you cannot use the type information anyway. best, Ulf ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest
Re: [Interest] Problems with qt_add_qml_module
I'm trying to create a QML extension module using CMake, and I've run into some road blocks. I want to create a QML module including a "plugin", based on my own code rather than an auto-generated plugin stub. My first question is why? The most important part of a QML module is generally a backing library that contains all the meat to the module: Its C++-based types, its compiled QML code etc. In addition you get a very, very thin plugin that is only there to load the backing library. Except for very specific exceptions, you should always put all your code into the backing library and let qt_add_qml_module generate the plugin. Then you have the choice of either linking the library directly into your application at compile time or loading the plugin at run time. The QML engine is clever enough to avoid the plugin loading overhead if it detects the backing library to be already linked. Now, the specific exceptions are run time style selection for QtQuick.Controls and image providers. If you are affected by those, you should set NO_GENERATE_PLUGIN_SOURCE, NO_PLUGIN_OPTIONAL, and explicitly specify PLUGIN_TARGET and CLASS_NAME. Then you can use target_sources on the generated plugin target to add your own implementation of the specified plugin class. Be aware that you need to manually reference some symbols from the backing library in order to prevent the linker from "optimizing" the dependency away. The examples that add custom plugins, unfortunately, all add only an image provider, but no actual QML types. In this case you can get away with making the plugin the same as the backing library. Otherwise you shouldn't do this. best regards, Ulf ___ Interest mailing list Interest@qt-project.org https://lists.qt-project.org/listinfo/interest