Reputation: 332
My goal is to populate a combo box with serial ports available on the (Windows) machine. I created a wrapper for QSerialPortInfo that I would like to publish as QML root context property. I used the QtQuick2 application template as starting point.
#include <QList>
#include <QObject>
class QuickSerialPortInformer : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QString> portselectionmodel READ portselectionmodel CONST)
public:
static const QuickSerialPortInformer *getInstance();
QList<QString> portselectionmodel() const;
protected:
QuickSerialPortInformer();
private:
static const QuickSerialPortInformer *Instance;
};
#endif // QUICKSERIALPORTINFORMER_H
I register the object like this:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
/* here */ engine.rootContext()->setContextProperty("serialPortInformer", QuickSerialPortInformer::getInstance());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
The QList<QString>
returned by the property shall serve as model for the combobox. My guess is that QList<QString>
is converted to a JavaScript array
of strings and then converted back to whatever the C++ part of the ComboBox
's model property needs. After all, the property accepts an array of strings defined in JavaScript. However, it seems that when assigning the property of the QuickSerialPortInformer
to the model
of the ComboBox
no such marshalling is done. As pointed out in the comments, only some C++-types may be used as models for QtQuick views. One of them is QStringList
, which I am using now.
ComboBox {
id: portSelector
model: serialPortInformer.portselectionmodel
}
My problem is that SerialPortInformer
is unknown in the qml context. I suspect that the context I am using to register the object is not visible to the script I am executing with engine.load()
. I tried to reverse the order of load()
and setContextProperty()
to no avail. What am I missing?
After renaming SerialPortInformer
to serialPortInformer
as suggested in the comments and documentation another problem arises. serialPortInformer
is a boolean
of value true
in QML instead of the expected object.
I was able to solve the "boolean"-mystery to the extent that I have been able to make my use case work. The answer is that my SerialPortInformer::getInstance()
was returning a pointer to const
. As soon as I removed the const
-qualifier serialPortInformer
was of type object and exposed the desired attribute. The only reason I can think of why this compiled in the first place is that the const QVariant &
overload of the method was called instead, using a non-explicit
constructor. So my note-to-self is: Never register pointers to constant QObject
s in a QML context
Upvotes: 3
Views: 1254
Reputation: 2296
Since you just want to read the QStringList
, just make QStringList
as the return type of a Q_INVOKABLE method. Also, I don't see you creating an object of QuickSerialPortInformer
which you might be creating in your original code. To sum up your code should something like this,
#include <QList>
#include <QObject>
class QuickSerialPortInformer : public QObject
{
Q_OBJECT
Q_INVOKABLE QStringList portselectionmodel();
....
};
main.cpp
int main(int argc, char *argv[])
{
...
QuickSerialPortInformer portInformer;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("serialPortInformer",&portInformer);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
Upvotes: 1
Reputation: 312
First, implement your Q_PROPERTY
as follows (see documentation):
Q_PROPERTY(QStringList portSelectionModel READ portSelectionModel WRITE setPortSelectionModel NOTIFY portSelectionModelChanged)
Not sure why your singleton is not working, but try implementing it like this:
public:
static QuickSerialPortInformer& Instance()
{
static QuickSerialPortInformer instance;
return instance;
}
~QuickSerialPortInformer()=default;
private:
QuickSerialPortInformer()=default;
QuickSerialPortInformer(QuickSerialPortInformer const&)=delete;
void operator = (QuickSerialPortInformer const&)=delete;
On your main.cpp:
QQmlContext* quickSerialPortInformerCtx = engine.rootContext();
quickSerialPortInformerCtx->setContextProperty("quickserialportinformer", &QuickSerialPortInformer::Instance());
On your qml:
ComboBox {
model: quickserialportinformer.portSelectionModel
}
It should work, I've used this implementation before.
Upvotes: 0