My simplified code is
import QtQuick 2.0
Item {
ListModel {
id: items_model
dynamicRoles: true
}
Component.onCompleted: {
var data = {
"items": [{
"name": "test1",
"rows": ["1", "2", "3"]
}, {
"name": "test2",
"rows": ["1", "2", "3"]
}, {
"name": "test3",
"rows": ["1", "2", "3"]
}]
}
data.items.forEach(function (element) {
items_model.append(element)
console.log(element.rows)
})
console.log(items_model.count)
}
ListView {
orientation: ListView.Horizontal
model: items_model
delegate: Item {
height: parent.height
width: 50
Column {
anchors.fill: parent
Text {
text: name
}
Repeater {
model: rows
delegate: Text {
text: rows.get(index) + " " + index
Component.onCompleted: {
console.log(rows.get(index))
}
}
}
}
}
}
}
But I have DynamicRoleModelNode, not text
qml: DynamicRoleModelNode(0x563014585300)
qml: DynamicRoleModelNode(0x563014585330)
qml: DynamicRoleModelNode(0x563014585360)
qml: DynamicRoleModelNode(0x563014570350)
qml: DynamicRoleModelNode(0x563014570380)
qml: DynamicRoleModelNode(0x5630145703b0)
qml: DynamicRoleModelNode(0x563014558e00)
qml: DynamicRoleModelNode(0x563014558e30)
qml: DynamicRoleModelNode(0x563014558e60)
modelData not provided here besause of rows converted to QQmlListModel
How can i extract text from it?
Take a look at the documentation about models here:
https://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html#models
In particular:
Models that do not have named roles (such as the ListModel shown below) will have the data provided via the modelData role. The modelData role is also provided for models that have only one role. In this case the modelData role contains the same data as the named role.
So in your case, the string data you are looking for will be provided in a variable called modelData in the context of the Text component.
Note that in your specific case, this cannot be done directly because the ListModel is converting the child role items into a ListModel itself instead of a StringList model. This may be a bug in Qt as you would think you should normally get a StringList there.
To work around it, you can store your child items in a separate map and apply them directly to the Repeater so that you correctly get a StringList model and then modelData will indeed work.
Something like this:
property var itemsMap: ({
"test1": ["1", "2", "3"],
"test2": ["1", "2", "3"],
"test3": ["1", "2", "3"]
})
Item {
ListModel {
id: items_model
dynamicRoles: true
}
Component.onCompleted: {
var data = {
"items": [{
"name": "test1"
}, {
"name": "test2"
}, {
"name": "test3"
}]
}
data.items.forEach(function (element) {
items_model.append(element)
})
console.log(items_model.count)
}
ListView {
orientation: ListView.Horizontal
model: items_model
delegate: Item {
height: parent.height
width: 50
Column {
anchors.fill: parent
Text {
text: name
}
Repeater {
model: itemsMap[name]
delegate: Text {
text: modelData
}
}
}
}
}
}
As workaround wrap text to object
data.items.forEach(function (element) {
element.rows.forEach(function (item, index) {
element.rows[index] = {text: item}
})
items_model.append(element)
})
and
Repeater {
model: rows
delegate: Text {
text: text
}
}
Related
I don't know how to dynamically modify the data bound in the model. This is the official example document in QT.
I added a button to it and tried to modify the data of name through object, but failed.
ApplicationWindow {
id: window
width: 300
height: 300
visible: true
//I added this button
Button {
x:179
y:235
text: "Button"
z: 1
onClicked: {
//There's no use here!
t1.model.rows[0].name = "cat2"
console.log(t1)
}
}
// Button End
TableView {
id: t1
anchors.fill: parent
columnSpacing: 1
rowSpacing: 1
clip: true
model: TableModel {
TableModelColumn {
display: "name"
}
TableModelColumn {
display: "color"
}
rows: [{
"name": "cat",
"color": "black"
}, {
"name": "dog",
"color": "brown"
}, {
"name": "bird",
"color": "white"
}]
}
delegate: Rectangle {
implicitWidth: 100
implicitHeight: 50
border.width: 1
Text {
text: display
anchors.centerIn: parent
}
}
}
}
The problem is that when the button is triggered, the modified row value fails to take effect.
I tried to check the help document or search for answers in the search engine, but I got so little information that I had to ask experienced programmers for help here.
You have to use the setData method:
onClicked: {
var ix = t1.model.index(0, 0);
if (t1.model.setData(ix, "display", "cat2"))
console.log("success");
else
console.log("failed");
}
There are 9 parameters that I need to use TextField1 to input value.
So I use
ListModel lstPara {
ListElement{
text:"A";value:"123"
}...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: TextField1 {
}
}
}
But now there is a parameter that i need to use in another QML type to set the value, all others are used in TextField1.
I tried to define ListModel like this
ListModel lstPara{
ListElement {
text: "A";
type: 1";
value: "123"
}
ListElement {
text: "B";
type: 2";
value: "321"
}
...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
(some code : like this)
delegate: {
return type === 1 ?
TextField1 {
}
:
another QML type {
}
}
}
}
The code above can not run.
And I don`t want to write 8 TextField1 and 1 another QML type.
So, is there a way to use ListModel?
You can't directly use an Item declaration in a conditional expression like that, but you can do it with a Component. Here's an example of how to do it using a Loader as your delegate, and choosing which Component to load based on the model:
ListModel {
id: lstPara
ListElement {
text: "A"
type: 1
value: "123"
}
ListElement {
text: "B"
type: 2
value: "321"
}
}
Grid {
id:grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: Loader {
sourceComponent: type === 1 ? someText : otherText
onLoaded: {
item.text = text
item.value = value
}
}
}
Component {
id: someText
Text {
property string value
color: "blue"
}
}
Component {
id: otherText
Text {
property string value
color: "red"
}
}
}
I want to get the name of listElement (append dynamicaly with python) when I click for reuse this name with python function. How can I get this name ?
In this example I can only get the index 0 element...
QML part :
Rectangle {
id: listRowDb
anchors.top: toolbar.bottom
width: head_row.width
height: units.gu(100)
ListModel {
id: listDb
ListElement {
name: ""
}
}
Component {
id: listDbDelegate
Row {
spacing: 100
Text { text: 'Nom de la DB : ' + name}
}
}
ListView {
id: listView1
anchors.fill: parent
model: listDb
delegate: listDbDelegate
#Don't work. When I click I get the index 0 name.
MouseArea {
anchors.fill: parent
onClicked: {python.call('example.speak2', [listDb.get(listView1.currentIndex).name], function(returnValue) {console.log(returnValue)});}
}
}
}
Python {
id: python
Component.onCompleted: {
addImportPath(Qt.resolvedUrl('../src/'));
importModule('example', function() {
console.log('module imported');
#Just a litlle test
python.call('example.speak', ['Hello World!'], function(returnValue) {
console.log('example.speak returned ' + returnValue);
})
#Python list all DB and append in listModel
python.call('example.listDb', [], function(returnValue) {
for(var i=0; i<returnValue.length; i++) {
listDb.append({"name":returnValue[i]});
}
});
});
}
The python part work (only print element name)
You have to set the MouseArea on the delegate:
Component {
id: listDbDelegate
Row {
spacing: 100
Text {
text: 'Nom de la DB : ' + name
MouseArea{
anchors.fill: parent
onClicked: console.log(name, index)
}
}
}
}
ListView {
id: listView1
anchors.fill: parent
model: listDb
delegate: listDbDelegate
}
I can recover an element in my model:
property var model: [
{ title: i18n.tr("Standard"), descr: i18n.tr("Style by default"), style:"default" },
{ title: i18n.tr("None"), descr: i18n.tr("Icons without style", style:"none" }
]
ListItem.ItemSelector {
id: styleIconList
model: settingsColumn.model
delegate: OptionSelectorDelegate {
property var item: model.modelData ? model.modelData : model
text: item.title
subText: item.descr
}
onSelectedIndexChanged: {
launchermodular.settings.iconStyle = model.get(selectedIndex).style //here is problem
}
}
Can you tell me how?
Thank you
I have trouble retrieving the index of a delegate that is instantiated inside a DelegateModel for a ListView.
The minimal example as following:
LastProcedures.qml
ListModel {
ListElement {
procedure: "Liver Resection"
surgeon: "Prof. Dr. Joyride"
recent: true
}
...
}
main.qml
ListView {
id: list_lastProcedures
model: displayDelegateModel
}
DelegateModel {
id: displayDelegateModel
delegate: lastProceduresDelegate
model: LastProcedures {}
groups: [
DelegateModelGroup {
includeByDefault: false
name: "recent"
}
]
filterOnGroup: "recent"
Component.onCompleted: {
var rowCount = model.count;
items.remove(0,rowCount);
for( var i = 0;i < rowCount;i++ ) {
var entry = model.get(i);
// Only the recent three
if((entry.recent == true) && (items.count < 3)) {
items.insert(entry, "recent");
}
}
}
}
Component {
id: lastProceduresDelegate
Text{
text: model.index
}
}
The text index prints always -1. Without a DelegateModel it prints the index in the ListView. How can I access the correct index of the delegate in the Listview?
you can use "lastProceduresDelegate.DelegateModel.itemsIndex" instead of "model.index"
just like this:
Component {
id: lastProceduresDelegate
Text{
text: lastProceduresDelegate.DelegateModel.itemsIndex
}
I ended up with not removing all entries and adding them back to groups, but instead just remove unwanted entries. This ways the index stays valid.
If someone could explain this behavior further, that would be nice.
DelegateModel {
id: displayDelegateModel
delegate: lastProceduresDelegate
model: LastProcedures {}
groups: [
DelegateModelGroup {
includeByDefault: true
name: "recenttrue"
}
]
filterOnGroup: "recenttrue"
Component.onCompleted: {
for( var i = 0;i < items.count;i++ ) {
var entry = items.get(i).model;
// Only the recent
if((entry.recent != true)) {
items.removeGroups(i, 1, "recenttrue");
}
}
}
}
The DelegateModel has some hidden magic regarding groups (it's not very visible but it's here ). For each group you create, the DelegateModel attached property will receive two new properties: <group>Index and in<Group>.
In your case this means you will get the following properties: recentIndex and inRecent (or in your own answer: recenttrueIndex and inRecenttrue).
I think with what you want to do you should go the recenttrue route and draft the Component as follows:
Component {
id: lastProceduresDelegate
Text {
text: lastProceduresDelegate.DelegateModel.recenttrueIndex
}
}