Use QQmlListProperty to show and modify QList in Qml - c++

again, well i have a question (and maybe a problem), i make a program with qt and qml in qt5 and qml with qtquick 2.0, and i have a c++ model qlist, and i need modify the list in runtime, i use q QQmlListProperty and show the items in qml, but they are not hide and show in the moment when i add or remove my code is next:
class ConceptsList: public QObject{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Concept> concepts READ concepts NOTIFY conceptsChanged)
Q_CLASSINFO("DefaultProperty", "concepts")
public:
ConceptsList(QObject *parent=0);
QQmlListProperty<Concept> concepts();
Q_INVOKABLE static void append_concept(QQmlListProperty<Concept> *list, Concept *cpt);
Q_INVOKABLE void removeConcept(int index);
Q_INVOKABLE void addConcept(QString m_id,QString description, QString quantity, QString price, QString unit, QString total);
Q_INVOKABLE int countConcepts();
static void clearConcepts(QQmlListProperty<Concept> *property);
static int conceptsSize(QQmlListProperty<Concept> *property);
static Concept *conceptAt(QQmlListProperty<Concept> *property, int index);
signals:
void conceptsChanged();
private:
QList<Concept *> m_concepts;
}
I use a listview and delegate and i not have problems to view, but my question is if i can use a QQmlListProperty and modify the Qlist, or i will change a form to expose qlist to qml, if it's posible how call the method from qml, or how implement in C++, i ask because exists really little number or examples with work this form.
in qml my code is next:
ConceptsList{
id:cpts
concepts:[
Concept{
m_id:"7"
m_quantity: "3"
m_price: "1"
m_unit:"1"
m_description:"algo"
m_total:"2"
}
]
}
ListView {
id: listConceptsView
objectName: "list"
anchors.fill: parent
anchors.margins: 5
clip: true
focus: true
highlight: highlightBar
highlightFollowsCurrentItem: false
Component{
id: tableConceptDelegate
Item{
anchors.margins: 4
width: 515
height: 27
clip: true
Row {
spacing: 4
Text {
height: 26; width: 76
text: model.m_id
color: "black"
font.bold: true
horizontalAlignment: Text.AlignHCenter
}
...
...
Text {
height: 26; width: 120
text: model.m_total//amountTotal
color: "black"
font.bold: true
horizontalAlignment: Text.AlignHCenter
}
}
MouseArea {
id: mouse_area1
anchors.fill: parent
onClicked:
{
listConceptsView.currentIndex = index
}
}
}
}
delegate: tableConceptDelegate
model:cptCpt // i define this alias how cptCpt: cpt.concepts
}

I got the answer for my self, first, I stopped using the attribute Q_INVOCABLE in the method append_concept, second, I added a line of code in the implementation of addConcept. Here is the code:
Before:
Q_INVOKABLE static void append_concept(QQmlListProperty<Concept> *list, Concept *cpt);
Now:
static void append_concept(QQmlListProperty<Concept> *list, Concept *cpt);
Maybe this not affect, but i prefer not take risks.
And in the implementations of addConcept and removeConcept:
void ConceptsList::addConcept(QString m_id, QString quantity, QString price, QString unit, QString description)
{
Concept *cpt=new Concept(m_id, quantity, unit, price, description);
m_concepts.append(cpt);
this->conceptsChanged();
}
void ConceptsList::removeConcept(int index)
{
m_concepts.removeAt(index);
this->conceptsChanged();
}

Related

QML ListView data retreival from mysql database

I my test database, I have table named users, which represents loggable users. Now, I've succesfully connected to database through Qt/C++, exposed class to QML (also without problems), setup QML ListView to show this users with custom model and delegate (file main.qml) and when I run my app, five items are shown in ListView. Now, ListView's delegate is composed from Image and Text sections (Image section for loggable user image, which resides as BLOB in mysql database and Text section for user name, which also resides as VARCHAR in mysql database - both fields are in same table named users):
import QtQuick 2.3
Item
{
id: uePeopleItemDelegate
property Image uePersonImage
property string uePersonName
width: 256
height: 256
antialiasing: true
clip: true
Rectangle
{
id: ueRectangleMain
color: "#000000"
radius: 16
anchors.fill: parent
antialiasing: true
border.color: "#ffffff"
border.width: 4
clip: true
//opacity: 0.7
Grid
{
antialiasing: true
anchors.rightMargin: 12
anchors.leftMargin: 12
anchors.bottomMargin: 12
anchors.topMargin: 12
anchors.fill: parent
spacing: 4
rows: 2
columns: 1
}
Row
{
id: ueRowImage
anchors.rightMargin: 12
anchors.leftMargin: 12
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
anchors.bottomMargin: 48
anchors.topMargin: 12
}
Image
{
id: uePersonleImage
x: 12
y: 12
width: 232
height: 196
antialiasing: true
fillMode: Image.PreserveAspectFit
source: ""
}
Column
{
id: ueColumnPeopleInfo
x: 12
y: 214
width: 232
height: 30
spacing: 0
}
Text
{
id: ueTextPersonName
x: 12
y: 214
width: 232
height: 30
color: "#ffffff"
text: uePersonName
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 16
}
}
}
And here is screenshot of delegates:
If I delete/add user in database, the number of delegates changes according to number of table user records, which works perfect. But now I have two problems: In main.qml, I instantiate this ListView:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtMultimedia 5.0
import QtQuick.Layouts 1.0
import QtTest 1.1
import "gui/delegates"
ApplicationWindow
{
id: ueWindowMain
title: qsTr("TestApp")
width: Screen.desktopAvailableWidth
height: Screen.desktopAvailableWidth
visible: true
opacity: 1.0
contentOrientation: Qt.LandscapeOrientation
color: "black"
ListView
{
id: uePeopleListView
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.ApplyRange
anchors.right: parent.right
anchors.rightMargin: 0
anchors.bottom: parent.top
anchors.bottomMargin: -128
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
orientation: ListView.Horizontal
flickableDirection: Flickable.HorizontalFlick
antialiasing: true
spacing: 16
delegate: UePeopleItemDelegate
{
id: uePersonDelegate
uePersonImage: "" // Q1: How do I retreive image (user image) from database?
uePersonName: "" // Q2: How do I retreive text (user name) from database?
}
model: uePeopleModel
}
}
How do I retreive data (image and text) to show it in delegate in lines 49 and 50? Here are also header and implementation of my model class:
#ifndef UEPEOPLEMODEL_H
#define UEPEOPLEMODEL_H
#include <QImage>
#include <QVariant>
#include <QStringList>
#include <QDebug>
#include <QHash>
#include <QByteArray>
#include <QtSql/QSqlError>
#include <QtSql/QSqlQueryModel>
#include <QtSql/QSqlRecord>
#include <QModelIndex>
#include "../settings/uedefaults.h"
class UePeopleModel : public QSqlQueryModel
{
Q_OBJECT
private:
QHash<int, QByteArray> m_ueRoleNames;
void ueGenerateRoleNames();
public:
UePeopleModel(QObject *parent=0);
~UePeopleModel();
QVariant data(const QModelIndex &index,
int role) const Q_DECL_OVERRIDE;
void ueRefresh();
inline QHash<int, QByteArray> ueRoleNames() const
{ return this->m_ueRoleNames; }
};
#endif // UEPEOPLEMODEL_H
and implementation:
#include "uepeoplemodel.h"
UePeopleModel::UePeopleModel(QObject* parent)
: QSqlQueryModel(parent)
{
QSqlDatabase db;
if(!QSqlDatabase::connectionNames().contains(UePosDatabase::UeDatabaseConnectionNames::DATABASE_CONNECTION_NAME_PEOPLE,
Qt::CaseInsensitive))
{
db=QSqlDatabase::addDatabase(UePosDatabase::DATABASE_DRIVER,
UePosDatabase::UeDatabaseConnectionNames::DATABASE_CONNECTION_NAME_PEOPLE);
} // if
db.setHostName(/*this->uePosSettings()->ueDbHostname()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_HOSTNAME);
db.setDatabaseName(/*this->uePosSettings()->ueDbName()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_NAME);
db.setUserName(/*this->uePosSettings()->ueDbUser()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_USERNAME);
db.setPassword(/*this->uePosSettings()->ueDbPassword()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_PASSWORD);
if(db.open())
{
this->setQuery(UePosDatabase::UeSqlQueries::UeTablePeople::SQL_QUERY_GET_ALL_PEOPLE,
db);
this->ueGenerateRoleNames();
}
else
{
qDebug() << db.lastError().text();
}
} // default constructor
UePeopleModel::~UePeopleModel()
{
} // default destructor
QVariant UePeopleModel::data(const QModelIndex &index,
int role) const
{
QVariant value;
if(role<Qt::UserRole)
{
value=QSqlQueryModel::data(index,
role);
}
else
{
int iColumnIndex=role-Qt::UserRole-1;
QModelIndex modelIndex=this->index(index.row(),
iColumnIndex);
value=QSqlQueryModel::data(modelIndex,
Qt::DisplayRole);
} // if
return value;
// QVariant value=QSqlQueryModel::data(index,
// role);
// if(value.isValid()&&role==Qt::DisplayRole)
// {
// switch(index.column())
// {
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_ID:
// return value.toInt();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_NAME:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_APPPASSWORD:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_CARD:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_IMAGE:
// {
// QImage image;
// image.loadFromData(value.toByteArray());
// return image;
// } // case
// default:
// return value;
// } // switch
// } // if
// return QVariant();
} // data
void UePeopleModel::ueRefresh()
{
this->setQuery(UePosDatabase::UeSqlQueries::UeTablePeople::SQL_QUERY_GET_ALL_PEOPLE);
} // ueRefresh
void UePeopleModel::ueGenerateRoleNames()
{
this->ueRoleNames().clear();
for(int iIndex=0; iIndex<this->record().count(); iIndex++)
{
this->ueRoleNames().insert(Qt::UserRole+1+iIndex,
this->record().fieldName(iIndex).toUtf8());
} // for
} // ueGenerateRoleNames
The default QML image provider can only work with URLs or file paths. If you want to load a QML image from a C++ image class, such as QPixmap or QImage you will have to go about and implement your own image provider. I've described one possible implementation strategy here:
After that it is fairly straightforward, the data() method won't be returning an image wrapped in a QVariant, just a custom image URL targeted at the custom image provider, and that's what you are going to use in the QML delegate. Naturally, inside the database, you will still have a blob, and you will still use the fromData() method to construct the image, however the image provider will be used to associate the custom image source string to the actual image. Naturally, you must find a way to control the lifetime of the actual image so you don't get memory leaks. My suggestion is to implement something like the Pixmap item from the answer listed above, and make it a part of your delegate - this way the image will be deleted when no longer needed. You will however also need to implement setData() for the Pixmap and pass the QPixmap pointer in text form and store it in pix.
It might be easier and wiser to NOT store the image in the database, but as a regular file, and just store the relative path to it in the database, it will save you some trouble. As for the question "how to retrieve data" - I'd say go there and actually read the documentation...

Accessing/modifying all instances of a type in Qt/QML

In QML, I have a custom object type (a separate QML file) and I would like a way of accessing and/or modifying each instance of this type. For a very simple example:
MyText.qml:
Text {
height: 100
width: 100
color: "red"
function logStuff() {
console.log("This is MyText")
}
}
SomePage.qml:
MyText {
id: text1
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: "foo"
}
MyText {
id: text2
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: "bar"
}
When an event occurs (e.g. a button on SomePage.qml being clicked or a signal being emitted), I would like to be able to change all instances of MyText to have the same value for one property, or call each MyText's logStuff() function.
Note: in my actual use case, there are actually several dozen of these instances, and there will be other Text elements which are not instances of MyText.
I have a little experience with jQuery and was hoping there would be something similar to jQuery selectors, but I have been unable to find anything remotely similar. Either a QML or a C++ (or mixed) solution would be fine.
I would like to suggest a method using C++.
Since there is no way to get base class of QML object you can base the searching on objectName property witch is accessible in C++.
Suppose we have base QML object:
import QtQuick 2.3
Item {
id: base
objectName: "BaseItem"
property int someValue: 0
onSomeValueChanged: {
console.log("'someValue' was changed to " + someValue + " for " + base);
}
}
And another QML file, where these objects are used:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 300
height: 300
id: mainWindow
Base {
id: derived1
}
Base {
id: derived2
}
Base {
id: derived3
}
}
The idea is that all derived object inherit the same objectName from the base class.
So now you can simply find all of the objects in C++:
QObject *root = engine.rootObjects().first();
QList<QObject *> list = root->findChildren<QObject *>("BaseItem");
qsrand(QTime::currentTime().msec());
foreach(QObject *item,list) {
item->setProperty("someValue",qrand());
}
Here I just change the property but you also can call some method with QMetaObject::invokeMethod() etc.
I'm not sure to understand your need but I would just use a Connections element like so :
SomePage.qml:
MyText {
id: text1
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: "foo"
Connections {
target: senderItemId
onFooChanged: logStuff()
}
}
MyText {
id: text2
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: "bar"
Connections {
target: senderItemId
onFooChanged: logStuff()
}
}
There's no API call in Qt to do this (that I could find), but you can find all the QML QObject's by a type using QObject::children() and QMetaObject in C++. Below is an implementation example.
QList<QObject *> MySingleton::findObjectsOfComponent(QObject *parent, QQmlComponent *component) const
{
auto object = component->create(QQmlEngine::contextForObject(parent));
auto foundObjects = findObjectsOfObject(parent, object);
delete object;
return foundObjects;
}
QList<QObject *> MySingleton::findObjectsOfObject(QObject *parent, const QObject *object) const
{
auto className = object->metaObject()->className();
QList<QObject*> items;
for(auto child : parent->children()) {
if(child->metaObject()->className() == className) {
items.append(child);
}
items.append(findObjectsOfObject(child, object));
}
return items;
}
Then you should register MySingleton as a QML singleton and use a Component to find the type.
Component {
id: errorLabelComponentId
ErrorLabel {}
}
Button {
onClicked: {
//Un-ignore all error labels
var errorLabels = MySingleton.findObjectsOfComponent(pageId, errorLabelComponentId)
for(var i in errorLabels) {
//ignoreError is a property of ErrorLabel {}
errorLabels[i].ignoreError = false
}
}
}

Binding C++ to QML code when using Components

---edited url and changed dynamic part to something compilable----
(Using Qt 5.3)
I tried to create a compact sample, but its still too big to post all the files here separately, so i added a link to "uploaded.to" as i cannot seem to attach a zip file here :-((
(warning, spam links and / or waiting time, any better fileshare site you recommend ?
Here is a link to "bindtest.zip" via uploaded.com, beware of spam/ugly pix:
http://ul.to/lqemy5jx
Okay, i will try to post the essence of the files here anyways:
I tried to create a simple Class in C++ containing a StringList and an index.
I Instantiated two Objects of this Class and exposed them via "setContextProperty"
This should be used in QML to initialize a ListView and to be in sync with it.
So whenever a User changes the index in QML, C++ should be notified AND vice versa.
So when i create two Component qml files using the hardwired names set in "setContextProperty" it seems to work fine.
But for the life of me i cannot create a single component file and pass the DataObject to it as a parameter, i simply do not know how to do it, although i tried.
My "final" target ist to create a QML Object dynamically and pass the DataObject to it, this does not work either :-(
So here it comes, code snippets of my sample Project:
Declaring my oh-so-simple Class (DataObject.h)
#ifndef DATAOBJECT_H
#define DATAOBJECT_H
#include <QObject>
#include <QDebug>
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY( int index MEMBER m_index NOTIFY indexChanged )
public slots:
int count() const { return m_Elements.count(); }
QString at(int idx) const { return m_Elements.at(idx); }
public:
void setIndex(int theInt) { m_index = theInt; }
signals:
void indexChanged(int);
public: // too lazy to write accessors for this sample, so make it public
QStringList m_Elements;
private:
int m_index;
};
#endif // DATAOBJECT_H
Registering it in main.cpp:
qmlRegisterType<DataObject>("bindtestTypes", 1, 0, "DataObject");
Here is the part of "dialog.cpp" that initializes and exposes two DataObects:
//preparing first list
m_firstDO.m_Elements = QStringList() << "A" << "B" << "C" << "D";
m_firstDO.setIndex(0);
//preparing second list
m_secondDO.m_Elements = QStringList() << "a" << "b" << "c" << "d";
m_secondDO.setIndex(3);
//publish the 2 Dataobjects
m_engine.rootContext()->setContextProperty( "cppDataList_1", &m_firstDO);
m_engine.rootContext()->setContextProperty( "cppDataList_2", &m_secondDO);
Here is the QML file "ShowLists.qml" that should simply show the 2 ListVies on Top of each other, i commented the 2 NOT working approaches that i would love to work, especially the dynamic one:
import QtQuick 2.2
import QtQuick.Window 2.1
import bindtestTypes 1.0
Window {
visible: true
width: 200
height: 400
Rectangle{
anchors.fill: parent
//dynamic: does not work :-(
// need to click on it to create it
// Rectangle{
// id:upperList
// anchors.top: parent.top;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_1});
// }
// }
// }
// Rectangle{
// id:lowerList
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// width:200
// height:200
// MouseArea{
// anchors.fill: parent
// onClicked: {
// var component = Qt.createComponent("SimpleList.qml");
// var dyncbb = component.createObject(parent, {"theDO": cppDataList_2});
// }
// }
// }
//static: would not be my first choice but isnt working anyways...
// SimpleList {
// id:upperList
// property DataObject theDO: cppDataList_1
// anchors.top: parent.top;
// anchors.left: parent.left
// }
// SimpleList {
// id:lowerList
// property DataObject theDO: cppDataList_2
// anchors.bottom: parent.bottom;
// anchors.left: parent.left
// }
//hardwired works, but its not workable for my rather complex project...
SimpleList1 {
id:upperList
anchors.top: parent.top;
anchors.left: parent.left
}
SimpleList2 {
id:lowerList
anchors.bottom: parent.bottom;
anchors.left: parent.left
}
}
}
Here is the first hardwired SimpleList1.qml that works fine, as well as the second:
import QtQuick 2.2
ListView {
id: list_view
width: 200
height: 200
currentIndex: cppDataList_1.index
model: cppDataList_1.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: cppDataList_1.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: cppDataList_1.index = currentIndex;
}
This is the "SimpleList.qml" that i cannot seem to get to work:
import QtQuick 2.2
import bindtestTypes 1.0
Rectangle {
ListView {
id: list_view
property DataObject theDO
width: 200
height: 200
currentIndex: theDO.index
model: theDO.count()
delegate: Rectangle {
height: 20
width: 200
Text { text: theDO.at(index); color: (list_view.currentIndex === index)?"red":"black" }
MouseArea{ anchors.fill: parent; onClicked: list_view.currentIndex = index }
}
onCurrentIndexChanged: theDO.index = currentIndex
}
}
So, can anyone of you help me to get this solved ??
IF you dare to follow the uploaded link and run my sample you can see one more glitch.
It displays 2 Windows, one QQQuickWIndow and a Widget.
In the Widget i can change the indexes as well as in the QML Window.
At first they are in sync but then the QML Window does not get updated anymore by changing the index in the widget, i hope its a glitch and not another general error i made.
Greetings & thanks for any help !
Nils
Argh, i found the problem, i did a very simple mistake:
The property i want to set in the SimpleList Component has to be in the root Object, so instead of this:
Rectangle {
ListView {
id: list_view
property DataObject theDO
...
It has to be done this way:
Rectangle {
property DataObject theDO
ListView {
id: list_view
...
Wow, thats an easy solution for a (seemingly) complex Problem.
Greetings,
Nils

QQuickWidget and C++ interaction

I am experiencing with the new QQuickWidget. How can I interact between the QQuickWidget and C++?
C++
QQuickWidget *view = new QQuickWidget();
view->setSource(QUrl::fromLocalFile("myqml.qml"));
view->setProperty("test", 0);
myLayout->addWidget(view);
QML
import QtQuick 2.1
Rectangle {
id: mainWindow
width: parent.width
height: parent.height
Text {
id: text
width: mainWindow.width
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: test
}
}
text: test does not work: ReferenceError: test is not defined
How can I give my QML file some properties via C++?
Is it also possible to get the Text object in C++ and update its text?
Give it a try:
view->rootContext()->setContextProperty("test", "some random text");
instead of
view->setProperty("test", 0);
setProperty(name, val) works if object has the property name defined as Q_PROPERTY.
It is possible to pass QObject-derived object as view's context property:
class Controller : public QObject
{
Q_OBJECT
QString m_test;
public:
explicit Controller(QObject *parent = 0);
Q_PROPERTY(QString test READ test WRITE setTest NOTIFY testChanged)
QDate test() const
{
return m_test;
}
signals:
void testChanged(QString arg);
public slots:
void setTest(QDate arg)
{
if (m_test != arg) {
m_test = arg;
emit testChanged(arg);
}
}
};
Controller c;
view->rootContext()->setContextProperty("controller", &c);
Text {
id: text
width: mainWindow.width
font.pixelSize: 20
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: controller.test
}
Is it also possible to get the Text object in C++ and update its text?
In general, it doesn't seem to be the best approach -- c++ code shouldn't be aware of presentation if it follows model-view pattern.
However it is possible as described here.

Attached properties in QML

Can I create my own attached for everything property like a Component?
Item{
Component.onCompleted : {} // Component is attached to everyone Items
}
Yes, for example:
#include <QQmlEngine>
#include <QTimer>
class TestAttached : public QObject
{
Q_OBJECT
// Declare property with setter and getter
Q_PROPERTY(int val READ getVal WRITE setVal)
public:
TestAttached() Q_DECL_EQ_DEFAULT;
explicit TestAttached(QObject *parent):
QObject{parent},
m_val{100500}
{
Q_ASSERT(parent);
qDebug() << "* We just have created the object of attached properties for" << parent->metaObject()->className();
}
~TestAttached() Q_DECL_EQ_DEFAULT;
// define static function qmlAttachedProperties(QObject *object)
// which returns pointer to instance of TestAttached class
static TestAttached *qmlAttachedProperties(QObject *object)
{
TestAttached* testAttached { new TestAttached{object} };
QTimer* timer { new QTimer{testAttached} };
connect(timer, &QTimer::timeout,
[testAttached] {
testAttached->setVal(testAttached->getVal()+1);
emit testAttached->testDone(testAttached->getVal());
});
timer->start(3000);
return testAttached;
}
inline int getVal() const
{
return m_val;
}
inline void setVal(int val)
{
m_val = val;
}
signals:
void testDone(int val);
private:
int m_val;
};
// Makes the type TestAttached known to QMetaType (for using with QVariant)
QML_DECLARE_TYPE(TestAttached)
// declares that the TestAttached supports attached properties.
QML_DECLARE_TYPEINFO(TestAttached, QML_HAS_ATTACHED_PROPERTIES)
Register attached type in QML system (e.g. at main.cpp):
qmlRegisterUncreatableType<TestAttached>("my.test", 1, 0, "Test", QObject::tr("Test is an abstract type that is only available as an attached property."));
And try it in main.qml:
import QtQuick 2.15
import QtQuick.Window 2.15
//import our module
import my.test 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World of Attached properties")
Rectangle {
color: focus ? Qt.lighter("red") : "red"
width: parent.width/2; height: parent.height
anchors.left: parent.left
Component.onCompleted: {
console.log("Rectangle is completed", Test.val)
}
Keys.onReturnPressed: {
console.log("Rect 1: Attachet value is", Test.val)
}
Test.val: 20000
Test.onTestDone: function(num) {
console.log("We received", num, "from attached object")
}
MouseArea {
anchors.fill: parent
onClicked: parent.focus = true
}
}
Rectangle {
color: focus ? Qt.lighter("blue") : "blue"
width: parent.width/2; height: parent.height
anchors.right: parent.right
focus: true
Keys.onReturnPressed: {
// The attached object will created after return pressed
console.log("Rect 2: Attachet value is", Test.val)
}
MouseArea {
anchors.fill: parent
onClicked: parent.focus = true
}
}
}
You may not be able to attach properties to Items or Components you did not create. But why would you want to do that anyway?
Instead you could consider using signals and global properties.
For global properties that you can access from anywhere you can set the context property of the root context of the declarative view.
i.e,
QmlApplicationViewer viewer;
MyPropertyClass myProp;
viewer->rootContext()->setContextProperty("MyPropClass", &myProp);
Now, in your QML file you can access the properties of this class as
Rectangle {
Text {
text: MyPropClass.getMyPropText()
}
MouseArea {
onClicked: { MyPropClass.text = "Clicked" }
}
}
This will invoke the Q_INVOKABLE method getMyPropText() from the MyPropertyClass. and Q_PROPERTY 'text' can be set when some signal is emitted.
Would this suit you needs?