《qt quick核心编程》笔记四

11 Model/View

在这里插入图片描述

Delegate实际上可以看成是Item的一个模板

11.1 ListView

ListView用于显示一个条目列表,数据来自于Model,每个条目的外观来自于Delegate
要使用ListView必须指定一个Model、一个Delegate
Model可以是QML内建类型,如ListModel、XmlListModel,也可以是C++中实现的QAbstracItemModel或QAbstractListModel的派生类

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15Window {width: 560height: 300color: "#EEEEEE"visible: trueComponent {id: phoneModelListModel {ListElement {name: "iPhone 3GS"cost: "1000"manufacturer: "Apple"}ListElement {name: "iPhone 4"cost: "1800"manufacturer: "Apple"}ListElement {name: "iPhone 4S"cost: "2300"manufacturer: "Apple"}ListElement {name: "iPhone 5"cost: "4900"manufacturer: "Apple"}ListElement {name: "B199"cost: "1590"manufacturer: "HuaWei"}ListElement {name: "MI 2S"cost: "1999"manufacturer: "XiaoMi"}ListElement {name: "GALAXY S5"cost: "4699"manufacturer: "Samsung"}}}Component {id: headviewItem {width: parent.widthheight: 30RowLayout {anchors.verticalCenter: parent.verticalCenterText {text: "Name"font.bold: truefont.pixelSize: 20Layout.preferredWidth: 120}Text {text: "Cost"font.bold: truefont.pixelSize: 20Layout.preferredWidth: 80}Text {text: "Manufacturer"font.bold: truefont.pixelSize: 20Layout.fillWidth: true}}}}Component {id: footerViewItem {signal cleansignal addsignal insertsignal moveproperty alias text: txt.textwidth: listView.widthheight: 30Text {id: txtanchors.left: parent.leftcolor: "green"height: parent.heightverticalAlignment: Text.AlignVCenter}Button {id: clearAllanchors.right: parent.rightanchors.verticalCenter: parent.verticalCenterheight: parent.heighttext: "clear"onClicked: {clean()}}Button {id: addOneanchors.right: clearAll.leftanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCenterheight: parent.heighttext: "Add"onClicked: {add()}}Button {id: insertOneanchors.right: addOne.leftanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCenterheight: parent.heighttext: "Insert"onClicked: {insert()}}Button {id: moveDownanchors.right: insertOne.leftanchors.rightMargin: 5anchors.verticalCenter: parent.verticalCenterheight: parent.heighttext: "MoveDown"onClicked: {move()}}}}Component {id: phoneDelegateItem {id: wrapperwidth: listView.widthheight: 30MouseArea {anchors.fill: parentonClicked: wrapper.ListView.view.currentIndex = indexonDoubleClicked: wrapper.ListView.view.model.remove(index)}RowLayout {anchors.verticalCenter: parent.verticalCenterwidth: parent.widthText {id: col1text: namecolor: wrapper.ListView.isCurrentItem ? "red" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.preferredWidth: 120}Text {text: costcolor: wrapper.ListView.isCurrentItem ? "red" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.preferredWidth: 80}Text {text: manufacturercolor: wrapper.ListView.isCurrentItem ? "red" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.fillWidth: true}}}}ListView {id: listViewanchors.fill: parentdelegate: phoneDelegateheader: headviewfooter: footerViewmodel: phoneModel.createObject(listView)focus: truehighlight: Rectangle {color: "lightblue"}add: Transition {ParallelAnimation {NumberAnimation {property: "opacity"from: 0to: 1.0duration: 1000}NumberAnimation {property: "y"from: 0duration: 1000}}}displaced: Transition {SpringAnimation {properties: "y"spring: 3damping: 0.1epsilon: 0.25}}remove: Transition {SequentialAnimation {NumberAnimation {properties: "y"to: 0duration: 600}NumberAnimation {properties: "opacity"to: 0duration: 400}}}move: Transition {NumberAnimation {properties: "y"duration: 300easing.type: Easing.InQuart}}populate: Transition {NumberAnimation {property: "opacity"from: 0to: 1.0duration: 3000}}Component.onCompleted: {footerItem.clean.connect(model.clear)footerItem.add.connect(addOne)footerItem.insert.connect(insertOne)footerItem.move.connect(moveDown)}onCurrentIndexChanged: {console.log("index:", currentIndex)if (currentIndex >= 0) {var data = listView.model.get(currentIndex)footerItem.text = "%1 -> %2 -> %3".arg(data.name).arg(data.cost).arg(data.manufacturer)} else {footerItem.text = ""}}function addOne() {console.log("add")model.append({"name": "max3","cost": "1799","manufacturer": "samsung"})}function insertOne() {console.log("insert")model.insert(Math.round(Math.random() * model.count), {"name": "HTC One E8","cost": "2499","manufacturer": "htc"})}function moveDown() {console.log("moveDown:", currentIndex)if (currentIndex + 1 < model.count) {model.move(currentIndex, currentIndex + 1, 1)}}}
}
  1. ListModel专门用于定义列表数据,内部维护一个ListElement的列表,一个ListElement对象代表一个数据。
  2. 多个role构成一个ListElementrole包含一个名字和一个值,名字必须以小写字母开头,值必须是简单的常量(字符串、布尔值、数字、枚举值)
  3. ListElement中定义的role,可以在Delegate中通过名称访问
  4. Delegate使用Row管理Text对象来展现role,Text对象的text属性对应于role的名称
  5. ListView的delegate属性类型是Component,Component的顶层元素是Row,Row内嵌三个Text对象来展示Model定义的ListElement的三个role
  6. ListView给delegate暴露一个index属性,代表当前delegate示例对应的Item的索引位置,必要时可通过它来访问数据
  7. ListView定义delayRemoveisCurrentItemnextSectionpreviousSectionsectionview等附加属性,以及addremove两个附加信号,可以在delegate中直接访问(只有delegate的顶层Item才能直接使用这些附加属性和信号,非顶层Item则需通过顶层Item的id来访问这些附加属性)
  8. ListView的highlight属性可以指定一个Component对象,它的Z序小于delegate实例化出来的Item对象。highlightFollowsCurrentItem属性指定高亮背景是否跟随当前条目,当前条目变动时,高亮背景经过一个平滑的动画效果进行过渡

11.1.1 ListModel访问数据

count属性代表Model中有多少条数据
dynamicRoles属性为true时,则Model中的roles对应值的类型可以动态改变,但是性能将会严重下降,默认false。要使用它必须在添加数据之前
get(int)方法用于获取指定索引位置的数据,返回一个QML对象

var data = listView.model.get(listView.currentIndex);
listView.footerItem.text = data.name + ", " + data.cost + ", " + data.manufacturer;

11.1.2 ListModel删除数据

remove(int index, int count)方法用于删除数据

  • index指明删除数据的索引位置
  • count指示要删除的数据条数,默认为1

11.1.3 ListModel修改数据

setProperty(int index, string property, variant value)方法用于修改数据

listView.model.setProperty(5, "cost", 16999);
  • index指明修改数据的索引位置
  • property数据内role的名字
  • value要修改成的值

set(int index, jsobject dict)方法用于替换数据

listView.model.set(0, {"name": "Z5S mini", "cost": 1999, "manufacturer": "ZhongXing"});
  • index指明要替换数据的索引位置
  • dict要替换成的数据

11.1.4 ListModel添加数据

append(jsobject dict)用于在末尾添加一条数据
insert(int index, jsobject value)用于在指定位置添加一条数据

listView.model.append({"name" : "MX3","cost" : "1799","manufacturer": "MeiZu"}
)

11.1.5 ListModel移动数据

move(int indexSrc, int indexDst, int count)用于将一条数据移动到指定的位置

  • indexSrc要移动的数据索引
  • indexDst移动到的目标位置索引
  • count移动的条数

11.1.6 section列表分组

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15Window {width: 560height: 300color: "#EEEEEE"visible: trueComponent {id: phoneModelListModel {// Apple sectionListElement {name: "iPhone 5"cost: "4900"manufacturer: "Apple"}ListElement {name: "iPhone 3GS"cost: "1000"manufacturer: "Apple"}ListElement {name: "iPhone 4"cost: "1800"manufacturer: "Apple"}ListElement {name: "iPhone 4S"cost: "2300"manufacturer: "Apple"}//HuaWei sectionListElement {name: "B199"cost: "1590"manufacturer: "HuaWei"}// Sumsung sectionListElement {name: "GALAXY S4"cost: "3099"manufacturer: "Samsung"}ListElement {name: "C8816D"cost: "590"manufacturer: "HuaWei"}ListElement {name: "GALAXY S5"cost: "4699"manufacturer: "Samsung"}// XiaoMi sectionListElement {name: "MI 2S"cost: "1999"manufacturer: "XiaoMi"}ListElement {name: "MI 3"cost: "1999"manufacturer: "XiaoMi"}}}Component {id: phoneDelegateItem {id: wrapperwidth: parent.widthheight: 30ListView.onAdd: {console.log("count:", ListView.view.count)}MouseArea {anchors.fill: parentonClicked: wrapper.ListView.view.currentIndex = index}RowLayout {anchors.left: parent.leftanchors.verticalCenter: parent.verticalCenterspacing: 8Text {id: col1text: namecolor: wrapper.ListView.isCurrentItem ? "red" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.preferredWidth: 120}Text {text: costcolor: wrapper.ListView.isCurrentItem ? "green" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.preferredWidth: 80}Text {text: manufacturercolor: wrapper.ListView.isCurrentItem ? "red" : "black"font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18Layout.fillWidth: true}}}}Component {id: sectionHeaderRectangle {width: parent.widthheight: childrenRect.heightcolor: "lightsteelblue"Text {text: sectionfont.bold: truefont.pixelSize: 20}}}ListView {id: listViewanchors.fill: parentdelegate: phoneDelegatemodel: phoneModel.createObject(listView)focus: truehighlight: Rectangle {color: "lightblue"}section.property: "manufacturer"section.criteria: ViewSection.FullStringsection.delegate: sectionHeader}
}

section.property:分组的依据,对应于数据的role-name
section.criteria:指定section.property的判断条件

  • ViewSection.FullString(默认,全串匹配,不区分大小写)
  • ViewSection.Firstcharacter(首字母匹配,不区分大小写)

section.delegate:设定一个Component决定如何显示每个section
section.labelPositioning:决定当前或下一个section标签的显示策略

  • ViewSection.InlineLabels,这是默认方式。分组标签嵌入到Item之间显示。
  • ViewSection.CurrentLabelAtStart,当view移动时,当前分组的标签附着在view的开始。
  • ViewSection.NextLabelAtEnd,当view移动时,下一个分组标签附着在view的尾端。

11.2 XmlListModel

XmlListModel用于从XML数据中直接创建一个只读的model,可以用作其他view元素的数据源
XmlListModel使用XPath表达式来提取XML文档中的数据

<videos.xml>

<videos><video name='冰雪奇缘' date='2013-11-19' ><attr tag='导演'>詹妮弗·李</attr><attr tag='演员'>伊迪娜·门泽尔/克里斯汀·贝尔</attr><attr tag='评分'>9.2</attr><attr tag='简介'>在四面环海、风景如画的阿伦达王国,生活着两位可爱美丽的小公主,艾莎和安娜。艾莎天生具有制造冰雪的能...</attr><poster img='http://g3.ykimg.com/0516000052D779CD67583960490A8E1A' /><page link='http://v.youku.com/v_show/id_XNjk1ODc2NDMy.html' /><playtimes>12184709</playtimes></video><video name='功夫' date='2004-12-23' ><attr tag='导演'>周星驰</attr><attr tag='演员'>周星驰/元秋/元华/林子聪/梁小龙/陈国坤</attr><attr tag='评分'>7.0</attr><attr tag='简介'>1940年代的上海,自小受尽欺辱的街头混混阿星(周星驰 饰)为了能出人头地,可谓窥见机会的缝隙就往...</attr><poster img='http://g1.ykimg.com/0516000051BAD11A67583912FF0277C1' /><page link='http://v.qq.com/cover/u/uiq0rxuywu508qr.html' /><playtimes>4012749</playtimes></video><video name='西游·降魔篇' date='2013-02-10'><attr tag='导演'>周星驰</attr><attr tag='演员'>舒淇/文章/黄渤/李尚正/陈炳强/周秀娜</attr><attr tag='评分'>8.1</attr><attr tag='简介'>大唐年间妖魔横行,一小渔村因为饱受鱼妖之害请来道士(冯勉恒 饰)除妖,年轻驱魔人陈玄奘(文章 饰)...</attr><poster img='http://g2.ykimg.com/0516000051B436EB67583928E30DCCDD' /><page link='http://v.youku.com/v_show/id_XNTI2Mzg4NjAw.html' /><playtimes>25421498</playtimes></video><video name='小时代' date='2013-06-27' ><attr tag='导演'>郭敬明</attr><attr tag='演员'>杨幂/郭采洁/郭碧婷/谢依霖/柯震东/凤小岳</attr><attr tag='评分'>8.9</attr><attr tag='简介'>这是一个梦想闪耀的时代,一个理想冷却的时代;这是最坏的时代,这也是最好的时代,这是我们的小时代。在...</attr><poster img='http://g1.ykimg.com/0516000051F22C1C67583931E8015597' /><page link='http://v.youku.com/v_show/id_XNTg3NjkzMzIw.html' /><playtimes>99075808</playtimes></video><video name='倩女幽魂' date='1987-07-18'><attr tag='导演'>程小东</attr><attr tag='演员'>张国荣/王祖贤/午马</attr><attr tag='评分'>8.1</attr><attr tag='简介'>书生宁采臣(张国荣 饰)收账不成,无处可归,遂夜宿鬼寺兰若寺,遇上侠士燕赤霞(午马 饰),二人成为...</attr><poster img='http://g2.ykimg.com/051600004FC32F0797927377D9052FBF' /><page link='http://v.youku.com/v_show/id_XMjE0ODk3MjUy.html' /><playtimes>1579516</playtimes></video><video name='那些年,我们一起追的女孩' date='2011-08-19' ><attr tag='导演'>九把刀</attr><attr tag='演员'>柯震东/陈妍希/郝邵文</attr><attr tag='评分'>8.5</attr><attr tag='简介'>青春是一场大雨。即使感冒了,还盼望回头再淋它一次。人生就是不停的战斗,在还没有获得女神青睐时,左手...</attr><poster img='http://g3.ykimg.com/05160000531420D26758391C5C08485A' /><page link='http://v.qq.com/cover/t/tu0bpgju3a1xno6.html' /><playtimes>3807121</playtimes></video>
</videos>

<main.qml>

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 560height: 300color: "#EEEEEE"visible: trueComponent {id: videoModelXmlListModel {source: "videos.xml"id: xmlModelquery: "/videos/video"XmlRole {name: "name"query: "@name/string()"}XmlRole {name: "date"query: "@date/string()"}XmlRole {name: "img"query: "poster/@img/string()"}XmlRole {name: "director_tag"query: "attr[1]/@tag/string()"}XmlRole {name: "director "query: "attr[1]/string()"}XmlRole {name: "actor_tag"query: "attr[2]/@tag/string()"}XmlRole {name: "actor"query: "attr[2]/string()
"}XmlRole {name: "rating"query: "attr[3]/number()"}XmlRole {name: "desc"query: "attr[4]/string()"}XmlRole {name: "playtimes"query: "playtimes/number()"}}}Component {id: videoDelegateItem {id: wrapperwidth: listView.widthheight: 120MouseArea {anchors.fill: parentonClicked: wrapper.ListView.view.currentIndex = index}Image {id: posteranchors.left: parent.leftanchors.top: parent.topsource: imgwidth: 80height: 120fillMode: Image.PreserveAspectFit}ColumnLayout {anchors.left: poster.rightanchors.leftMargin: 4anchors.right: wrapper.rightanchors.top: poster.topheight: parent.heightspacing: 2Text {Layout.fillWidth: truetext: "<b>" + name + "</b>(" + rating + "," + playtimes + ")"color: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: dateLayout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: director_tag + ": <font color=\"#0000aa\">" + director + "</font>"Layout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: actor_tag + " : <font color=\"#0000aa\"> " + actor + "</font>"Layout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: descLayout.fillHeight: trueLayout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 16wrapMode: Text.WrapmaximumLineCount: 2elide: Text.ElideRight}}}}ListView {id: listViewanchors.fill: parentspacing: 4delegate: videoDelegatemodel: videoModel.createObject(listView)focus: truehighlight: Rectangle {width: parent.widthcolor: "lightblue"}}
}

source:指定XmlListModel使用的XML文档的位置
xml:指定作为model数据源头的XML字符串,utf-8编码,优先生效
query:一个XPath表达式,以"/“或”//"开始,和XmlRole的query结合使用
roles:XmlRole对象的列表,XmlListModel通过它来从XML文档中提取数据
count:当前model内数据的个数
namespaceDeclarations:保存在XPath中使用的命名空间
status:model的当前状态

  • XmlListModel.Null
  • XmlListModel.Ready
  • XmlListModel.Loading
  • XmlListModel.Error

progress:表示当前XML文档的下载进度,real类型,从0.0到1.0
get():得到索引位置的数据对象,然后可以根据role-name访问数据
reload():重新加载model,可以通过指定关键角色来只更新和关键角色匹配的数据

11.3 使用C++ Model

ListView可以使用C++中定义的Model,XmlListModel就是C++实现(QQuickXmlListModel)
C++实现Model必须从QAbstractItemModelQAbstractListModel继承实现
<videoListModel.h>

#ifndef VIDEOLISTMODEL_H
#define VIDEOLISTMODEL_H
#include <QAbstractListModel>
class VideoListModelPrivate;
class VideoListModel : public QAbstractListModel {Q_OBJECTQ_PROPERTY(QString source READ source WRITE setSource)
public:VideoListModel(QObject *parent = 0);~VideoListModel();int rowCount(const QModelIndex &parent) const;QVariant data(const QModelIndex &index, int role) const;QHash<int, QByteArray> roleNames() const;QString source() const;void setSource(const QString &filePath);Q_INVOKABLE QString errorString() const;Q_INVOKABLE bool hasError() const;Q_INVOKABLE void reload();Q_INVOKABLE void remove(int index);private:VideoListModelPrivate *m_dptr;
};
#endif

<videoListModel.cpp>

#include "videoListModel.h"
#include <QDebug>
#include <QFile>
#include <QVector>
#include <QXmlStreamReader>
typedef QVector<QString> VideoData;
class VideoListModelPrivate {
public:VideoListModelPrivate() : m_bError(false) {int role = Qt::UserRole;m_roleNames.insert(role++, "name");m_roleNames.insert(role++, "date");m_roleNames.insert(role++, "director_tag");m_roleNames.insert(role++, "director");m_roleNames.insert(role++, "actor_tag");m_roleNames.insert(role++, "actor");m_roleNames.insert(role++, "rating_tag");m_roleNames.insert(role++, "rating");m_roleNames.insert(role++, "desc_tag");m_roleNames.insert(role++, "desc");m_roleNames.insert(role++, "img");m_roleNames.insert(role++, "playpage");m_roleNames.insert(role++, "playtimes");}~VideoListModelPrivate() { clear(); }void load() {QXmlStreamReader reader;QFile file(m_strXmlFile);if (!file.exists()) {m_bError = true;m_strError = "File Not Found!";return;}if (!file.open(QFile::ReadOnly)) {m_bError = true;m_strError = file.errorString();return;}reader.setDevice(&file);QStringRef elementName;VideoData *video;while (!reader.atEnd()) {reader.readNext();if (reader.isStartElement()) {elementName = reader.name();if (elementName == "video") {video = new VideoData();QXmlStreamAttributes attrs = reader.attributes();video->append(attrs.value("name").toString());video->append(attrs.value("date").toString());} else if (elementName == "attr") {video->append(reader.attributes().value("tag").toString());video->append(reader.readElementText());} else if (elementName == "poster") {video->append(reader.attributes().value("img").toString());} else if (elementName == "page") {video->append(reader.attributes().value("link").toString());} else if (elementName == "playtimes") {video->append(reader.readElementText());}} else if (reader.isEndElement()) {elementName = reader.name();if (elementName == "video") {m_videos.append(video);video = 0;}}}file.close();if (reader.hasError()) {m_bError = true;m_strError = reader.errorString();}}void reset() {m_bError = false;m_strError.clear();clear();}void clear() {int count = m_videos.size();if (count > 0) {for (int i = 0; i < count; i++) {delete m_videos.at(i);}m_videos.clear();}}QString m_strXmlFile;QString m_strError;bool m_bError;QHash<int, QByteArray> m_roleNames;QVector<VideoData *> m_videos;
};VideoListModel::VideoListModel(QObject *parent): QAbstractListModel(parent), m_dptr(new VideoListModelPrivate) {}
VideoListModel::~VideoListModel() { delete m_dptr; }
int VideoListModel::rowCount(const QModelIndex &parent) const {return m_dptr->m_videos.size();
}
QVariant VideoListModel::data(const QModelIndex &index, int role) const {VideoData *d = m_dptr->m_videos[index.row()];return d->at(role - Qt::UserRole);
}
QHash<int, QByteArray> VideoListModel::roleNames() const {return m_dptr->m_roleNames;
}
QString VideoListModel::source() const { return m_dptr->m_strXmlFile; }
void VideoListModel::setSource(const QString &filePath) {m_dptr->m_strXmlFile = filePath;reload();if (m_dptr->m_bError) {qDebug() << " VideoListModel,error - " << m_dptr->m_strError;}
}
QString VideoListModel::errorString() const { return m_dptr->m_strError; }
bool VideoListModel::hasError() const { return m_dptr->m_bError; }
void VideoListModel::reload() {beginResetModel();m_dptr->reset();m_dptr->load();endResetModel();
}
void VideoListModel::remove(int index) {beginRemoveRows(QModelIndex(), index, index);delete m_dptr->m_videos.takeAt(index);endRemoveRows();
}

<main.cpp>

#include "videoListModel.h"
#include <QApplication>
#include <QColor>
#include <QQmlApplicationEngine>
#include <QtQml>int main(int argc, char *argv[]) {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endifQApplication app(argc, argv);qmlRegisterType<VideoListModel>("an.qt.CModel", 1, 0, "VideoListModel");QQmlApplicationEngine engine;engine.load("qrc:/main.qml");return app.exec();
}
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import an.qt.CModel 1.0Window {width: 560height: 300color: "#EEEEEE"visible: trueComponent {id: videoDelegateItem {id: wrapperwidth: listView.widthheight: 120MouseArea {anchors.fill: parentonClicked: wrapper.ListView.view.currentIndex = index}Image {id: posteranchors.left: parent.leftanchors.top: parent.topsource: imgwidth: 80height: 120fillMode: Image.PreserveAspectFit}ColumnLayout {anchors.left: poster.rightanchors.leftMargin: 4anchors.right: wrapper.rightanchors.top: poster.topheight: parent.heightspacing: 2Text {Layout.fillWidth: truetext: "<b>" + name + "</b>(" + rating + "," + playtimes + ")"color: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: dateLayout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: director_tag + ": <font color=\"#0000aa\">" + director + "</font>"Layout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: actor_tag + " : <font color=\"#0000aa\"> " + actor + "</font>"Layout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 18elide: Text.ElideRight}Text {text: descLayout.fillHeight: trueLayout.fillWidth: truecolor: wrapper.ListView.isCurrentItem ? "blue" : "black"font.pixelSize: 16wrapMode: Text.WrapmaximumLineCount: 2elide: Text.ElideRight}}}}ListView {id: listViewanchors.fill: parentspacing: 4delegate: videoDelegatemodel: VideoListModel {source: ".\\videos.xml"}focus: truehighlight: Rectangle {width: parent.widthcolor: "lightblue"}}
}

当允许在QML中修改C++实现的Model时,比如删除,就需要做如下动作(如删除):

  1. 调用基类的beginRemoveRows()
  2. 针对要删除的数据进行特定处理,如释放内存
  3. 调用基类的endRemoveRows()

11.4 TableView

TableView和ListView类似,多出了滚动条、挑选、可调整尺寸的表头等特性
TableView的数据也通过Model提供,可以使用ListModel、XmlListModel或使用C++从、QAbstractItemModelQAbstractTableModel等继承来实现Model

11.5 GridView

GridView和ListView类似,不同在于Item的呈现方式

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 480height: 400visible: trueComponent {id: videoModelXmlListModel {source: "videos.xml"id: xmlModelquery: "/videos/video"XmlRole {name: "name"query: "@name/string()"}XmlRole {name: "img"query: "poster/@img/string()"}XmlRole {name: "rating"query: "attr[3]/number()"}}}Component {id: videoDelegateItem {id: wrapperwidth: videoView.cellWidthheight: videoView.cellHeightMouseArea {anchors.fill: parentonClicked: wrapper.GridView.view.currentIndex = index}Image {id: posteranchors.horizontalCenter: parent.horizontalCenteranchors.top: parent.topanchors.topMargin: 3source: imgwidth: 100height: 150fillMode: Image.PreserveAspectFit}Text {anchors.top: poster.bottomanchors.topMargin: 4width: parent.widthtext: namecolor: wrapper.GridView.isCurrentItem ? "blue" : "black"font.pixelSize: 18horizontalAlignment: Text.AlignHCenterelide: Text.ElideMiddle}}}GridView {id: videoViewanchors.fill: parentcellWidth: 120cellHeight: 190delegate: videoDelegatemodel: videoModel.createObject(videoView)focus: truehighlight: Rectangle {height: videoView.cellHeight - 8color: "lightblue"}}
}

flow:指定Item的流模式,GridView.LeftToRightGridView.TopToBottom
cellWidth:单元格宽度
cellHeight:单元格高度

11.6 Repeater

Repeater用于创建多个基于Item的组件,丢给它的父(通常是定位器或布局管理器)来管理
count:指定要创建多少个基于Item的对象
model:指定数据类型,数字、字符串列表、对象列表、ListModel等常见的model
delegate:待实例化的组件,默认属性,定义时通常不显示初始化
itemAt(index):根据索引返回对应的delegate实例

11.6.1 model为数字

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 480height: 400visible: trueRowLayout {anchors.fill: parentspacing: 4Repeater {model: 8Rectangle {width: 46height: 30color: "steelblue"Text {anchors.fill: parentcolor: "black"font.pointSize: 14verticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentertext: index}}}}
}

11.6.2 model为字符串列表

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 480height: 400visible: trueRow {anchors.centerIn: parentspacing: 8Repeater {model: ["Hello", "Qt", "Quick"]Text {color: "blue"font.pointSize: 18font.bold: trueverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCentertext: modelData}}}
}

11.6.3 model为对象列表

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 480height: 400visible: trueColumn {anchors.fill: parentanchors.margins: 4spacing: 4Repeater {model: [{"name": "Zhang San","mobile": "13888888888
"}, {"name": "Wang Er","mobile": "13999999999
"}, {"name": "Liu Wu","mobile": "15866666666"}]Row {height: 30Text {width: 100color: "blue"font.pointSize: 13font.bold: trueverticalAlignment: Text.AlignVCentertext: modelData.name}Text {width: 200font.pointSize: 13verticalAlignment: Text.AlignVCentertext: modelData.mobile}}}}
}

11.6.4 model为ListModel

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.XmlListModel 2.15Window {width: 480height: 400visible: trueColumn {anchors.fill: parentanchors.margins: 4spacing: 4Repeater {model: ListModel {ListElement {name: "MI4"cost: "1999"manufacturer: "Xiaomi"}ListElement {name: "MX4"cost: "1999"manufacturer: "Meizu"}ListElement {name: "iPhone6"cost: "5500"manufacturer: "Apple"}ListElement {name: "C199"cost: "1599"manufacturer: "Huawei"}}Row {height: 30Text {width: 120color: "blue"font.pointSize: 14font.bold: trueverticalAlignment: Text.AlignVCentertext: name}Text {width: 100font.pointSize: 14verticalAlignment: Text.AlignVCentertext: cost}Text {width: 100font.pointSize: 12verticalAlignment: Text.AlignVCentertext: manufacturer}}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/92759.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QGraphicsView实现简易地图6『异步加载-无底图』

前文链接&#xff1a;QGraphicsView实现简易地图5『经纬网格』 同步加载&#xff0c;虽然程序已做到最少瓦片加载&#xff0c;但或多或少都存在一定程度上的卡顿现象&#xff0c;或者说是不够流畅吧。因此尝试采用异步加载&#xff0c;大致思路是每次缩放或漫游时计算所需重新加…

服务器如何防止cc攻击

对于搭载网站运行的服务器来说&#xff0c;cc攻击应该并不陌生&#xff0c;特别是cc攻击的攻击门槛非常低&#xff0c;有个代理IP工具&#xff0c;有个cc攻击软件就可以轻易对任何网站发起攻击&#xff0c;那么服务器如何防止cc攻击?请看下面的介绍。 服务器如何防止cc攻击&a…

K8S系列文章之 Docker安装使用Kafka

通过Docker拉取镜像的方式进行安装 照例先去DockerHub找一下镜像源&#xff0c;看下官方提供的基本操作&#xff08;大部分时候官方教程比网上的要清晰一些&#xff0c;并且大部分教程可能也是翻译的官方的操作步骤&#xff0c;所以直接看官方的就行&#xff09; 老实说Kafka…

LangChain入门:构建LLM驱动的应用程序的初学者指南

LangChain & DemoGPT 一、介绍 你有没有想过如何使用大型语言模型&#xff08;LLM&#xff09;构建强大的应用程序&#xff1f;或者&#xff0c;也许您正在寻找一种简化的方式来开发这些应用程序&#xff1f;那么你来对地方了&#xff01;本指南将向您介绍LangChain&#x…

Kubernetes入门 五、深入Pod:探针和生命周期

目录 探针探针类型LivenessProbeReadinessProbeStartupProbe&#xff1a; 探测方式ExecActionTCPSocketActionHTTPGetAction 参数配置操作示例 生命周期钩子函数生命周期 探针 所谓的探针就是容器内应用的监测机制&#xff0c;为了确保容器在部署后确实处在正常运行状态。 比…

PyTorch翻译官网教程-NLP FROM SCRATCH: CLASSIFYING NAMES WITH A CHARACTER-LEVEL RNN

官网链接 NLP From Scratch: Classifying Names with a Character-Level RNN — PyTorch Tutorials 2.0.1cu117 documentation 使用CHARACTER-LEVEL RNN 对名字分类 我们将建立和训练一个基本的字符级递归神经网络(RNN)来分类单词。本教程以及另外两个“from scratch”的自然…

学习笔记整理-DOM-01-基础知识

一、DOM基本概念 1. DOM基本概念 DOM是JS操控HTML和CSS的桥梁。DOM是JS操作HTML变得优雅。 DOM(Document Object Model&#xff0c;文档对象模型)是JavaScript操作HTML文档的接口&#xff0c;使文档操作变得非常优雅、简便。DOM最大的特点就是将文档表示为节点树。 节点的node…

【数据结构与算法】十大经典排序算法-选择排序

&#x1f31f;个人博客&#xff1a;www.hellocode.top &#x1f3f0;Java知识导航&#xff1a;Java-Navigate &#x1f525;CSDN&#xff1a;HelloCode. &#x1f31e;知乎&#xff1a;HelloCode &#x1f334;掘金&#xff1a;HelloCode ⚡如有问题&#xff0c;欢迎指正&#…

opencv基础55-获取轮廓的特征值及示例

轮廓自身的一些属性特征及轮廓所包围对象的特征对于描述图像具有重要意义。本节介绍几个轮廓自身的属性特征及轮廓所包围对象的特征。 宽高比 可以使用宽高比&#xff08;AspectRation&#xff09;来描述轮廓&#xff0c;例如矩形轮廓的宽高比为&#xff1a; 宽高比 宽度&am…

Vue3使用vue-print-nb插件调起打印功能

一、效果图 二、使用方式 安装插件 //Vue2.0版本安装方法 npm install vue-print-nb --save yarn add vue-print-nb//Vue3.0版本安装方法&#xff1a; npm install vue3-print-nb --save yarn add vue3-print-nb在全局引用 import Print from vue-print-nb Vue.use(Print)打…

Stable Diffusion WebUI 从零基础到入门

本文主要介绍Stable Diffusion WebUI的实际操作方法&#xff0c;涵盖prompt推导、lora模型、vae模型和controlNet应用等内容&#xff0c;并给出了可操作的文生图、图生图实战示例。适合对Stable Diffusion感兴趣&#xff0c;但又对Stable Diffusion WebUI使用感到困惑的同学&am…

序列模型和循环网络

Sequence Modeling and Recurrent Networks Sequence modeling tasks 在以往的模型中&#xff0c;各个输入之间是独立分布的 x ( i ) x^{(i)} x(i) 之间是相互独立的&#xff0c;同样输出 y ( i ) y^{(i)} y(i)之间也是相互独立的。 但是在序列模型中&#xff0c;输入输出是…

STM32基于CubeIDE和HAL库 基础入门学习笔记:功能驱动与应用

文章目录&#xff1a; 一&#xff1a;LED与按键驱动程序 main.c 1.闪灯 led.h led.c 2.按键控制LED亮灭 key.h key.c 二&#xff1a;蜂鸣器与继电器驱动程序 main.c 1.蜂鸣器 buzzer.h buzzer.c delay.h delay.c 2.继电器 relay.h relay.c 三&#xff1…

“MongoDB基础知识【超详细】

"探索MongoDB的无边之境&#xff1a;沉浸式数据库之旅" 欢迎来到MongoDB的精彩世界&#xff01;在这个博客中&#xff0c;我们将带您进入一个充满创新和无限潜力的数据库领域。无论您是开发者、数据工程师还是技术爱好者&#xff0c;MongoDB都将为您带来一场令人心动…

PLY模型格式详解【3D】

本文介绍PLY 多边形文件格式&#xff0c;这是一种用于存储被描述为多边形集合的图形对象。 PLY文件格式的目标是提供一种简单且易于实现但通用的格式足以适用于各种模型。 PLY有两种子格式&#xff1a;易于入门的 ASCII 表示形式和用于紧凑存储和快速保存和加载的二进制格式。 …

搭建 Python 环境 | Python、PyCharm

计算机 计算机能完成的工作&#xff1a; 算术运算逻辑判断数据存储网络通信…更多的更复杂的任务 以下这些都可以称为 “计算机”&#xff1a; 一台计算机主要由以下这几个重要的组件构成 CPU 中央处理器&#xff1a;大脑&#xff0c;算术运算&#xff0c;逻辑判断 存储器&…

Redis——常见数据结构与单线程模型

Redis中的数据结构 Redis中所有的数据都是基于key&#xff0c;value实现的&#xff0c;这里的数据结构指的是value有不同的类型。 当前版本Redis支持10种数据类型&#xff0c;下面介绍常用的五种数据类型 底层编码 Redis在实现上述数据结构时&#xff0c;会在源码有特定的…

Docker数据卷容器

1.数据卷容器介绍 即使数据卷容器c3挂掉也不会影响c1和c2通信。 2.配置数据卷容器 创建启动c3数据卷容器&#xff0c;使用-v参数设置数据卷。volume为目录&#xff0c;这种方式数据卷目录就不用写了&#xff0c;直接写宿主机目录。 创建c1、c2容器&#xff0c;使用–volum…

三星霸主地位“无可撼动“,DRAM内存市场份额创近 9 年新低仍第一

三星电子在DRAM市场的竞争地位一直备受关注。据报告显示&#xff0c;除了市场份额下降外&#xff0c;三星电子在上半年的销售额也出现了下滑。这主要是由于全球消费电子产品需求下滑&#xff0c;导致三星电子的芯片需求减少。 存储芯片业务所在的设备解决方案部门的营收和利润也…