修正版已转移到 Qt中文文档

目录

前言

该文章翻译的官方文档 原文: https://doc.qt.io/qt-5/modelview.html

Model/View 说明

  每个UI开发人员都应该了解Mode/View编程,本教程将对Model/View进行全面的介绍。
  表(Table)、列表(List)和树(Tree)控件是gui中经常使用的控件。这些控件访问数据的方式有两种。比较传统的方法是控件包含一个用于存储数据的内部容器。这种方法非常直观,但是,在许多重要的应用程序中,它会导致数据同步问题。第二种方法是Model/View编程,其中控件不维护内部数据容器。它们通过标准化接口访问外部数据,因此避免了数据重复。乍一看,这似乎很复杂,但是一旦您仔细观察,就会发现它不仅容易掌握,而且Mode/View编程的许多好处也变得更加清晰。

在这个过程中,我们将了解到Qt提供的一些基本技术,例如:

  • 标准控件与Mode/View控件的区别
  • 窗体和模型之间的适配器
  • 开发一个简单的Mode/View的应用程序
  • 预定义模型
  • 中级主题比如:
    • 树形视图(Tree views)
    • 选项 (Selection)
    • 代理(Delegates)
    • 模型试验调试(Debugging with model test)

   您还将了解到新的应用程序是否可以通过Mode/View编程更容易地编写,或者经典的标准控件是否也可以工作。本教程包括用于编辑和集成到项目中的示例代码。教程的源代码位于qt的examples/widgets/tutorial s/modelview目录中。
更多详细信息,参照:

1.介绍

   Mode/View是一种用于在处理数据集的控件中分离数据和视图的技术。标准控件不是为将数据与视图分离而设计的,这就是为什么Qt有两种不同类型的控件。两种类型的控件看起来都一样,但它们与数据的交互方式不同。

标准控件使用的数据是作为控件一部分的
视图(View)类操作外部数据(模型)

1.1 标准控件

  让我们更仔细地看一看标准的表格控件。表格控件是用户可以更改的数据元素的二维数组。通过读写表格控件提供的数据元素,表格控件可以集成到程序流中。这种方法在许多应用程序中非常直观和有用,但是使用标准表格控件显示和编辑数据库表可能会有问题。必须协调两个数据副本:一个在控件外部;一个在控件内部。开发人员负责同步两个版本。除此之外,表示和数据的紧密耦合使得编写单元测试更加困难。

1.2 使用Model/View

  Mode/View提供了一个使用更通用的体系结构的解决方案。Mode/View消除了标准控件可能出现的数据一致性问题。Mode/View还可以更容易地使用同一数据的多个视图,因为一个模型可以传递给多个视图。最重要的区别是Mode/View控件不在表单元格后面存储数据。实际上,它们直接从您的数据操作。因为视图类不知道数据的结构,所以需要提供一个包装器,使数据符合QabstracteModel接口。视图使用此接口读取和写入数据。实现QabstratemModel的类的任何实例都称为模型。一旦视图接收到指向模型的指针,它将读取和显示其内容,并成为其编辑器。

1.3 Model/View 控件概览

下面是Model/View 控件及其相应标准控件。

控件 标准控件 Model/View 视图类
QListWidget QListView
QTableWidget QTableView
QTreeWidget QTreeView
QColumnView shows a tree as a hierarchy of lists
QComboBox can work as both a view class and also as a traditional widget

1.4 在表和模型之间使用适配器

  在窗体和模型之间使用适配器非常方便。我们可以直接从表内部编辑存储在表中的数据,但是在文本字段中编辑数据更为方便。没有直接的Model/View能够在对控件的一个数据而不是数据集操作时将数据和视图分离开。比如QLineEdit,QCheckBox…因此我们需要一个适配器来将表单连接到数据源。
  QdataWidgetMapper是一个很好的解决方案,因为它将表单控件映射到表行,并使为数据库表构建表单变得非常容易。

  另一个是QCompleter。 Qt中的QCompleter能够自动完成适配,例如QComboBox,如下所示,QLineEdit。 QCompleter使用模型作为其数据源。

2. 一个简单的 Model/View 应用程序

  如果要开发模Model/View 应用程序,应该从哪里开始? 我们建议从一个简单的示例开始并逐步扩展它。 这使得理解架构变得更加容易。 在调用IDE之前,尝试详细了解Model/View 体系结构已被证明对许多开发人员来说不太方便。 从具有演示数据的简单Model/View 应用程序开始,实质上更容易。 试试看! 只需将您自己的数据替换为以下示例中的数据即可。
下面是7个非常简单和独立的应用程序,它们显示了Model/View 编程的不同方面。 源代码可以在examples / widgets / tutorials / modelview目录中找到。

2.1 只读表

我们从使用QTableView显示数据的应用程序开始。 我们稍后会添加编辑功能。
(文件来源:examples / widgets / tutorials / modelview / 1_readonly / main.cpp)

  // main.cpp#include <QtWidgets/QApplication>#include <QtWidgets/QTableView>#include "mymodel.h"int main(int argc, char *argv[]){QApplication a(argc, argv);QTableView tableView;MyModel myModel(0);tableView.setModel( &myModel );tableView.show();return a.exec();}

main()函数:
这是有趣的部分:我们创建一个MyModel实例并使用tableView.setModel(&myModel); 将它的指针传递给tableView。 tableView将调用它收到的指针的方法来找出两件事:

  • 应该显示多少行和列
  • 每个单元格中应该显示什么内容

该模型需要一些代码来响应这一点。
我们有一个表数据集,所以让我们从QAbstractTableModel开始,因为它比更通用的QAbstractItemModel更容易使用。
(文件来源:examples / widgets / tutorials / modelview / 1_readonly / mymodel.h)

  // mymodel.h#include <QAbstractTableModel>class MyModel : public QAbstractTableModel{Q_OBJECTpublic:MyModel(QObject *parent);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;};

QAbstractTableModel需要实现三个抽象方法。
(文件来源:examples / widgets / tutorials / modelview / 1_readonly / mymodel.cpp)

  // 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()。 使用参数索引指定行和列信息,并将角色设置为Qt :: DisplayRole。 其他角色将在下一节中介绍。 在我们的示例中,生成了应显示的数据。 在实际的应用程序中,MyModel将有一个名为MyData的成员,它作为所有读写操作的目标。
  这个小例子展示了模型的被动性。 该模型不知道何时使用或需要哪些数据。 它只是在每次视图请求时提供数据。
当模型的数据需要更改时会发生什么? 视图如何实现数据已更改并需要再次读取? 该模型必须发出一个信号,指示哪些单元格范围已经改变。 这将在2.3节中说明。

2.2 使用角色扩展只读表格示例

除了控制视图显示的文本外,模型还控制文本的外观。 当我们稍微改变模型时,我们得到以下结果:

  事实上,除了data()方法外,不需要更改任何内容来设置字体、背景颜色、对齐方式和复选框。下面是产生上面所示结果的data()方法。不同的是,这次我们使用参数int role根据其值返回不同的信息片段。 (文件来源:examples / widgets / tutorials / modelview / 2_formatting / mymodel.cpp)

// mymodel.cppQVariant MyModel::data(const QModelIndex &index, int role) const{int row = index.row();int col = index.column();// generate a log message when this method gets calledqDebug() << QString("row %1, col%2, role %3").arg(row).arg(col).arg(role);switch(role){case Qt::DisplayRole:if (row == 0 && col == 1) return QString("<--left");if (row == 1 && col == 1) return QString("right-->");return QString("Row%1, Column%2").arg(row + 1).arg(col +1);break;case Qt::FontRole:if (row == 0 && col == 0) //change font only for cell(0,0){QFont boldFont;boldFont.setBold(true);return boldFont;}break;case Qt::BackgroundRole:if (row == 1 && col == 2)  //change background only for cell(1,2){QBrush redBackground(Qt::red);return redBackground;}break;case Qt::TextAlignmentRole:if (row == 1 && col == 1) //change text alignment only for cell(1,1){return Qt::AlignRight + Qt::AlignVCenter;}break;case Qt::CheckStateRole:if (row == 1 && col == 0) //add a checkbox to cell(1,0){return Qt::Checked;}}return QVariant();}

每个格式化属性都将通过对data()方法的单独调用从模型中请求。角色参数用于让模型知道正在请求哪个属性:

enum Qt::ItemDataRole Meaning Type
Qt::DisplayRole 文本 Qt::DisplayRole
Qt::FontRole 字体 QFont
BackgroundRole 用于单元格背景的画笔 用于单元格背景的画笔
Qt::TextAlignmentRole 文本对齐方式 enum Qt::AlignmentFlag
Qt::CheckStateRole 使用qvariant()取消复选框,或者使用qt::checked和Qt::Unchecked enum Qt::ItemDataRole

请参阅qt命名空间文档以了解有关qt::itemDataRole枚举功能的更多信息。
  现在,我们需要确定使用分离的模型如何影响应用程序的性能,所以让我们跟踪视图调用data()方法的频率。为了跟踪视图调用模型的频率,我们在data()方法中放置了一个debug语句,该语句将记录到错误输出流中。在我们的小示例中,data()将被调用42次。每次将光标悬停在字段上时,都将再次调用data(),每个单元格调用7次。这就是为什么在调用data()并缓存昂贵的查找操作时,确保数据可用非常重要。

2.3 表单元内嵌入时钟

我们仍然有一个只读表,但这次内容会每秒更改一次,因为我们显示的是当前时间。 (文件来源:examples/widgets/tutorials/modelview/3 ngmodel/mymodel.cpp)

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

没有什么东西可以让时钟滴答作响。我们需要每秒钟告诉视图时间已经改变,需要重新阅读。我们用计时器来做这个。在构造函数中,我们将其间隔设置为1秒,并连接其超时信号。
(文件来源:examples/widgets/tutorials/modelview/3 ngmodel/mymodel.cpp)

  MyModel::MyModel(QObject *parent):QAbstractTableModel(parent){//    selectedCell = 0;timer = new QTimer(this);timer->setInterval(1000);connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit()));timer->start();}

响应槽函数:
(文件来源: examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)

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

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

2.4 设置列和行的标题

可以通过该方法隐藏标题:tableView->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){if (orientation == Qt::Horizontal) {switch (section){case 0:return QString("first");case 1:return QString("second");case 2:return QString("third");}}}return QVariant();}

请注意,方法headerdata()还有一个参数角色,其含义与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_OBJECTpublic:MyModel(QObject *parent);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 QTableViewsignals:void editCompleted(const QString &);};

  我们使用二维数组qstring m_griddata来存储数据。这使得m_griddata成为mymodel的核心。MyModel的其余部分就像一个包装器,将m_GridData适配到QabstratemModel接口。我们还引入了editCompleted()信号,这使得将修改后的文本传输到窗口标题成为可能。
(文件来源:examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)

  bool MyModel::setData(const QModelIndex & index, const QVariant & value, int role){if (role == Qt::EditRole){//save value from editor to member m_gridDatam_gridData[index.row()][index.column()] = value.toString();//for presentation purposes only: build and emit a joined stringQString 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;}

  每次用户编辑单元格时都会调用setdata()。index参数告诉我们哪个字段已被编辑,value提供编辑过程的结果。角色将始终设置为qt::editRole,因为我们的单元格只包含文本。如果存在复选框,并且用户权限设置为允许选中该复选框,则还将使用设置为qt::checkStateRole的角色进行调用。
(文件来源:示例/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()信号,以便读取已更改的数据。

3. 中级主题

3.1 TreeView

  您可以将上面的示例转换为具有树视图的应用程序。只需将QTableView替换为QTreeView,这将导致读/写树。不必更改模型。树不会有任何层次结构,因为模型本身没有任何层次结构。

QListView, QTableView和QTreeView都使用le 一个包含列表、表和树的抽象模型。这使得可以使用同一模型中的几种不同类型的视图类。

到目前为止,我们的示例模型是这样的:

我们要展示一棵真正的树。为了建立一个模型,我们将数据包装在上面的示例中。这一次我们使用QStandardItemModel,它是一个层次数据容器,也实现了QAbstractItemModel。若要显示树,QStandardItemModel必须填充 QStandardItems,它能够保存文本、字体、复选框或画笔等项的所有标准属性。

(文件来源:examples/widgets/tutorials/modelview/6_treeview/mainwindow.cpp)

// modelview.cpp#include <QTreeView>#include <QStandardItemModel>#include <QStandardItem>#include "mainwindow.h"const int ROWS = 2;const int COLUMNS = 3;MainWindow::MainWindow(QWidget *parent): QMainWindow(parent){treeView = new QTreeView(this);setCentralWidget(treeView);standardModel = new QStandardItemModel ;QList<QStandardItem *> preparedRow =prepareRow("first", "second", "third");QStandardItem *item = standardModel->invisibleRootItem();// adding a row to the invisible root item produces a root elementitem->appendRow(preparedRow);QList<QStandardItem *> secondRow =prepareRow("111", "222", "333");// adding a row to an item starts a subtreepreparedRow.first()->appendRow(secondRow);treeView->setModel(standardModel);treeView->expandAll();}QList<QStandardItem *> MainWindow::prepareRow(const QString &first,const QString &second,const QString &third){QList<QStandardItem *> rowItems;rowItems << new QStandardItem(first);rowItems << new QStandardItem(second);rowItems << new QStandardItem(third);return rowItems;}

我们只是实例化一个QStandardItemModel并将几个QStandardItems添加到构造函数中。 然后我们可以创建分层数据结构,因为QStandardItem可以容纳其他QStandardItem。 节点在视图中折叠和展开。

3.2 使用所选内容

我们想要访问所选项目的内容,以便将其与层次结构级别一起输出到窗口标题中。

所以让我们创建几个项目:
(文件来源:examples / widgets / tutorials / modelview / 7_selections / mainwindow.cpp)

  #include <QTreeView>#include <QStandardItemModel>#include <QItemSelectionModel>#include "mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent){treeView = new QTreeView(this);setCentralWidget(treeView);standardModel = new QStandardItemModel ;QStandardItem *rootNode = standardModel->invisibleRootItem();//defining a couple of itemsQStandardItem *americaItem = new QStandardItem("America");QStandardItem *mexicoItem =  new QStandardItem("Canada");QStandardItem *usaItem =     new QStandardItem("USA");QStandardItem *bostonItem =  new QStandardItem("Boston");QStandardItem *europeItem =  new QStandardItem("Europe");QStandardItem *italyItem =   new QStandardItem("Italy");QStandardItem *romeItem =    new QStandardItem("Rome");QStandardItem *veronaItem =  new QStandardItem("Verona");//building up the hierarchyrootNode->    appendRow(americaItem);rootNode->    appendRow(europeItem);americaItem-> appendRow(mexicoItem);americaItem-> appendRow(usaItem);usaItem->     appendRow(bostonItem);europeItem->  appendRow(italyItem);italyItem->   appendRow(romeItem);italyItem->   appendRow(veronaItem);//register the modeltreeView->setModel(standardModel);treeView->expandAll();//selection changes shall trigger a slotQItemSelectionModel *selectionModel= treeView->selectionModel();connect(selectionModel, SIGNAL(selectionChanged (const QItemSelection &, const QItemSelection &)),this, SLOT(selectionChangedSlot(const QItemSelection &, const QItemSelection &)));}

视图管理单独选择模型中的选项,可以使用selectionModel()方法检索。 我们检索选择模型,以便将槽函数连接到其selectionChanged()信号。
(文件来源:examples / widgets / tutorials / modelview / 7_selections / mainwindow.cpp)

  void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/){//get the text of the selected itemconst QModelIndex index = treeView->selectionModel()->currentIndex();QString selectedText = index.data(Qt::DisplayRole).toString();//find out the hierarchy level of the selected itemint hierarchyLevel=1;QModelIndex seekRoot = index;while(seekRoot.parent() != QModelIndex()){seekRoot = seekRoot.parent();hierarchyLevel++;}QString showString = QString("%1, Level %2").arg(selectedText).arg(hierarchyLevel);setWindowTitle(showString);}

  我们通过调用treeView->selectionModel()->currentIndex()获得与选择相对应的模型索引,并通过使用模型索引获得字段的字符串。然后我们只计算项目的层次级别。顶级项没有父级,parent()方法将返回默认构造的QModelIndex()。这就是为什么我们使用parent()方法迭代到顶层,同时计算迭代期间执行的步骤。
  可以检索选择模型(如上所示),但也可以使用QAbstractItemView::setSelectionModel进行设置。因为只使用了一个选择模型的一个实例,所以有3个视图类可以进行同步选择。要在3个视图之间共享选择模型,请使用SelectionModel(),并使用SetSelectionModel()将结果分配给第二个和第三个视图类。

3.3 预定义的模型

使用Model/View的典型方法是包装特定数据,使其可用于视图类。然而,Qt还为常见的底层数据结构提供了预定义的模型。如果可用的数据结构之一适合您的应用程序,那么预定义的模型是一个不错的选择。

QStringListModel 存储字符串列表
QStandardItemModel 存储任意层次项目
QFileSystemModel、QDirModel 封装本地文件系统
QSqlQueryModel 封装SQL结果集
QSqlTableModel 封装SQL表
QSqlRelationalTableModel 封装带外键的SQL表
QSortFilterProxyModel 排序和/或筛选其他模型

3.4 代理

  在目前为止的所有示例中,数据在单元格中显示为文本或复选框,并作为文本或复选框进行编辑。 提供这些演示和编辑服务的组件称为委托。 我们才刚刚开始使用委托,因为视图使用默认委托。 但是想象一下,我们想要一个不同的编辑器(例如,滑块或下拉列表)或想象我们想要将数据呈现为图形。 让我们来看一个名为Star Delegate的例子,其中星星用于显示评级:

视图有一个setItemDelegate()方法,该方法替换默认委托并安装自定义委托。可以通过创建继承自QStyledItemDelegate的类来编写新委托。为了编写一个显示星型并且没有输入功能的委托,我们只需要重写2个方法。

  class StarDelegate : public QStyledItemDelegate{Q_OBJECTpublic:StarDelegate(QWidget *parent = 0);void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const;QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const;};

  paint() 根据基础数据的内容绘制星号。可以通过调用index.data()来查找数据。委托的sizeHint()方法用于获取每个星的尺寸,因此单元将提供足够的高度和宽度来容纳星。
  如果要在视图类的网格中使用自定义图形表示来显示数据,则编写自定义委托是正确的选择。如果您想离开网格,您将不会使用自定义委托,而是使用自定义视图类。
其他参考:

  • Spin Box Delegate Example
  • QAbstractItemDelegate Class Reference
  • QSqlRelationalDelegate Class Reference
  • QStyledItemDelegate Class Reference
  • QItemDelegate Class Reference

3.5 使用ModelTest进行调试

  模型的被动性为程序员提供了新的挑战。模型中的不一致可能导致应用程序崩溃。由于模型受到视图中无数调用的影响,很难找出哪个调用使应用程序崩溃,以及哪个操作引入了问题。
  QtLabs提供了一个名为ModelTest的软件,它在您的编程运行时检查模型。每次模型更改时,ModelTest都会扫描模型并使用断言报告错误。这对于树模型尤其重要,因为它们的层次结构特性会留下许多细微不一致的可能性。
  与视图类不同,ModelTest使用超出范围的索引来测试模型。这意味着您的应用程序可能会与ModelTest一起崩溃,即使没有它也能完美运行。因此,在使用ModelTest时,还需要处理超出范围的所有索引。

Qt Model/View教程相关推荐

  1. QT Model/View 编程:MVC模型视图编程:实例实现(二)

    目录 样例001:现有模型中使用视图Using views with an existing model 样例002:使用模型索引 样例003:使用模型 样例004:使用模型的多个视图 样例005:委 ...

  2. (一) Qt Model/View 的简单说明

    目录: (一) Qt Model/View 的简单说明 .预定义模型 (二)使用预定义模型 QstringListModel例子 (三)使用预定义模型QDirModel的例子 (四)Qt实现自定义模型 ...

  3. Qt Model/View 学习(4) - 实现自己的QAbstractTableModel类(支持显示与修改)

    目录 0. 前言 1. Data设计 2. Model类设计 2.1 数据显示与对齐.字体修改 2.2 数据修改 3. 小结 0. 前言 可算到了这一篇了! 上一篇文章中把Qt::ItemDataRo ...

  4. Qt Model/View(MVD)模型分析

           最近在看Qt的Model/View Framework,在网上搜了搜,好像中文的除了几篇翻译没有什么有价值的文章.E文的除了Qt的官方介绍,其它文章也很少.看到一个老外在blog中写道M ...

  5. Model/View 教程

     说明:这篇博客基本都是翻译于Qt官方的Model/View Tutorial教程,无法理解的地方建议转到原文,同时,由于译者水平有限,如有差错欢迎指出. 原文:http://qt-project ...

  6. Qt Model/View 学习笔记 (四)

     创建新的Models 介绍 model/view组件之间功能的分离,允许创建model利用现成的views.这也可以使用标准的功能 图形用户接口组件像QListView,QTableView和Q ...

  7. Qt Model/View编程介绍

    Qt中包含了一系列的项视图类,它们使用model/view的架构去管理数据之间的关系以及它们被展示给用户的方式.这种由这种架构引进的功能分离特性给了开发者很大的灵活性去自定义自己的展示方式,并且提供了 ...

  8. Qt Model/View/Delegate浅谈 - QAbstractListModel

    为什么80%的码农都做不了架构师?>>>    待补充... ##子类化 当子类化QAbstractListModel时,必须提供rowCount()和data()这2个函数的实现, ...

  9. 【QT Model/View】QTableView中使用委托实现表格中插入箭头

    一.应用场景 在QTableView表格中,右键插入一行数据,需要在表格上标记待插入的位置,插入完成后标记消除 二.源码实现 箭头代理继承QItemDelegate,重写paint事件,画出箭头形状 ...

最新文章

  1. 易扩展的SLAM框架-OpenVSLAM
  2. jsp页面textarea中换行替换问题
  3. 前端,校招,面淘宝,指南
  4. 为JavaOne 2014做好准备!
  5. 【原创】如何写一个框架:步骤(下)
  6. java中关于时间的格式化
  7. 通过千千静听歌词服务器下载歌词(Delphi版)
  8. SQL 查看SQL语句的执行时间 直接有效的方法
  9. 一些常见监控服务如Nagios、Cacti和Zabbix的搭建
  10. 论文-公式对齐和右侧自动编号
  11. 知识图谱(Knowledge Graph, KG)(王昊奋老师-课程学习笔记)
  12. Tedddby Activator V5.1,免费绕过iOS 14.7Beta,支持iCloud登录
  13. 2020-4-20 深度学习笔记20 - 深度生成模型 3 (实值数据上的玻尔兹曼机)
  14. java 必须try catch的异常_【java基础之异常】死了都要try,不淋漓尽致地catch我不痛快!...
  15. Xinetd服务的安装与配置
  16. C/C++ fstream
  17. 论文排版中的页眉页脚设置
  18. WPARAM与LPARAM 之区别
  19. Google gflags使用说明
  20. 如何下载百度云文档需要用劵下载的内容

热门文章

  1. 海报打印我用OfficeExcel就搞定(转)
  2. 怎么还原计算机系统还原,电脑如何一键还原,小编教你电脑怎么还原系统
  3. 产品经理做什么事情?
  4. Error running 'Unnamed': Unable to open debugger port (127.0.0.1:8749)
  5. 8749病毒详细分析报告 [转]
  6. 卡西欧手表显示html,卡西欧手表(MTP-1246D)肿么调整日期、星期以及24小时显示?
  7. Java 实用程序包
  8. HashMap和Hashtable的区别
  9. keil5打开工程报错:error:not found device
  10. asp.net 页面背景音乐