PDA

View Full Version : Access QQmlListProperty<QObject> in qml



bibhukalyana
3rd February 2017, 19:07
Hi,

I have a qt function of return type QQmlListProperty<QObject>.
How can I use it in QML ?

Thanks.

anda_skoa
4th February 2017, 10:17
That function should be associated as the READ function of a property.

In QML you just use the property.

Cheers,
_

bibhukalyana
4th February 2017, 11:51
Thanks for the reply.

Is it possible Without property ?

Actually I am writing a class which will convert QList<QObject*>(QObject inherited class) to QAbstractListModel so that I can use it in QML.

So I took a child class of QAbstractListModel and redefined the data function where I was reading the corresponding Properties.

My QObject inherited sub class has a property of QQmlListProperty<QObject> type and now how to use it in QML side.

For QList<QString> I am able to access by index in QML but for QQmlListProperty it is not working.

Please help me or give some suggestion for the issues.

Thanks.

anda_skoa
5th February 2017, 08:08
I am afraid I don't understand.

Your object has a QQmlListProperty property but you don't want to use it?

Can you post some code?

Cheers,
_

bibhukalyana
6th February 2017, 06:40
Below I am giving one example.

I have two QObject derived class "Country" and "State"

State:


class State : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QList<QString> cmList READ cmList WRITE setCmList NOTIFY cmListChanged)

public:

QString name()
{
return m_name;
}

void setName(QString val)
{
m_name = val;
emit nameChanged();
}

QList<QString> cmList()
{
return m_CMList;
}

void setCmList(QList<QString> val)
{
m_CMList = val;
emit cmListChanged();
}

signals:
void nameChanged();
void cmListChanged();

private:
QString m_name;
QList<QString> m_CMList;
};



Country:



class Country : public QObject
{
Q_OBJECT

Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QList<QString> pmList READ pmList WRITE setPmList NOTIFY pmListChanged)
Q_PROPERTY(QQmlListProperty<State> stateList READ stateList CONSTANT)

public :

Country()
{
}

QString name()
{
return m_name;
}

void setName(QString val)
{
m_name = val;
emit nameChanged();
}

QList<QString> pmList()
{
return m_PMList;
}

void setPmList(QList<QString> val)
{
m_PMList = val;
emit pmListChanged();
}

QQmlListProperty<State> stateList()
{
return QQmlListProperty<State>(this, m_StateList);
}

void appendState(State* obj)
{
m_StateList.append(obj);
}

signals:
void nameChanged();
void pmListChanged();
private:
QString m_name;
QList<QString> m_PMList;
QList<State*> m_StateList;
};


Metatype:


Q_DECLARE_METATYPE(QQmlListProperty< Country > )
Q_DECLARE_METATYPE(QQmlListProperty< State > )


I also have "QAbstractListModel" derived model class called "MyModel"



class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
MyModel();

void setList(const QList<QObject*> &list)
{
m_list = list;
}

int rowCount(const QModelIndex &parent) const
{
m_list.length();
}
int columnCount(const QModelIndex &parent) const
{
if(!m_list.isEmpty())
return m_list.at(0)->metaObject()->propertyCount() - m_list.at(0)->metaObject()->propertyOffset();
return 0;
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section);
Q_UNUSED(orientation);
Q_UNUSED(role);
return QVariant();
}

QHash<int, QByteArray> roleNames() const
{
QHash<int, QByteArray> roles;

if(!m_list.isEmpty())
{
const QMetaObject* metaObject = m_list.at(0)->metaObject();
int r = Qt::UserRole+1;

for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i)
{
roles[r++] = QByteArray(metaObject->property(i).name());
}
}


return roles;
}
QVariant data(const QModelIndex &index, int role) const
{
if (!index.isValid() || m_list.isEmpty())
{
return QVariant();
}
else
{
}

role = role - Qt::UserRole;


if(role > 0 && m_list.length() > index.row())
{
const QMetaObject* metaObject = m_list.at(index.row())->metaObject();

role += metaObject->propertyOffset();

if(role <= metaObject->propertyCount())
{
return metaObject->property(role-1).read(m_list.at(index.row()));
}
}

return QVariant();
}
private:
QList<QObject*> m_list;
};



I have a QList<Country*> and I want to display it on my QML ListView.
Below I gave a small example how i am using it.



qmlRegisterType<Country>("mytest.country", 1, 0, "Country");
qmlRegisterType<State>("mytest.state", 1, 0, "State");

QList<QString> cmList;
QList<QString> pmList;

cmList << "cm1" << "cm2";
pmList << "pm1" << "pm2";

State s1, s2, s3, s4;

s1.setName("S1");
s1.setCmList(cmList);

s2.setName("S2");
s2.setCmList(cmList);

s3.setName("S3");
s3.setCmList(cmList);

s4.setName("S4");
s4.setCmList(cmList);

Country c1, c2;

c1.setName("C1");
c1.setPmList(pmList);
c1.appendState(&s1);
c1.appendState(&s2);

c2.setName("C2");
c2.setPmList(pmList);
c2.appendState(&s3);
c2.appendState(&s4);

QList<QObject*> countryList;

countryList << &c1 << & c2;

MyModel myModel;
myModel.setList(countryList);

QQmlApplicationEngine engine;
QQmlContext * context = engine.rootContext();

context->setContextProperty("myModel", &myModel);

engine.load("../test/testqml.qml");


QML:


import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQml 2.0

import mytest.country 1.0
import mytest.state 1.0

ApplicationWindow {

visible: true
width: 600
height: 600

Rectangle
{
id: rect1
color: "transparent"
anchors.fill: parent

anchors.verticalCenter: parent.verticalCenter


ScrollView
{
id: slv1

width: parent.width
height: parent.height
contentItem: listView1
focus: true

ListView
{
id: listView1

height: parent.height
width: parent.width
anchors.left: parent.left
anchors.leftMargin: 10
model: myModel

focus: true
currentIndex: 0

delegate: Item{
id: delegateRect
width: listView1.width
height: 80

Rectangle
{
width: listView1.width - 10
height: listView1.height - 10
color: "transparent"

Item
{
id: textIteml
width: 150
height:150

Text
{
anchors.verticalCenter: textIteml.verticalCenter
color: "red"
font.pointSize: 18
text: name
font.family: "corpoSLig"
width: textIteml.width
elide: Text.ElideRight
}

}

Item
{
id: textIteml1
width: 150
height:150
anchors.left: textIteml.right
anchors.leftMargin: 5

Text
{
anchors.verticalCenter: parent.verticalCenter
color: "red"
font.pointSize: 18
text: pmList[0]
font.family: "corpoSLig"
width: textIteml.width
elide: Text.ElideRight
}

}

Item
{
id: textIteml2
width: 150
height:150
anchors.left: textIteml1.right
anchors.leftMargin: 5

Text
{
anchors.verticalCenter: parent.verticalCenter
color: "red"
font.pointSize: 18
text: stateList[0] //------------------------------Error : Unable to assign [undefined] to QString
text: stateList //------------------------------Error : Unable to assign QQmlListProperty<State> to QString
//How to print 1st state name
font.family: "corpoSLig"
width: textIteml.width
elide: Text.ElideRight
}

}
}

}

}
}
}

}




My main target is there will be a single model class and it can convert any QList<QObjetc*> to model base on the properties.

Thanks.

anda_skoa
6th February 2017, 08:32
Strange. I assume you have verified that the data() method actually returns the correct value, right?

In any case the usage of QQmlListProperty in this context looks weird, there is no QML code that creates instances inside the list.
This looks more like the use case of a QList<QObject*> or QList<State*> in this case.

Some general observations:

* your property setters lack the check for change, they always emit the notify signal even it the value did not change
* not sure why you register State and Country as QML types, especially why in different modules?
* are you aware that most QtQuick components can only deal with list models, not table models? I.e. columnCount == 1

Cheers,
_

bibhukalyana
6th February 2017, 09:28
Strange. I assume you have verified that the data() method actually returns the correct value, right?


I tried "QQmlListProperty AtFunction " and able to print correct data but after that my program was crashing.



In any case the usage of QQmlListProperty in this context looks weird, there is no QML code that creates instances inside the list.
This looks more like the use case of a QList<QObject*> or QList<State*> in this case.


What I gave you is just an example. But my real classes are thrift generated classes and which is not in my control.
I have to export those classes to Qml.



your property setters lack the check for change, they always emit the notify signal even it the value did not change

Thanks for the comment. Original implementation has the check for change. Here I just left all checks.



are you aware that most QtQuick components can only deal with list models, not table models? I.e. columnCount == 1


In our case there are some limitations(For nested QList we have to read only one index mostly 0)

I don't have that much Knowledge regarding QtQuick/Qml. Can you please suggest some solutions for it so that I can display QList<QOblect> in QML ?

Thanks.

anda_skoa
7th February 2017, 07:18
What I gave you is just an example. But my real classes are thrift generated classes and which is not in my control.
I have to export those classes to Qml.

Thrift as in Apache Thrift? Why does it generate QML specific properties, i.e. of type QQmlListProperty?

Anyway, have you tried just returning the object itself from the model's data() function?



Thanks for the comment. Original implementation has the check for change. Here I just left all checks.

Ah, ok. Common mistake to forget that, so commented on it :)



I don't have that much Knowledge regarding QtQuick/Qml. Can you please suggest some solutions for it so that I can display QList<QOblect> in QML ?

All views can handle that as a model, e.g. ListView, but also Repeater, etc.

Cheers,
_