Списки
Панелі списків можуть допомогти у показі об'єктів на основі моделі у привабливий спосіб. Щоб правильно користуватися панеллю списку, вам слід стежити за трьома речима:
- Моделлю, яка містить дані, які має бути показано на вашій панелі списку
- Делегата, який визначає, як буде показано кожне з елементів моделі
- Самої панелі списку, на якій буде показано відомості щодо моделі, відповідно до параметрів делегата
Якщо вам потрібні додаткові пояснення, у документації Qt є інформативна сторінка щодо цього питання.
Базові дані щодо моделей та панелей перегляду
У панелі списку є дві ключові властивості, на які нам слід звернути увагу:
- model, яка приймає дані або
id
об'єкта, який містить дані - delegate, яка приймає компонент, якими ми скористаємося для показу даних у моделі
Модель є невидимою, оскільки вона лише містить дані. Типово, делегат буде загорнуто до Component, щоб його можна було використати повторно: він слугує як шаблон того, як створити екземпляр кожного делегата.
Тут наведено приклад, який містить точно одну панель перегляду списку, одну модель та один делегат, з використанням 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`
}
}
}
}
І точно той самий приклад, вбудований:
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`
}
}
}
}
Розбираємося із моделями
Модель містить дані, які буде використано для заповнення панелі перегляду списку. Для різних способів використання моделей передбачено різні способи доступу до даних:
СПОСІБ ВИКОРИСТАННЯ | ДОСТУП | УМОВИ ВИКОРИСТАННЯ |
---|---|---|
Моделі Qt із декількома ролями | model.index, model.somerole | У більшості випадків |
Моделі Qt з однією роллю | model.index, model.somerole, model.modelData | У більшості випадків для прототипізації |
Модель масиву JavaScript | model.index, model.modelData | Для прототипізації |
Цілочисельна модель | model.index, model.modelData | Для прототипізації |
Ви можете ознайомитися із іншими способами використання моделей у документації Qt.
У наведеній вище таблиці «Моделі Qt» означають і C++-специфічні моделі, подібні до QAbstractListModel, і QML-специфічні моделі, зокрема ListModel. На цій сторінці підручника акцент зроблено на QML-специфічних моделях. Далі ми наведемо настанови щодо з'єднання моделей C++ із QML за допомогою QAbstractListModel.
Властивість model.index
зроблено доступною для будь-якої моделі. Вона містить індекс (позиція) кожного делегата. Її можна скоротити до index
для зручності.
Властивість model.якась_роль
, про яку ми згадували вище, є лише замінником. Це не специфічна властивість, яка надходить від QML: записом якась_роль
може бути будь-яка роль, яку визначено моделлю. У першому прикладі коду на цій сторінці, який показано над таблицею, модельplasmaProductsModel
містить ролі product
і target
, доступ до яких можна здійснювати за допомогою model.product
і model.target
, відповідно.
Так само, як model.index
можна скоротити до index
, кожну властивість model.якась_роль
можна скоротити до простого якась_роль
(наприклад, product
) для зручності, але рекомендуємо перетворювати їх до потрібних властивостей:
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`
}
}
}
}
Крім того, якщо модель містить лише одну роль або взагалі не містить ролі, доступ до даних також можна здійснювати за допомогою властивості model.modelData
, що можна скоротити до modelData
(і як така також має бути обов'язковою властивістю):
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 // Це відповідає model.software
}
}
}
}
Для порівняння, ось як наведених вище код міг би виглядати з масивом JavaScript, без ролі:
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
}
}
}
}
Використання цілого числа для моделі може бути корисним для дуже специфічних випадків, а саме, прототипізації та тестування:
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}`
}
}
}
}
Розбираємося із переглядами і делегатами
Повернімося до початкового прикладу:
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`
}
}
}
}
На відміну від моделі (яка, в основному, містить дані) і компонента делегата (який з'являється, лише якщо створено екземпляр), панель перегляду є візуальним компонентом, екземпляр якого створюється негайно, і тому потребує або встановлення його розмірностей, або використання прив'язок чи компонувань.
Оскільки панелі перегляду є типово списками вмісту, які користувач міг би гортати, при їхньому додаванні до Kirigami.ScrollablePage, панелі перегляду стають переглядами основних даних із невеличкою фаскою навколо, і немає потреби заповнювати вмістом усю сторінку. Якщо панель перегляду додано до простої Kirigami.Page, доведеться встановлювати її розмірності належним чином, перш ніж її буде показано. Іншими словами, на панелі із гортанням вище anchors.fill: parent
є необов'язковим; якщо було б використано просту сторінку, ця інструкція була б обов'язковою.
Можна використовувати декілька програмних інтерфейсів панелей перегляду, деякі походять з Qt, а деякі — з Kirigami. Ось найпоширеніші:
- ListView Qt
- GridView Qt
- TableView Qt
- TreeView Qt
- CardsListView Kirigami
- ColumnView Kirigami
Для делегата, з іншого боку, завжди має бути встановлено розмірності. Загалом, його розмірності буде встановлено лише так, щоб використовувати усю ширину панелі перегляду.
Типові помилки
Вище сказане означає, що делегати не повинні мати нижніх прив'язок, оскільки делегат не повинен мати тієї самої висоти, що і панель перегляду. Іншими словами, у вас, ймовірно, ніколи не виникатиме потреби використовувати anchors.fill: parent
.
Крім того, хоча можна встановити його розмірності за допомогою parent і прив'язок, яким, зазвичай, є contentItem панелі перегляду, ось так:
Controls.ItemDelegate {
anchors.left: parent.left
anchors.right: parent.right
text: // ...
}
Не гарантовано, що батьківським елементом делегата буде панель перегляду, і тому такого коду слід уникати. Замість цього, скористайтеся долученою властивістю ListView.view для вказування батьківської панелі перегляду делегата:
Controls.ItemDelegate {
width: ListView.view.width
text: // ...
}
Найпоширенішим використанням делегата є використання у межах Component, що не створює екземпляра делегата негайно. При побудові панелі перегляду делегат буде використано як шаблон для створення усіх елементів на панелі перегляду.
Хоча ви ви можете створювати власні нетипові компоненти, які буде використано як делегати без специфічних для делегата програмних інтерфейсів Qt (наприклад, компонування, що містить небагато записів Item), QtQuick Controls надає у ваше розпорядження програмні інтерфейси, якими простіше скористатися:
- ItemDelegate (делегати, які містять лише текст)
- CheckDelegate (делегати з пунктом із позначкою)
- RadioDelegate (делегати із варіантом вибору)
- SwitchDelegate (делегати з перемикачем)
- SwipeDelegate (делегати, над якими можна проводити пальцем)
Вам слід надавати перевагу використанню делегатів основної бібліотеки Qt там, де це можливо.
Над цими делегатами Qt Kirigami надає у ваше розпорядження власні еквіваленти із доданими функціональними можливостями підзаголовків і піктограм:
- TitleSubtitle
- IconTitleSubtitle
- SubtitleDelegate
- CheckSubtitleDelegate
- RadioSubtitleDelegate
- SwitchSubtitleDelegate
Елементи програмного інтерфейсу, назви яких завершуються на «Delegate», можна встановлювати як безпосередні делегати панелі перегляду, як у попередніх прикладах, де використано 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"
}
}
}
}
Обидва, TitleSubtitle і IconTitleSubtitle, вважаються такими, які слід використовувати для перевизначення contentItem делегата Qt, приклад:
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"
}
}
}
}
}
Із практичним прикладом використання делегатів Kirigami можна ознайомитися у файлі ListItemTest у сховищі Kirigami.