74 The ListModel is a simple hierarchy of elements containing data roles. The contents can |
74 The ListModel is a simple hierarchy of elements containing data roles. The contents can |
75 be defined dynamically, or explicitly in QML: |
75 be defined dynamically, or explicitly in QML: |
76 |
76 |
77 For example: |
77 For example: |
78 |
78 |
79 \code |
79 \snippet doc/src/snippets/declarative/listmodel.qml 0 |
80 ListModel { |
80 |
81 id: fruitModel |
81 Roles (properties) must begin with a lower-case letter. The above example defines a |
82 ListElement { |
|
83 name: "Apple" |
|
84 cost: 2.45 |
|
85 } |
|
86 ListElement { |
|
87 name: "Orange" |
|
88 cost: 3.25 |
|
89 } |
|
90 ListElement { |
|
91 name: "Banana" |
|
92 cost: 1.95 |
|
93 } |
|
94 } |
|
95 \endcode |
|
96 |
|
97 Roles (properties) must begin with a lower-case letter.The above example defines a |
|
98 ListModel containing three elements, with the roles "name" and "cost". |
82 ListModel containing three elements, with the roles "name" and "cost". |
99 |
83 |
100 Values must be simple constants - either strings (quoted), bools (true, false), numbers, |
84 Values must be simple constants - either strings (quoted and optionally within a call to QT_TR_NOOP), |
101 or enum values (like Text.AlignHCenter). |
85 bools (true, false), numbers, or enum values (like Text.AlignHCenter). |
102 |
86 |
103 The defined model can be used in views such as ListView: |
87 The defined model can be used in views such as ListView: |
104 \code |
88 |
105 Component { |
89 \snippet doc/src/snippets/declarative/listmodel-simple.qml 0 |
106 id: fruitDelegate |
90 \dots 8 |
107 Item { |
91 \snippet doc/src/snippets/declarative/listmodel-simple.qml 1 |
108 width: 200; height: 50 |
92 \image listmodel.png |
109 Text { text: name } |
|
110 Text { text: '$'+cost; anchors.right: parent.right } |
|
111 } |
|
112 } |
|
113 |
|
114 ListView { |
|
115 model: fruitModel |
|
116 delegate: fruitDelegate |
|
117 anchors.fill: parent |
|
118 } |
|
119 \endcode |
|
120 |
93 |
121 It is possible for roles to contain list data. In the example below we create a list of fruit attributes: |
94 It is possible for roles to contain list data. In the example below we create a list of fruit attributes: |
|
95 |
|
96 \snippet doc/src/snippets/declarative/listmodel-nested.qml model |
|
97 |
|
98 The delegate below displays all the fruit attributes: |
|
99 |
|
100 \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate |
|
101 \image listmodel-nested.png |
|
102 |
|
103 |
|
104 \section2 Modifying list models |
|
105 |
|
106 The content of a ListModel may be created and modified using the clear(), |
|
107 append(), set() and setProperty() methods. For example: |
|
108 |
|
109 \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate |
|
110 |
|
111 When creating content dynamically, note that the set of available properties cannot be changed |
|
112 except by first clearing the model. Whatever properties are first added to the model are then the |
|
113 only permitted properties in the model until it is cleared. |
|
114 |
|
115 |
|
116 \section2 Using threaded list models with WorkerScript |
|
117 |
|
118 ListModel can be used together with WorkerScript access a list model |
|
119 from multiple threads. This is useful if list modifications are |
|
120 synchronous and take some time: the list operations can be moved to a |
|
121 different thread to avoid blocking of the main GUI thread. |
|
122 |
|
123 Here is an example that uses WorkerScript to periodically append the |
|
124 current time to a list model: |
|
125 |
|
126 \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 |
|
127 |
|
128 The included file, \tt dataloader.js, looks like this: |
|
129 |
|
130 \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 |
|
131 |
|
132 The application's \tt Timer object periodically sends a message to the |
|
133 worker script by calling \l WorkerScript::sendMessage(). When this message |
|
134 is received, \l {WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in |
|
135 \tt dataloader.js, which appends the current time to the list model. |
|
136 |
|
137 Note the call to sync() from the \l {WorkerScript::onMessage}{WorkerScript.onMessage()} handler. |
|
138 You must call sync() or else the changes made to the list from the external |
|
139 thread will not be reflected in the list model in the main thread. |
|
140 |
|
141 \section3 Limitations |
|
142 |
|
143 If a list model is to be accessed from a WorkerScript, it \bold cannot |
|
144 contain list data. So, the following model cannot be used from a WorkerScript |
|
145 because of the list contained in the "attributes" property: |
122 |
146 |
123 \code |
147 \code |
124 ListModel { |
148 ListModel { |
125 id: fruitModel |
149 id: fruitModel |
126 ListElement { |
150 ListElement { |
129 attributes: [ |
153 attributes: [ |
130 ListElement { description: "Core" }, |
154 ListElement { description: "Core" }, |
131 ListElement { description: "Deciduous" } |
155 ListElement { description: "Deciduous" } |
132 ] |
156 ] |
133 } |
157 } |
134 ListElement { |
|
135 name: "Orange" |
|
136 cost: 3.25 |
|
137 attributes: [ |
|
138 ListElement { description: "Citrus" } |
|
139 ] |
|
140 } |
|
141 ListElement { |
|
142 name: "Banana" |
|
143 cost: 1.95 |
|
144 attributes: [ |
|
145 ListElement { description: "Tropical" }, |
|
146 ListElement { description: "Seedless" } |
|
147 ] |
|
148 } |
|
149 } |
158 } |
150 \endcode |
159 \endcode |
151 |
160 |
152 The delegate below will list all the fruit attributes: |
|
153 \code |
|
154 Component { |
|
155 id: fruitDelegate |
|
156 Item { |
|
157 width: 200; height: 50 |
|
158 Text { id: name; text: name } |
|
159 Text { text: '$'+cost; anchors.right: parent.right } |
|
160 Row { |
|
161 anchors.top: name.bottom |
|
162 spacing: 5 |
|
163 Text { text: "Attributes:" } |
|
164 Repeater { |
|
165 model: attributes |
|
166 Component { Text { text: description } } |
|
167 } |
|
168 } |
|
169 } |
|
170 } |
|
171 \endcode |
|
172 |
|
173 \section2 Modifying list models |
|
174 |
|
175 The content of a ListModel may be created and modified using the clear(), |
|
176 append(), and set() methods. For example: |
|
177 |
|
178 \code |
|
179 Component { |
|
180 id: fruitDelegate |
|
181 Item { |
|
182 width: 200; height: 50 |
|
183 Text { text: name } |
|
184 Text { text: '$'+cost; anchors.right: parent.right } |
|
185 |
|
186 // Double the price when clicked. |
|
187 MouseArea { |
|
188 anchors.fill: parent |
|
189 onClicked: fruitModel.set(index, "cost", cost*2) |
|
190 } |
|
191 } |
|
192 } |
|
193 \endcode |
|
194 |
|
195 When creating content dynamically, note that the set of available properties cannot be changed |
|
196 except by first clearing the model - whatever properties are first added are then the |
|
197 only permitted properties in the model. |
|
198 |
|
199 |
|
200 \section2 Using threaded list models with WorkerScript |
|
201 |
|
202 ListModel can be used together with WorkerScript access a list model |
|
203 from multiple threads. This is useful if list modifications are |
|
204 synchronous and take some time: the list operations can be moved to a |
|
205 different thread to avoid blocking of the main GUI thread. |
|
206 |
|
207 Here is an example that uses WorkerScript to periodically append the |
|
208 current time to a list model: |
|
209 |
|
210 \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 |
|
211 |
|
212 The included file, \tt dataloader.js, looks like this: |
|
213 |
|
214 \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 |
|
215 |
|
216 The application's \tt Timer object periodically sends a message to the |
|
217 worker script by calling \tt WorkerScript::sendMessage(). When this message |
|
218 is received, \tt WorkerScript.onMessage() is invoked in |
|
219 \tt dataloader.js, which appends the current time to the list model. |
|
220 |
|
221 Note the call to sync() from the \c WorkerScript.onMessage() handler. |
|
222 You must call sync() or else the changes made to the list from the external |
|
223 thread will not be reflected in the list model in the main thread. |
|
224 |
|
225 \section3 Limitations |
|
226 |
|
227 If a list model is to be accessed from a WorkerScript, it \bold cannot |
|
228 contain list data. So, the following model cannot be used from a WorkerScript |
|
229 because of the list contained in the "attributes" property: |
|
230 |
|
231 \code |
|
232 ListModel { |
|
233 id: fruitModel |
|
234 ListElement { |
|
235 name: "Apple" |
|
236 cost: 2.45 |
|
237 attributes: [ |
|
238 ListElement { description: "Core" }, |
|
239 ListElement { description: "Deciduous" } |
|
240 ] |
|
241 } |
|
242 } |
|
243 \endcode |
|
244 |
|
245 In addition, the WorkerScript cannot add any list data to the model. |
161 In addition, the WorkerScript cannot add any list data to the model. |
246 |
162 |
247 \sa {qmlmodels}{Data Models}, WorkerScript, QtDeclarative |
163 \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative |
248 */ |
164 */ |
249 |
165 |
250 |
166 |
251 /* |
167 /* |
252 A ListModel internally uses either a NestedListModel or FlatListModel. |
168 A ListModel internally uses either a NestedListModel or FlatListModel. |
633 |
549 |
634 if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { |
550 if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { |
635 QDeclarativeCustomParserNode node = |
551 QDeclarativeCustomParserNode node = |
636 qvariant_cast<QDeclarativeCustomParserNode>(value); |
552 qvariant_cast<QDeclarativeCustomParserNode>(value); |
637 |
553 |
638 if (node.name() != "ListElement") { |
554 if (node.name() != listElementTypeName) { |
639 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); |
555 const QMetaObject *mo = resolveType(node.name()); |
640 return false; |
556 if (mo != &QDeclarativeListElement::staticMetaObject) { |
|
557 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements")); |
|
558 return false; |
|
559 } |
|
560 listElementTypeName = node.name(); // cache right name for next time |
641 } |
561 } |
642 |
562 |
643 { |
563 { |
644 ListInstruction li; |
564 ListInstruction li; |
645 li.type = ListInstruction::Push; |
565 li.type = ListInstruction::Push; |
702 d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list |
622 d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list |
703 } else { |
623 } else { |
704 QByteArray script = variant.asScript().toUtf8(); |
624 QByteArray script = variant.asScript().toUtf8(); |
705 int v = evaluateEnum(script); |
625 int v = evaluateEnum(script); |
706 if (v<0) { |
626 if (v<0) { |
707 error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); |
627 if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) { |
708 return false; |
628 d[0] = char(QDeclarativeParser::Variant::String); |
|
629 d += script.mid(12,script.length()-14); |
|
630 } else { |
|
631 error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value")); |
|
632 return false; |
|
633 } |
709 } else { |
634 } else { |
710 d[0] = char(QDeclarativeParser::Variant::Number); |
635 d[0] = char(QDeclarativeParser::Variant::Number); |
711 d += QByteArray::number(v); |
636 d += QByteArray::number(v); |
712 } |
637 } |
713 } |
638 } |
727 |
652 |
728 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps) |
653 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps) |
729 { |
654 { |
730 QList<ListInstruction> instr; |
655 QList<ListInstruction> instr; |
731 QByteArray data; |
656 QByteArray data; |
|
657 listElementTypeName = QByteArray(); // unknown |
732 |
658 |
733 for(int ii = 0; ii < customProps.count(); ++ii) { |
659 for(int ii = 0; ii < customProps.count(); ++ii) { |
734 const QDeclarativeCustomParserProperty &prop = customProps.at(ii); |
660 const QDeclarativeCustomParserProperty &prop = customProps.at(ii); |
735 if(prop.name() != "") { // isn't default property |
661 if(prop.name() != "") { // isn't default property |
736 error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); |
662 error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); |