QML ListView sections from the code

Question:

I am unable to implement listview with sections. I’ve successfully repeated an example from Qt documentation but there is ListModel used which works okay, but not a var.

How it works with example:

ListView {
    width: 100
    height: 100
    id: listview
    model: 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" }
    }

    delegate: Text { text: name; font.pixelSize: 18 }

    section.property: "size"
    section.criteria: ViewSection.FullString
    section.delegate: Component {
        id: sectionHeading
        Rectangle {
            width: container.width
            height: childrenRect.height
            color: "lightsteelblue"

            Text {
                text: section
                font.bold: true
                font.pixelSize: 20
            }
        }
    }
}

GUI qt example

But when I try to use some model from the code (in my case it is a QVariant from PyQt5) it does not work at all:

ListView {
    width: 100
    height: 100
    id: listview
    property var m: [
        {
            name: "Animal",
            size: "Big"
        },
        {
            name: "Dog",
            size: "Small"
        },
        {
            name: "Cat",
            size: "Small"
        }
    ]
    model: m

    delegate: Text { text: modelData.name; font.pixelSize: 18 }

    section.property: "modelData.size"
    section.criteria: ViewSection.FullString
    section.delegate: Component {
        id: sectionHeading
        Rectangle {
            width: container.width
            height: childrenRect.height
            color: "lightsteelblue"

            Text {
                text: section
                font.bold: true
                font.pixelSize: 20
            }
        }
    }
}

The reason I choose var here because there is not any other method to receive a model from Python , so any list or map from python needs to be wrapped as QVariant.

Asked By: Victor Polevoy

||

Answers:

From Qt documentation:

model : model

This property holds the model providing data for the list.
The model provides the set of data that is used to create the items in the view. Models can be created directly in QML using ListModel, XmlListModel or VisualItemModel, or provided by C++ model classes. If a C++ model class is used, it must be a subclass of QAbstractItemModel or a simple list.

So you cannot provide an array as a model, it should be one of the objects above.
You can create such model and access it from Python code to add/remove items.

ListView {
    width: 100
    height: 100
    id: listview
    delegate: Text { text: name; font.pixelSize: 18 }

    model: ListModel { id: listModel }

    section.property: "size"
    section.criteria: ViewSection.FullString
    section.delegate: Component {
        id: sectionHeading
        Rectangle {
            width: container.width
            height: childrenRect.height
            color: "lightsteelblue"

            Text {
                text: section
                font.bold: true
                font.pixelSize: 20
            }
        }
    }

    function callFromPython {
        listModel.append({name: "Animal",size: "Big"});
        listModel.append({name: "Dog",size: "Small"});
        listModel.append({name: "Cat",size: "Small"});
    }
}
Answered By: folibis

There were a couple of issues with your code snippet:

  • Bug: Use section.property: "size" (not section:property: "modelData.size")
  • Warn: Use font.pointSize: 14 instead of (font.pixelSize: 20)
    • For cross-platform support, including high DPI and retina displays, best to use pointSize over pixelSize
  • Warn: Replace width: 100 and height: 100 with anchors.fill: parent.
    • Avoid pixel measurements whenever we can avoid it
  • Info: Use Frame and ListView.view.width for sizing delegates
    • Frame gives a padded border to components
  • Info: Added ScrollBar.vertical

Here’s a full working example:

import QtQuick
import QtQuick.Controls
Page {
    ListView {
        anchors.fill: parent
        model:[
            { name: "Animal", size: "Big" },
            { name: "Dog", size: "Small" },
            { name: "Cat", size: "Small" }
        ]
        delegate: Frame {
            width: ListView.view.width - 20
            Text {
                text: modelData.name
                font.pointSize: 12
            }
        }
        section.property: "size"
        section.delegate: Frame {
            width: ListView.view.width - 20
            background: Rectangle { color: "lightsteelblue" }
            Text {
                text: section
                font.bold: true
                font.pointSize: 14
            }
        }
        ScrollBar.vertical: ScrollBar {
            width: 20
            policy: ScrollBar.AlwaysOn
        }
    }
}

You can Try it Online!

Answered By: Stephen Quan
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.