I'm trying to figure out how to get QList> from C++ signal in QML, i'm only getting either QVariant(RecordList, ) or QVariant(QList, ). Tried with different supported sequence type and they work perfectly (QList. I'll appreciate if somebody can help me to understand my error. Kind regards.
jsonreaderasync.h
typedef QPair<qreal,qreal>Record;
typedef QList<Record>RecordList;
class JsonReaderAsync : public QObject
{
Q_OBJECT
public:
explicit JsonReaderAsync(QObject *parent = nullptr);
Q_INVOKABLE void start(const QString& fileName);
signals:
void started();
//void finished(QList<qreal> record);
void finished(RecordList record);
//void finished(QList<Record> record);
};
jsonreaderasync.cpp
#include <QDebug>
#include "jsonreaderasync.h"
Q_DECLARE_METATYPE(Record)
Q_DECLARE_METATYPE(RecordList)
//Q_DECLARE_METATYPE(QList<Record>)
JsonReaderAsync::JsonReaderAsync(QObject *parent) : QObject(parent)
{
qRegisterMetaType<Record>("Record");
qRegisterMetaType<RecordList>("RecordList"); // qml prints QVariant(RecordList, )
//qRegisterMetaType<QList<Record>>("QList<Record>"); //qml prints QVariant(QList<Record>, )
}
void JsonReaderAsync::start(const QString &fileName)
{
QList<Record> record;
record.append(qMakePair(1,1));
record.append(qMakePair(1,2));
record.append(qMakePair(1,3));
// QList<qreal> foo;
// foo.append(1);
// foo.append(1);
// foo.append(1);
emit finished(record);
}
main.cpp
#include "jsonreaderasync.h"
typedef QPair<qreal,qreal>Record;
typedef QList<QPair<qreal,qreal>>RecordList;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine *engine = new QQmlApplicationEngine;
JsonReaderAsync* dataReaderAsync = new JsonReaderAsync();
engine->rootContext()->setContextProperty("JsonReaderAsync", dataReaderAsync);
engine->load(QUrl(QStringLiteral("main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.13
import QtQuick.Window 2.13
import QtQuick.Controls 2.12
import QtCharts 2.3
Window {
id: appWindow
visible: true
minimumWidth : 400
minimumHeight: 400
Connections
{
target: JsonReaderAsync
onStarted:{
console.log("onStarted")
}
onFinished:{
console.log("onFinished")
console.log(record)
console.log(record[0])
}
}
Button {
width : 40
height: 40
anchors.centerIn: parent
onClicked: {
JsonReaderAsync.start("")
}
}
}
In QML, the meta-type system is the only way that QML engine can access C++ structures from a QML environment.
That is, only Predefined C++ Types and custom objects that have Q_PROPERTY declarations could access from QML environment.
Here's my recommend (simplest) modification:
jsonreaderasync.h
#include <QObject>
#include <QVariant>
class Record : public QPair<qreal, qreal> {
Q_GADGET
Q_PROPERTY(qreal first MEMBER first CONSTANT FINAL)
Q_PROPERTY(qreal second MEMBER second CONSTANT FINAL)
public:
Record() = default;
Record(qreal a, qreal b) : QPair<qreal, qreal>(a, b) {}
};
class JsonReaderAsync : public QObject
{
Q_OBJECT
public:
explicit JsonReaderAsync(QObject *parent = nullptr);
Q_INVOKABLE void start(const QString& fileName);
signals:
void started();
void finished(QVariantList record); // RecordList
};
jsonreaderasync.cpp
#include <QDebug>
#include "jsonreaderasync.h"
JsonReaderAsync::JsonReaderAsync(QObject *parent) : QObject(parent)
{
qRegisterMetaType<Record>("Record");
}
void JsonReaderAsync::start(const QString &fileName)
{
QVariantList record;
record.append(QVariant::fromValue(Record(1,1)));
record.append(QVariant::fromValue(Record(1,2)));
record.append(QVariant::fromValue(Record(1,3)));
emit finished(record);
}
Now you can access records from QML:
onFinished: {
for (var i = 0; i < record.length; ++i)
console.log(record[i].first + "->" + record[i].second);
}
Note that converting between C++ objects and QML objects does incur a bit overhead, if these codes are performance sensitive, please consider using C++ Data Models.
While #GPBeta solution works, I wanted more flexibility for Qml supported pairs. I tried to work with templates, but Q_GADGET doesn't support it. There might be a smart wrapper (Template + QVariant) solution, I guess... Nonetheless, here is my approach to the problem:
class PairQml {
Q_GADGET
Q_PROPERTY(QVariant first MEMBER first CONSTANT FINAL)
Q_PROPERTY(QVariant second MEMBER second CONSTANT FINAL)
public:
PairQml() = default;
PairQml(QVariant f, QVariant s): first(f), second(s) {}
QVariant first;
QVariant second;
};
Don't forget to register: qRegisterMetaType<PairQml>("PairQml");
Related
I don't want to expose a slot to QML, but is required to have a slot, because the slot is connected to internal signal.
I marked the slot as private, but the slots is accessible in QML e.g. Code completion/suggestion etc. My CPP Custom Class is registered in the main cpp.
h File:
#ifndef MYQMLTYPE_H
#define MYQMLTYPE_H
#include <QObject>
#include <QTimer>
class MyQMLType : public QObject
{
Q_OBJECT
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
public:
explicit MyQMLType(QObject *parent = nullptr);
public slots:
int increment(int value);
private slots:
void hideslot(void);
signals:
void messageChanged();
public:
QString message() const;
void setMessage(const QString& value);
private:
QString m_message;
QTimer *m_timer;
};
#endif // MYQMLTYPE_H
Cpp File:
#include "myqmltype.h"
MyQMLType::MyQMLType(QObject *parent) : QObject(parent)
{
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &MyQMLType::hideslot);
m_timer->start(1000);
}
int MyQMLType::increment(int value)
{
return value + 1;
}
void MyQMLType::hideslot()
{
// private slot
}
QString MyQMLType::message() const {
return m_message;
}
void MyQMLType::setMessage(const QString& value) {
if(m_message != value) {
m_message = value;
messageChanged(); // trigger signal of property change
}
}
Main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "MyQMLType.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<MyQMLType>("com.yourcompany.xyz", 1, 0, "MyQMLType");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml:
import QtQuick 2.15
import QtQuick.Window 2.15
import com.yourcompany.xyz 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MyQMLType{
id: myqmltype
}
Rectangle{
id: rect
anchors.centerIn: parent
width: 100
height: 100
color: myqmltype.hideslot(); // This slot is avaiable in qml
}
}
Question:
Why is the private slot exposed to QML?
How to hide the slot?
Edit
Optimized Cpp File with private slot
Your premise is wrong
I don't want to expose a slot to QML, but is required to have a slot, because the slot is connected to internal signal.
Using the new Qt 5 connect syntax with pointer to member function you don't need a function to be a slot to be able to connect to it.
Just put your hideslot declaration in the private section of your class and you'll have what you want. It won't be exposed to QML and you would still be able to connect to it in C++.
As to why the private slot is exposed to QML, it is because all slots and Q_INVOKABLE functions are exposed to QML, regardless of their access.
I receive a 2D array from UART that sent from Arduino.
I can show it in debug, but I can not save it in a QList variant to set text for matrix of rectangle in QML.
I want to show text on QML each rectangle.
How I can do?
This is Arduino code. I send 2d array 17x17
void setup(){
pinMode(LED_BUILTIN,OUTPUT);
analogWrite(LED_BUILTIN,255);
Serial.begin(115200);
void loop(){
double data[17][17];
if(Serial.available()){
delay(100);
for(int i=0; i<17; i++){
for(int j=0; j<17; j++){
data[i][j] = i+j+0.01;
sendData(data[i][j]);
delay(10);
}
}
}
return;
}
void sendData(double data){
Serial.print((data));
}
This is readSerial function:
void serial::readSerial(){
serialData = arduino.readAll();
qDebug()<< serialData <<"\n";
}
QML file:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 17*square_size
height: 17*square_size
title: qsTr("Hello World")
property int square_size: 30
Grid {
id: grid
columns: 17
Repeater{
id: rpt
model: 17*17
Rectangle{
width: square_size
height: square_size
border.color: "black"
border.width: 1
Text {
anchors.centerIn: parent
text: Serial.model_data[index]
}
}
}
}
}
serial.h:
#include <QQmlApplicationEngine>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QtDebug>
class serial: public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QString> text READ text NOTIFY textChanged)
public:
explicit serial(QQmlApplicationEngine *engine, QObject *parent = nullptr);
~serial();
void setupSerial();
Q_INVOKABLE QList<QString> text(){
return m_text;
}
private slots:
void readSerial();
private:
QQmlApplicationEngine* m_engine;
/* Varian of Arduino*/
QSerialPort arduino;
bool arduino_is_avaiable;
QString portName;
QByteArray serialData;
/*Varian of text*/
QList<QString> m_text;
signals:
void textChanged();
public slots:
};
#endif // SERIAL_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "serial.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
serial myserial(&engine);
engine.rootContext()->setContextProperty("Serial", &myserial);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
I think you are missing the property on the Serial class to read the data: text: Serial.model_data[index]. The fastest solution to get something on screen would be to add that property and fill it as follows:
Serial.h (I left some parts out)
class Serial : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList model_data READ model_data NOTIFY modelDataChanged)
public:
explicit Serial(QObject *parent = nullptr);
QVariantList model_data() const { return model_data_; }
signals:
void modelDataChanged();
private slots:
void readSerial();
private:
QVariantList model_data_;
};
The readSerial() function would look like this:
void Serial::readSerial()
{
QByteArray serialData = QByteArray(17 * 17 * 8, 0); //shortcut for testing
double *arr = reinterpret_cast<double*>(serialData.data());
model_data_.clear();
for(int i=0;i<17*17;i++)
{
model_data_.append(arr[i]);
}
emit modelDataChanged();
}
What happens is that the QVariant list is filled with the doubles, which have been reinterpreted from byte to doubles. Note that this has to be done every time the serialData changes.
However, this might not be ideal in case you want to go any further with this. When changing dimensions, this setup will fail, both on arduino sending side, arduino reading side and on qml side. You should consider sending the dimensions of the array, reading those back and acting on it. One idea might be QAbstractListModel, which already has the notion of rows/columns; another, simpler idea might be to add properties to the Serial class stating the dimensions (which where read in readSerial())
I need to pass structures between cpp and QML. If i use property i should create an individual set and get functions, My structure contains minimum 5 members so i felt it's not good to use set and get for all those members.
Following is an example of what i am trying to do :
MyClass.h
#include <QObject>
#include <QDebug>
using namespace std;
struct MyStruct {
Q_GADGET
int m_val;
QString m_name1;
QString m_name2;
QString m_name3;
QString m_name4;
Q_PROPERTY(int val MEMBER m_val)
Q_PROPERTY(QString name1 MEMBER m_name1)
Q_PROPERTY(QString name2 MEMBER m_name2)
Q_PROPERTY(QString name3 MEMBER m_name3)
Q_PROPERTY(QString name4 MEMBER m_name4)
};
class MyClass:public QObject
{
Q_OBJECT
Q_PROPERTY(MyStruct mystr READ getMyStruct
WRITE setMyStruct NOTIFY myStructChanged)
public:
explicit MyClass(QObject *parent = nullptr);
MyStruct strObj;
// Edit: changed get function
MyStruct getMyStruct() const
{
return strObj;
}
// Edit: Added set function
void setMyStruct(myStruct val)
{
mystr = val;
emit myStructChanged();
}
signals:
void myStructChanged();
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include <QObject>
#include "MyClass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyClass classObj;
engine.rootContext()->setContextProperty("classObj",&classObj);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
Main.qml
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Window 2.3
ApplicationWindow {
id: applicationWindow
visible: true
width: 600
height: 400
title: qsTr("My App")
MainForm{
id : mainform
Component.onCompleted: {
console.log("name===="+classObj.mystr.name1)
//EDIT added more code to explain the use case.
classObj.myStr.name1 = "abc" //Calls setter
classObj.mystr.name2 = "ans" // Calls setter
}
}
}
If i print just (classObj.myVariant) i am getting QVariant(MyStruct) but when i tried to access any parameter like classObj.myVariant.name1 i am getting "undefined" and also how to set a variant from QML?
[UPDATE] - Should also add MyStruct to Q_DECLARE_METATYPE as below: Q_DECLARE_METATYPE(MyStruct)
You need meta data to access C++ objects from QML.
For non QObject derived, this is achieved by using the Q_GADGET macro, and exposing the members as properties:
struct MyStruct {
Q_GADGET
int m_val;
QString m_name1;
QString m_name2;
QString m_name3;
QString m_name4;
Q_PROPERTY(int val MEMBER m_val)
Q_PROPERTY(QString name1 MEMBER m_name1)
Q_PROPERTY(QString name2 MEMBER m_name2)
Q_PROPERTY(QString name3 MEMBER m_name3)
Q_PROPERTY(QString name4 MEMBER m_name4)
};
your struct or simple class must have Q_GADGET as minimum
you should declare properties in order to access from qml
you must declare your struct/class by Q_DECLARE_METATYPE()
you must register it using qRegisterMetaType<>() somewhere before loading qml file by engine such as main.cpp
so you will have something like this:
//review carefully
struct MyStruct {
Q_GADGET //<-- 1.
Q_PROPERTY(QString str1 MEMBER m_str1) //<-- 2.
public: //<-- important
QString m_str1;
};
Q_DECLARE_METATYPE(MyStruct) //<-- 3.
and use somewhere:
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = nullptr);
Q_INVOKABLE MyStruct setNewConfig(QString v); //<-- e.g.
//...
}
main.cpp
//...
qmlRegisterType<Controller>("AppKernel", 1, 0, "Controller");
qRegisterMetaType<MyStruct>(); //<-- 4.
//...
engine.load(url);
//...
so it is usable in qml
main.qml
//...
Controller {
id: con
}
FileDialog {
id: fileDialog
nameFilters: ["Config file (*)"]
onAccepted: {
var a = con.setNewConfig(file);
console.log(a.str1); //<-- yeah! it is here
}
}
//...
NOTE 1: Be careful, it seems that nested classes/struct not supported by Qt meta
NOTE 2: You can expose struct just like a class. Inherit from QObject and use Q_OBJECT. See this article from Evgenij Legotskoj
NOTE 3: Above instructions makes struct/class known to qml and you can access properties/members, but is not instantiable in qml. see Qt document
NOTE 4: Be aware that qmlRegisterType<>() method is marked as "obsolete" in Qt 5.15+. Keep yourself updated ;)
I have a Qt application and I'd like to show some log. I use a TextArea. However, if the log is large or the events come too fast the GUI can't draw Textarea fast enough.
I have analyzed this problem with Qt Creator (QML Profiler) and if the log is large it takes 300 ms to draw the GUI. I use this software on a Raspberry Pi2.
Any ideas how to solve this? Should I use other QML controls? Thanks.
QML code:
TextArea {
text: appHandler.rawCommunication
readOnly: true
}
C++ code:
Q_PROPERTY(QString rawCommunication READ rawCommunication WRITE setrawCommunication NOTIFY rawCommunicationChanged)
void setrawCommunication(QString val)
{
val.append("\n");
val.append(m_rawCommunication);
m_rawCommunication = val;
emit rawCommunicationChanged(m_rawCommunication);
}
Use a view, like ListView. They instantiate their delegates as needed, based on which data the view says it needs to show depending on the position the user is at in the list. This means that they perform much better for visualising large amounts of data than items like TextArea, which in your case is going to keep a massive, ever-growing string in memory.
Your delegate could then be a TextArea, so you'd have one editable block of text per log line. However, if you don't need styling, I'd recommend going with something a bit lighter, like TextEdit. Taking it one step further: if you don't need editable text, use plain old Text. Switching to these might not make much of a difference, but if you're still seeing slowness (and have lots of delegates visible at a time), it's worth a try.
I tried the ListView suggestion but it has several drawbacks:
No easy way to keep the view positioned at the bottom when new output is added
No selection across lines/delegates
So I ended up using a cached TextArea, updating once every second:
TextArea {
id: outputArea_text
wrapMode: TextArea.Wrap
readOnly: true
font.family: "Ubuntu Mono, times"
function appendText(text){
logCache += text + "\n";
update_timer.start();
}
property string logCache: ""
Timer {
id: update_timer
// Update every second
interval: 1000
running: false
repeat: false
onTriggered: {
outputArea_text.append(outputArea_text.logCache);
outputArea_text.logCache = "";
}
}
Component.onCompleted: {
my_signal.connect(outputArea_text.appendText)
}
}
try this approach:
create a c++ logger class
append all logs to this class
and print them using some action, example a button click
this will solve your performance issue
Example of code:
Logger.h
#ifndef LOGGER_H
#define LOGGER_H
#include <QQmlContext>
#include <QObject>
#include <QStringList>
#include <QQmlEngine>
#include <QString>
#include <QtCore>
#include <QDebug>
class Logger : public QObject
{
Q_OBJECT
public:
explicit Logger(QObject *parent = 0);
~Logger();
Q_INVOKABLE QStringList *getLogStream();
Q_INVOKABLE void printLogStream();
Q_INVOKABLE void appendLog(QString log);
Q_INVOKABLE void log(QString log="");
Q_INVOKABLE void log(QString fileName, QString log);
signals:
public slots:
private:
QStringList* stringStream_;
};
#endif // LOGGER_H
Logger.cpp
#include "logger.h"
Logger::Logger(QObject *parent) :
QObject(parent),
stringStream_(new QStringList)
{
}
~Logger(){
if(stringStream_ != NULL)
{
delete stringStream_;
stringStream_ = NULL;
}
}
QStringList* Logger::getLogStream(){
return stringStream_;
}
void Logger::printLogStream()
{
QStringListIterator itr(*stringStream_);
while (itr.hasNext())
qDebug()<< itr.next()<<"\n";
}
void Logger::appendLog(QString log){
stringStream_->push_back(log) ;
}
void Logger::log(QString fileName,QString log)
{
#ifdef ENABLElogs
fileName.push_front(" [");
if(!fileName.contains(".qml"))
{
fileName.append(".qml]:");
}
qDebug()<<fileName<<log;
#else
Q_UNUSED(log);
Q_UNUSED(fileName);
#endif
}
void Logger::log(QString log)
{
#ifdef ENABLElogs
qDebug()<<log;
#else
Q_UNUSED(log);
#endif
}
main.cpp
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include "logger.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer *viewer = new QtQuick2ApplicationViewer;
Logger* stream = new Logger;
viewer->rootContext()->setContextProperty("Stream",stream);
viewer->setMainQmlFile(QStringLiteral("qml/project/main.qml"));
viewer->showExpanded();
return app.exec();
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 800
height: 480
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
Component.onCompleted: Stream.appendLog("Text object is completed")
}
Column{
x:300
Button{
text:"append"
onClicked: {
Stream.appendLog("MouseArea object clicked")
}
Component.onCompleted: Stream.appendLog("Button object is completed")
}
Button{
text:"logger"
onClicked: {
Stream.printLogStream()
}
Component.onCompleted: Stream.appendLog("Button logger object is completed")
}
}
TextArea{
text:"blablabla"
Component.onCompleted: Stream.appendLog("TextArea object is completed")
}
Component.onCompleted: Stream.appendLog("the main object is completed")
}
project.pro
#add this line
# comment it, run qmake and recompile to disable logs
DEFINES += ENABLElogs
using this approch you can stop all logs with a single line change when you want to release your soft
However, I have included complete code, using "QAbstractListModel" for a Logging heavy data to QML
listmodel.h
#ifndef LISTMODEL_H
#define LISTMODEL_H
#include <QAbstractListModel>
class ListModel: public QAbstractListModel
{
Q_OBJECT
public:
ListModel();
// Q_PROPERTY(QStringList logs READ name WRITE nameChanged)
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
Q_INVOKABLE QVariant activate(int i);
private:
QStringList m_list;
};
#endif // LISTMODEL_H
listmodel.cpp
#include "listmodel.h"
#include <QFile>
#include <QHash>
ListModel::ListModel()
{
QFile file("/home/ashif/LogFile");
if(!file.open(QIODevice::ReadOnly))
{
qDebug( "Log file open failed" );
}
bool isContinue = true;
do
{
if(file.atEnd())
{
isContinue = false;
}
m_list.append(file.readLine());
}
while( isContinue);
}
int ListModel::rowCount(const QModelIndex & parent ) const
{
return m_list.count();
}
QVariant ListModel::data(const QModelIndex & index, int role ) const
{
if(!index.isValid()) {
return QVariant("temp");
}
return m_list.value(index.row());
}
QVariant ListModel::activate(int i)
{
return m_list[i];
}
main.qml
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
ListView
{
width: 200; height: 250
anchors.centerIn: parent
model:mylistModel
delegate: Text
{
text:mylistModel.activate(index)
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include "logger.h"
#include "listmodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Logger myLogger;
ListModel listModel;
engine.rootContext()->setContextProperty("mylistModel", &listModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
I'm trying to use a QDeclarativeListProperty in order to manage a list of parameters, mostly for the purposes of displaying them in a ListView. However, I would also like to be able to directly access the parameters in QML from the QDeclarativeListProperty so that I can display/modify individual parameters on different screens.
My class is called ParameterClass, for which I've created a QList:
class SystemData : public QObject
{
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<ParameterClass> parameters READ parameters CONSTANT)
QDeclarativeListProperty<ParameterClass> parameters();
...
QList<ParameterClass *> m_parameterList;
}
I've also registered the ParameterClass class and set up an instance of my SystemData as a property, which I know is necessary.
m_context->setContextProperty("SystemData", m_pSystemData);
qmlRegisterType<ParameterClass>();
Now, what I want to do within QML is something like this:
Rectangle {
id: frame
property variant parameter: SystemData.parameters[5]
...
}
I'm just not getting it to work: I keep getting back [undefined]. Am I wasting my time, or am I missing something?
Edit:
I've changed things to use the suggestion from ... . Here are some selections from my updated code.
main.cpp:
#include <QApplication>
#include <QSplashScreen>
#include <QLocale>
#include <QLibraryInfo>
#include <QDeclarativeView>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QObject>
#include <QDeclarativeListProperty>
#include "systemdata.h"
#include "parameterclass.h"
static const QString contentPath = "qrc:/qml/qml/pk_ui/";
static const QString filename(contentPath + "main.qml");
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QDeclarativeView mainView;
SystemData* systemData = SystemData::getInstance();
QThread thread;
UpdateWorker updateWorker;
QObject::connect((const QObject*)systemData, SIGNAL(startWork()),
(const QObject*)&updateWorker, SLOT(doWork()));
updateWorker.moveToThread(&thread);
thread.start();
systemData->startUpdates();
QFont defaultFont;
defaultFont.setFamily("Sans Serif");
QApplication::setFont(defaultFont);
// Register types to be available in QML
qmlRegisterType<ParameterClass>();
qmlRegisterUncreatableType<SystemEnum>("SystemEnums", 1, 0, "SystemEnum", QString());
mainView.engine()->rootContext()->setContextProperty("SystemData", systemData);
// Set view optimizations not already done for QDeclarativeView
mainView.setResizeMode(QDeclarativeView::SizeRootObjectToView);
mainView.setAttribute(Qt::WA_OpaquePaintEvent);
mainView.setAttribute(Qt::WA_NoSystemBackground);
mainView.setSource(QUrl(filename));
mainView.show();
return app.exec();
}
The ParameterClass looks like this:
class ParameterClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int type READ get_type NOTIFY typeChanged)
Q_PROPERTY(bool enabled READ get_ParameterEnabled WRITE set_ParameterEnabled NOTIFY enabledChanged)
Q_PROPERTY(int groupID READ get_GroupID WRITE set_GroupID NOTIFY groupIDChanged)
Q_PROPERTY(int unitID READ get_UnitID WRITE set_UnitID NOTIFY unitIDChanged)
Q_PROPERTY(int securityLevel READ get_SecurityLevel WRITE set_SecurityLevel NOTIFY securityLevelChanged)
Q_PROPERTY(QString parameterName READ get_ParameterName NOTIFY parameterNameChanged)
Q_PROPERTY(QString shortDescription READ get_ShortDescription NOTIFY shortDescriptionChanged)
Q_PROPERTY(int currentValue READ get_CV WRITE set_valueptrvalue NOTIFY currentValueChanged)
Q_PROPERTY(int lowerBound READ get_LB NOTIFY lowerBoundChanged)
Q_PROPERTY(int upperBound READ get_UB NOTIFY upperBoundChanged)
public:
struct ValueTypes
{
enum
{
IntegerType,
StringType,
StringListType
};
};
ParameterClass(QObject *parent = 0);
int get_type();
bool get_ParameterEnabled();
int get_GroupID();
int get_UnitID();
int get_SecurityLevel();
QString get_ParameterName();
QString get_ShortDescription();
int get_CV() { return *CurrentValuePtr; }
int get_LB() { return *LowerBoundPtr; }
int get_UB() { return *UpperBoundPtr; }
void set_ParameterEnabled(bool InParameterEnabled);
void set_GroupID(int InGroupID);
void set_UnitID(int InUnitID);
void set_SecurityLevel(int InSecurityLevel);
signals:
void typeChanged();
void enabledChanged();
void groupIDChanged();
void unitIDChanged();
void securityLevelChanged();
void parameterNameChanged();
void shortDescriptionChanged();
private:
int type;
bool ParameterEnabled;
int GroupID;
int UnitID;
int SecruityLevel;
QString ParameterName;
QString ShortDescription;
int * CurrentValuePtr;
int * LowerBoundPtr;
int * UpperBoundPtr;
};
And my QML file:
Rectangle {
id: frame
property int val: SystemData.parameters[4].currentValue
...
}
It looks like I'm still getting an undefined value in this case. I'm trying to debug now, so that I can provide more information.
It's very much possible. The key is to make sure you register the QML type and set the context property before setting the source on your QDeclarativeView.
Here's a working example -
main.cpp:
#include <QApplication>
#include <QtDeclarative>
class MyPropertyObject : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value CONSTANT)
public:
MyPropertyObject(int value = -1) : m_value(value) { }
int value() const {
return m_value;
}
private:
int m_value;
};
class MyObject : public QObject {
Q_OBJECT
Q_PROPERTY(QDeclarativeListProperty<MyPropertyObject> props READ props CONSTANT)
public:
MyObject() {
m_props.append(new MyPropertyObject(55));
m_props.append(new MyPropertyObject(44));
m_props.append(new MyPropertyObject(33));
}
QDeclarativeListProperty<MyPropertyObject> props() {
return QDeclarativeListProperty<MyPropertyObject>(this, m_props);
}
private:
QList<MyPropertyObject *> m_props;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDeclarativeView view;
view.engine()->rootContext()->setContextProperty(QLatin1String("tester"), new MyObject);
qmlRegisterType<MyPropertyObject>();
view.setSource(QUrl("qrc:///qml/main.qml"));
view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
view.resize(300, 300);
view.show();
return a.exec();
}
#include "main.moc"
main.qml:
import QtQuick 1.1
Rectangle {
property variant foo: tester.props[2].value
Text {
anchors.centerIn: parent
text: parent.foo
}
}
Note: read the docs for the QDeclarativeListProperty constructors. The one I'm using for this example is not the preferred one, but is good for quick prototypes.