I have a code of tree view on qml:
Item {
width: 899
height: 471
TreeView {
x: 215
y: 161
width: 470
height: 150
headerVisible: true
frameVisible: false
TableViewColumn {
title: "File Name"
role: "fileName"
width: 200
}
TableViewColumn {
title: "Size"
role: "size"
width: 70
}
TableViewColumn{
title: "Last Change"
role: "lastChange"
width: 200
}
}
}
How do i add values to the red squares in the picture below?
I've been looking on youtube and documentation and hardly could understand anything that relates to that.
Define a model in TreeView.
model: ListModel {
id: fileModel
ListElement {
fileName: "myfile"
size: "1KB"
lastChange: "yesterday"
}
ListElement {
fileName: "yourfile"
size: "1MB"
lastChange: "today"
}
ListElement {
fileName: "otherfile"
size: "1GB"
lastChange: "tomorrow"
}
}
Appending row to model.
fileModel.append({
fileName: "anotherfile",
size: "1TB",
lastChange: "another day"});
Quick try
Best way is from C++. You create your model there, from scratch by subclassing from QAbstractItemModel or by filling a QStandardItemModel with nodes. Then, you make that model available to QML, for instance by setting it as a context property. Then you bind the model property of TreeView to that model from C++.
Please note that QtQuickControls 1, which features TreeView, is not generally recommended for serious use. It is quite inefficient and not really maintained any more.
Related
I'm using a QML ListView with section, click on item to remove with animation. Here the code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
visible: true
width: 400
height: 400
ListView {
id: list
anchors.fill: parent
clip: true
spacing: 0
onContentYChanged: console.log("onContentYChanged: " + contentY)
onContentHeightChanged: console.log("onContentHeightChanged: " + contentHeight)
model: ListModel {
id: myModel
ListElement {name: "Item 1";type: "A"}
ListElement {name: "Item 2";type: "A"}
ListElement {name: "Item 3";type: "B"}
}
delegate: Rectangle {
width: parent.width
height: 50
color: (index % 2 == 1) ? "#5678a2" : "#88a345"
Text {
anchors.verticalCenter: parent.verticalCenter
text: name
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("remove: " + index + ", contentY:" + list.contentY)
myModel.remove(index)
}
}
}
section.property: "type"
section.delegate: Rectangle {
height: 30
Text {
anchors.verticalCenter: parent.verticalCenter
text: section
}
}
displaced: Transition {
NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.OutCubic }
}
remove: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 500 }
NumberAnimation { property: "scale"; from: 1.0; to: 0; duration: 500 }
}
}
}
When I clicked on the first item(Item 1), it got deleted, but the Item 2 was flying up to outside the window. The ListView displayed the remaining items in wrong positions. ContentY changed to 80 (which was the y position of Item 2 before) instead of remaining at 0.
qml: onContentHeightChanged: 300
qml: onContentHeightChanged: 240
qml: onContentHeightChanged: 210
qml: remove: 0, contentY:0
qml: onContentYChanged: 80
qml: onContentHeightChanged: 160
It will work correctly if:
Delete other items except the top one.
Disable either the section or animation.
I tried your code with Qt 5.13.1. And currently downloading Qt 5.15. For now it looks like it is a bug with section, because I found a lot of not not closed bug reports on bugtracker. I can suggest 2 ways of solving your problem.
Performing animation while locking removal.
Using model with categories.
1st solution I tested by meself. Here is what you need to change to try it:
Delete ListView's removal animations. Add following code to your delegate
ListView.onRemove: SequentialAnimation {
PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
ParallelAnimation {
NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: 500 }
NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 500 }
}
PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
}
What is this? ListView has a signal remove() which is called BEFORE removing an item from the view. It is described in documentation It is also noted, that
If a remove transition has been specified, it is applied after this signal is handled, providing that delayRemove is false.
So in delegate you simply block removal from view, do you animation and unblock it. I suppose it won't be as clean and beautiful as you want it to be simply because view doesn't andjust it's size in this case.
2nd solution
I didn't try to implement it, but I can imagine having a model like this:
ListModel {
id: myModel
ListElement { type: "category"; name: "cat1" }
ListElement { name: "delegate1"; type: "delegate"; catrgory: "cat1"}
ListElement { name: "delegate2"; type: "delegate"; catrgory: "cat1"}
ListElement { name: "delegate3"; type: "delegate"; catrgory: "cat1"}
ListElement { type: "category"; name: "cat2" }
ListElement { name: "delegate4"; type: "delegate"; catrgory: "cat2"}
To use this as you want, you will need to castomize your delegate accordingly and removal function accordingly, which will lead to much more complex code in comparison to what it would be if section would work properly.
UPD: Same problem in 5.15
I found in the doc TreeView doc that you can use onExpanded to know when a Node from a TreeView is expanded but how can you know when a Node is closed ?
Code example:
TreeView {
TableViewColumn {
title: "Name"
role: "fileName"
width: 300
}
TableViewColumn {
title: "Permissions"
role: "filePermissions"
width: 100
}
model: fileSystemModel
onExpanded {
console.log("expanded :" + index)
}
}
From the documentation, onCollapsed is the opposite of onExpanded, so you can check when that signal is emitted.
Is it possible to create QML Items out of a DelegateModel?
Here is a example DelegateModel:
DelegateModel
{
id: delegateModel
model: ListModel
{
ListElement { name: "#FAFAFA"; test: "object1" }
ListElement { name: "#000000"; test: "object2" }
}
delegate: Rectangle
{
objectName: test
width: 50
height: 50
color: name
}
Component.onCompleted:
{
Utils.var_dump(items,3)
items.create(0)
Utils.var_dump(items.get(0),3)
}
}
The Result should look like this:
Rectangle
{
objectName: "object1"
width: 50
height: 50
color: "#FAFAFA"
}
Rectangle
{
objectName: "object2"
width: 50
height: 50
color: "#000000"
}
For every ListElement there is a created delegate with the inserted ListElement data.
You can do that with anything that is usable to instantiate a Model (a View)
For example you could use it as a model for a ListView, a GridView or a Repeater. As the model provides the delegate on its own, you do not need to specify any delegate in the View, that instantiates it.
Column {
Repeater {
model: delegateModel
// delegate: ... <--- Nothing here! Uses the delegate from the Model.
}
}
If you use the create(index)-Method, the delegate will be created, but has no parent, so it is not displayed. So you need to set the parent, to have it shown:
Button {
onClicked: {
for (var a = 0; a < dm.items.count; a++) {
var o = dm.items.create(a)
o.parent = r
}
}
}
You need to be aware, that the DelegateModel (without Package and Parts) can't be used in multiple views, as each entry/delegate can be instantiated only once at the same time. If you want to have that,
consider using a QSortFilterProxyModel to filter the stuff, and use as much Views that provide their own delegates, as you want.
I am using a ListView with a model and a delegate.
The model is a simple ListModel with three items. Each item has a value with the key myFirstRole.
The delegate contains a Repeater component to create an arbitrary number of Labels. The Labels have to use data from the model.
The model of the repeater can not be set to the Listview's model as I have the Repeater using other data.
Here is a minimal example of what I am trying to achieve:
//MyDelegate.qml
Component {
Item {
id: root
width: childrenRect.width
height childrenRect.height
Repeater {
model: 5 //It's not an option to set the repeaters model to the ListViews model. This example just illustrates my problem.
Label {
text: root.ListView.view.model.myFirstRole //This is the line where I want to be able to access the ListView's model, but I can't figure out how to properly reefer to it.
}
}
}
}
//MyListView.qml
ListView {
id: root
delegate: MyDelegate {}
model: ListModel {
ListElement {
myFirstRole: "one"
}
ListElement {
myFirstRole: "two"
}
ListElement {
myFirstRole: "three"
}
}
}
Using Qt 5.7.0 with MSVC2015 32bit
I think that you can't access the roles via the special model property mentioned here (which is what I'm assuming you were trying to do) from the scope of the Repeater. Instead, you can declare a property at the root level of the component that can then be used in nested scopes:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
ListView {
anchors.fill: parent
model: ListModel {
ListElement { myFirstRole: "Dog" }
ListElement { myFirstRole: "Cat" }
}
delegate: Item {
id: root
width: childrenRect.width
height: childrenRect.height
property string myFirstRoleData: myFirstRole
Repeater {
model: 5
Text {
text: myFirstRoleData
}
}
}
}
}
This might get a bit tedious if you have a lot of properties though. From some quick playing around, it looks like it's also possible to store the entire model object in a property:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
ListView {
anchors.fill: parent
model: ListModel {
ListElement { myFirstRole: "Dog" }
ListElement { myFirstRole: "Cat" }
}
delegate: Item {
id: root
width: childrenRect.width
height: childrenRect.height
property var modelData: model
Repeater {
model: 5
Text {
text: root.modelData.myFirstRole
}
}
}
}
}
modelData is probably not the best name to use though, seeing as Qt uses that name for models with only one role, but... if you're going with this approach, you're gonna have more than one role anyway. :)
It looks like Qt Quick Controls' (1) Tumbler does this too.
//MyDelegate.qml
Item {
id: root
property var listViewModel // pass the model data to here
width: 100
height: 50
Column {
Repeater {
model: 5 // Use a different model here
Text {
width: 50
height: 10
text: listViewModel.myFirstRole //This is the line where I want to be able to access the ListView's model, but I can't figure out how to properly reefer to it.
}
}
}
}
//MyListView.qml
ListView {
id: root
width: 100
height: 500
delegate: MyDelegate {
listViewModel: model // set the model data here
}
model: ListModel {
ListElement {
myFirstRole: "one"
}
ListElement {
myFirstRole: "two"
}
ListElement {
myFirstRole: "three"
}
}
}
See the comments in the code. It is not trivial to guess what you want to achive, but I hope I guessed right.
I got a fonctionnal ListModel defined like this :
ListModel {
id: leftGrid
ListElement { icon: "Images/1.png" }
ListElement { icon: "Images/2.png" }
}
The thing is that I'd like to define ListElement in separate qml files but I really don't know how to do it...
I wrote the qml like this :
//myWidget.qml
import QtQuick 1.0
ListElement {
icon: "Images/X.png"
}
But I don't know how to "invoke" or "instanciate" it in my main file...
I tried :
ListModel {
id: leftGrid
ListElement { icon: "Images/1.png" }
myWidget //qml file
}
and :
ListModel {
id: leftGrid
ListElement { icon: "Images/1.png" }
ListElement { myWidget }
}
Both doesn't work...
Any help with be welcomed, thanks in advance.
I don't think it's possible to have ListElements as separate files. This is because when you do that you are implicitly creating a Component, with your contents inside (in this case the ListElement). However the ListModel can only accept ListElements as its children, not Components with nested ListElements inside.
What you can do however to dynamically define your model items is to declare a ListModel, then add your data via a piece of javascript, for example in your Component.onCompleted handler.
If you look at the API for ListModel you will see it has an append() method, among others.
You can pass a JS dictionary to this method and it will add a new ListElement to the list and populate its properties according to the dictionary.
Example:
import QtQuick 1.0
Rectangle {
width: 360
height: 360
ListView {
id:list
anchors.fill: parent
model: ListModel {
ListElement { foo: "hello" }
}
delegate: Text {
text: foo
width: ListView.view.width
}
Component.onCompleted: {
list.model.append({ "foo": "world" })
}
}
}
Your list will appear with two items in it: "hello" and "world"