Qt MV架构-视图类

一、基本概念

在MV架构中,视图包含了模型中的数据项,并将它们呈现给用户。数据项的表示方法,可能和数据项在存储时用的数据结构完全不同。

这种内容与表现分离之所以能够实现,是因为使用了

  1. QAbstractItemModel提供的一个标准模型接口;
  2. 一个标准视图接口;
  3. 模型索引所提供的一种通用的方法;

来表示数据。

视图通常管理从模型获取数据的整体布局。视图可以自己渲染独立的数据项,也可以使用委托来处理渲染和编辑。

1. 项目导航和选择行为

除了呈现数据,视图还处理项目间的导航,以及项目选择的某些方面。

表1和表2分别罗列了视图中的选择行为(QAbstractItemView::SelectionBehaviour)和选择模式(QAbstractItemView::SelectionMode

表1 视图类的选择行为(QAbstractItemView::SelectionBehaviour)

常量 描述
QAbstractItemView::SelectItems 选择单个项目
QAbstractItemView::SelectRows 只选择行
QAbstractItemView::SelectColumns 只选择列

表2 视图类的选择模式

常量 描述
QAbstractItemView::SingleSelection 当用户选择一个项目, 索所有已选择的项目将成为未选择态,而且用户无法在已经选择的项目上单击来取消选择。
QAbstractItemView::ContiguousSelection 如果用户在单击一个项目的同时按着Shift键,所有在当前和单击项目之间的项目都将被选择或者取消选择,这依赖于被单击项目的状态。
QAbstractItemView::ExtendedSelection 具有ConiguousSelection的特性,而且还可以按着Ctrl键进行不连续的选择。
QAbstractItemView::MultiSelection 用户选择一个项目时,不影响其他已经选择的项目。
QAbstractItemView::NoSelection 项目无法被选择。

对于一些视图,例如QTreeViewQTreeView,在显示项目的同时还可以显示表头。这是通过QHeaderView类来实现的,它们使用QAbstractItemModel::headerData()从模型中获取数据,然后一般使用一个标签来显示表头信息。可以通过子类化QHeaderView来设置标签的显示。

Qt中已经提供了QListView,QTableViewQTreeView这三个现成的视图,不过都是使用规范的格式显示数据。

如果想要实现条形图、饼状图等特殊显示方式,需要重新实现视图。

二、项目选择

MV架构对项目的选择提供了非常方便的处理方法。

视图中被选择的项目的信息,存储在一个QItemSelectionModel实例中,这样被选择的项目模型索引便保持在一个独立的模型中,与所有的视图都是独立的。

当在一个模型上设置多个视图时,就可以实现在多个视图之间共享选择

选择由选择范围指定,只需要记录每一个选择范围开始和结束的模型索引即可,非连续的选择可以使用多个选择范围来描述。

选择可以看作是在选择模型中保存的一个模型索引集合,最近的项目选择被称为当前选择。

1. 当前项目、被选择项目

视图中总是有一个当前项目和一个被选择的项目,两者是独立的状态。

在同一时间,一个项目可以既是当前项目,同时也是被选择项目。视图负责确保总是有一个项目作为当前项目来实现键盘导航。

表3 当前项目和被选择的项目的区别

当前项目 被选择的项目
只能有一个当前项目 被选择的项目
使用键盘导航键或者鼠标按键可以改变当前项目 项目是否处于被选择状态,取决于几个预先定义好的模式,例如单项选择、多重选择等。
如果按下F2键或者双击都可以编辑当前项目 当前项目可以通过指定一个范围来一起被使用
当前项目会显示焦点矩形 被选择的项目会使用选择矩形来表示

当操作选择时,可以将QItemnSelectionModel看作一个项目模型中所有项目的选择状态的一个记录。

一旦设置了一个选择模型,所有的项目集合都可以被选择、取消选择或者切换选择状态,而不需要知道哪一个项目已经被选择了。所有被选择项目的索引都可以被随时进行检索,其他的组件也可以通过信号和槽机制来获取选择模型的改变信息。

2. 选择模型

标准的视图类中提供了默认的选择模型,可以在大多数的应用中直接使用。

属于一个视图的选择模型可以使用这个视图的selectionModel()函数获得,而且还可以在多个视图之间使用setSelectionModel()函数来共享该选择模型,所以一般是不需要重新构建一个选择模型的。

三、代码实例

实现两个视图共享数据模型和选择模型。

MainWindow.h

cpp 复制代码
#pragma once

#include <QMainWindow>

class QTableView;
class QItemSelection;
class QModelIndex;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;
public slots:
	void getCurrentItemData();
	void toggleSelection();
	void updateSelection(const QItemSelection &selected, const QItemSelection &deselected);
	void changeCurrent(const QModelIndex &current, const QModelIndex &previous);
private:
    Ui::MainWindow *ui;
	QTableView* tableView;
	QTableView* tableView2;
};

MainWindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_MainWindow.h"
#include <QStandardItemModel>
#include <QTableView>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    auto model = new QStandardItemModel(7, 4, this);
    for (int row = 0; row < 7; ++row) {
	    for (int column = 0; column < 4; ++column) {
		    auto item = new QStandardItem(QString("%1").arg(row * 4 + column));
		    model->setItem(row, column, item);
	    }
    }

    tableView = new QTableView;
    tableView->setModel(model);
    setCentralWidget(tableView);

	QItemSelectionModel* selectionModel = tableView->selectionModel();

	QModelIndex topLeft;
	QModelIndex bottomRight;
	topLeft = model->index(1,1,QModelIndex());
	bottomRight = model->index(5,2,QModelIndex());
	QItemSelection selection(topLeft, bottomRight);
	selectionModel->select(selection, QItemSelectionModel::Toggle);

    ui->menubar->addAction(tr("当前项目"), this, &MainWindow::getCurrentItemData);
    ui->menubar->addAction(tr("切换选择"), this, &MainWindow::toggleSelection);

	connect(selectionModel, &QItemSelectionModel::selectionChanged,this, &MainWindow::updateSelection);
	connect(selectionModel, &QItemSelectionModel::currentChanged,this,&MainWindow::changeCurrent);
    {
	    tableView2 = new QTableView;
    	tableView2->setWindowTitle("tableView2");
    	tableView2->resize(400,300);
    	tableView2->setModel(tableView->model());
    	tableView2->setSelectionModel(tableView->selectionModel());
    	tableView2->show();
    }
}

MainWindow::~MainWindow() {
    delete ui;
	delete tableView2;
}

void
MainWindow::getCurrentItemData()
{
	qDebug() << tr("当前项目内容")
	<< tableView->selectionModel()->currentIndex().data().toString();
}

void
MainWindow::toggleSelection()
{
	QModelIndex topLeft     = tableView->model()->index(0,0,QModelIndex());
	QModelIndex bottomRight = tableView->model()->index(
		tableView->model()->rowCount(QModelIndex()) - 1,
		tableView->model()->columnCount(QModelIndex()) - 1,
		QModelIndex());
	QItemSelection curSelection(topLeft, bottomRight);
	tableView->selectionModel()->select(curSelection, QItemSelectionModel::Toggle);
}

void
MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected)
{
	QModelIndex index;
	QModelIndexList list = selected.indexes();
	foreach(index, list)
	{
		QString text = QString("%1,%2").arg(index.row()).arg(index.column());
		tableView->model()->setData(index, text);
	}
	list = deselected.indexes();

	foreach(index, list)
	{
		tableView->model()->setData(index, "");
	}
}

void
MainWindow::changeCurrent(const QModelIndex &current, const QModelIndex &previous)
{
	qDebug() << tr("move(%1,%2) to (%3,%4)").
	arg(previous.row()).arg(previous.column()).
	arg(current.row()).arg(current.column());
}

参考资料: Qt Creator快速入门第2版 (霍亚飞 著)

相关推荐
Qter_Sean4 分钟前
自己动手写Qt Creator插件
开发语言·qt
何曾参静谧8 分钟前
「QT」文件类 之 QIODevice 输入输出设备类
开发语言·qt
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
小白学大数据3 小时前
Python爬虫开发中的分析与方案制定
开发语言·c++·爬虫·python
冰芒猓4 小时前
SpringMVC数据校验、数据格式化处理、国际化设置
开发语言·maven
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
杜杜的man5 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang
java小吕布5 小时前
Java中Properties的使用详解
java·开发语言·后端
versatile_zpc6 小时前
C++初阶:类和对象(上)
开发语言·c++