Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Designer plugin for custom library types/controls?
QtWS25 Last Chance

Designer plugin for custom library types/controls?

Scheduled Pinned Locked Moved Solved QML and Qt Quick
designerquickplugin
11 Posts 2 Posters 4.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    L Offline
    LScott
    wrote on 15 Mar 2018, 22:46 last edited by
    #1

    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.

    R 1 Reply Last reply 16 Mar 2018, 09:01
    0
    • L LScott
      15 Mar 2018, 22:46

      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.

      R Offline
      R Offline
      raven-worx
      Moderators
      wrote on 16 Mar 2018, 09:01 last edited by raven-worx
      #2

      @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)

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      0
      • L Offline
        L Offline
        LScott
        wrote on 16 Mar 2018, 16:55 last edited by
        #3

        @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.

        1 Reply Last reply
        0
        • L Offline
          L Offline
          LScott
          wrote on 16 Mar 2018, 18:30 last edited by
          #4

          Okay, update. I've been playing around. It can kind of work with the following folder structure:

          /MyLib
          ----/MyLib/Rectangle
          --------/MyLib/Rectangle/qmldir
          /mylib.dll

          qmldir:

          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...

          R 1 Reply Last reply 19 Mar 2018, 07:33
          0
          • L LScott
            16 Mar 2018, 18:30

            Okay, update. I've been playing around. It can kind of work with the following folder structure:

            /MyLib
            ----/MyLib/Rectangle
            --------/MyLib/Rectangle/qmldir
            /mylib.dll

            qmldir:

            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...

            R Offline
            R Offline
            raven-worx
            Moderators
            wrote on 19 Mar 2018, 07:33 last edited by
            #5

            @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?

            --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
            If you have a question please use the forum so others can benefit from the solution in the future

            1 Reply Last reply
            0
            • L Offline
              L Offline
              LScott
              wrote on 19 Mar 2018, 17:41 last edited by
              #6

              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...

              R 1 Reply Last reply 19 Mar 2018, 17:54
              0
              • L LScott
                19 Mar 2018, 17:41

                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...

                R Offline
                R Offline
                raven-worx
                Moderators
                wrote on 19 Mar 2018, 17:54 last edited by
                #7

                @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 ../../)

                --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                If you have a question please use the forum so others can benefit from the solution in the future

                1 Reply Last reply
                0
                • L Offline
                  L Offline
                  LScott
                  wrote on 19 Mar 2018, 19:18 last edited by
                  #8

                  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.

                  R 1 Reply Last reply 19 Mar 2018, 19:50
                  0
                  • L LScott
                    19 Mar 2018, 19:18

                    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.

                    R Offline
                    R Offline
                    raven-worx
                    Moderators
                    wrote on 19 Mar 2018, 19:50 last edited by
                    #9

                    @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)

                    --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                    If you have a question please use the forum so others can benefit from the solution in the future

                    1 Reply Last reply
                    0
                    • L Offline
                      L Offline
                      LScott
                      wrote on 19 Mar 2018, 21:48 last edited by LScott
                      #10

                      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.

                      1 Reply Last reply
                      0
                      • L Offline
                        L Offline
                        LScott
                        wrote on 23 Mar 2018, 17:37 last edited by LScott
                        #11

                        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.

                        1 Reply Last reply
                        1

                        10/11

                        19 Mar 2018, 21:48

                        • Login

                        • Login or register to search.
                        10 out of 11
                        • First post
                          10/11
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved