C++ class updating properties for QML elements
-
Hi to all,
I have a problem and two solutions. I will know what is the best. The scenario is the following:
In some cases I have a set of properties e.g. width and height of a object from which other properties depends on (bound to it) e.g. button_size and button_spacing. The relation is based on some algoritm e.g. button_size = getButtonSize(width) and button_spacing = getButtonSpacing(width, height)
The possible solutions are the following:
I create a Qt/C++ class ButtonsProperties that exposes only properties to the QML document. This means that in the C++ class I expose the following QOBJECT components:
- btnSpacing
- btnSize
- width
- height
Then in Qml I create the following:
@
ButtonProperties {
id: myButtonClasswidth: parent.width height: parent.height
}
@
Then when the element is set in the QML document I can use the other property for which the class has calculated the value as in the following example:
@
Recatngle {
id: myButton
width: myButtonClass.btnSize
height: myButtonClass.btnSize
}
@and so on.
The alternative is to explicitly declare the calculation functions as INVOKABLE in the C++ class so I can use the method itself when I set the property in the QML document as in the following example.
@
Rectangle {
id: myButton
width: myButtonClass.getBtnSize(parent.widht)
height: myButtonClass.getBtnSize(parent.width)
}
@And so on. In both cases the binding is respected and there are no binding loops. What is the most correct usage ?
Many thanks to all the suggestions.
-
I would keep all UI related properties and especially calculations very far away from any C++ code. QML is there to create the UI for you so you are better off having everything there and keep application logic and processing in C++. This is what I suggest, although you can do all kinds of things in both QML and C++... I would just not advice for it :)
So to your question; I would say definitely use the first approach.
What comes to size calculation for UI based on some other properties; have you considered using states? In my own code I prefer to keep the logic in QML very light, as usually you don't need to write a lot of code to achieve the UI state changes. QML States is a perfect tool for this. You can put all the "logic" (states, connections...) of a component into a separate file and hook the component to some signals for example to do the state changes where you change the properties.
-
Hello Kipeli, every idea or advice is always welcome keeping my mind open :)
I agree with your point of view; my question was also to investigate if a method was more complex and/or resource consuming respect the other. Working in QML keeping in mind only the properties and state changes seems best to me. I have tested the second method exposed above and when it worked I thought that the second was probably a more elegant and well structured way.
Using a function in the C++ code based on properties assignement from QML that creates automatically the new properties as all the needed data (properties) are done solve the problem at all.
In some cases there are methods that should be called as methods. When possible I keep these separated from the QML code using js components. When operation is simple to manage in C++ or it is necessary to manage parameters depending by the plaform I should use C++ classes where different behaviors are addressed with #ifdef preprocessor instructions.
@
#ifdef Q_OS_SYMBIAN
#include "symbian_constants.h"
#else
#include "desktop_constants.h"
#endif
@
So the same application remain full compatible with different platforms deciding what code should be used / included at compile time.Last, for methods that should remain methods I have found a way to register the class in QML without creating the corresponding object. I have not investigated in-depth in this direction and I am not sure if this is a good way to be adopted for direct function calls from QML. I try to simplify with an example:
In C++ I create the class MyMethods and in main.cpp I register it.
@
QmlApplicationViewer viewer;
// Construct FileHelper instance and expose it to QML.
MyMethods myMethods;viewer.engine()->rootContext()->setContextProperty("myMethods", &myMethods);
@
so in QML I can use property assignements or events managing directly the method calls without defining any property:
@
Item {
id: myItem
width: myMethods.setWidth()
...
@
This last way don't require importing the class in the QML documents but it seems to me difficult to port and reuse it in more than one application.What do you thing about this?
Many thanks.
-
It sounds like you are creating one app for multiple platforms and every platform has its own UI - and you use the C++ code to remain portable between UIs?
Well. If I were you I would simply create multiple QML UIs for each platforms (since it's so easy and fast), set the different UIs in their own folders, and then in C++ just load the appropriate "root UI" from the correct folder.
I know this sounds like a lot of work and copy-pasting, but IMHO it's still more elegant and not really that much of work since it's mostly copying the UI files as they are into correct folders. This way you have the UI still separate from C++ and it's easier for you to make platform specific changes in the UI while keeping the logic the same.
-
Hi,
A approch could be, i guess, to use a QDeclarativePropertyValueSource.
Depending on circumstances, if you had to pass by the C++ and if the speed is important for the property you bind. -
Keep QML as dumb as possible: just layout your UI. Prefer to inject the properties as context property. If application logic is supplied by a well structured QObject, you can use it to any view possible like QWidget, QML, WebView, so one logic serves many views. When writing these engines (app logic) think view agnostic.