Skip to main content
Ir al contenido

Vistas de lista

Una vista de lista le puede ayudar a mostrar fácilmente muchos componentes de forma dinámica.

Las Listviews le pueden ayudar a mostrar los objetos de un modelo de una forma atractiva. Para usar una vista de lista, debe seguir la pista de tres cosas:

  1. El modelo, que contiene los datos que quiere que muestre la vista de lista
  2. El delegado, que define cómo se muestran los elementos del modelo
  3. La vista de lista, propiamente dicha, que mostrará la información del modelo como indica el delegado

Si necesita más aclaraciones, la documentación de Qt tiene una página informativa sobre este tema.

Lo esencial de modelos y vistas

Una vista de lista tiene dos propiedades esenciales a las que debemos prestar atención:

  • model, que acepta los datos o el id del objeto que contiene los datos
  • delegate, que acepta el componente que usaremos para mostrar los datos del modelo

El modelo no es visible, ya que solo contiene datos. Normalmente, el delegado se envuelve en un componente para ser reutilizable: sirve como modelo para crear instancias de cada delegado.

Este es un ejemplo que contiene exactamente una vista de lista, un modelo y un delegado, usando 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`
            }
        }
    }
}

Y exactamente el mismo ejemplo en línea:

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`
            }
        }
    }
}

Entendiendo los modelos

El modelo contiene los datos que se usarán para rellenar la vista de lista. Los distintos modos de usar los modelos tienen diferentes formas de acceder a los datos:

MODO DE USOCÓMO SE ACCEDECUÁNDO SE USA
Modelos de Qt con más de un rolmodel.index, model.someroleEn la mayoría de casos
Modelos de Qt con un rolmodel.index, model.somerole, model.modelDataEn la mayoría de los casos, para la creación de prototipos
Modelo de matriz de JavaScriptmodel.index, model.modelDataPara la creación de prototipos
Modelo de enterosmodel.index, model.modelDataPara la creación de prototipos

Puede leer más sobre otras formas de usar modelos en la documentación de Qt.

En la tabla anterior, «Modelos de Qt» se refiere a modelos específicos de C++, como QAbstractListModel, y a modelos específicos de QML, como ListModel. Esta página del tutorial se centrará solo en los modelos específicos de QML. Más adelante ofrecemos un tutorial para Conectar modelos de C++ a QML usando QAbstractListModel.

La propiedad model.index está disponible para todos los modelos y contiene el índice (la posición) de cada delegado. Se puede abreviar a index por conveniencia.

The model.somerole property mentioned above is just a placeholder, it is not a specific property that comes from QML: somerole can be any role that is defined by the model. In the first code example of this page shown above the table, the plasmaProductsModel model has the product and target roles, which can be accessed with model.product and model.target, respectively.

Just as model.index can be shortened to index, each model.somerole property can be shorted to just somerole (like product) for convenience, but it is recommended that they be turned into required properties:

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`
            }
        }
    }
}

Additionally, if the model contains only one role or has no role at all, its data can also be accessed with the property model.modelData, which can also be shortened to modelData (and as such would also need to be a required property):

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 // Esto coincide con model.software
            }
        }
    }
}

A modo de comparación, así es como se vería el código anterior con una matriz JavaScript, sin ningún 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
            }
        }
    }
}

El uso de un número entero para el modelo puede ser útil para casos muy específicos, como prototipos y pruebas:

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}`
            }
        }
    }
}

Entendiendo las vistas y los delegados

Volvamos al ejemplo 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: padre
            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 diferencia del modelo (que solo contiene datos) y un delegado Component (que solo aparece cuando se instancia), la vista es un componente visual que se instancia de forma inmediata y, por lo tanto, necesita tener unas dimensiones establecidas o usar anclas o Layouts.

As views are commonly lists of content the user would want to scroll through, when they are added to a Kirigami.ScrollablePage, views become the main content with little padding around them, and there is no need to make it fill the page. When the view is added to a simple Kirigami.Page, it will require to set its dimensions properly before it will show up. In other words: in the scrollable page above, anchors.fill: parent is not required; if a simple page was used, it would be required.

There are multiple views APIs can be used, some from Qt and some from Kirigami. Here are the most commonly used ones:

Por otra parte, el delegado siempre debe tener definidas sus dimensiones. Generalmente, estas dimensiones se definen para que usen solo la anchura completa de la vista.

The most common use of a delegate is within a Component, which does not instantiate the delegate immediately. When a view is constructed, the delegate is then used as a blueprint to make each item in the view.

While you can make your own custom components to be used as delegates without delegate-specific Qt APIs (for example, a Layout containing a few Items), QtQuick Controls does provide delegate APIs that are simpler to use:

Debería preferir el uso de los delegados propios de Qt siempre que sea posible.

Además de estos delegados de Qt, Kirigami proporciona sus propios equivalentes, con la funcionalidad añadida de subtítulos e iconos:

The API ending with "Delegate" can be set as a direct delegate of the view, just like the previous examples that used 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"
            }
        }
    }
}

Both TitleSubtitle and IconTitleSubtitle are expected to be used to override a Qt delegate's contentItem, for example:

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: padre
            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"
                }
            }
        }
    }
}

A practical example of using Kirigami delegates can be seen in the ListItemTest file in the Kirigami Repository.