مناظر القوائم
يمكن أن تساعدك عروض القوائم في عرض الكائنات من نموذج بطريقة جذابة. لاستخدام عرض قائمة، عليك تتبع ثلاثة أشياء:
- النموذج، الذي يحتوي على البيانات التي تريد أن يعرضها عرض القائمة الخاص بك
- المفوض، الذي يحدد كيف سيُعرض كل عنصر في النموذج
- عرض القائمة نفسه، الذي سيعرض المعلومات من النموذج وفقًا للمفوض
إذا كنت ترغب في مزيد من التوضيح، فإن توثيق كيوتي لديه صفحة إعلامية حول الموضوع.
أساسيات النماذج والعروض
عرض القائمة له خاصيتان أساسيتان يجب أن ننتبه إليهما:
- model، التي تقبل البيانات أو
idالكائن الذي يحمل البيانات - delegate، التي تقبل المكون الذي سنستخدمه لعرض البيانات في النموذج
النموذج غير مرئي، لأنه يحتوي فقط على بيانات. عادةً ما يُلف المفوض في مكون ليكون قابلاً لإعادة الاستخدام: فهو يعمل كمخطط لكيفية إنشاء كل مفوض.
هذا مثال يحتوي على عرض قائمة واحد، ونموذج واحد، ومفوض واحد، باستخدام 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`
}
}
}
}فهم النماذج
يحتوي النموذج على البيانات التي ستُستخدم لملء عرض القائمة. الطرق المختلفة لاستخدام النماذج لها طرق مختلفة للوصول إلى البيانات:
| طريقة الاستخدام | كيفية الوصول | متى يُستخدم |
|---|---|---|
| نماذج كيوتي بأكثر من دور | model.index, model.somerole | في معظم الحالات |
| نماذج كيوتي بدور واحد | model.index, model.somerole, model.modelData | في معظم الحالات، للنمذجة الأولية |
| نموذج مصفوفة جافاسكريبت | model.index, model.modelData | للنمذجة الأولية |
| نموذج عدد صحيح | model.index, model.modelData | للنمذجة الأولية |
يمكنك القراءة عن طرق أخرى لاستخدام النماذج في وثائق كيوتي.
في الجدول أعلاه، تشير "نماذج كيوتي" إلى كل من النماذج الخاصة بـ C++ مثل QAbstractListModel والنماذج الخاصة بـ QML مثل ListModel. ستركز صفحة البرنامج التعليمي هذه فقط على النماذج الخاصة بـ QML. في ما بعد نقدم برنامجًا تعليميًا لـ ربط نماذج C++ بـ QML باستخدام QAbstractListModel.
الخاصية model.index متاحة لكل نموذج وتحتوي على الفهرس (الموضع) لكل مفوض. يمكن اختصارها إلى index للتسهيل.
الخاصية model.somerole المذكورة أعلاه هي مجرد عنصر نائب، وليست خاصية محددة تأتي من QML: somerole يمكن أن يكون أي دور يُعرّفه النموذج. في مثال الكود الأول في هذه الصفحة المعروض أعلاه الجدول، نموذج plasmaProductsModel له دور product وtarget، والتي يمكن الوصول إليها بـ model.product وmodel.target على التوالي.
كما يمكن اختصار model.index إلى index، يمكن اختصار كل خاصية model.somerole إلى somerole فقط (مثل 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
}
}
}
}للمقارنة، إليك كيف سيبدو الكود أعلاه مع مصفوفة جافاسكريبت، بدون دور:
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`
}
}
}
}على عكس النموذج (الذي يحتوي فقط على البيانات) والمفوض Component (الذي يظهر فقط عند إنشاء مثيل له)، فإن العرض هو مكون مرئي يُنشأ مثيله فورًا ولذا يحتاج إما إلى تعيين أبعاده أو استخدام المراسي أو التخطيطات.
نظرًا لأن العروض عادةً ما تكون قوائم محتوى يرغب المستخدم في التمرير خلالها، فعند إضافتها إلى Kirigami.ScrollablePage، تصبح العروض المحتوى الرئيسي مع حشوة صغيرة حولها، ولا حاجة لجعلها تملأ الصفحة. عند إضافة العرض إلى Kirigami.Page بسيط، سيتطلب تعيين أبعاده بشكل صحيح قبل أن يظهر. بعبارة أخرى: في الصفحة القابلة للتمرير أعلاه، anchors.fill: parent غير مطلوب؛ إذا تم استخدام صفحة بسيطة، فسيكون مطلوبًا.
هناك عروض متعددة يمكن استخدامها من واجهات برمجة التطبيقات، بعضها من كيوتي وبعضها من كيريغامي. إليك الأكثر استخدامًا:
- ListView من كيوتي
- GridView من كيوتي
- TableView من كيوتي
- TreeView الخاص بكيو تي
- CardsListView الخاص بكيريغامي
- ColumnView الخاص بكيريغامي
أما المفوض فدائمًا ما يحتاج إلى ضبط أبعاده. عمومًا تُضبط أبعاده لاستخدام العرض الكامل للمنظر فقط.
الأخطاء الشائعة
يعني ما سبق أن المفوضين لا ينبغي أن يكون لهم مراسٍ سفلية، لأن المفوض لا يحتاج إلى نفس ارتفاع المنظر. بعبارة أخرى، ربما لن ترغب أبدًا في استخدام anchors.fill: parent.
بالإضافة إلى ذلك، بينما يمكن ضبط أبعاده باستخدام الأصل والمراسي، وهو عادةً contentItem الخاص بالمنظر، كما يلي:
Controls.ItemDelegate {
anchors.left: parent.left
anchors.right: parent.right
text: // ...
}ليس مضمونًا أن يكون أصل المفوض منظرًا، لذا ينبغي تجنب ذلك. بدلًا من ذلك، استخدم الخاصية المرفقة ListView.view للإشارة إلى المنظر الأصلي للمفوض:
Controls.ItemDelegate {
width: ListView.view.width
text: // ...
}الاستخدام الأكثر شيوعًا للمفوض يكون ضمن Component، الذي لا يُنشئ المفوض فورًا. عند بناء منظر، يُستخدم المفوض بعدها كمخطط لصنع كل عنصر في المنظر.
بينما يمكنك صنع مكوناتك المخصصة لاستخدامها كمفوضين دون واجهات برمجة تطبيقات كيو تي الخاصة بالمفوضين (مثلًا، تخطيط يحتوي على بضعة عناصر)، توفر عناصر تحكم كويك كيو تي واجهات برمجة تطبيقات للمفوضين يسهل استخدامها:
- ItemDelegate (مفوضون بنص فقط)
- CheckDelegate (مفوضون بصندوق اختيار)
- RadioDelegate (مفوضون بزر اختيار)
- SwitchDelegate (مفوضون بمفتاح)
- SwipeDelegate (مفوضون يمكن تمريرهم)
ينبغي تفضيل استخدام مفوضات كيو تي الأصلية حيثما أمكن.
فوق مفوضات كيو تي هذه، يوفر كيريغامي مكافئاتها الخاصة، مع وظائف إضافية للعناوين الفرعية والأيقونات:
- 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 الخاص بمفوض كيو تي، على سبيل المثال:
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"
}
}
}
}
}يمكن رؤية مثال عملي لاستخدام مفوضات كيريغامي في ملف ListItemTest في مستودع كيريغامي.