news 2026/8/18 13:16:49

Qt新手入门指南 - 如何创建模型/视图(三)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt新手入门指南 - 如何创建模型/视图(三)

每个UI开发人员都应该了解ModelView编程,本教程的目标是为大家提供一个简单易懂的介绍。

Qt是目前最先进、最完整的跨平台C++开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。

在上文中,我们主要为大家介绍了Model/View(模型/视图)的一些基本概念,本文将继续为大家介绍如何创建一个简单的模型/视图应用。

2. 一个简单的模型/视图应用

如果想开发一个模型/视图应用程序,应该从哪里开始呢?我们建议从一个简单的示例开始,逐步扩展它,这使得理解体系结构更加容易。对于许多开发人员来说,在调用IDE之前试图详细理解模型/视图体系结构是不太方便的,从具有演示数据的简单模型/视图应用程序开始实际上更容易。

下面是7个非常简单且独立的应用程序,它们展示了模型/视图编程的不同方面,源代码可以在examples/widgets/tutorials/modelview目录中找到。

2.3 表单元格内的时钟

我们仍然有一个只读表,但这一次内容每秒都在变化,因为正在显示当前时间。

(文件来源:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)

QVariant MyModel::data(const QModelIndex &index, int role) const { int row = index.row(); int col = index.column(); if (role == Qt::DisplayRole && row == 0 && col == 0) return QTime::currentTime().toString(); return QVariant(); }

时钟的滴答声缺少了一些东西,需要每一秒都告诉视图时间已经改变、需要再次读取。我们用定时器来做这个,在构造函数中,我们将其间隔设置为1秒,并连接其超时信号。

(文件来源:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)

MyModel::MyModel(QObject *parent) : QAbstractTableModel(parent) , timer(new QTimer(this)) { timer->setInterval(1000); connect(timer, &QTimer::timeout , this, &MyModel::timerHit); timer->start(); }

这里是对应的槽位:

(文件来源:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)

void MyModel::timerHit() { // we identify the top left cell QModelIndex topLeft = createIndex(0,0); // emit a signal to make the view reread identified data emit dataChanged(topLeft, topLeft, {Qt::DisplayRole}); }

通过发出dataChanged() 信号,我们要求视图再次读取左上角单元格中的数据。注意,没有显式地将dataChanged()信号连接到视图,这在调用setModel()时自动发生。

2.4 设置列和行标题

标题可以通过一个视图方法隐藏:ableView->verticalHeader()->hide();

然而,头部内容是通过模型设置的,所以我们重新实现headerData()方法:

(文件来源:examples/widgets/tutorials/modelview/4_headers/mymodel.cpp)

QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case 0: return QString("first"); case 1: return QString("second"); case 2: return QString("third"); } } return QVariant(); }

注意,方法headerData()也有一个参数role,其含义与MyModel::data()中的相同。

2.5 最小编辑示例

在本例中,我们将构建一个应用程序,通过重复输入到表格单元格中的值,自动用内容填充窗口标题。为了能够方便地访问窗口标题,我们将QTableView放在QMainWindow中。

模型决定编辑功能是否可用,为了启用可用的编辑功能,我们只需要修改模型,这是通过重新实现以下虚拟方法来实现的:setData() 和flags()。

(文件来源:examples/widgets/tutorials/modelview/5_edit/mymodel.h)

// mymodel.h #include <QAbstractTableModel> #include <QString> const int COLS= 3; const int ROWS= 2; class MyModel : public QAbstractTableModel { Q_OBJECT public: MyModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(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) override; Qt::ItemFlags flags(const QModelIndex &index) const override; private: QString m_gridData[ROWS][COLS]; //holds text entered into QTableView signals: void editCompleted(const QString &); };

我们使用二维数组QString m_gridData来存储数据,这使得m_gridData成为MyModel的核心。MyModel的其余部分就像一个包装器,并将m_gridData适配到QAbstractItemModel接口,同时我们引入了editCompleted()信号,这使得将修改后的文本传输到窗口标题成为可能。

(文件来源:examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)

bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole) { if (!checkIndex(index)) return false; //save value from editor to member m_gridData m_gridData[index.row()][index.column()] = value.toString(); //for presentation purposes only: build and emit a joined string QString result; for (int row = 0; row < ROWS; row++) { for (int col= 0; col < COLS; col++) result += m_gridData[row][col] + ' '; } emit editCompleted(result); return true; } return false; }

每次用户编辑单元格时都会调用setData(),index参数告诉我们编辑了哪个字段,value提供了编辑过程的结果。角色总是被设置为Qt::EditRole ,因为我们的单元格只包含文本,如果存在一个复选框,并且用户权限设置为允许选择该复选框,则调用也会将角色设置为Qt::CheckStateRole。

(文件来源:examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)

Qt::ItemFlags MyModel::flags(const QModelIndex &index) const { return Qt::ItemIsEditable | QAbstractTableModel::flags(index); }

可以使用flags()调整单元格的各种属性。

返回Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled足以向编辑器显示一个单元格可以被选择。

如果编辑一个单元格修改的数据多于该特定单元格中的数据,则模型必须发出dataChanged()信号,以便读取已更改的数据。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/18 13:10:15

ZotCard卡片笔记完整指南:6步走完从建卡到复盘的工作流

ZotCard卡片笔记完整指南&#xff1a;6步走完从建卡到复盘的工作流 【免费下载链接】zotcard ZotCard is a plug-in for Zotero, which is a card note-taking enhancement tool. It provides card templates (such as concept card, character card, golden sentence card, et…

作者头像 李华
网站建设 2026/8/18 13:09:26

Windows + Ubuntu 双系统开机直接进 Windows 的解决记录

Windows Ubuntu 双系统开机直接进 Windows 的解决记录 问题 安装 Ubuntu Windows 双系统后&#xff1a; 按 F12 可以手动选择 Ubuntu选择 Ubuntu 后&#xff0c;GRUB 可以正常显示 Ubuntu 和 Windows但如果开机不操作&#xff0c;就会直接进入 Windows 说明&#xff1a; Ubun…

作者头像 李华
网站建设 2026/8/18 13:05:56

Boost.Statechart:C++中构建类型安全层次化状态机的完整指南

1. 为什么我们需要一个“正经”的状态机库&#xff1f; 在软件开发的日常里&#xff0c;尤其是涉及复杂业务流程、设备控制、协议交互或者游戏逻辑时&#xff0c;“状态”和“状态转移”这两个概念几乎无处不在。你可能随手就用 enum 加一堆 if-else 或者 switch-case 撸…

作者头像 李华
网站建设 2026/8/18 13:04:01

深入解析端口复用:从TCP/UDP基础到SO_REUSEADDR/SO_REUSEPORT实战

1. 先搞清楚“端口独占”到底在说什么“同一个端口能不能被多个进程监听&#xff1f;” 这个问题&#xff0c;我见过太多人栽跟头&#xff0c;尤其是在面试和实际部署时。很多人被“端口独占”这个词骗了&#xff0c;以为只要端口被占用&#xff0c;其他进程就绝对碰不得。其实…

作者头像 李华