Vistes de llista

Una vista de llista pot ajudar-vos a mostrar amb facilitat molts components de forma dinàmica.

Les vistes de llista «Listviews» poden ajudar a mostrar objectes d'un model d'una manera atractiva. Per a utilitzar una vista de llista, haureu de realitzar un seguiment de tres coses:

  1. El model, el qual conté les dades que voleu mostrar a la vista de llista.
  2. El delegat, el qual defineix com es mostrarà cada element en el model.
  3. La vista de llista en si, la qual mostrarà la informació del model segons el delegat.

Per a més aclariments, la documentació de les Qt té una pàgina informativa sobre el tema.

Essencials de models i vistes

Una vista de llista té dues propietats essencials a les quals hem de parar atenció:

  • model, el qual accepta les dades o l'id de l'objecte que conté les dades
  • delegate, el qual accepta el component que utilitzarem per a mostrar les dades en el model

El model no és visible, ja que només conté dades. Normalment, el delegat s'embolcallarà en un Component perquè sigui reutilitzable: serveix com a esquema per a conèixer com instanciar cada delegat.

Aquí hi ha un exemple que conté exactament una vista de llista, un model i un delegat, utilitzant un Kirigami.SubtitleDelegate:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "List of Plasma products"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: plasmaProductsModel
            delegate: listDelegate
        }
        ListModel {
            id: plasmaProductsModel
            ListElement { product: "Plasma Desktop"; target: "desktop" }
            ListElement { product: "Plasma Mobile";  target: "mobile" }
            ListElement { product: "Plasma Bigscreen"; target: "TVs" }
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
            }
        }
    }
}

I el mateix exemple, en línia:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "List of Plasma products (inline)"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: ListModel {
                id: plasmaProductsModel
                ListElement { product: "Plasma Desktop"; target: "desktop" }
                ListElement { product: "Plasma Mobile";  target: "mobile" }
                ListElement { product: "Plasma Bigscreen"; target: "TVs" }
            }
            delegate: Controls.ItemDelegate {
                width: ListView.view.width
                text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
            }
        }
    }
}

Entendre els models

El model conté les dades que s'utilitzaran per a emplenar la vista de llista. Les maneres diferents d'utilitzar els models tenen maneres diferents d'accedir a les dades:

MANERA D'UTILITZARCOM ACCEDIR-HIQUAN EMPRAR-HO
Models Qt amb més d'un rolmodel.index, model.someroleEn la majoria dels casos
Models Qt amb un rolmodel.index, model.somerole, model.modelDataEn la majoria dels casos, per a fer prototips
Model de matriu JavaScriptmodel.index, model.modelDataPer a fer prototips
Model entermodel.index, model.modelDataPer a fer prototips

Podeu llegir sobre altres maneres d'usar els models a la documentació de les Qt.

A la taula anterior, els «models Qt» es refereixen tant a models específics de C++ com QAbstractListModel i a models específics de QML com ListModel. Aquesta pàgina de la guia d'aprenentatge només se centrarà en models específics de QML. Més endavant, proporcionem una guia d'aprenentatge per a Connectar models C++ a QML utilitzant QAbstractListModel.

La propietat model.index es posa a disposició de cada model i conté l'índex (la posició) de cada delegat. Es pot escurçar a index per comoditat.

La propietat model.somerole esmentada anteriorment és només una variable de substitució, no és una propietat específica que prové del QML: somerole pot ser qualsevol rol que estigui definit pel model. En el primer exemple de codi d'aquesta pàgina que es mostra a sobre de la taula, el model plasmaProductsModel té els rols product i target, als quals es pot accedir amb model.product i model.target, respectivament.

De la mateixa manera que model.index es pot escurçar a index, cada propietat model.somerole es pot escurçar a només somerole (com product) per comoditat, però es recomana que es converteixin en propietats requerides:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "List of Plasma products (shortened with required properties)"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: plasmaProductsModel
            delegate: listDelegate
        }
        ListModel {
            id: plasmaProductsModel
            ListElement { product: "Plasma Desktop"; target: "desktop" }
            ListElement { product: "Plasma Mobile";  target: "mobile" }
            ListElement { product: "Plasma Bigscreen"; target: "TVs" }
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                required property string product
                required property string target
                required property int index
                text: `${product} is KDE software developed for ${target} stored at index ${index} of this list`
            }
        }
    }
}

A més, si el model conté només un rol o no té cap rol, també es pot accedir a les seves dades amb la propietat model.modelData, que també es pot escurçar a modelData (i com a tal també necessitaria ser una propietat requerida):

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami


Kirigami.ApplicationWindow {
    title: "List of KDE software"
    width: 400
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: kdeSoftwareModel
            delegate: listDelegate
        }
        ListModel {
            id: kdeSoftwareModel
            ListElement { software: "Dolphin" }
            ListElement { software: "Discover" }
            ListElement { software: "KHelpCenter" }
            ListElement { software: "KCalc" }
            ListElement { software: "Ark" }
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                required property string modelData
                text: modelData // Aquest coincideix amb model.software
            }
        }
    }
}

Com a comparació, aquí és com es veuria el codi anterior amb una matriu JavaScript, sense rol:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "List of KDE software (as JS array)"
    width: 400
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: ["Dolphin", "Discover", "KHelpCenter", "KCalc", "Ark"]
            delegate: listDelegate
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                required property string modelData
                text: modelData
            }
        }
    }
}

L'ús d'un enter per al model pot ser útil per a casos molt específics, com el prototipatge i les proves:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "Simple list of indexes"
    width: 400
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            anchors.fill: parent
            model: 30
            delegate: listDelegate
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                required property string modelData
                text: `This delegate's index is: ${modelData}`
            }
        }
    }
}

Entendre les vistes i els delegats

Tornem a l'exemple original:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    title: "List of Plasma products"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            // anchors.fill: parent
            model: plasmaProductsModel
            delegate: listDelegate
        }
        ListModel {
            id: plasmaProductsModel
            ListElement { product: "Plasma Desktop"; target: "desktop" }
            ListElement { product: "Plasma Mobile";  target: "mobile" }
            ListElement { product: "Plasma Bigscreen"; target: "TVs" }
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
            }
        }
    }
}

A diferència del model (que només conté dades) i d'un delegat Component (que només apareixen quan s'instancien), la vista és un component visual instanciat immediatament i, per tant, necessita tenir les seves dimensions establertes o utilitzar ancoratges o disposicions.

Com que les vistes són normalment llistes de contingut per les quals l'usuari es desplaçarà, quan s'afegeixen a un Kirigami.ScrollablePage, les vistes es converteixen en el contingut principal amb poc farciment al voltant d'elles, i no hi ha cap necessitat de fer que ompli la pàgina. Quan la vista s'afegeix a una única Kirigami.Page, necessitarà establir les seves dimensions correctament abans que aparegui. En altres paraules: a la pàgina desplaçable de dalt, no es requereix anchors.fill: parent; si s'utilitzés una única pàgina, seria necessària.

Hi ha diverses API de vistes que es poden utilitzar, algunes de les Qt i altres de Kirigami. A continuació us detallem les més utilitzades:

D'altra banda, el delegat sempre ha de tenir les seves dimensions establertes. Generalment, les seves dimensions s'estableixen per a utilitzar només l'amplada completa de la vista.

L'ús més comú d'un delegat es troba dins d'un Component, que no instancia el delegat immediatament. Quan es construeix una vista, el delegat s'utilitza com a esquema per a fer cada element a la vista.

Tot i que podeu crear els vostres propis components personalitzats per a ser utilitzats com a delegats sense les API de les Qt específiques de delegats (per exemple, una disposició que conté alguns elements), els QtQuick Controls proporcionen les API de delegats que són més senzilles d'utilitzar:

Hauríeu de preferir utilitzar els delegats originals de les Qt quan sigui possible.

A sobre d'aquests delegats de les Qt, el Kirigami proporciona els seus propis equivalents, amb la funcionalitat afegida de subtítols i icones:

L'API que acaba amb «Delegate» es pot definir com un delegat directe de la vista, igual que els exemples anteriors que utilitzaven Controls.ItemDelegate:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
import org.kde.kirigami.delegates as KD

Kirigami.ApplicationWindow {
    title: "List of Plasma products"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            model: plasmaProductsModel
            delegate: listDelegate
        }
        ListModel {
            id: plasmaProductsModel
            ListElement { product: "Plasma Desktop"; target: "desktop" }
            ListElement { product: "Plasma Mobile";  target: "mobile" }
            ListElement { product: "Plasma Bigscreen"; target: "TVs" }
        }
        Component {
            id: listDelegate
            KD.CheckSubtitleDelegate {
                width: ListView.view.width
                text: `${model.product} is KDE software developed for ${model.target}.`
                subtitle: `This delegate is stored at index ${model.index} of this list`
                icon.name: "kde"
            }
        }
    }
}

Tant TitleSubtitle com IconTitleSubtitle s'espera que s'utilitzin per a substituir el «contentItem» d'un delegat de les Qt, per exemple:

import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
import org.kde.kirigami.delegates as KD

Kirigami.ApplicationWindow {
    title: "List of Plasma products"
    width: 600
    height: 400
    pageStack.initialPage: Kirigami.ScrollablePage {
        ListView {
            // anchors.fill: parent
            model: plasmaProductsModel
            delegate: listDelegate
        }
        ListModel {
            id: plasmaProductsModel
            ListElement { product: "Plasma Desktop"; target: "desktop" }
            ListElement { product: "Plasma Mobile";  target: "mobile" }
            ListElement { product: "Plasma Bigscreen"; target: "TVs" }
        }
        Component {
            id: listDelegate
            Controls.ItemDelegate {
                width: ListView.view.width
                text: `${model.product} is KDE software developed for ${model.target}.`
                contentItem: KD.IconTitleSubtitle {
                    title: parent.text
                    subtitle: `This delegate is stored at index ${model.index} of this list`
                    icon.name: "kde"
                }
            }
        }
    }
}

Es pot veure un exemple pràctic d'ús dels delegats del Kirigami al fitxer ListItemTest al repositori del Kirigami.