When I write a new component for QML in C++, I use the macro:
Q_PROPERTY(type READ getter WRITE setter NOTIFY signal)
Here I might use different types, e.g. int or QVariant. I was wondering, if I have any benefit of not using QVariant here.
To quote the documentation:
Without QVariant, this would be a problem for QObject::property() and for database work, etc.
So - what happens if I read or write to a property on the QML side?
My guess is, that it calls:
QVariant QObject::property(const char *name) const
bool QObject::setProperty(const char *name, const QVariant &value)
which means, that my properties, neatly defined as int, dobule, QString, ... returned by my getter will be wrapped as QVariant to be transferred from C++ to QML - a process that would be unnecessary if I would have defined it as QVariant from the beginning.
If I have a Component in QML it is very likely that I have bindings from one property to multiple other properties, so reading happens quite often. So it would be a good idea to have the type in Q_PROPERTY QVariant, as otherwise I would wrap it tons of times.
On the C++-Side I might decide, whether it is read often. If so, I buffer the value in the original data type for access within C++ but create the QVariant when ever the value changes, so for QML it is unnecessary to create it multiple times. - Or is this happening automatically? Does a QML Object has all it's property values buffered as QVariant?
No, i think you don't. At least if you expose it to QML or use in any kind of subclasses from QAbstractListModel( all values there returned by data function and return type is QVariant)
I have just run my app throught debugger with breakpoints to see what happens with Q_PROPERTY variables. Just added simple code snipped in MouseArray onClicked function:
var p = ExContact.phone;
var e = ExContact.status;
phone is QString and status is qint8
Going throught breakpoints shows:
p is undefined
p becomes string
p value is assigned
e is undefined
e becomes type number
e value is assigned
Calls trace is complicated in view but I can see what the QVariant is used and QMetaType is used.
So it looks like any type exposed throught Q_PROPERTY becomes QVariant anyway..
I performed some analysis and experiments on QML that lead me to this answer, which I deem very probable:
Experiment: Add a property property var test to an object, and analyze its meta object in C++. What I found is:
The QMetaObject::property() method always returns a QVariant as described in the signature of this method, but the type of the QVariant is always set to the type of the property. This means a property int someInt results in a QVariant::int while a property var someVar results in a QVariant::QVariant - which means at least here, we have to unpack the value twice. +1 for using the best fitting property type!
Experiment: Add breakpoints to the source file of QVariant (QtCore/qvariant.h) at the line containing inline QVariant(QVariant &&other) Q_DECL_NOTHROW : d(other.d) and inline QVariant &operator=(QVariant &&other) Q_DECL_NOTHROW, which I found likely to be called when I change the value of a QVariant in QML.
In QML I added a Button which incremented its x position upon click.
As expected, when ever I clicked the Button, those two lines were called repeatedly. Unfortunately, I am missing the insight, which QVariants where changed, and whether one of them is indeed our property for the position. However I don't think that Qt will have tons of QVariants somewhere in the backend, so I deem it likely, that my assumption is true, and on the QML-side the properties are handled as QVariant.
Take home message: Though the properties in QML are handled as QVariant it is beneficial to declare the properties using a better fitting type, as otherwise the result is not a QVariant::int but maybe a QVariant::QVariant::int.
In my experiments I found no trace of QJSValues flying around which I would not have been surprised to find instead of QVariant::QVariant when having a property var ...
Related
I have created two QML plugins with C++. Plugin A represents some specific UI item types and plugin B represents own "storage" singleton QML item type. The original motivation was to create QML storage singleton and use it through the most of QML UI code. Now I also want a direct interaction between A and B meaning they interact with each other just in C++ code bypassing QML. Only A needs to know about B, not the other way around. How can I achieve that?
No rootObject->findChild type of solution needed. The question is somewhat similar to Accessing QML objects from C++ except I need to be able to instantiate the object from QML PlugIn dynamic library in C++. I see the on-surface solution with dynamic library used by both QML and C++ code. But it is a bit more complicated: it has to be just one instance of code with data. Also there is probably a way to somehow "cast" the instance QML of plugin object to normal Qt plugin and then call whatever I want from it.
I think this should also work for Plugins and not only for registered types. I can't try it right now, I will remove this line once I tried it out later that day:
Each QML Singleton, defined in C++ needs to have a static method with the signature:
static QObject* someName(QQmlEngine *, QJSEngine *);
that returns a pointer to the single instance of this class.
I think usually you don't need the QQmlEngine or QJSEngine to make it work, so you can easily implement the full Singleton-Pattern by adding a second static method:
static QObject* instance();
which returns exactly the same.
You might alternatively have a
static MySingletonObject* instance();
so you don't need to cast afterwards.
Now you can just import your class, and you will have access to the very same instance, by calling this static method.
If above does not work, you might do it the hacky way:
You can alternatively use your root node and set a property:
property QtObject mySingleton: MySingleton
Then you can use findChild to grab your root node and read the very same property. To be able to do this you might need to access something of the singleton before, as otherwise the instance might be missing.
property Item singl: MySingleton
Component.onCompleted: {
// MySingleton.color = 'green' // If this line is commented out, it will fail for me for a QML-defined Singleton at least.
singl.color = 'red'
}
This solution works for me for QML-defined qmldir-registered singletons, as well as C++-defined and qmlRegisterSingleton(...)-registered singletons. With QML-defined singletons you might need access the singleton once, before you can assign it. I don't know the internal reasons for that.
I have the habit of writing my "propertyChanged" signals with an argument, such that the receiving end doesn't need to call the Q_PROPERTY's READ function explicitly.
I do this out of clarity and the assumption that in a QML data binding situation, no "expensive" call to the getter needs to be done to actually fetch the value, as it's already passed to QML as a signal argument.
My colleagues disagreed and said it was against "QML style", to which I responded the documentation clearly states it may have an argument that will take the new value of the underlying member:
NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property.
Nowhere in the documentation is it stated that the QML binding system uses this parameter to prevent an additional function call to the getter when handling the signal. I understand this call will probably be made from C++, so no "expensive" QML to C++ call will be made, but it still is an extra function call, which in principle could result in a visible performance penalty in case of many updates.
I tried inspecting the QML binding source code, but couldn't infer anything from it. I wonder if someone knows what the deal is: is the signal argument used or not?
I wonder if someone knows what the deal is: is the signal argument used or not?
Here's one way to check:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
class MyType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo(0)
{
}
bool foo() const
{
qDebug() << Q_FUNC_INFO;
return mFoo;
}
void setFoo(bool foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(mFoo);
}
signals:
void fooChanged(bool foo);
private:
bool mFoo;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
qmlRegisterType<MyType>("App", 1, 0, "MyType");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import App 1.0
Window {
width: 400
height: 400
visible: true
Switch {
id: fooSwitch
}
MyType {
id: myType
foo: fooSwitch.checked
onFooChanged: print("onFooChanged, foo =", foo)
// onFooChanged: print("onFooChanged myType.foo =", myType.foo)
}
}
The output when switching back and forth is:
qml: onFooChanged, foo = true
qml: onFooChanged, foo = false
So it's safe to say that the value is used and not the getter.
To see what the output would have been like if the signal argument hadn't been used, uncomment the commented out line and comment out the other one:
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = true
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = false
Passing the values of the changed properties in the onPropertyChanged-signal is, though possible, most surely not the QML style.
Would it be, then you should expect that at least for the basic types it is implemented, which is easily shown, it's not.
basictypes.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 400; height: 450
property int num: 5
Button {
text: num
onClicked: num += 1
}
onNumChanged: console.log(JSON.stringify(arguments), arguments.length)
}
As you can see in the output, that there are no arguments passed, when you change even one of the most basic types, such as int.
If now QML would use the optional, but rarely implemented passed value this would create overhead, as you would always need to check the existence of the argument before using it. Though a simple check is not to expensive, if it usually evaluates to false, and then you use the workaround, why do it beforehand?
Though I might not rule out, that there are any passed values in any onPropertyChanged-signals in the official realse, there are none for properties added in QML with property [type] [name]. There also none for most inherited properties (tested the Button: text, width, height).
assumption that in a QML data binding situation, no "expensive" call
to the getter needs to be done to actually fetch the value, as it's
already passed to QML as a signal argument.
Technically speaking, it is not likely that there is anything similar to what you describe. It just doesn't make sense. If your getters are expensive, you should take care to cache the result in a simpler form, and either update on changes or on demand.
If QML bindings were exclusively single property to single property, such an optimization could make sense. But bindings in QML are actually anonymous expressions, and the way it works is when any change notification for an object that is referenced by the expression triggers its reevaluation. In such a case it would add needless complexity to the implementation, having one value sent from the notification signal to keep and others which you have to call getters for.
Obviously, I am just postulating here. Another thing that makes me skeptic of the existence of such an optimization is that the property binding system isn't all that sophisticated or focused on efficiency. Which is evident from the fact that it cannot even eliminate redundant evaluations in the case of property value inter-dependencies. I've seen claims that such optimizations exist, but I've tested and it failed to avoid even the most simple of binding tree redundancies.
Naturally, if you insist on MEMBER properties, this is not so easy to prove, since the getters are auto generated, and you don't get to insert your debug statement there.
But if you try it for a regular property, you will find out that even if the signal emits the actual value, the getter is invoked nonetheless. There is absolutely no reason why an auto-generated getter would get a different treatment.
As mentioned in the linked answer, if you need the value emitted for the C++ part of the code, then keep, it it will not interfere with the QML part. If not, then simply don't emit the value, even if minuscule, it is still overhead. Emitting the value is the C++ way, bindings are a fundamentally different concept that is not really applicable in C++ (not without extensive verbosity), the QML way does not require to emit the changed value.
So your are both wrong. It is not really "against the QML style", as it will not hinder anything, and having the option to emit a value in the documentation in no way suggest that it is "in line with the QML style", or that it gets any special treatment. So if that's the reason you are doing it, you might as well stop, as it is entirely redundant.
Note that the code, generated in QtCreator by rightclicking on a property foo and selecting "Refactor/Generate Missing Q_PROPERTY Members"
Q_PROPERTY(bool foo READ foo NOTIFY fooChanged)
generates a signal containing the property
void fooChanged(bool foo);
So I would argue that this is the Qt/QML style
I want to be able to send Qt signals over the network. Serializing a signal call is quite straight forward using Qt's meta type system:
Create a qMetaMethod using the static method ::fromSignal
Get the method name, parameter names, their typeIds [1] and values using the created meta method.
Pack everything into your preferred format (JSON, XML) and send it.
But so far I could not figure out how to invoke a signal using serialized data: QMetaObject::invokeMethod(..) takes the signal / method name as string. The problem are the arguments: They must be provided as QGenericArgument and those can only be created by using the Q_ARG macro which requires the actual type (not a string of it's name or the typeId) and the concerning value. Also the number of arguments must be defined at compile time, there is is no invokeMethod(..) which takes a list of arguments.
Am I missing something? Or is there a better / alternative way to do this?
[1] Further question: How do I ensure that types always get the same typeId when they are registered using Q_DECLARE_METATYPE(..)?
It is false that you can't create a QGenericArgument yourself. You're advised not to, but what you're trying to do is very implementation-dependent anyway. There isn't much to it: you provide a type name, and a pointer to the data of a given type. E.g.:
QGenericArgument one() {
static const char type[] = "int";
static const int data = "1";
return QGenericArgument{type, (void*)&data);
}
See the Introspectable Visitor section of this answer for more example code.
How do I ensure that types always get the same typeId when they are registered using Q_DECLARE_METATYPE(..)?
You don't. You should be using type names, and each process should resolve these to typeids locally.
Unless you want to implement it yourself, use something ready made, like the MIT-licensed qt-remote-signals.
You really should consider using Qt Remote Object since they accomplish everything that you need and more (heartbeat, automatic re-connection upon disconnect, works with either QLocalSocket or QTcpSocket under the hood, etc) It the easiest to get signals across the network with minimal effort that I know of.
https://doc.qt.io/qt-5/qtremoteobjects-index.html
You just need to define .rep text file, which is like an IDL definition file
class MyRemoteObject {
{
SIGNAL(foo(int value));
SIGNAL(bar(float value));
SLOT(somethingChanged(int newValue));
};
and then it takes that .rep file to generate code for the server side (known as 'source') and code for the client side (known as 'replica') using a built-in repc compiler that is called by qmake or cmake. Every signals called by the 'source' is automatically sent across every connected 'replicas', and slots called by 'replicas' are received by the 'source'
I have a C++ method made Q_INVOKABLE. I can call this method from QML and it works when it returns basic types (like QString). But I can't with a custom type. How should I do this? Should I return a QVariant instead? Ideally, I would like to return a pointer to my custom type if possible.
EDIT
I do:
qmlRegisterType<MyType>("Mine", 1, 0, "MyType");
qmlEngine->rootContext()->setContextProperty("testObj", new MyType());
I can use testObj global object or create MyType QML component. But I cannot use it in some javascript code as a return type from a Q_INVOKABLE C++ method.
This is possible but you need to define your custom type
as an interface.
First of all in your main() function:
qmlRegisterInterface<MyType>("MyType");
Now you can proceed to create an object and return a pointer
in a Q_INVOKABLE:
MyType* example = new MyType(parent);
return example;
Note: it might be beneficial to pass the parent to your custom QObject to ensure that this object is deleted together with his parent (to avoid memory leaks).
Yes, QVariant is the way to go for your custom class in that construction. Make sure you register your class.
That being said, you may wish to consider using Q_PROPERTY instead, for your custom type and then you can access that even without a function call. If you need custom parameters to the method and you cannot rearrange the code, this is obviously not an option.
Note that the target of this answer are people with a similar problem, rather than the original asker.
The described method should work, at least in recent versions of Qt (I'm using Qt 5.12, but it should work in older versions too). Maybe it was a bug in one of the first QML releases.
Checklist: Verify you have done the following:
Registered your type qmlRegisterType<MyType>("Mine", 1, 0, "MyType"); (or use qmlRegisterUncreatableType)
Imported your custom types in qml: import Mine 1.0
Derived your class from QObject.
Added Q_OBJECT to your class definition.
When returning pointers from a Q_INVOKABLE method, please take object ownership into account.
I think Dimitri's answer only applies when your type is copyable (so it wouldn't work for QObject). My understanding is that when you have a type that is being manipulated by Qt, for example if it is being returned by an invokable, the type has to be registered as a metatype:
Non-copyable types like any QObject derived model cannot be metatypes
A pointer type is a different type, therefore it needs to register separately from the classes it points to
For pointer type Foo* you can register it with metatype system: qRegisterMetaType<Foo*>("Foo*");
Consider signal manager that receives the signal, checks for some conditions, and if they are met, transmits signal to slot, discarding signal otherwise:
signal(some args) ---> [manager] ---> slot(some args)
I can implement it for each given set of arguments using QMetaObject::invokeMethod, say for void signal(int) and void slot(int):
.h
class Manager: public QObject
{
Q_OBJECT
public:
Manager(QObject* sender,const char* signal, QObject* recv, const char* slot);
private:
bool isOkToSend();
QString slotInvokeSyntax;
QObject *recv;
private slots:
On_Signal(int);
}
.cpp
Manager::Manager(QObject* sender,const char* signal, QObject* recvIn, const char* slot)
: slotInvokeSyntax(slot)
, recv(recvIn)
{
connect(sender,signal,this,SLOT(On_Signal(int));
//retrieving method name only
slotInvokeSyntax.remove(0,1).remove(QRegExp("(*",Qt::CaseSensitive,QRegExp::Wildcard));
}
Manager::On_Signal(int val)
{
//invoking slot
if(isOkToSend())
QMetaObject::invokeMethod(recv,slotInvokeSyntax.toAscii().constData(),Q_ARG(int,val));
}
I would like to somehow generalize this for signals and slots with generic number/type of arguments - so that manager works on any pairs like signal(QString)-slot(QString), signal(int,int)-slot(int,int), ...
Is there any way to implement this functionality without adding slot for each of the argument types in Manager? In case my approach is in wrong in general, any suggestions on how to implement such manager are welcome!
EDIT
A bit of clarification on what am I trying to implement - I have large system with several possible states consisting of many smaller widgets (or sub-systems) (some sub-systems can also act both as stand-alone applications or as a part of the larger system). I'm trying to implement global observer that intercepts certain ui events (such as buttons clicks, edits in QLineEdit, QDropbox changes, etc.), and either let corresponding slot of the widget to be called, or discards it if desired action interferes with the global state of the system. I would like to do it through intercepting signal since it allows to avoid dependencies between system components and compiling each subsystem as stand-alone library (with observer not being dependent on any other part of the system and thus being put in core library), but I'm open to any other alternatives that will allow me to achieve that!
FYI, whenever you use something like this:
QString foo;
something(foo.toAscii().constData());
...you are accessing already freed memory because the data pointed to by the QByteArray::constData are valid only until the QByteArray instance lives and is not modified. In your case, the QByteArray is a temporary created by calling foo.toAscii() and is destroyed before the something is called. So this will crash at some point. edit: this does not apply to function calls, see the comments.
To your question -- it would be interesting to know what you're trying to achieve here. The Qt's metatype and metaobject system is indeed a rich one, but abusing it too much might not be the most ellegant way of solving your problem. That said, it's probably fine to use it in this "creative" way with mocked objects, in unit tests etc.
I haven't done this myself and I am not sure whether it's doable without having to touch the q_static_metacall, but it looks like QObject generic signal handler contains an answer to your question.
After Edit:
You said you're looking for a something like a common event bus; the alleged reason is to avoid excess rebuilds when the individual components change. I would not introduce a central interceptor to this architecture. If the toal amount of states is reasonably small, why don't you just let something emit signals upon entering a particular state and have all of your components react to that by enabling/disabling the individual QActions?