Designer plugin for custom library types/controls?
-
I've developed a static library using Qt that contains several custom types, Qt Quick controls and singletons. Currently I'm writing Qt Quick applications using this static library (i.e., I use qmlRegisterType and similar functions to register the controls from C++, then I can import and use the types in my QML files). I'm currently writing the QML for the layout manually, but I'd like to be able to use the Designer in Qt Creator in the future.
For the purposes of a minimal example, let's call this library 'MyLib', and assume it has a single Qt Quick control 'MyRectangle' declared in C++ and registered with QML using qmlRegisterType, under the URI: 'MyLib.Rectangle'. Say I have a simple QML application as follows:
//Import Qt Quick engine. import QtQuick 2.7 import MyLib.Rectangle 1.0 Item { anchors.fill: parent MyRectangle { anchors.centerIn: parent anchors.margins: 10 } }
If I link this application with 'MyLib' and run it, then everything works correctly and I can see my custom rectangle control. However, if I try to load this QML file into the Designer, I get the message: "QML module not found (MyLib.Rectangle)". Now, my understanding is that I need to compile my library as a plugin and load it in the Designer. So I compile it as a plugin, using this interface:
class DesignerPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlExtensionInterface_idd) public: void registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("MyLib")); qmlRegisterType<MyRectangle>("MyLib.Rectangle", 1, 0, "MyRectangle"); qmlRegisterModule(uri, 1, 0); qmlProtectModule(uri, 1); } };
Ok, so now I have MyLib.dll. Here's where the tutorials get hazy, but from what I understand, I need to generate the module with a 'qmldir' file. So I create a folder 'MyLib', containing the 'MyLib.dll' and a 'qmldir' file as follows:
module MyLib plugin MyLib designersupported
Now I add my folder to the 'QML_IMPORT_PATH' variable in qmake. Re-run qmake and try the Designer again, but I get the same message, albeit this time I can see the path to my 'MyLib' folder in the printout of 'QML_IMPORT_PATH'. Looking at the tutorials, I need to create a '.qmltypes' file? Ok, so I follow the tutorial and run 'qmlplugindump', which gives me the message:
typelist.qml:2:1: plugin cannot be loaded for module "MyLib": Cannot install element 'MyRectangle' into unregistered namespace 'MyLib.Rectangle'
This is where I'm stuck now. Obviously, it sees the plugin and manages to at least partially load it, as it can see the type that I'm trying to register. However, searching for this error message came up dry. I feel like I'm potentially overlooking something, and I'm sorry if this is painfully obvious, but I feel like I don't quite fully understand the QML extension plugin/URI system yet.
If anybody wants me to generate a runnable minimal example then let me know and I'll upload one.
-
@LScott said in Designer plugin for custom library types/controls?:
qmlRegisterType<MyRectangle>("MyLib.Rectangle", 1, 0, "MyRectangle");
i am not 100% sure, but there might be a high chance that you're not allowed to register an element with a.
in it's name.
(nevermind, thats not true) -
@raven-worx said in Designer plugin for custom library types/controls?:
a high chance that you're not allowed to register an element with a . in it's name.
I'm afraid I don't understand... The dot is only in the URI, and surely the dot, being the separator (e.g. QtQuick.Controls), that must be allowed. My actual library has lots of different things to register, so I really need to be able to separate it into modules.
Anyway I'll test it all at a top-level URI (e.g. the Rectangle having a URI of just 'MyLib') and see if that works.
-
Okay, update. I've been playing around. It can kind of work with the following folder structure:
/MyLib
----/MyLib/Rectangle
--------/MyLib/Rectangle/qmldir
/mylib.dllqmldir:
module MyLib.Rectangle plugin mylib ../../ designersupported
and I run:
qmlplugindump MyLib.Rectangle 1.0 /
It seems to go down through the folders, using '.' as a directory delimiter. This works, and I get valid output from 'qmlplugindump'. So this is fine for my simplified example. However, if I add in another type registration (let's call it 'MyCircle'):
qmlRegisterType<MyRectangle>("MyLib.Rectangle", 1, 0, "MyRectangle"); qmlRegisterType<MyCircle>("MyLib.Circle", 1, 0, "MyCircle");
The mere presence of this registration in the plugin class causes 'qmlplugindump' to fail with the error:
typelist.qml:2:1 plugin cannot be loaded for module "MyLib.Rectangle": Cannot install element 'MyCircle' into unregistered namespace 'MyLib.Circle'
Ideally, I guess I would like to have a folder structure matching my import URIs:
/mylib.dll /MyLib ----/MyLib/Rectangle --------/MyLib/Rectangle/qmldir ----/MyLib/Circle --------/MyLib/Circle/qmldir ----/MyLib/Square --------/MyLib/Square/qmldir ----/MyLib/Triangle --------/MyLib/Triangle/qmldir ----/MyLib/etc...
And being able to simply run:
qmlplugindump MyLib 1.0 /
And have it discover the 'qmldir' files and match them up with the relevant import URIs. Perhaps I need a top-level 'qmldir' that lists the different sub-modules in the various sub-directories. Is there a mechanism to declare sub-modules in a 'qmldir' file? I can't see any in the documentation at http://doc.qt.io/qt-5/qtqml-modules-qmldir.html
I'd love for somebody involved in the QQmlExtensionPlugin/Designer design to weigh in and let me know what I'm doing wrong. I feel like I'm fundamentally misunderstanding their original intentions...
-
@LScott
i think the following is more what you like?/MyLib |--- mylib.dll |--- qmldir
Then register each element with
qmlRegisterType<MyRectangle>("MyLib", 1, 0, "MyRectangle");
Then the qmldir file should look like this:module MyLib plugin mylib designersupported
Or do you really intend to create a separate module for each element?
-
It was my intention to separate the library into discrete modules, however if sub-modules are not supported, I guess there's not really any harm in lumping everything into one import. I'll just have to refactor parts of the library.
Luckily I haven't released any tools that use the library yet, so the interface can change without too much hassle :)
I'd still like to do it the way I intended, if at all possible. Perhaps there is a requested features list that this can go on?
I've managed to kind of hack it to load the plugin (I can run it in 'qmlscene'), but when loading a file that uses the import in the QML Designer, I get the very helpful error message:
Qt Quick emulation layer crashed.
I'm working on trying to run 'qml2puppet' in a more verbose mode, to try to figure out more about what's causing it to crash...
-
@LScott said in Designer plugin for custom library types/controls?:
It was my intention to separate the library into discrete modules, however if sub-modules are not supported, I guess there's not really any harm in lumping everything into one import. I'll just have to refactor parts of the library.
it is supported. But every module also needs to have a separate plugin. It's not possible to load the same plugin multiple times.
Like you specified in your distinct qmldir files (plugin mylib ../../
) -
Ah, I see. So if I wanted my submodules, I would need to create a separate plugin for each (each linking the static library MyLib). I think I understand, thank you.
Without getting too off-topic, is there a way to run the Designer ('qml2puppet') in a more verbose error reporting mode. I can run my plugin just fine in 'qmlscene', but 'qml2puppet' crashes without an explanatory message, so I'm not really sure where to start fixing it, as I'm not sure what's wrong with the plugin causing 'qml2puppet' to crash.
Anyway, thanks again.
-
@LScott said in Designer plugin for custom library types/controls?:
each linking the static library MyLib
whereas linking dynamically against your lib you would share the code amongst each plugin. The OS is that smart not to load a lib multiple times.
Without getting too off-topic, is there a way to run the Designer ('qml2puppet') in a more verbose error reporting mode
i cant tell you. But check the help of the commands (calling
-h
parameter or similar) -
Right, understood. I was just thinking that there would be a way for the Designer to load the plugin once and then use the loaded plugin for multiple modules, simply ignoring the namespaces not defined in any given module, with the knowledge that they may be handled in a different module, then print errors regarding unhandled namespaces at the end?
Perhaps I should modify the source of 'qmlplugindump' to ignore these errors and compile it for use in my own toolchain?
My issue running 'qml2puppet' is when running it manually, I get the message:
The application was unable to start correctly (0xc000007b).
I get the feeling it's expecting some symbols from libraries to already be loaded, so I loaded Dependency Walker and I'm trying to figure out what libraries/symbols are missing for me to run it.
-
After some tinkering, I've gotten the designer plugin to work the way I originally wanted. I simply commented out the 'module' directive in the 'qmldir' file:
# module MyLib typeinfo plugin.qmltypes plugin MyLib ../ designersupported
and added some qmake targets to automatically run 'qmlplugindump' post-build.
So now my plugin directory structure looks like this:
/imports |---MyLib.dll (or libMyLib.so) |---/MyLib |---|---qmldir |---|---plugin.qmltypes
Now I can get the plugin to run in 'qmlscene':
qmlscene -I <imports directory> <main qml file>
There are only two caveats in this case. First, QML files that are to import anything from the library must first import 'MyLib 1.0' before importing anything else ('MyLib.Rectangle', 'MyLib.Circle', etc.). This is so that the plugin is loaded initially to populate the other imports. Secondly, this will produce the following message when the plugin is loaded:
Module 'MyLib' does not contain a module identifier directive - it cannot be protected from external registrations.
However in my case, this is not an issue and can be safely overlooked.