Why doesn't this add the QObject* in QVector<QObject*> - qt

I've two ViewModels, MainVM and AddVM. In main.cpp, MainVM is declared this way:
MainVM *mvm;
int main(int argc, char *argv[])
{
...
mvm = new MainVM();
...
engine.rootContext()->setContextProperty("mainContext", mvm);
engine.rootContext()->setContextProperty("addContext", new AddVM());
...
}
and in MainVM, I've this Q_PROPERTY:
class MainVM : public QObject
{
Q_OBJECT
...
PROPERTY(QVector<Plot*>, plots)
...
public:
...
QSqlDatabase db;
int maxPlotId, maxSpaceId, maxTenantId, maxHeadId, maxLeaseId;
...
};
the PROPERTY macro does this:
#define PROPERTY(QType, name) \
Q_PROPERTY(QType name READ name WRITE set##name NOTIFY name##Changed) \
public: \
QType name(){return m_##name;} \
void set##name(QType value){if(m_##name != value){m_##name = value; emit name##Changed();}} \
Q_SIGNAL void name##Changed(); \
private: \
QType m_##name;
In my AddVM I've another Q_PROPERTY newPlot and a Q_INVOKABLE addNewPlot:
class AddVM : public QObject
{
Q_OBJECT
PROPERTY(Plot*, newPlot)
public:
explicit AddVM(QObject *parent = nullptr);
Q_INVOKABLE void addNewPlot();
};
on top of the AddVM.cpp, I've these:
#include "MainVM.h"
extern MainVM *mvm;
and addNewPlot function has these instructions:
void AddVM::addNewPlot()
{
mvm->db.open();
QSqlQuery query;
query.prepare("INSERT INTO Plots (Name, Description) VALUES(:Name, :Description)");
query.bindValue(":Name", newPlot()->name());
query.bindValue(":Description", newPlot()->description());
query.exec();
mvm->db.close();
mvm->plots().push_back(newPlot());
setnewPlot(new Plot());
newPlot()->setid(++mvm->maxPlotId);
}
everything in this function works as expected except the mvm->plots().push_back(newPlot()); line! This doesn't add the newPlot in the QVector of MainVM!
EDIT
Probably the best way is to redefine the getter in the macro like this:
QType& name(){return m_##name;} \
and with that my existing code works without any modification.

When you call mvm->plots(), it returns a copy of the real plots vector (That's my guess because you didn't show what plots() does). Thus, a new plot is added to the copy not the original vector. So, I think the best way is to add a function in MainVM that is called 'addPlot()` for example where you can add to the plots vector internally and directly.

Related

How can I add a new entry to a ListView using QML?

How can I add a new "blank" entry to a ListView that is populated using a QObjectList-based model?
The documentation outlines that we can use QList<QObject*> to populate a model (https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html), but I don't see anyway to add a new entry using this method...
I've made a sample program to show what I mean, the problem lies in the Main.qml's button onClick event.
Main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Window {
width: 640
height: 480
visible: true
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
ListView {
id: listView
Layout.fillWidth: true
Layout.preferredHeight: 100
model: sampleManager.sampleObjList
delegate: RowLayout {
anchors.fill: listView
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: modelData.id
}
}
}
Button {
text: "Add New Entry"
Layout.fillWidth: true
Layout.fillHeight: true
onClicked: function(mouse) {
// How do I add a new entry to listView.model...
// This says append does not exist...
listView.model.append({});
}
}
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "SampleManager.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
SampleManager* sample_manager = new SampleManager();
engine.rootContext()->setContextProperty("sampleManager", sample_manager);
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();
}
SampleManager.h
#ifndef SAMPLEMANAGER_H
#define SAMPLEMANAGER_H
#include <QObject>
#include "QMLMacros.h"
#include "SampleObj.h"
class SampleManager :
public QObject
{
Q_OBJECT
QML_WRITABLE_PROPERTY(QList<SampleObj*>, sampleObjList)
public:
explicit SampleManager(
QObject *parent = nullptr);
signals:
public slots:
private:
};
#endif // SAMPLEMANAGER_H
SampleManager.cpp
#include "SampleManager.h"
#include "SampleObj.h"
SampleManager::SampleManager(
QObject *parent) :
QObject(parent)
{
m_sampleObjList.append(new SampleObj(this));
m_sampleObjList.append(new SampleObj(this));
m_sampleObjList[0]->set_id(0);
m_sampleObjList[1]->set_id(1);
}
SampleObj.h
#ifndef SAMPLEOBJ_H
#define SAMPLEOBJ_H
#include <QObject>
#include "QMLMacros.h"
class SampleObj : public QObject
{
Q_OBJECT
QML_WRITABLE_PROPERTY(int, id);
public:
explicit SampleObj(
QObject *parent = nullptr);
signals:
private:
};
#endif // SAMPLEOBJ_H
SampleObj.cpp
#include "SampleObj.h"
SampleObj::SampleObj(
QObject *parent) :
QObject(parent),
m_id(0)
{
}
QMLMacros.h (Just some macros I found online for easily declaring a Q_PROPERTY)
#ifndef QMLMACROS_H
#define QMLMACROS_H
#include <QObject>
/*!
\defgroup QT_QML_HELPERS Qt helper macros
Brings a couple of macros that can help saving development time,
by avoiding manual code duplication, often leading to heavy copy-and-paste,
which is largely error-prone and not productive at all.
*/
/*!
\def QML_WRITABLE_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY that will be readable / writable from QML.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
void set_{name} ({type}); // public setter slot
void {name}Changed ({type}); // notifier signal
\endcode
\b Note : Any change from either C++ or QML side will trigger the
notification.
*/
#define QML_WRITABLE_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name WRITE set_##name NOTIFY name##Changed) \
private: \
type m_##name{}; \
\
public: \
type get_##name() const { return m_##name; } \
Q_SIGNALS: \
void name##Changed(type name); \
public Q_SLOTS: \
void set_##name(type name) { \
if (m_##name != name) { \
m_##name = name; \
emit name##Changed(m_##name); \
} \
} \
\
private:
/*!
\def QML_READONLY_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY that will be readable from QML and writable
from C++.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
void update_{name} ({type}); // public setter method
void {name}Changed ({type}); // notifier signal
\endcode
\b Note : Any change from C++ side will trigger the notification to QML.
*/
#define QML_READONLY_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name NOTIFY name##Changed) \
private: \
type m_##name{}; \
\
public: \
type get_##name() const { return m_##name; } \
bool update_##name(type name) { \
bool ret = false; \
if ((ret = m_##name != name)) { \
m_##name = name; \
emit name##Changed(m_##name); \
} \
return ret; \
} \
Q_SIGNALS: \
void name##Changed(type name); \
\
private:
/*!
\def QML_CONSTANT_PROPERTY(type, name)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a \c Q_PROPERTY for a constant value exposed from C++ to
QML.
\param type The C++ type of the property
\param name The name for the property
It generates for this goal :
\code
{type} m_{name}; // private member variable
{type} get_{name} () const; // public getter method
\endcode
\b Note : There is no change notifier because value is constant.
*/
#define QML_CONSTANT_PROPERTY(type, name) \
protected: \
Q_PROPERTY(type name READ get_##name CONSTANT) \
private: \
type m_##name; \
\
public: \
type get_##name() const { return m_##name; } \
\
private:
#define QML_LIST_PROPERTY(CLASS, NAME, TYPE) \
public: \
static int NAME##_count(QQmlListProperty<TYPE> *prop) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
return (instance != NULL ? instance->m_##NAME.count() : 0); \
} \
static void NAME##_clear(QQmlListProperty<TYPE> *prop) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
if (instance != NULL) { \
instance->m_##NAME.clear(); \
} \
} \
static void NAME##_append(QQmlListProperty<TYPE> *prop, TYPE *obj) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
if (instance != NULL && obj != NULL) { \
instance->m_##NAME.append(obj); \
} \
} \
static TYPE *NAME##_at(QQmlListProperty<TYPE> *prop, int idx) { \
CLASS *instance = qobject_cast<CLASS *>(prop->object); \
return (instance != NULL ? instance->m_##NAME.at(idx) : NULL); \
} \
QList<TYPE *> get_##NAME##s(void) const { return m_##NAME; } \
\
private: \
QList<TYPE *> m_##NAME;
/*!
\def QML_ENUM_CLASS(name, ...)
\ingroup QT_QML_HELPERS
\hideinitializer
\details Creates a class that contains a C++ enum that can be exposed to
QML.
\param name The name for the class
\param ... The variadic list of values for the enum (comma-separated)
It generates for this goal :
\li The \c {name} C++ QObject-derived class
\li The \c {name}::Type enumeration containing the values list
\li The \c Q_ENUMS macro call to allow QML usage
Example in use :
\code
QML_ENUM_CLASS (DaysOfWeek, Monday = 1, Tuesday, Wednesday, Thursday,
Friday, Saturday, Sunday) \endcode
\b Note : The QML registration using \c qmlRegisterUncreatableType() will
still be needed.
*/
#define QML_ENU_CLASS(name, ...) \
class name : public QObject { \
Q_GADGET \
public: \
enum Type { __VA_ARGS__ }; \
Q_ENUMS(Type) \
};
class QmlProperty : public QObject {
Q_OBJECT
}; // NOTE : to avoid "no suitable class found" MOC note
#endif // QMLMACROS_H
Not sure if anyone will ever run into this issue, but I could not find a solution. Unfortunately, I was forced to use create my own version of QAbstractListModel in order to effectively manage a list consisting of 1 int and 1 string per row within QML :(. An overly complicated solution for a minor minor problem, not elegant at all unfortunately.

QVariant call a function of a stored object, defined in the main part of a project

The common part (crating a lib) of my project currently holds a interface Class of the type:
class CanBeAddedToGroup{
public:
void addToGroup(Group& )=0;
}
Now i also wantred to use the programm on a Class containing data in a QVariant, so i started it off simple:
class DataContainingClass: public CanBeAddedToGroup{
QMap<QString,QVarient> data;
public:
void addToGroup(Group& ){
QMap<QString,QVarient>::itterator itter = data.begin();
QMap<QString,QVarient>::itterator end= data.end();
for(;itter !=end;itter ++){
<Handle Data>
}
}
};
}
Now one of the datatypes addedt to the list (outside the lib) is of the type:
class DataClass: public QObject, public CanBeAddedToGroup{
void addToGroup(Group& );
}
Q_DECLARE_METATYPE(DataClass)
And it is added to the map using "QVariant::fromValue(", now i need a way in the "DataContainingClass" to check if the Data is derived from a QObject, so i know static_cast(variant.data()) is valid.
Then i could try to dynamic_cast the Object pointer to CanBeAddedToGroup, and call it.
--- EDIT ---:
The Problem IS NOT: Having a QObject and check if it inherits another QObject, it is not even checking if a Class inherits from another one, it is to know if the data i have actually IS a QObject.
Minimal Example:
Header File:
#include <QObject>
#include <QDebug>
class DmbClass: public QObject{
Q_OBJECT
public:
DmbClass(){TST="RealOne";}
DmbClass(const DmbClass&d){TST="Clone";}
QString TST;
};
Q_DECLARE_METATYPE(DmbClass)
class Tst{
public:
virtual void tstFunct()=0;
};
class CanClass: public QObject, public Tst{
Q_OBJECT
public:
CanClass(){TST="RealOne";}
CanClass(const CanClass&d){TST="Clone";}
QString TST;
virtual void tstFunct() override{
qDebug()<<"tst";
}
};
Q_DECLARE_METATYPE(CanClass)
class DmbGadget{
Q_GADGET
public:
DmbGadget(){TST="RealOne";}
DmbGadget(const DmbGadget&d){TST="Clone";}
QString TST;
};
Q_DECLARE_METATYPE(DmbGadget)
C File:
// QObject in QVariant
DmbClass dC;
QVariant varC=QVariant::fromValue(dC);
const void* vPC = varC.data();
DmbClass dCc = varC.value<DmbClass>();
QObject* objC = (QObject*)varC.data();
QObject* schouldWork = objC->parent();
Tst* schouldBeNull = dynamic_cast<Tst*>(objC);
// Object using correct base class in QVariant
CanClass dT;
QVariant varT=QVariant::fromValue(dT);
const void* vPT = varT.data();
CanClass dTc = varC.value<CanClass>();
QObject* objT = (QObject*)varT.data();
QObject* schouldWork2 = objT->parent();
Tst* schouldNotNull = dynamic_cast<Tst*>(objT);
schouldNotNull->tstFunct();
// Q_Gadget in QVariant
DmbGadget dG;
QVariant varG=QVariant::fromValue(dG);
const void* vPG = varG.data();
DmbGadget dGg = varG.value<DmbGadget>();
QObject* objD = (QObject*)varG.data();
//QObject* schouldSegFault = objD->parent();
// base value in QVariant
QVariant var4=4;
const void* vP4 = var4.data();
QObject* obj4 = (QObject*)var4.data();
//QObject* schouldSegFault2 = obj4 ->parent();
I need a way to distinguisch cases 1&2 from 3&4 ("schouldSegFault"), without using something only defined outside of the lib.
I Already Tryed:
int tst4 = qRegisterMetaType<CanClass>("CanClass");
QMetaType help2(tst4);
But help2 has a MetaObject of 0, so i cant check for the inheriance from QObject.
Edit/for who added "Proper way to check QObject derived class type in Qt" ther was te issue in my programm that the class inherits from another QObjectclass so i cant chack for inheriance of my interface (even when defined as Q_Gadget) using inherits, since it would only be true for the first element.
PS: For everyone trying to call functions on a QVariant containing a Object rather than a pointer might be interested in this approach:
How to support comparisons for QVariant objects containing a custom type? / https://pastebin.com/tNLa0jSa
While having a global registry for types is what i wished i could avoid for the case.
Just try to use QVariant::value and see if the value in a QVariant can be converted to your target class. Here's a minimal example:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyQObjectClass : public QObject {
Q_OBJECT
public:
explicit MyQObjectClass(QObject *parent = nullptr) : QObject(parent) {}
void greet() { qDebug() << "I am a MyQObjectClass!"; }
};
Q_DECLARE_METATYPE(MyQObjectClass*)
int main(int, char *[])
{
MyQObjectClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++) {
auto value = iter.value();
// Try to convert to MyQObjectClass*
auto obj = value.value<MyQObjectClass*>();
if (obj != nullptr) {
qDebug() << iter.key() << "is an instance of MyQObjectClass";
obj->greet();
continue;
}
qDebug() << iter.key() << "is not an instance of MyQObjectClass";
}
}
#include "main.moc"
Running it should yield the following output on the console:
"bar" is an instance of MyQObjectClass
I am a MyQObjectClass!
"foo" is not an instance of MyQObjectClass
The important parts:
Make sure the class you want to store in a QVariant derives from QObject and has the Q_OBJECT macro.
When iterating over the map, use QVariant::value() and try to convert the contained value to your target class. In the example, I use QVariant::value<MyQObjectClass*>() - according to the documentation, this either returns the contained instance of MyQObjectClass* if the value can be converted to it or - which is the case if the QVariant contains either basic values or gadgets - a default constructed value. In the case of a pointer this would be a null pointer, so just check if the value returned is null. That's it.
Never work on Qvariant::data() directly.
Update
Just as a remark: The Qobject class declared the copy constructor and assignment operators as private:
From the official documentation:
QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.
Hence, you cannot copy around instances of QObject (and consequentially you cannot store them in QVariant). Instead, you pass around pointers to QObject instances.
Update #2
If your interface class cannot derive directly from QObject, you might consider using Qt's plugin mechanism instead. Here's the above example slightly edited to fit this approach:
#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QDebug>
class MyInterfaceClass {
public:
MyInterfaceClass() {}
virtual ~MyInterfaceClass() {}
virtual void greet() = 0;
};
#define MyInterfaceClass_IID "org.example.MyInterfaceClass"
Q_DECLARE_INTERFACE(MyInterfaceClass, MyInterfaceClass_IID)
class MyConcreteClass : public QObject, public MyInterfaceClass {
Q_OBJECT
Q_INTERFACES(MyInterfaceClass)
public:
MyConcreteClass(QObject *parent = nullptr) : QObject(parent) {}
void greet() override { qDebug() << "I am a MyInterfaceClass!"; }
};
int main(int, char *[])
{
MyConcreteClass obj;
QVariantMap map;
map.insert("foo", QString("Hello World"));
map.insert("bar", QVariant::fromValue(&obj));
QVariantMap::iterator iter = map.begin();
QVariantMap::iterator end= map.end();
for(;iter !=end;iter ++) {
auto value = iter.value();
// Try to convert to QObject*:
auto obj = value.value<QObject*>();
if (obj != nullptr) {
// Try if we can cast to our interface class:
auto ifc = qobject_cast<MyInterfaceClass*>(obj);
if (ifc != nullptr) {
qDebug() << iter.key() << "is an instance of MyInterfaceClass";
ifc->greet();
}
continue;
}
qDebug() << iter.key() << "is not an instance of MyInterfaceClass";
}
}
#include "main.moc"
You need to:
Define your interface class and register it with Qt using the Q_DECLARE_INTERFACE macro.
Declare your concrete classes, deriving from QObject and your interface class. In addition, you need to tell Qt about the interface part using the Q_INTERFACES macro.
When checking the values in your map, first try to convert to a QObject* via QVariant::value(). If this succeeds, you can try to qobject_cast to your interface class.
Your design is completely broken: QObjects cannot be used as unrestricted values. They cannot be copied nor moved, and your implementations of copy constructors are hiding this fundamental fact.

undefined reference to `Counter::valueChanged(int)'

i'm trying to learn qt.this is my first example that I'm practicing.but i have this error:C:\Qt2\Qt5.2.1\Tools\QtCreator\bin\recognize_signal_slot\main.cpp:19: error: undefined reference to `Counter::valueChanged(int)'
I don't know what I should do..someone told me you should put your class in header file.but I couldn't understand what he said.can anyone tell me step by step.thank you so much.
here is my code in main.cpp :
#include <QCoreApplication>
#include <QObject>
class Counter : public QObject
{
int m_value;
public:
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
void Counter::setValue(int value)
{
if (value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Counter d, b;
QObject::connect(&d, SIGNAL(valueChanged(int)),
&b, SLOT(setValue(int)));
d.setValue(12); // a.value() == 12, b.value() == 12
return a.exec();
}
[What's Wrong?]
You signals & slots are not invoked by Meta-Object-Compiler (MOC).
Suggested reading: Why Does Qt Use Moc for Signals and Slots?.
[Solution]
Step 1. Add Q_OBJECT macro to the QObject derivatives that use signals & slots.
class Counter : public QObject
{
Q_OBJECT // <-----HERE
int m_value;
public:
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
};
Step 2. move your class declaration to counter.h and implementation to counter.cpp. Since MOC searches header files that contain Q_OBJECT, it's better to keep your QObject classes and main well separate, even for a small test project.
Step 3. Clean all ---> run qmake ---> rebuild (qmake will automatically call MOC to translate signals & slots syntax into compilable C++ code)

Why we are giving Q_OBJECT in private section only?

Why we usually give Q_OBJECT in private section only?I tried a sample program by giving it in public section ,I found no difference.If anybody knows can you tell me the reason.
By default the Q_OBJECT macro expands to:
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
Q_OBJECT_GETSTATICMETAOBJECT \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
at least on mine Qt 4.8, so you can see that it switches to public visibility level to declare the needed functions and the back to private in order to ensure that nothing is broken. So if you declare your Q_OBJECT in a public section you might have the funny consequence that things after are not public anymore :)
If you put Q_OBJECT under public identifier, all your public declaration will become private. For example:
class Dialog : public QDialog
{
public:
Q_OBJECT
Dialog(QWidget *parent = 0); // <- now declared as private

Can a QDeclarativeListProperty be accessed directly, instead of as a model?

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.

Resources