【Qt6】列表模型——便捷类型
前一篇水文中,老周演示了 QAbstractItemModel 抽象类的继承方法。其实,在 Qt 的库里面,QAbstractItemModel 类也派生了两个基类,能让开发者继承起来【稍稍】轻松一些。
这两个类是 QAbstractListModel 和 QAbstractTableModel。
- QAbstractListModel类专门用来实现一维列表结构模型的。它实现了 index、parent 等方法,并且把 columnCount 方法变成了私有成员(一维列表不需要它)。继承时直接实现 rowCount、data、setData 这几个方法即可;
- QAbstractTableModel类专门用来实现二维表结构的模型。它实现了 index、parent 等方法。继承时咱们要实现 rowCount、columnCount、data、setData 等方法。
虽然它帮咱们实现了一些成员,但实际上也省不了多功夫的。下面咱们用 QAbstractListModel 举例,和上篇中的一样,操作一个 QList<int> 数据。毕竟一维的比较简单,演示出来大伙都容易懂。
// 头文件
#ifndef LIST_H
#define LIST_H #include <QAbstractListModel>
#include <QList> class CustListModel: public QAbstractListModel
{
Q_OBJECT public:
explicit CustListModel(QObject* parent = nullptr);
~CustListModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
// 私有,存数据用
QList<int> m_list;
}; #endif
先弄 rowCount 方法,返回总行数,这个简单一点。
int CustListModel::rowCount(const QModelIndex &parent) const
{
if(parent.isValid())
{
return 0;
}
return m_list.size();
}
实现 data 方法,获取数据时用。
QVariant CustListModel::data(const QModelIndex &index, int role) const
{
if( role == Qt::DisplayRole || role == Qt::EditRole)
{
if(!index.isValid())
{
return QVariant();
}
// 获取索引
int i = index.row();
// 返回指定索引处的值
return m_list.at(i);
}
return QVariant();
}
要返回 QList<T> 中指定的元素,用 at 方法,传递索引给它即可。
实现 setData 方法,编辑结束后用于更新数据的。
bool CustListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(!index.isValid())
return false;
if(role == Qt::EditRole || role == Qt::DisplayRole)
{
// 解包数据
bool ok;
int val = value.toInt(&ok);
if(ok)
{
// 设置值
m_list.replace(index.row(), val);
// 发出信号
emit dataChanged(index,index,{role});
return true;
}
}
return false;
}
要修改某个索引处的值,用 replace 方法替换。
实现 flags 方法,表明该模型的列表项支持交互、编辑、被选择。
Qt::ItemFlags CustListModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
}
ItemIsEnabled 表明列表项是活动的,用户可操作的;ItemIsEditable 表明列表项可编辑;ItemIsSelectable 表示列表项可以选择。
下面这两个方法是有点麻烦的,这里先介绍一下。
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
row 参数表示要插入元素的索引,count 表示插入元素个数,插入元素后,原来的元素向后移动。例如:A、B、C、D,现在我要在B处插入两个元素。那么 row = 1,count = 2,B 处放入 E、F,B、C、D 向后移,变成 A、E、F、B、C、D。
然后,删除元素的方法也类似。
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
row 是要删除的索引,count 是连续要删掉的元素个数。
insertRows 和 removeRows 方法的返回值含义相同:成功返回 true,失败返回 false。
好,现在上代码。
bool CustListModel::insertRows(int row, int count, const QModelIndex &parent)
{
if(parent.isValid())
return false;
// 注意这里!!
beginInsertRows(parent, row, row + count - 1);
// 开始插入
m_list.insert(row, count, 0);
// 注意这里!!
endInsertRows();
return true;
} bool CustListModel::removeRows(int row, int count, const QModelIndex &parent)
{
if(parent.isValid())
return false;
// 注意这里!!!
beginRemoveRows(parent, row, row + count - 1);
// 删除
m_list.remove(row, count);
// 注意这里!!!
endRemoveRows();
return true;
}
在插入数据前必须调用 beginInsertRows 方法。注意这个方法的参数含义和 insertRows 有些不一样。
void beginInsertRows(const QModelIndex &parent, int first, int last)
insertRows 方法是指定开始索引 ,然后连续加入多少个元素,而 beginInsertRows 方法是你插入元素后,它们在列表中的开始索引和结束索引。如 A、B、C,在B处插入两个元素。开始索引是 1,结束索引是 2,即结束索引的计算方法是: endIndex = startIndex + count -1。插入元素结束后必须调用 endInsertRows 方法。这一对方法的作用是让用户界面上的视图(如 QListView、QTableView 等)能及时做出响应。
同理,在删除元素时,beginRemoveRows 和 endRemoveRows 方法也必须调用。beginRemoveRows 方法的参数与 beginInsertRows 方法一样,也是索引的起止值。即要删除元素的起始索引,和被删除的最后一个元素的索引。如1、2、3、4,要删除2、3、4,那么起始索引是 1,结束索引 3。
模型已完工,下面做个界面试一试。
int main(int argc, char** argv)
{
QApplication myapp(argc, argv);
// 窗口
QWidget *window = new QWidget;
// 布局
QGridLayout *layout = new QGridLayout;
window->setLayout(layout);
// 列表视图
QListView* view = new QListView(window);
// 实例化模型
CustListModel* model = new CustListModel(window);
// 设置到视图中
view->setModel(model);
layout->addWidget(view, 0, 0, 3, 1); // 按钮
QPushButton* btnAdd=new QPushButton("新增", window);
QPushButton* btnDel = new QPushButton("移除", window);
QPushButton* btnShowAll = new QPushButton("显示列表", window);
layout->addWidget(btnAdd, 0, 1);
layout->addWidget(btnDel, 1, 1);
layout->addWidget(btnShowAll, 2, 1);
layout->setColumnStretch(0, 1);
// 连接clicked信号
QObject::connect(
btnAdd,
&QPushButton::clicked,
[&view, &model, &window]()
{
bool res;
int val = QInputDialog::getInt(
window, //父窗口
"输入", //窗口标题
"请输入整数:", //提示文本
0, //默认值
0, //最小值
1000, //最大值
1, //步长值
&res //表示操作结果
);
if(!res)
return;
// 索引为count
int i = model->rowCount();
// 添加一项
model->insertRow(i);
// 获取新索引
QModelIndex newIndex = model->index(i);
// 设置此项的值
model->setData(newIndex, QVariant(val), Qt::DisplayRole);
}
);
QObject::connect(
btnDel,
&QPushButton::clicked,
[&window, &view, &model]()
{
// 当前项
QModelIndex curri = view->currentIndex();
if(!curri.isValid())
return;
// 删除
model->removeRow(curri.row());
}
);
QObject::connect(
btnShowAll,
&QPushButton::clicked,
[&model, &window]()
{
// 获取总数
int c = model->rowCount();
QString msg;
for(int x = 0; x < c; x++)
{
// 获取值
QVariant value = model->data(model->index(x), Qt::DisplayRole);
if(value.isValid())
{
int n = qvariant_cast<int>(value);
msg += QString(" %1").arg(n);
}
}
QMessageBox::information(window,"提示",msg);
}
); // 标题
window->setWindowTitle("小型列表");
// 显示
window->show(); return QApplication::exec();
}
QListView 组件用来显示模型中的数据。三个按钮分别用来添加、删除和显示列表项。最后一个按钮显示模型中的所有数据,它的作用是验证模型是否工作正常。
“新增”按钮被点击后,先通过 QInputDialog.getInt 静态方法,弹出一个输入内容的对话框,然后把输入的值添加到模型中。模型中添加元素用的就是 insertRow 方法——只插入一个元素,它调用了咱们上面实现的 insertRows 方法,只是把 count 参数设置为 1。这里我实现的是新元素总是追加在列表最后,即新元素的行号等于 rowCount。
删除元素时,QListView 组件的 currentIndex 方法返回当前选中项的索引。在单选模式下,99.9% 被选中的项是等同于当前项的。多选模式下就不好说了,所以,如果需要,可以访问 selectionModel 方法,再通过 selectedIndexes 方法获得被选项的列表。
看看效果。
QAbstractTableModel 类的用法也是差不多的,只不过要实现行和列的读写。
这两个类哪怕帮咱们实现了一些代码,但用起来还是麻烦,要是有不需要继承、开箱就用的类就会好多了。Qt 还真的提供了这样的类型。
首先说的是 QStringListModel,这个就是针对 QStringList 类的——字符串列表。QStringList 其实就是 QList<QString>。这个 QStringListModel 模型就是以字符串列表为数据源的。不需要继承(除非你需要自定义),直接可用。
咱们演练一下。
// 头文件
#ifndef DEMO_H
#define DEMO_H #include <QWidget>
#include <QPushButton>
#include <QListView>
#include <QVBoxLayout> class MyWindow : public QWidget
{
Q_OBJECT public:
explicit MyWindow(QWidget* parent = nullptr);
~MyWindow(); private:
QVBoxLayout* m_layout;
QPushButton* m_btn;
QListView* m_view;
// 用于连接clicked信号
void onClicked();
}; #endif
三个组件:按钮被点击后加载数据;QListView 是视图组件,显示数据;QVBoxLayout 是布局用的,界面元素沿垂直方向排列(纵向)。公共成员就是构造函数和析构函数。析构是空白的,实现构造函数即可。
#include "../include/demo.h"
#include <QStringList>
#include <QStringListModel> MyWindow::MyWindow(QWidget * parent)
: QWidget::QWidget(parent)
{
m_layout = new QVBoxLayout();
setLayout(m_layout);
// 按钮
m_btn = new QPushButton("加载数据", this);
m_layout->addWidget(m_btn);
// 视图
m_view = new QListView(this);
m_layout->addWidget(m_view, 1);
// 连接信号
connect(m_btn, &QPushButton::clicked, this, &MyWindow::onClicked);
} MyWindow::~MyWindow()
{
}
下面是重点,实现 onClicked 成员方法,构建数据并在视图中显示。
void MyWindow::onClicked()
{
// 创建字符串列表
QStringList list;
list << "手榴弹" << "地雷" << "鱼雷" << "燃烧弹";
// 创建模型实例
QStringListModel *model = new QStringListModel(list, this);
// 设置给视图
m_view->setModel(model);
}
代码不复杂。QStringList 和我们熟悉的 cout 一样,可以用“<<”运算符写入字符串。每一次运算就写入一个元素,故上述代码向列表填充了四个元素。接着实例化 QStringListModel 类,传给 setModel 方法就可以与视图关联。这里实例化模型类最好使用指针类型,可以将 this 传给构造函数,这样,当前窗口会管理它的生命周期,你不用担心堆内存没有释放。
main 函数就没什么了,初始化应用程序,显示窗口就完事了。
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyWindow* win = new MyWindow();
win->setWindowTitle("示例");
win->resize(320, 275);
win->show(); return QApplication::exec();
}
运行代码后,点一下按钮,就能看到字符串列表了。
QStringListModel 只针对字符串列表,对于复杂一些的列表项,还是不够用的。所以咱们要请出下一位表演者—— QStandardItemModel。字面翻译:标准列表模型。这个模型就不仅仅可以用字符串列表当源了,它可以设置更复杂的数据。比如图标、显示文本、背景色、文本颜色等。
为了便于使用,标准列表模型中的“项”由 QStandardItem 封装。你可以先创建 QStandardItem 实例,设置好各种数据后,再添加进 QStandardItemModel 中。在设置子项,你可以不用 setData,而改用 setItem 方法。setItem 方法调用时只要指出行号、列号(如果是一维列表,调用另一个重载可以忽略列号)即可。
对于行标题、列标题,还有更方便的 setHorizontalHeaderLabels(水平)和 setVerticalHeaderLabels(垂直)方法,只要提供一个字符串列表,就能设置行列标题了。
现在,咱们了解一下 QStandardItem 这厮怎么用,这个很重要,用好标准模型得靠它。它的构造函数传参允许你指定显示文本、图标,或者总共多少行多少列。毕竟它有四个重载:
QStandardItem();
explicit QStandardItem(const QString &text);
QStandardItem(const QIcon &icon, const QString &text);
explicit QStandardItem(int rows, int columns = 1);
当然,可以调用无参数构造函数,然后再调用 set**** 方法来设置各项数据,比如:
- setText:设置要显示的文本,就是你想让用户看到的文本;
- setIcon:设置图标。不管是 QTableView 视图还是 QTreeView 视图,其实只有第一列才会显示图标。说直接点就是其他非第一列的项,你设置了图标也是呵呵;
- setToolTip:设置工具提示。就是当鼠标悬浮在上面时显示的文本;
- setWhatsThis:设置 What the FK,不,是 What is This 帮助信息;
- setFont:设置显示该项所使用的字体;
- setTextAlignment:设置显示该项时,文本的对齐方式;
- setBackground:设置该项的背景颜色;
- setForeground:前景色,就是设置文本的颜色;
- setStatusTip:显示在状态栏上的提示信息(这个好像不常用);
- setDragEnabled、setDropEnabled:该项支不支持拖放操作;
- setAutoTristate:树形视图时用得上,比如父节点被 check 后,是否所有子节点也跟着 check;
- setCheckable:该项是否显示一个 CheckBox 框,让用户可以在那里勾来勾去;
- setSelectable:该项允许用户选择吗;
- setEnabled:若是 true,用户可以操作该项,如选中它;如为 false,此项为禁用状态;
- setEditable:该项允许编辑吗。
你要是觉得 setEnabled、setEditable、setSelectable 这些方法麻烦,可以用 setFlags 方法一次性解决,用 Or 运算组合的 Qt::ItemFlags 枚举来设置。
当然,还有些成员我没列出,看着那么多成员方法好像很复杂,其实我们不一定全调用一遍的,看需要,不需要的可以不管的。
下面咱们也弄个例子。这个例子,我直接从 QTableView 类派生。
// 头文件 #ifndef CUST_H
#define CUST_H #include<qwidget.h>
#include<qstandarditemmodel.h>
#include <qtableview.h>
#include <qcolor.h> class MyTableView : public QTableView
{
Q_OBJECT public:
explicit MyTableView(QWidget* parent = nullptr);
~MyTableView(); private:
QStandardItemModel *model;
}; #endif
私有成员是模型对象,然后,咱们实际要做的是实现构造函数,实例化模型,再往里面塞数据。
MyTableView::MyTableView(QWidget *parent)
: QTableView(parent)
{
// 实例化模型类
model = new QStandardItemModel(this);
// 五行三列
// model->setRowCount(5);
// model->setColumnCount(3);
// 设置列标题
model->setHorizontalHeaderLabels({"学号", "姓名", "分数"});
// 好,我们开始准备数据
// 第一行
QStandardItem *cell00 = new QStandardItem(QIcon("a.png"), "2572");
QStandardItem *cell01 = new QStandardItem("小李");
QStandardItem *cell02 = new QStandardItem("58");
// 不合格,让它显示黄色
cell02->setBackground(QColor("yellow"));
cell02->setForeground(QColor("red"));
// 设置到模型中
model->setItem(0, 0, cell00);
model->setItem(0, 1, cell01);
model->setItem(0, 2, cell02);
// 第二行
QStandardItem *cell10 = new QStandardItem(QIcon("a.png"), "2055");
QStandardItem *cell11 = new QStandardItem("小陈");
QStandardItem *cell12 = new QStandardItem("85");
model->setItem(1, 0, cell10);
model->setItem(1, 1, cell11);
model->setItem(1, 2, cell12);
// 第三行
QStandardItem *cell20 = new QStandardItem(QIcon("a.png"), "1069");
QStandardItem *cell21 = new QStandardItem("小杜");
QStandardItem *cell22 = new QStandardItem("70");
model->setItem(2, 0, cell20);
model->setItem(2, 1, cell21);
model->setItem(2, 2, cell22);
// 第四行
QStandardItem *cell30 = new QStandardItem(QIcon("a.png"), "2469");
QStandardItem *cell31 = new QStandardItem("小王");
QStandardItem *cell32 = new QStandardItem("100");
// 满分,给他点奖励
cell32->setBackground(QColor("blue"));
cell32->setForeground(QColor("white"));
model->setItem(3, 0, cell30);
model->setItem(3, 1, cell31);
model->setItem(3, 2, cell32);
// 第五行
QStandardItem *cell40 = new QStandardItem(QIcon("a.png"), "6394");
QStandardItem *cell41 = new QStandardItem("小张");
QStandardItem *cell42 = new QStandardItem("89");
model->setItem(4, 0, cell40);
model->setItem(4, 1, cell41);
model->setItem(4, 2, cell42); // 设置模型
setModel(model);
} MyTableView::~MyTableView()
{
}
setRowCount、setColumnCount 方法可以不调用,模型会根据你放的数据调整。有两点得注意:
1、这里每个 QStandardItem 代表的是一个单元格的数据,而不是一行;
2、QStandardItem 类型的变量要声明为指针类型,并且要在堆上分配;不能用栈分配,会显示空白(提前析构了)。
然后,main 函数就更简单了。
#include <QApplication>
#include "cust.h" int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MyTableView *win = new MyTableView();
win->setWindowTitle("月考结果");
win->resize(300, 260);
win->show(); return QApplication::exec();
}
结果如下图:
第一行和第四行,咱们修改过背景色和文本颜色。
// 不合格,让它显示黄色
cell02->setBackground(QColor("yellow"));
cell02->setForeground(QColor("red"));
……
// 满分,给他点奖励
cell32->setBackground(QColor("blue"));
cell32->setForeground(QColor("white"));
好了,今天就聊到这儿,QStandardItemModel 还有其他耍法,尤其是在树形结构上。咱们下一期继续扯。
【Qt6】列表模型——便捷类型的更多相关文章
- 列表 list 容器类型数据(str字符串, list列表, tuple元组, set集合, dict字典)--->元组 tuple-->字符串 str
# ### 列表 list 容器类型数据(str字符串, list列表, tuple元组, set集合, dict字典) # (1)定义一个列表 listvar = [] print(listvar, ...
- 列表 ->join---> 字符串 转类型:x--->y类型 y(x)
列表 ->join---> 字符串 转类型:x--->y类型 y(x)
- Python操作符重载总结&列表模型
操作符重载 二元运算符 特殊方法 + __add__,__radd__ - __sub__,__rsub__ * __mul__,__rmul__ / __div__,__rdiv__,__trued ...
- python基础(6):列表和字典类型
前面我们所接触的数据类型都是存单个值,今天我们来看看可以存多个值的列表和字典. 预习: 1.有列表data=['alex',49,[1900,3,18]],分别取出列表中的名字,年龄,出生的年,月,日 ...
- python 基础-----数字,字符串,列表,字典类型简单介绍
一.第一个python小程序 1.下载安装python2.7和python3.6的版本及pycharm,我们可以再解释器中输入这样一行代码: 则相应的就打出了一句话.这里的print是打印的意思.你输 ...
- python数据类型——列表和元组类型
列表类型(list) 定义一个列表类型很简单: l = ['a','b','c','d','e','f'] 变量l即为列表类型,可以用type方法查看: print(type(l)) 列表的增删改查 ...
- Django——7 常用的查询 常用的模型字段类型 Field的常用参数 表关系的实现
Django 常用的查询 常用的查询方法 常用的查询条件 常用字段映射关系 Field常用参数 表关系的实现 查用的查询方法 这是需要用到的数据 from django.http import Htt ...
- Python第五天 列表练习 元组类型 字典类型 小购物车练习
# 反转 reverse# l=['lili','asdf','qwer','80000']# l.reverse()# print(l) # ['80000', 'qwer', 'asdf', 'l ...
- k3 cloud中列表字段汇总类型中设置了汇总以后没有显示出汇总值
处理方法,需要bos中的分组列信息中设置求和: 选择对应字段将其从左侧添加到右侧 方法二: 直接针对[价税合计]字段的列表汇总类型进行设置,步骤如下图所示
- HTML中改变列表的序号类型
HTML中,<ol>标签表示有序列表,每一个表项的编号默认从数字开始.比如 <html> <head> <title>test</title> ...
随机推荐
- Health Kit文档大变样,一起尝鲜!
Health Kit文档全新升级,开发场景更清晰,聚焦你关心的问题,快来一起尝鲜! 文档入口请戳:文档入口~ 如果你是运动健康的老朋友,可以从旧文档页面上方的提示信息中进入:最新版本哦. 一. 架构调 ...
- liunx操作系统下配置服务器
centos7 下配置服务器基本步骤 1,yum install 服务器名称 2,关闭防火墙,配置服务器配置文件,开启服务, 3,创建文件,设置访问权限, 4,本地登陆,测试服务器能否连通
- Hive执行计划之hive依赖及权限查询和常见使用场景
目录 概述 1.explain dependency的查询与使用 2.借助explain dependency解决一些常见问题 2.1.识别看似等价的SQL代码实际上是不等价的: 2.2 通过expl ...
- 体验昇腾Ascend C 编程语言极简易用的算子开发
摘要:昇腾Ascend C编程语言,让基于昇腾AI的算法创新更加简单. 本文分享自华为云社区<CANN黑科技解密|昇腾Ascend C编程语言 - 极简易用的算子开发体验>,作者:昇腾CA ...
- Python编程和数据科学中的数据处理:如何从数据中提取有用的信息和数据
目录 引言 数据分析和数据处理是数据科学和人工智能领域的核心话题之一.数据科学家和工程师需要从大量的数据中提取有用的信息和知识,以便更好地理解和预测现实世界中的事件.本文将介绍Python编程和数据科 ...
- 大数据实战手册-开发篇之pycharm远程开发调试
2.1 pycharm远程开发调试 2.1.1 python版本一致 #版本都保持3.6.6 #root cd /usr/local/python3/bin/pip3 list 备注:[python模 ...
- 一篇随笔学会CSS
CSS3 1.CSS介绍 HTML+CSS+JS 结构+表现+交互 2.CSS发展史 Cascading Style Sheet 层叠级联样式表 表现层:美化网页(字体.颜色.边距.宽高.网页定位.网 ...
- Set 接口及其常用方法
Set 接口基本介绍 Set接口是Collection接口的一个子接口,其主要特点如下: 不允许重复元素:Set接口的实现类不会包含重复的元素.更正式地说,不包含任何一对使得e1.equals(e2) ...
- Centos使用keepalived配置MySQL双主热备集群
目录 安装MySQL 下载安装包 卸载mariadb-lib 安装依赖 安装gcc 安装perl 永久关闭selinux 安装 配置 创建mysql数据库管理用户和组 创建数据目录 修改my.cnf配 ...
- Python 运行 shell 命令的一些方法
哈喽大家好,我是咸鱼 我们知道,python 在自动化领域中被广泛应用,可以很好地自动化处理一些任务 就比如编写 Python 脚本自动化执行重复性的任务,如文件处理.数据处理.系统管理等需要运行其他 ...