I'm developing an application. I need to add data to TreeView but I have some problems with TreeView's nested model.
I created a List Model which is
ListModel {
id: fruitModel
ListElement {
name: "Apple"
price: 2.45
}
}
I am able to show this item by the way of
TreeView {
id: mytreeView
anchors.fill: parent
model: fruitModel
TableViewColumn {
title: "Name"
role: "name"
width: 200
}
TableViewColumn {
title: "Price"
role: "price"
width: 200
}
}
However , I need to show nested items like
ListModel {
ListElement {
categoryName: "Fruits"
collapsed: true
subItems: [
ListElement { itemName: "Orange" , itemPrice: 2.40},
ListElement { itemName: "Apple" , itemPrice: 2.40},
ListElement { itemName: "Pear" , itemPrice: 2.40},
ListElement { itemName: "Lemon" , itemPrice: 2.40}
]
}
}
I listed these items in ListView but I need to list these items in TreeView. So, how can I list this nested ListModel in TreeView using QML?
First, you need to correct the roles to match your new nested model/ListElement's scope/roles. And build your model correctly (you using "," for ListElement items .. use ";") as corrected below.
Second , use listmodel function object get(index) to iterate down and lockup the nested model.
THis will work!:
ListModel {
id:fruitModel
ListElement {
categoryName: "Fruits"
collapsed: true
subItems: [
ListElement { itemName: "Orange" ; itemPrice: 2.40},
ListElement { itemName: "Apple" ; itemPrice: 2.40},
ListElement { itemName: "Pear" ; itemPrice: 2.40},
ListElement { itemName: "Orange" ; itemPrice: 2.40}
]
}
ListElement {
categoryName: "Vegetable"
collapsed: true
subItems: [
ListElement { itemName: "Broccoli" ; itemPrice: 4.20},
ListElement { itemName: "Garlic" ; itemPrice: 7.40},
ListElement { itemName: "beens" ; itemPrice: 6.40},
ListElement { itemName: "Eggplant" ; itemPrice: 1.40}
]
}
}
TreeView {
id: mytreeView
anchors.fill: parent
model:fruitModel.get(0).subItems // get nested model this way , or by Javascript!
TableViewColumn {
title: "Name"
role: "itemName" // here
width: 200
}
TableViewColumn {
title: "Price"
role: "itemPrice" // here
width: 200
}
//Component.onCompleted: model= fruitModel.get(0).subItems
}
UpDate:
To display multiple nested elements , you can use section property with ListView and display nested model with TreeView :
ListView {
anchors.fill: parent
model: fruitModel
delegate: nameDelegate
focus: true
highlight: Rectangle {
color: "lightblue"
width: parent.width
}
section {
property: "categoryName"
criteria: ViewSection.FullString
delegate: Rectangle {
color: "#b0dfb0"
width: parent.width
height: childrenRect.height + 4
Text { anchors.left: parent.left
font.pixelSize: 16
font.bold: true
text: section
}
}
}
}
Component {
id: nameDelegate
TreeView {
id: mytreeView
width: parent.width
model:fruitModel.get(index).subItems // get nested model
TableViewColumn {
title: "Name"
role: "itemName" // here
width: 200
}
TableViewColumn {
title: "Price"
role: "itemPrice" // here
width: 200
}
}
}
Related
import QtQuick 2.7
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate: Item{
height: 50
width: animalColumn.width
TextField{
text: styleData.value
anchors.fill: parent
}
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 50
resizable: false
movable: false
}
}
}
This code is not acting as I expect. Textfield gets focus only when I am clicking the header with right button. And there is a Textfield works fine next to the first column header, but it's not what I want.
How to add a editable TableView header in qml?
I find this headerDelegate to be rather buggy or not supposed to be used in this scenario. I would rather implemnt a header from scrath to use for your use-case. Try this as a starting point:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
}
TextField{
id: tableViewCustomHeader
property int curRow: tableView.currentRow
property bool isActual: curRow >= 0
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
width: animalColumn.width
height: isActual ? 20 : 0
onCurRowChanged: {
if ( isActual >= 0 ) reloadText();
focus = true;
}
onTextChanged: if ( isActual >= 0 ) saveText()
}
TableView {
id: tableView
anchors {
top: tableViewCustomHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: tstModel
headerVisible: false
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
You can edit this anyway you want afterwards.
If it's a strict requirement to go with headerDelegate - this what I've end up with:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
ListElement { animal: "cat" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate:
TextField{
property int curRow: tableView.currentRow
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
onCurRowChanged: {
if ( curRow >= 0 ) reloadText()
}
onTextChanged: saveText()
width: parent.width
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
But, again, it works in a very strange way once you are adding TextField to it, so I would vote against it.
I want to disable the dragging of my List view using pressing and holding a mouse button while moving the cursor. I am trying to implement some other feature such as multiple selection using this command. I would only like the scrolling to be enabled. The interactive property of the flickable totally disables the movement of the view. Is there some workaround for this?
I guess there are some ways to achieve what you need, but the following solution works.
The idea is to have a MouseArea and set the interactive property to false when the signals onPressed and onClicked are emitted. interactive should be set to true again in the onReleased handler.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: listModel
ListElement {
name: "ttt"
number: "111"
}
ListElement {
name: "rrr"
number: "222"
}
ListElement {
name: "sss"
number: "333"
}
ListElement {
name: "xxx"
number: "444"
}
ListElement {
name: "yyy"
number: "555"
}
ListElement {
name: "zzz"
number: "666"
}
ListElement {
name: "aaa"
number: "777"
}
ListElement {
name: "bbb"
number: "888"
}
ListElement {
name: "ccc"
number: "999"
}
ListElement {
name: "ddd"
number: "011"
}
ListElement {
name: "eee"
number: "022"
}
ListElement {
name: "fff"
number: "033"
}
}
ListView {
id: myList
width: 180; height: 100
clip: true
Component {
id: contactsDelegate
Rectangle {
id: wrapper
width: 180
height: contactInfo.height
color: "lightblue"
Text {
id: contactInfo
text: name + ": " + number
color: "black"
}
MouseArea {
anchors.fill: parent
onPressed: {
myList.interactive = false
console.debug("onPressed")
}
onClicked: {
myList.interactive = false
console.debug("onClicked")
}
onReleased: {
myList.interactive = true
console.debug("onReleased")
}
}
}
}
model: listModel
delegate: contactsDelegate
focus: true
}
}
I need to create nested list view and as shown below, and highlight the main list and sub-list with different color
I have tried with ListView highlight but there are issue like the highlight showing for child as well as parent as shown
below image.
I am using the code from here with some minor modification.
Here is the full code
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
id: loginWindow
//visibility: "Maximized"
visible: true
width: 720
height: 720
Item {
width: 200
height: 720
ListView {
id: list
anchors.fill: parent
model: nestedModel
delegate: categoryDelegate
highlight: Rectangle {
color: "#FF00AAFF" //"#FF59ACFF";
radius: 2
}
}
ListModel {
id: nestedModel
ListElement {
categoryName: "Veggies"
collapsed: true
// A ListElement can't contain child elements, but it can contain
// a list of elements. A list of ListElements can be used as a model
// just like any other model type.
subItems: [
ListElement {
itemName: "Tomato"
},
ListElement {
itemName: "Cucumber"
},
ListElement {
itemName: "Onion"
},
ListElement {
itemName: "Brains"
}
]
}
ListElement {
categoryName: "Fruits"
collapsed: true
subItems: [
ListElement {
itemName: "Orange"
},
ListElement {
itemName: "Apple"
},
ListElement {
itemName: "Pear"
},
ListElement {
itemName: "Lemon"
}
]
}
ListElement {
categoryName: "Cars"
collapsed: true
subItems: [
ListElement {
itemName: "Nissan"
},
ListElement {
itemName: "Toyota"
},
ListElement {
itemName: "Chevy"
},
ListElement {
itemName: "Audi"
}
]
}
}
Component {
id: categoryDelegate
Column {
width: 200
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "#33FF5225"
height: 50
width: 200
Text {
anchors.verticalCenter: parent.verticalCenter
x: 15
font.pixelSize: 24
text: categoryName
}
Rectangle {
color: "red"
width: 30
height: 30
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
// Toggle the 'collapsed' property
onClicked: {
list.currentIndex = index
nestedModel.setProperty(index, "collapsed",
!collapsed)
}
}
}
}
Loader {
id: subItemLoader
// This is a workaround for a bug/feature in the Loader element. If sourceComponent is set to null
// the Loader element retains the same height it had when sourceComponent was set. Setting visible
// to false makes the parent Column treat it as if it's height was 0.
visible: !collapsed
property variant subItemModel: subItems
sourceComponent: collapsed ? null : subItemColumnDelegate
onStatusChanged: if (status == Loader.Ready) item.model = subItemModel
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model: subItemRepeater.model
width: 200
Repeater {
id: subItemRepeater
delegate: Rectangle {
x: 10
color: "#33FF5225"
height: 40
width: 190
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}
}
How can I overcome this issue. Basically I need to highlight Parent and child Item with different color.
Edit:
I can highlight parent list using the code
color:categoryDelegate.ListView.isCurrentItem ? "#FF00AAFF" : "#CCBBBBBB"
but coud'nt a find similar way to change the color of child list (sub list) on click.
Change the color property of the delegate in subItemRepeater to your choice.
Example
Component {
id: subItemColumnDelegate
Column {
...
Repeater {
id: subItemRepeater
delegate: Rectangle {
...
color: "purple"
...
}
}
}
}
Similarly change the color property categoryItem in the categoryDelegate.
Example
Component {
id: categoryDelegate
Column {
...
Rectangle {
id: categoryItem
...
color: "blue"
...
}
}
}
EDIT:
In that case the the overall concept is wrong. In the comment of the original code the author has written A ListElement can't contain child elements, but it can contain a list of elements. So we can't highlight the child item. But the following approach will give good result to you.
import QtQuick 2.0
Rectangle {
width: 360
height: 360
ListModel {
id: model1
ListElement {
name: "name"
}
ListElement {
name: "name"
}
ListElement {
name: "name"
}
}
ListModel {
id: model2
ListElement {
name: "inside"
}
ListElement {
name: "inside"
}
ListElement {
name: "inside"
}
}
ListView {
id: outer
model: model1
delegate: listdelegate
anchors.fill: parent
}
Component {
id: listdelegate
Item {
width: 100
height: col.childrenRect.height
Column {
id: col
anchors.left: parent.left
anchors.right: parent.right
Text {
id: t1
text: name
}
ListView {
id: insidelist
model: model2
property int collapseHeightFlag: childrenRect.height
delegate: Component {
id: delegate2
Item {
width: 100
height: col2.childrenRect.height
Column {
id: col2
anchors.left: parent.left
anchors.right: parent.right
Text {
id: name1
text: name
}
}
MouseArea {
anchors.fill: parent
onClicked: {
insidelist.currentIndex = index;
}
}
}
}
contentHeight: contentItem.childrenRect.height
height: 0
anchors.left: parent.left
anchors.right: parent.right
clip: true
highlight: Rectangle {
color: "pink"
radius: 2
}
focus: true
}
}
Rectangle {
color: "red"
width: 10
height: 10
anchors.right: parent.right
anchors.rightMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
MouseArea {
anchors.fill: parent
onClicked: {
if(insidelist.height === insidelist.collapseHeightFlag) {
insidelist.height = 0;
}
else
insidelist.height = insidelist.collapseHeightFlag;
}
}
}
}
}
}
I've found the following code in a QML example. I have a question about sections: is there a way to hide all the elements belonging to a section when the user clicks on the section header? For example, is it possible to hide "Ant" and "Flea" when the user clicks the "Tiny" header?
Here is the code:
Rectangle {
id: container
width: 300
height: 360
ListModel {
id: animalsModel
ListElement { name: "Ant"; size: "Tiny" }
ListElement { name: "Flea"; size: "Tiny" }
ListElement { name: "Parrot"; size: "Small" }
ListElement { name: "Guinea pig"; size: "Small" }
ListElement { name: "Rat"; size: "Small" }
ListElement { name: "Butterfly"; size: "Small" }
ListElement { name: "Dog"; size: "Medium" }
ListElement { name: "Cat"; size: "Medium" }
ListElement { name: "Pony"; size: "Medium" }
ListElement { name: "Koala"; size: "Medium" }
ListElement { name: "Horse"; size: "Large" }
ListElement { name: "Tiger"; size: "Large" }
ListElement { name: "Giraffe"; size: "Large" }
ListElement { name: "Elephant"; size: "Huge" }
ListElement { name: "Whale"; size: "Huge" }
}
// The delegate for each section header
Component {
id: sectionHeading
Rectangle {
id: sectionHeadingRectangle
width: container.width
height: childrenRect.height
color: "lightsteelblue"
Text {
text: section
font.bold: true
font.pixelSize: 20;
}
}
}
Component {
id: section
Rectangle {
width: container.width
height: mainText.height
Text { id: mainText; text: name; font.pixelSize: 18 }
}
}
ListView {
id: view
anchors.fill: parent
// width: parent.width
model: animalsModel
delegate: section
section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
}
}
It is possible to do this by sending a custom signal when a section is clicked and having all delegates connect to this signal and check if the section clicked correspond to theirs, and hide accordingly :
Rectangle {
id: container
width: 300
height: 360
ListModel {
id: animalsModel
ListElement { name: "Ant"; size: "Tiny" }
ListElement { name: "Flea"; size: "Tiny" }
ListElement { name: "Parrot"; size: "Small" }
ListElement { name: "Guinea pig"; size: "Small" }
ListElement { name: "Rat"; size: "Small" }
ListElement { name: "Butterfly"; size: "Small" }
ListElement { name: "Dog"; size: "Medium" }
ListElement { name: "Cat"; size: "Medium" }
ListElement { name: "Pony"; size: "Medium" }
ListElement { name: "Koala"; size: "Medium" }
ListElement { name: "Horse"; size: "Large" }
ListElement { name: "Tiger"; size: "Large" }
ListElement { name: "Giraffe"; size: "Large" }
ListElement { name: "Elephant"; size: "Huge" }
ListElement { name: "Whale"; size: "Huge" }
}
// The delegate for each section header
Component {
id: sectionHeading
Rectangle {
id: sectionHeadingRectangle
width: container.width
height: childrenRect.height
color: "lightsteelblue"
Text {
text: section
font.bold: true
font.pixelSize: 20;
}
MouseArea {
anchors.fill: parent
onClicked: view.sectionClicked(section)
}
}
}
Component {
id: section
Rectangle {
id: rect
width: container.width
height: shown ? mainText.height : 0
visible: shown
property bool shown: true
Text { id: mainText; text: name; font.pixelSize: 18 }
Connections {
target: rect.ListView.view
onSectionClicked: if (rect.ListView.section === name) shown = !shown;
}
}
}
ListView {
id: view
anchors.fill: parent
// width: parent.width
signal sectionClicked(string name)
model: animalsModel
delegate: section
section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
}
}
I'm trying to develop an application which has multi-layered navigation in drop-downs.
Below is the snapshot of the requirement. On clicking QSR only the subitems of QSR should pop out with proper alignment.
But with my implementation, I'm getting following result. The pop out window is not aligned, but extending itself to the list.
Below is the my code snippet
import QtQuick 1.1
Item {
width: 500
height: 300
ListView {
anchors.fill: parent
model: nestedModel
delegate: categoryDelegate
}
ListModel {
id: nestedModel
ListElement {
categoryName: "QSR"
collapsed: true
subItems: [
ListElement { itemName: "KFC" },
ListElement { itemName: "Mc Donalds" },
ListElement { itemName: "Pizza Hut" },
ListElement { itemName: "Brain's" }
]
}
ListElement {
categoryName: "Drinks"
collapsed: true
subItems: [
ListElement { itemName: "Pepsi" },
ListElement { itemName: "Coke" },
ListElement { itemName: "7up" },
ListElement { itemName: "Bacardi" }
]
}
}
Component {
id: categoryDelegate
Column {
width: 200
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "white"
height: 50
width: 200
anchors.left: parent.right
Text {
anchors.right: parent.right
x: 15
font.pixelSize: 24
text: categoryName
}
MouseArea {
anchors.fill: parent
onClicked: nestedModel.setProperty(index, "collapsed", !collapsed)
}
}
Loader {
id: subItemLoader
visible: !collapsed
property variant subItemModel : subItems
sourceComponent: collapsed ? null : subItemColumnDelegate
onStatusChanged: if (status == Loader.Ready) item.model = subItemModel
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model : subItemRepeater.model
width: 200
Repeater {
id: subItemRepeater
delegate: Rectangle {
color: "#cccccc"
height: 40
width: 200
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}
Any help to achieve original requirement will be appreciable.
Have a close look at the delegate for your main list view: It's Column (put items on top of each over) of the main menu item and Loader for the popup menu. Obviously as soon as you load a popup menu it's displayed under its corresponding menu item in the list.
To get the behavior you intended you have to move the Loader for the popup menu out of the ListView:
import QtQuick 1.1
Item {
width: 400
height: 300
Row {
anchors.fill: parent
Loader {
id: subItemLoader
width: 200
height: parent.height
property variant subItemModel: null
sourceComponent: subItemModel == null? null: subItemColumnDelegate
function setModel() {
if (status == Loader.Ready) item.model = subItemModel
}
onStatusChanged: setModel()
onSubItemModelChanged: setModel()
}
ListView {
width: 200
height: parent.height
model: nestedModel
delegate: categoryDelegate
}
}
ListModel {
id: nestedModel
ListElement {
categoryName: "QSR"
subItems: [
ListElement { itemName: "KFC" },
ListElement { itemName: "Mc Donalds" },
ListElement { itemName: "Pizza Hut" },
ListElement { itemName: "Brain's" }
]
}
ListElement {
categoryName: "Drinks"
subItems: [
ListElement { itemName: "Pepsi" },
ListElement { itemName: "Coke" },
ListElement { itemName: "7up" },
ListElement { itemName: "Bacardi" }
]
}
}
Component {
id: categoryDelegate
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "white"
height: 50
width: 200
Text {
anchors.centerIn: parent
x: 15
font.pixelSize: 24
text: categoryName
}
MouseArea {
anchors.fill: parent
onClicked: {
if (subItemLoader.subItemModel != subItems)
subItemLoader.subItemModel = subItems
else
subItemLoader.subItemModel = null
}
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model : subItemRepeater.model
width: 200
height: 500
Repeater {
id: subItemRepeater
delegate: Rectangle {
color: "#cccccc"
height: 40
width: 200
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}