Qt Model/View教程——只读Table

本文深入探讨Qt的Model/View编程模式,对比标准部件与Model/View部件,阐述Model/View如何解决数据同步问题,通过示例展示如何构建简单的Model/View应用程序。

点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,可以点个在看,让它可以帮助到更多老铁~

一直想学习Qt Model/View,最终还是看的官方教程,现在将官方教程重新在梳理下。

 

每个UI开发人员都应该了解Model/View编程!可见Model/View在UI编程中的重要性!

那它为什么这么重要呢?

Table,、List和Tree widgets是GUI中经常使用的组件。 这些小部件可以通过两种不同的方式访问其数据。 传统方式部件使用内部容器进行存储数据。,这种方法非常直观,但是,在许多特别的应用程序中,它会导致数据同步问题。 第二种方法是模型/视图编程,其中小部件不维护内部数据容器。 他们通过标准化接口访问外部数据,因此避免了数据重复。 乍一看,这似乎很复杂,但是一旦仔细研究,不仅容易掌握,而且模型/视图编程的许多好处也变得更加清晰。

 

整个教程的目录如下:

 

标准部件和模型/视图部件之间的区别

表单和模型之间的适配器

开发一个简单的模型/视图应用程序

预定义模型

中级主题:

Tree views

Selection

Delegates

Debugging with model test

 

一、 概述

模型/视图是一种用于将数据与处理数据集的小部件中的视图分离的技术。 标准窗口小部件并非旨在将数据与视图分离,这就是为什么Qt具有两种不同类型的窗口小部件的原因。 两种类型的小部件外观相同,但是它们与数据的交互方式不同。

 

1. 标准部件

 Table Widget是用户可以更改的数据元素的2D部件。 可以通过读写表小部件提供的数据元素将表小部件集成到程序中。 此方法非常直观,在许多应用程序中很有用,但是使用标准表窗口部件显示和编辑数据库表可能会出现问题。 数据的两个副本必须协调一致:一个在小部件外部;另一个在小部件内部。  开发人员必须负责同步两个数据副本。 除此之外,数据的紧密耦合使编写单元测试更加困难。

 

2. Model/View

Model/View使用了更加灵活的体系结构来提供解决方案。Model/View消除了标准小部件可能发生的数据一致性问题, 而且Model/View还可以让同一数据源在多个视图上进行显示变得更加方便;因为一个Model可以传递给许多Views。 最重要的区别是Model/View部件不在表单内部存储数据。 实际上,Model/View直接对您的数据进行操作。 由于视图类不知道数据的结构,因此需要提供包装器以使数据符合QAbstractItemModel接口【译者注:这就是为什么要setMode】。 View使用该接口进行读取和写入数据,实现QAbstractItemModel的类的任何实例都称为模型【译者注:什么是Model】。 一旦View接收到指向模型的指针,它将读取并显示其内容并成为其编辑器【译者注:setModel后,View自动读取数据并显示】。

 

二、 一个简单的Model/View应用程序

如果要开发Model/View应用程序,应该从哪里开始? 我们建议从一个简单的示例开始【译者注:我表示非常赞同!】,并逐步扩展它,这使得了解架构变得容易得多。 事实证明,在调用集成好的接口前尝试详细了解Model/View体系结构对于许多开发人员来说并不方便。 从具有演示数据的简单Model/View应用程序开始要容易得多。 试试看! 只需将以下示例中的数据替换为您自己的数据即可。

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

 

1. 只读Table

我们从使用QTableView来显示数据的应用程序开始。之后我们将添加编辑功能。

只读table,效果如下:

我们创建MyModel的实例并使用tableView.setModel(&myModel), 将其指针传递给tableView ,tableView将调用它收到的指针获得以下信息:

应显示多少行和多少列

每个单元格应显示什么内容

 

Model需要一些代码来对此做出响应。我们有一个表数据集,因此让我们从QAbstractTableModel开始,因为它比更通用的QAbstractItemModel更加易于使用。【译者注:以后会更加了解这两个类的】

mymodel.h 代码:

#include <QAbstractTableModel>


class MyModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    MyModel(QObject *parent);
    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE ;
    int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
};

QAbstractTableModel需要实现三种抽象方法。

mymodel.cpp 代码:

#include "mymodel.h"


MyModel::MyModel(QObject *parent)
    :QAbstractTableModel(parent)
{
}


int MyModel::rowCount(const QModelIndex & /*parent*/) const
{
   return 2;
}


int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
    return 3;
}


QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole)
    {
       return QString("Row%1, Column%2")
                   .arg(index.row() + 1)
                   .arg(index.column() +1);
    }
    return QVariant();
}

行数和列数由MyModel :: rowCount()和MyModel :: columnCount()提供 。 当视图必须知道单元格的文本是什么时,它将调用方法MyModel :: data() 。 行和列信息由参数index指定,并且角色设置为Qt :: DisplayRole 。 下一节将介绍其他角色。 在我们的示例中,应显示的数据已生成。 在实际的应用程序中, MyModel会有一个名为MyData的成员,该成员充当所有读取和写入操作的目标。

 

这个小例子说明了模型的被动性质。 该模型不知道何时使用它或需要哪些数据。 每次视图请求时,它仅提供数据。

 

当需要更改模型数据时会发生什么? 视图如何认识到数据已更改并且需要再次读取? 该模型必须发出一个信号,该信号指示已更改了哪些单元格范围。 这将在第2.3节中演示。

 

总结:

之前由于项目需要,使用过Qt的文件系统模型,当时直接用的现成的程序,那会儿就很不明白为什么一定要setModel,设置完后又会自己显示。教程看到这之后,终于明白了。所以我比较喜欢知道为什么这么做、这么做应该会有一个什么样的结果。

 

最后祝大家元旦快乐!2020,体验全力做一件事的幸福与艰辛!

欢迎关注公众号:

Qt中,可借助自定义的CSVTabel类(继承自QTableView)来实现将CSV文件数据以model/view形式加载到QTableView中显示。将解析后的CSV文件数据存储在QStandardItem组织的表格里,每个单项都能进行编辑,并且修改会同步到文件中。不过,要留意dataChanged信号,因为一写入数据它就会被触发 [^2]。 以下是实现此功能的示例代码: ```python # 假设使用Python和PyQt import sys import csv from PyQt5.QtWidgets import QApplication, QTableView from PyQt5.QtCore import Qt from PyQt5.QtGui import QStandardItemModel, QStandardItem def load_csv_to_tableview(file_path, table_view): model = QStandardItemModel() with open(file_path, 'r', encoding='utf-8') as file: reader = csv.reader(file) for row_index, row in enumerate(reader): for col_index, cell in enumerate(row): item = QStandardItem(cell) model.setItem(row_index, col_index, item) table_view.setModel(model) if __name__ == '__main__': app = QApplication(sys.argv) table_view = QTableView() file_path = 'your_file.csv' load_csv_to_tableview(file_path, table_view) table_view.show() sys.exit(app.exec_()) ``` ### 代码解释 - 定义`load_csv_to_tableview`函数,它接收CSV文件路径和`QTableView`对象作为参数。 - 创建`QStandardItemModel`对象来存储CSV数据。 - 以只读模式打开CSV文件,使用`csv.reader`逐行读取文件内容。 - 遍历每一行和每一个单元格,创建`QStandardItem`对象并将其添加到`model`中。 - 最后将`model`设置给`QTableView`。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值