Qt 模型/视图:如何正确处理底层数据

Posted

技术标签:

【中文标题】Qt 模型/视图:如何正确处理底层数据【英文标题】:Qt Model/View: how to handle underlying data properly 【发布时间】:2015-10-13 15:08:40 【问题描述】:

我观看了大量视频,并花了很多时间阅读有关模型的论文,如何使用它们,总体思路非常清楚。但是,我仍然没有得到真正让我慢下来的几件事。

我意识到模型只能作为视图和数据之间的接口。但是,当我查看示例代码时,大多数时候,某种数据结构被发送到模型,模型中的所有函数都使用该内部模型数据结构来执行所需的事情:评估标题、行数等。这样的例子构造函数(在这种情况下内部模型 QList 是addressBook):

AddressbookModel::AddressbookModel(const QString& addresses,
  QObject *parent): QAbstractTableModel(parent)

  QStringList records = addresses.split(’\n’);
  QStringList line;
  foreach(QString record, records)
  addressBook.append(splitCSVLine(record));

这看起来不错,但是当我尝试考虑在程序中的其他地方修改底层数据时,当某种模型“附加”到该数据结构时,它会变得非常混乱。

例如,让我们看一下学习资料中的示例代码:

    // addressbook/main.cpp
#include <QtGui>
#include "addressbookmodel.h"
int main( int argc, char* argv[] )

  QApplication app( argc, argv );
  QFile file("addressbook.csv");
  if ( !file.open(QIODevice::ReadOnly|QIODevice::Text) )
    return 1;
  QString addresses = QString::fromUtf8(file.readAll());
  AddressbookModel model(addresses);
  QTableView tableView;
  tableView.setModel(&model);
  tableView.show();
  return app.exec();

这里有一个静态变量addresses,然后发送到model。现在,用户将能够查看和修改该数据。但是,如果我想在程序的其他地方更多地使用这些数据怎么办?如果我向addresses 插入新条目怎么办?我意识到模型不会看到这些变化,并且在这个示例(以及更多示例)中,底层数据结构甚至不是作为指针发送的。

所以我的问题是:如何正确管理数据,什么时候我会有来自“幕后”的新数据——不仅仅是来自模型?我应该只在模型类中使用数据管理(实现所需的功能等)吗?我应该以某种方式仅将数据指针传递给模型吗?当我想到使用代理模型进行过滤时,一切都变得更加棘手,因为它们也可以工作并且以自己的方式“处理”数据。也许我错过了有关此架构的一些重要内容,但它确实让我停了下来。

【问题讨论】:

你应该从这里开始:doc.qt.io/qt-5/model-view-programming.html 好的,我会试着看看那个页面。虽然我觉得我见过很多,但这个看起来更广泛。 【参考方案1】:

使用 Qts 数据模型可能会非常令人困惑。您将需要处理您自己的大部分“更新”。例如,如果您在 QAbstractItemModel::setData 的重载中更改模型数据,您将不得不自己发出 QAbstractItemModel::dataChanged。插入、删除或移动条目也是如此。如果您有时间,您应该阅读 SaZ 发布的链接,但有关在哪个过载时发出什么内容的一些快速信息,您可以查看QAbstractItemModel Documentation。

关于“幕后”修改数据: 最佳做法是更改模型上的数据,即调用 QAbstractItemModel::setData 更改一些数据。但由于此函数旨在以“可显示格式”获取数据,因此最好创建自己的函数。在此函数内部,您需要“通知”模型您的更改。这样所有视图都会正确更新。

例如,如果您的“AddressRecord”具有name 属性:

void AddressbookModel::changeName(QModelIndex addressIndex, QString name) 
    //For this example I assume you are using a simple list model with only one column
    //The addressIndex´s column is always 0 in this case, and the parent invalid
    addressBook[addressIndex.row()].setName(name);
    emit dataChanged(addressIndex, addressIndex);

如您所见,您将不得不以某种方式使用QModelIndex-class,它表示模型中条目的位置。

我希望我至少能提供一点帮助,但 Qts 模型视图框架可能会非常棘手,尤其是当您必须添加、删除、移动或排序数据时。不过要想更深入的了解,恐怕还是要亲自试一试!

【讨论】:

谢谢,非常感谢您的回答,但是当使用代理模型时,所有这些与情况有何关系?它们就像模型的包装器一样工作,所以当我在源模型中编写所有数据处理逻辑时,我还能使用代理模型和这种数据处理方法吗?据我记得,我为视图设置代理模型,而不是源模型。 没错!如果您正确更改源模型中的数据,代理模型将收到这些更改的通知并执行其设计的任何操作。 (例如排序,过滤,...)并通知视图它对源模型中的更改生成的更改。当然,代理模型的实现也必须是正确的。

以上是关于Qt 模型/视图:如何正确处理底层数据的主要内容,如果未能解决你的问题,请参考以下文章

在 Qt 中,如何正确实现委托?

给定填充 ro QList 的 Qt 模型视图

Qt--模式视图设计

如何彻底改变 QAbstractTableModel 的底层数据?

Qt:如何在模型/视图设置中同步对来自多个线程的数据的访问?

Qt模型/视图和QTableView的基本概念