QML Plugin w/ QML resources
Hello all,
new to this form, and getting started with Qt/Quick. I am currently trying to factor out generic parts of our UI code into a plugin which would then provide new UI components usable in multiple projects. I chose the plugin path (over QML module), because deployment is a *little* less complicated (why in the world do I need to maintain the qmldir file? Why can't the plugin shared lib provide all info when loaded?), and since the code is fairly dynamic I prefer the typed environment of a C++ implementation over Javascript. However, I did not want to lose the advantages of the dedicated QML syntax. I therefore decided to include QML files as resources inside the plugin lib, load the components at runtime and create and parameterize items as required. In particular, I have a navigation panel which can show an arbitrary number of navigation tiles which will navigate to a configurable target.
Now, here comes issue 1: I am able to load the QML component inside the componentComplete() method of my contributed type (call it NavigationPanel). I then try to find the GridLayout which is defined inside the resource file by id (findChild<QQuickItem *>(name)) - which fails.
issue 2: I then try to get at the GridLayout using childItems().at(2), assuming that is the right one (theres only 2 nested items). I then go on to find nested NavigationTile objects (another contributed type) and use them to parameterize items created from another loaded QML component, and add the items to the GridLayout. When I start the application, nothing displays
Heres my questions: is my approach even feasible? Why am I not able to retrieve the GridLayout child by id? What do I need to do to make everything display correctly? Do I need to implement updatePaintNode (and set QQuickItem::ItemHasContents), and if so, how?
heres an example of the app code that uses my contributed types:
Code:
Rectangle {
NavigationPanel {
NavigationTile {
target: "nextpage1.qml"
}
NavigationTile {
target: "nextpage2.qml"
}
}
}
and heres the QML that gets loaded from within the NavigationPanel implementation:
Code:
Rectangle {
Rectangle {
GridLayout {
id: tilesGrid
}
}
}
I hope this makes sense to someone out there
thanks,
Christian
Re: QML Plugin w/ QML resources
findChild returns objects by name based on objectName property, not their id. However using findChild to access some object like that seems really weird to me. I don't know what your elements do so it is hard for me to determine what you are trying to achieve. I just have an impression that you are overcomplicating things :) How does "NavigationTile" differ from Loader? What is its relation to NavigationPanel?
Re: QML Plugin w/ QML resources
Quote:
findChild returns objects by name based on objectName property, not their id
huh? Never heard of that property. I am also not able to find any reference to it in the docs.
Quote:
How does "NavigationTile" differ from Loader? What is its relation to NavigationPanel?
NavigationTile (and NavigationPanel, for that matter) behaves like Loader, in that it loads a QML component and creates an item from it. However, they reside in C++ code because I want to deploy a plugin (and dont want to use Javascript - tried to, didnt seem to work). Essentially, both types are wrappers that instatiate items from QML and parameterize them. NavigationTile describes the tiles nested inside the panel, corresponding to Rectangles inside the GridLayout
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
huh? Never heard of that property. I am also not able to find any reference to it in the docs.
QObject::objectName :)
Quote:
NavigationTile (and NavigationPanel, for that matter) behaves like Loader, in that it loads a QML component and creates an item from it. However, they reside in C++ code because I want to deploy a plugin (and dont want to use Javascript - tried to, didnt seem to work). Essentially, both types are wrappers that instatiate items from QML and parameterize them. NavigationTile describes the tiles nested inside the panel, corresponding to Rectangles inside the GridLayout
It seems an overkill to me to do it from within C++. If you have to have a C++ plugin then why not implement the code in QML, embed it into the C++ plugin using the resource system and then expose the types from within the plugin?
Re: QML Plugin w/ QML resources
Quote:
It seems an overkill to me to do it from within C++.
it sure isn't totally convenient, but still better IMO than Javascript. And additionally, I get better deployability (one shared lib vs. multiple QML files)
Quote:
If you have to have a C++ plugin then why not implement the code in QML, embed it into the C++ plugin using the resource system and then expose the types from within the plugin?
but that is exactly what I am trying to to! Now just tell me how, and I am done :). Maybe you have an example I can look at?
regards,
Chris
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
it sure isn't totally convenient, but still better IMO than Javascript.
If you extrapolate that then you will end up saying there is no sense in using QML at all.
Quote:
And additionally, I get better deployability (one shared lib vs. multiple QML files)
As I said, those QML files can be embedded in the plugin library.
Quote:
but that is exactly what I am trying to to! Now just tell me how, and I am done :). Maybe you have an example I can look at?
I'd need to look for an example, but the general idea is to expose qml types with paths pointing to the resource system using a qmldir file.
Re: QML Plugin w/ QML resources
Quote:
If you extrapolate that then you will end up saying there is no sense in using QML at all.
I dont agree. Right now I have the overall design nicely kept in QML, and I like it. Setting that up in C++ would be a nightmare. However, I am keeping the dymamic code in C++
Quote:
the general idea is to expose qml types with paths pointing to the resource system using a qmldir file.
sorry to say, but that is way too general to be useful. I have already spent a couple of days working/investigation on this. I am also not sure that you have caught on to my problem: I am loading the QML file as a resource embedd within the shared lib, no qmldir involved. What I am looking for is an example of a C++ plugin which loads an embedded QML component and somehow exposes that as a custom type.
thanks
Re: QML Plugin w/ QML resources
The easiest way to do it is to have a regular directory structure containing qml files, put that all into qrc file and use QQmlEngine::addImportPath() where you will point to the qrc file. In my opinion having a C++ component whose only task is to expose a QML type that is already in a QML file makes no sense. If you already have a QML file and you want to expose it to QML then do that directly.
Re: QML Plugin w/ QML resources
Just saying: if you are using findChild() to get access to an object defined in a QML scene, you are almost certainly doing something less than ideal.
It creates a dependency of the C++ code toward the QML code, something one usually wants to avoid (goal being that the UI depends on the compiled code, not the other way around).
Cheers,
_
Re: QML Plugin w/ QML resources
Quote:
In my opinion having a C++ component whose only task is to expose a QML type that is already in a QML file makes no sense
agreed. My plugin does a lot more, this is just the reduction of one problem.
I'll try again. Heres the QML interface I want to offer to my client:
Code:
NavigationPanel {
rows: 2
columns: 2
cellSpacing: 5
cellPadding: 2
NavigationTile {
color: "read"
image: "some.png"
columnSpan: 1
rowSpan: 2
target: "someother.qml"
}
NavigationTile {
color: "read"
image: "some.png"
target: "someother.qml"
}
}
now, internally, the QML representation for NavigationPanel and NavigationTile will be much more complex. NavigationPanel will consist of 2 nested Rectangles (the outer one being the one the navigation target item is placed into when a tile is clicked), and a nested GridLayout. The NavigationTile contains, among other things, a Loader object that will load the target page. The internal representations should not be exposed to the clients of my component, they should only need to write the code shown above.
As I see it, implementing this will always involve a lot of imperative code, which I prefer to write in C++ (I actually tried Javascript, but got stuck)
Re: QML Plugin w/ QML resources
I don't see a need for any complex imperative code here. I don't know what you have so far so let's try from scratch... BTW. I didn't test the code below, so it can contain minor errors but the general idea should be valid.
NavigationPanel -- contains two rectangles and a grid layout:
Code:
import QtQuick 2.2
import QtQuick.Layouts 1.0
Item {
id: root
property alias rows: grid.rows
property alias columns: grid.columns
property alias cellSpacing: grid.rowSpacing
property int cellPadding: 0
default property list<NavigationTile> tiles
Rectangle {
id: outer
Rectangle {
id: inner
}
}
GridLayout {
id: grid
columnSpacing: rowSpacing
Repeater {
model: root.tiles
Item {
margins: root.cellPadding
Image {
anchors.fill: parent
source: modelData.image
}
Rectangle {
anchors.fill: parent
color: modelData.color
}
Layout { rowSpan: modelData.rowSpan; columnSpan: modelData.columnSpan }
}
}
}
}
NavigationTile -- definition of a tile
Code:
import QtQuick 2.2
QtObject {
id: tile
property color color
property string image
property int columnSpan: 1
property int rowSpan: 1
property string target
}
What's next?
Re: QML Plugin w/ QML resources
looks not bad, actually ;). At least at first sight I can see how this might fit my bill. It seems that wrapping ones head around this paradigm is not as easy as one would think. There are several things in your code which can understand when I read them, but don't see how I would have come up with them. I am not in my Qt office right now, so I will try it out tomorrow and report. Thanks much so far
what if I insisted on doing this in C++? Just joking..
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
looks not bad, actually ;). At least at first sight I can see how this might fit my bill. It seems that wrapping ones head around this paradigm is not as easy as one would think. There are several things in your code which can understand when I read them, but don't see how I would have come up with them. I am not in my Qt office right now, so I will try it out tomorrow and report. Thanks much so far
Here is an actually working code:
Code:
import QtQuick 2.2
import QtQuick.Layouts 1.0
Item {
id: root
property alias rows: grid.rows
property alias columns: grid.columns
property alias cellSpacing: grid.rowSpacing
property int cellPadding: 0
//default property list<NavigationTile> tiles
property list<NavigationTile> tiles
default property alias __tiles: root.tiles
Rectangle {
id: outer
Rectangle {
id: inner
}
}
GridLayout {
id: grid
columnSpacing: rowSpacing
anchors.fill: parent
Repeater {
model: root.tiles
Item {
anchors.margins: root.cellPadding
implicitWidth: 100
implicitHeight: 100
Image {
anchors.fill: parent
source: modelData.image
z: 1
}
Rectangle {
anchors.fill: parent
color: modelData.color
}
Layout.rowSpan: modelData.rowSpan
Layout.columnSpan: modelData.columnSpan
}
}
}
}
Code:
import QtQuick 2.2
QtObject {
id: tile
property color color
property string image
property int columnSpan: 1
property int rowSpan: 1
property string target
}
And main qml file:
Code:
import QtQuick 2.2
NavigationPanel {
width: 600
height: 400
columns: 2
rows: 2
NavigationTile {
color: "red"
image: "/usr/share/icons/default.kde4/64x64/apps/kde.png"
rowSpan: 2
}
NavigationTile {
color: "yellow"
image: "/usr/share/icons/default.kde4/64x64/apps/kontact.png"
}
NavigationTile {
color: "orange"
image: "/usr/share/icons/default.kde4/64x64/apps/ktip.png"
}
}
Quote:
what if I insisted on doing this in C++? Just joking..
Then I would continue to say it was an overkill and a waste of effort. I don't know which part you'd want to do in C++.
At this pace we'll have implemented a complete Metro UI by the end of next week...
Re: QML Plugin w/ QML resources
one more related question: now that I have these pretty QML files that are usable from all my Metro-UI-like applications, how do I deploy them? I don't like the idea of having to copy them over to each deployment machine, and set the QML_IMPORT_PATH environment variable so they are found. I also dont like the idea of having to copy them to each application project and add them to the local resources.
Also, I wonder how I would maintain these files from within QtCreator. AFAIK there is not project type "QML library" that would only contain QML files and a qmldir file. My ideal solution would be to be able to setup such a project, then define a dependency from the application project to this one, and have the build system automatically package the QML files from the "library" within the application resources. But as it stands, this looks like all manual work.. or did I again overlook something?
Suggestions?
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
one more related question: now that I have these pretty QML files that are usable from all my Metro-UI-like applications, how do I deploy them?
There is a number of approaches you can take.
Quote:
I don't like the idea of having to copy them over to each deployment machine, and set the QML_IMPORT_PATH environment variable so they are found. I also dont like the idea of having to copy them to each application project and add them to the local resources.
Well... one way or the other you will have to deploy them to each machine or make the machines fetch it over network each time the application is executed. I really hate having to deploy my application to every machine that is to launch this application too, but that's how computers are organized.
Quote:
Also, I wonder how I would maintain these files from within QtCreator. AFAIK there is not project type "QML library" that would only contain QML files and a qmldir file.
Qt Quick UI project seems a good choice.
Quote:
My ideal solution would be to be able to setup such a project, then define a dependency from the application project to this one, and have the build system automatically package the QML files from the "library" within the application resources.
That would make you "copy them to each appplication project and add them to the local resources".
Quote:
But as it stands, this looks like all manual work.. or did I again overlook something?
Suggestions?
As I said, there is a number of approaches you can take depending on what you want to achieve and what tools you have at hand. You might for instance create a qml-bundle from those files and deploy that. However I'd suggest deploying the library as a set of files in a location where your applications will be able to find them. Just like with QML extensions bundled with Qt.
Re: QML Plugin w/ QML resources
Quote:
That would make you "copy them to each appplication project and add them to the local resources".
well, not me - the build system would do it for me. My issue is not with redundant resources, but with development and deployment convenience.
What about putting all reusable QML files in one place, calling
Code:
rcc -binary myresource.qrc -o navigationlibrary.rcc
on them and then putting that file int the application project, together with a
Code:
QResource::registerResource("navigationlibrary.rcc");
in main.cpp ? Would those QML files be found? I suppose not, because the qmldir file would be missing..
Quote:
However I'd suggest deploying the library as a set of files in a location where your applications will be able to find them. Just like with QML extensions bundled with Qt.
I was actually wondering how the Qt-bundled QML files are found at runtime. How exactly is it?
My goal is a fully automated development process for library and application, and a deployment that consists of as few files as possible
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
well, not me - the build system would do it for me.
If that's ok with you then it is easy to achieve with qmake.
Quote:
What about putting all reusable QML files in one place, calling
Code:
rcc -binary myresource.qrc -o navigationlibrary.rcc
on them and then putting that file int the application project, together with a
Code:
QResource::registerResource("navigationlibrary.rcc");
in main.cpp ? Would those QML files be found? I suppose not, because the qmldir file would be missing..
qmldir should be in that qrc file as well. And I already suggested doing that a couple of posts ago :)
Quote:
I was actually wondering how the Qt-bundled QML files are found at runtime. How exactly is it?
Have a look into your $QTDIR/qml/ and $QTDIR/imports (where QTDIR is the directory where Qt is installed).
Quote:
My goal is a fully automated development process for library and application, and a deployment that consists of as few files as possible
Usually these two requirements are contradictive. If you want as few files as possible, then go for static linking and embedding everything in the main application binary. If you are after easy development then dump all the files you need in a predefined set of directories and copy them everywhere you need.
Re: QML Plugin w/ QML resources
Quote:
If that's ok with you then it is easy to achieve with qmake.
manually, that is. I happen to be a big fan of IDE automation.
Quote:
Have a look into your $QTDIR/qml/ and $QTDIR/imports (where QTDIR is the directory where Qt is installed).
I know where the files are. What I dont understand is how they are found at runtime from within my app on a system with no Qt installation. I found a post describing a deployment that consisted of basically the complete Qt install bundled together with the app, consisting of hundreds of files. Yuck!
Quote:
If you want as few files as possible, then go for static linking and embedding everything in the main application binary.
how would this be done? Where is this described? Will this really do away with lots of QML files flying around? What about QML plugins?
Quote:
If you are after easy development then dump all the files you need in a predefined set of directories and copy them everywhere you need.
what I want is an IDE project setup that will allow all flexibility during development, AND a "deploy" button that will generate an installable package according to my liking (static, shared libs, whatever). Following your suggestion, I would maintain the QML files in a Qt Quick UI project and manually create (and invoke) a makefile that would deploy the files during development to some central location where the other projects can find them (HOW again do they find them??) . For deployment the same makefile would then generate a resource file. All this seems completely non-automated by the IDE, and therefore a lot of hacking, testing, failing and retrying (been through that for a few days already. Hated it!). Or maybe I go back to the QML plugin project type, because that will at least allow me to trigger the build from within the IDE
bottom line: if I am not mistaken, IDE automation leaves a lot to be desired (stuff that is a matter of course in other systems). If I am mistaken, please fill me in..
1 Attachment(s)
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
manually, that is. I happen to be a big fan of IDE automation.
The IDE does not write source code for you. You can consider the installation boiler-plate code as yet another part of the source code. At some point Qt Creator may support this out of the box, I'm sure Qt Creator could use your contribution in this regard :)
Quote:
I know where the files are. What I dont understand is how they are found at runtime from within my app on a system with no Qt installation.
The engine looks for them in particular directories. The set of directories is configurable either via qt.conf or from within the application itself.
Quote:
I found a
post describing a deployment that consisted of basically the complete Qt install bundled together with the app, consisting of hundreds of files. Yuck!
That's surely an overkill. Nevertheless it is true that if a particular file is needed by an application and this file is not found by default in the system then you need to deploy it. It's the same with any framework -- you also have to deploy new versions of .NET framework in Windows environments if you want to use applications that require new .NET environment. These are "hundred of files" as well.
Quote:
how would this be done? Where is this described?
I believe this is described in the docs and in many places in the internet, including our wiki.
Quote:
Will this really do away with lots of QML files flying around?
If you embed them in the application binary then yes.
Quote:
What about QML plugins?
With static binaries you cannot load any plugins at runtime so these (in form of so called "static plugins") need to be embedded in the application executable as well. Which of course will make your application executable quite big.
Quote:
what I want is an IDE project setup that will allow all flexibility during development, AND a "deploy" button that will generate an installable package according to my liking (static, shared libs, whatever). Following your suggestion, I would maintain the QML files in a Qt Quick UI project and manually create (and invoke) a makefile that would deploy the files during development to some central location where the other projects can find them (HOW again do they find them??) . For deployment the same makefile would then generate a resource file. All this seems completely non-automated by the IDE, and therefore a lot of hacking, testing, failing and retrying (been through that for a few days already. Hated it!). Or maybe I go back to the QML plugin project type, because that will at least allow me to trigger the build from within the IDE
You can do all that with a qmake or qbs script. Then just invoke build & install in Qt Creator and you are done.
Quote:
bottom line: if I am not mistaken, IDE automation leaves a lot to be desired (stuff that is a matter of course in other systems). If I am mistaken, please fill me in..
I would really love to have an IDE that writes my programs for me but I believe this is not going to happen anytime soon. And as far as my private opinion is concerned, the IDE automating too much causes many inexperienced to-be-programmers (who otherwise believe themselves to be great developers) to fail completing simple programming tasks or understanding simple computer mechanisms if there is no graphical IDE which does the work for them upon pressing a single button. I know it is not required from a developer to know how electrons travel through circuits in their computers but I would require a developer who uses C++ to be able to create a C++ class from scratch without the IDE having to provide boiler-plate code for them.
BTW. The current version of your "pseudo-metro" ui (not a single line of imperative code):
Attachment 10541
generated with this main qml:
Code:
import QtQuick 2.2
Rectangle {
width: 600
height: 400
color: Qt.rgba(114/255, 0, 172/255, 1)
function icon(name) { return "/usr/share/icons/default.kde4/64x64/apps/"+name+".png" }
NavigationPanel {
anchors.fill: parent
anchors.margins: cellSpacing
columns: 6
rows: 3
cellPadding: 10
cellSpacing: 4
NavigationTile {
color: Qt.rgba(25/255,153/255,0,1)
image: icon("kde")
title: "KDE"
columnSpan: 2
borderWidth: 1
}
NavigationTile {
color: Qt.rgba(255/255,152/255,29/255,1)
image: icon("kde")
columnSpan: 2
borderWidth: 1
}
NavigationTile {
color: Qt.rgba(70/255,23/255,180/255,1)
image: icon("knotes")
title: "Notes"
borderWidth: 1
}
NavigationTile {
color: Qt.rgba(25/255,153/255,0,1)
image: icon("kde")
borderWidth: 1
}
NavigationTile { color: "orange"; image: icon("kontact"); columnSpan: 2 }
NavigationTile { color: "orange"; image: icon("ktip"); columnSpan: 2 }
NavigationTile { color: "orange"; image: icon("ktip") }
NavigationTile { color: "orange"; image: icon("ktip") }
NavigationTile { color: "orange"; image: icon("ktip"); columnSpan: 2 }
NavigationTile { color: "orange"; image: icon("ktip") }
NavigationTile { color: "orange"; image: icon("ktip") }
}
}
Re: QML Plugin w/ QML resources
Quote:
And as far as my private opinion is concerned, the IDE automating too much causes many inexperienced to-be-programmers (who otherwise believe themselves to be great developers) to fail completing simple programming tasks or understanding simple computer mechanisms if there is no graphical IDE which does the work for them upon pressing a single button
that documents that we come from different worlds. I have 25 years of C, C++, Smalltalk (10 yrs) and Java (15 yrs) behind me, and I have come to appreciate systems where I can push a few buttons to get at least the project infrastructure setup perfectly. There is way enough complexity left to handle in the application domain space. And I am really glad if I can staff my project with "inexperienced to-be-programmers" (whatever they think of themselves), because those are far easier to come by.
Anyway, thanks for your help so far. Really appreciated. I'll be back
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
that documents that we come from different worlds. I have 25 years of C, C++, Smalltalk (10 yrs) and Java (15 yrs) behind me, and I have come to appreciate systems where I can push a few buttons to get at least the project infrastructure setup perfectly.
This is ok if you know how to do the same without the button. My point is that buttons work for typical simple cases but as a project grows, buttons tend to stop working and one has to get his hands dirty.
Quote:
And I am really glad if I can staff my project with "inexperienced to-be-programmers" (whatever they think of themselves), because those are far easier to come by.
Yes, and then they come to QtCentre or similar site asking where they can find a tutorial on X (where X is the exact task they are supposed to do for you), then they copy the example line by line and come back saying it wouldn't do what they wanted.
Quote:
Anyway, thanks for your help so far. Really appreciated.
No problem.
Re: QML Plugin w/ QML resources
Quote:
then they copy the example line by line
BTW, you seem to have forgotten to include the updated NavigationPanel.qml and NavigationTile.qml files in your previous post
Re: QML Plugin w/ QML resources
Quote:
Originally Posted by
doulos
BTW, you seem to have forgotten to include the updated NavigationPanel.qml and NavigationTile.qml files in your previous post
No, I haven't forgotten that :)