PDA

View Full Version : property binding in a repeater



sedi
22nd March 2015, 10:10
Hi,
I have an item with 12 text fields, laid out as a grid and produced with a repeater.
Now I want to make bindings to one property each (line 29). How can I do that elegantly?

I don't even know which words to use for a google search on this...
I can imagine it might be possible with some kind of list in the property section?

Here's a condensed code that should clarify what I mean:


import QtQuick 2.2
import QtQuick.Controls 1.1

Item {

id: personButtonBasis

property string data00Text: ""
property string data01Text: ""
property string data02Text: ""
property string data03Text: ""
property string data04Text: ""
property string data05Text: ""
property string data06Text: ""
property string data07Text: ""
property string data08Text: ""
property string data09Text: ""
property string data10Text: ""
property string data11Text: ""


Grid {
id: dataTextGrid
columns: 3
Repeater {
id: dataFieldRepeater
model:12
Text {
text: ???????????
}
}

}


I tried in vain to "compose" the property name as in this (non-working) example (located starting from line 29):

Binding {
target: personButtonBasis;
property: (index<10)? "data0%1Text".arg(index) : "data%1Text".arg(index)
value: text
}
It might well be a very stupid question, as I am still very new to the QML world.

joko
23rd March 2015, 08:54
I think you need to implement it using ListModel



Item {
id: personButtonBasis

ListModel {
id: buttonModel
ListElement {
text: "Button 1"
}
ListElement {
text: "Button 2"
}
ListElement {
text: "Button 3"
}
ListElement {
text: "Button 4"
}
}


Grid {
id: dataTextGrid
columns: 3
Repeater {
id: dataFieldRepeater
model: buttonModel
Text {
text: model.text
}
}
}
}

anda_skoa
23rd March 2015, 09:11
What you would usually do in such a situation is to have a model instead of all these string properties.

In the easiest case the model would just be a list of strings



Item {
property var texts: [ "a", "b", c" ]

Repeater {
model: parent.texts

Text {
text: modelData
}
}
}


Cheers,
_

sedi
1st April 2015, 10:41
Thanks, anda_skoa!
That's exactly what I did - and it works! I have a rather complex model with several lists in it and it was not easy to get it done, but here are a few details for others with the same problem. This is not a compileable example (too big), it may have been shortened too much and may contain simplification mistakes - but it might perhaps be able to shorten the solution finding process of others anyway.

I need several buttons, each with several data fields on it. The latter are defined here:

class DataFieldContent: public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
signals:
void textChanged();
void colorChanged();
public:
[...]

//getters
QString text();
QColor color();

//setters
void setText(QString text);
void setColor(QColor color);
};


All my data fields are organized in a C++ class "Content":


class Content: public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<DataFieldContent>dataFieldContents READ dataFieldContents NOTIFY dataFieldContentsChanged)

signals:
void dataFieldContentsChanged();
public:
[...]
QQmlListProperty<DataFieldContent> dataFieldContents() { return
QQmlListProperty<DataFieldContent> (this,
0,
&appendDataFieldContent,
&dataFieldContentsCount,
&dataFieldContentAt,
&dataFieldContentsClear);
}

static void appendDataFieldContent(QQmlListProperty<DataFieldContent> *list, DataFieldContent *p);
static int dataFieldContentsCount(QQmlListProperty<DataFieldContent> *list);
static DataFieldContent* dataFieldContentAt(QQmlListProperty<DataFieldContent> *list, int i);
static void dataFieldContentsClear(QQmlListProperty<DataFieldContent> *list);

protected:
QList<DataFieldContent*>m_dataFieldContents;


As I need a bunch of those Buttons, there is also a GroupContent, basically a List of Contents (Note that QList does not derive from QObject, you can't "just use" it).


class GroupContent: public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Content> contents READ contents NOTIFY contentsChanged)

signals:
void contentsChanged();
public slots:
void contentsChangedSlot();

public:
[...]
QQmlListProperty <Content> contents();

static void appendContent(QQmlListProperty<Content> *list, Content *p);
static int contentsCount(QQmlListProperty<Content>*list);
static Content* contentAt(QQmlListProperty<Content> *list, int i);
static void contentsClear(QQmlListProperty<Content> *list);

void addContent(Content* p) { m_Contents.append(p);}


protected:
QList<Content*> m_Contents;
};

On the QML side we need a display: MyButton.qml:




Item {
id: ButtonBasis
property var dataFields;

Grid {
[...]
Repeater {
id: dataFieldRepeater
model: ButtonBasis.dataFields
Text {
text: dataFieldRepeater.model[index].text
color: dataFieldRepeater.model[index].color
[...]

}
}
}
}


And all that is linked together in main.qml:




Repeater {
id: contentRepeater
model: groupContent.contents
delegate: MyButton {
dataFields: contentRepeater.model[index].dataFieldContents
[...]
}
}


Don't forget to register your classes to QML (e.g. in main.cpp) by:


qmlRegisterType<DataFieldContent>("de.yourdomain.whatsoever.whatsoever",2,0,"DataFieldContent");
qmlRegisterType<Content>("de.yourdomain.whatsoever.whatsoever",2,0,"Content");
qmlRegisterType<GroupContent>("de.yourdomain.whatsoever.whatsoever",2,0,"GroupContent");

anda_skoa
1st April 2015, 14:10
Inside a delegate you can always address the current data entry like this



model.property


E.g. in your case


text: model.text
color: model.color


Cheers,
_

sedi
1st April 2015, 23:31
You are right, of course. Normally that should be the easier solution. I have, although, tried that in vain before - probably the problem was my use of nested delegates - so "model" had to be specifically addressed.

anda_skoa
2nd April 2015, 07:45
In such a case I would suggest keeping the data locally referencable instead



delegate: Item {
id: delegateRoot
readonly property text: model.text

Text {
text: delegateRoot.text // or here also: parent.text
}
}

That way you can potentially use the delegate in other views, referencing the model through a named view binds it to that specific view instance (can't even rename the view without breaking the delegate!)

Cheers,
_