一、基本概念
在MV架構中,視圖包含了模型中的數據項,并將它們呈現給用戶。數據項的表示方法,可能和數據項在存儲時用的數據結構完全不同。
這種內容與表現分離之所以能夠實現,是因為使用了
QAbstractItemModel
提供的一個標準模型接口;- 一個標準視圖接口;
- 模型索引所提供的一種通用的方法;
來表示數據。
視圖通常管理從模型獲取數據的整體布局。視圖可以自己渲染獨立的數據項,也可以使用委托來處理渲染和編輯。
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 | 項目無法被選擇。 |
對于一些視圖,例如QTreeView
和QTreeView
,在顯示項目的同時還可以顯示表頭。這是通過QHeaderView
類來實現的,它們使用QAbstractItemModel::headerData()
從模型中獲取數據,然后一般使用一個標簽來顯示表頭信息。可以通過子類化QHeaderView
來設置標簽的顯示。
Qt中已經提供了QListView
,QTableView
和QTreeView
這三個現成的視圖,不過都是使用規范的格式顯示數據。
如果想要實現條形圖、餅狀圖等特殊顯示方式,需要重新實現視圖。
二、項目選擇
MV架構對項目的選擇提供了非常方便的處理方法。
視圖中被選擇的項目的信息,存儲在一個QItemSelectionModel
實例中,這樣被選擇的項目模型索引便保持在一個獨立的模型中,與所有的視圖都是獨立的。
當在一個模型上設置多個視圖時,就可以實現在多個視圖之間共享選擇。
選擇由選擇范圍指定,只需要記錄每一個選擇范圍開始和結束的模型索引即可,非連續的選擇可以使用多個選擇范圍來描述。
選擇可以看作是在選擇模型中保存的一個模型索引集合,最近的項目選擇被稱為當前選擇。
1. 當前項目、被選擇項目
視圖中總是有一個當前項目和一個被選擇的項目,兩者是獨立的狀態。
在同一時間,一個項目可以既是當前項目,同時也是被選擇項目。視圖負責確保總是有一個項目作為當前項目來實現鍵盤導航。
表3 當前項目和被選擇的項目的區別
當前項目 | 被選擇的項目 |
---|---|
只能有一個當前項目 | 被選擇的項目 |
使用鍵盤導航鍵或者鼠標按鍵可以改變當前項目 | 項目是否處于被選擇狀態,取決于幾個預先定義好的模式,例如單項選擇、多重選擇等。 |
如果按下F2鍵或者雙擊都可以編輯當前項目 | 當前項目可以通過指定一個范圍來一起被使用 |
當前項目會顯示焦點矩形 | 被選擇的項目會使用選擇矩形來表示 |
當操作選擇時,可以將QItemnSelectionModel
看作一個項目模型中所有項目的選擇狀態的一個記錄。
一旦設置了一個選擇模型,所有的項目集合都可以被選擇、取消選擇或者切換選擇狀態,而不需要知道哪一個項目已經被選擇了。所有被選擇項目的索引都可以被隨時進行檢索,其他的組件也可以通過信號和槽機制來獲取選擇模型的改變信息。
2. 選擇模型
標準的視圖類中提供了默認的選擇模型,可以在大多數的應用中直接使用。
屬于一個視圖的選擇模型可以使用這個視圖的selectionModel()
函數獲得,而且還可以在多個視圖之間使用setSelectionModel()
函數來共享該選擇模型,所以一般是不需要重新構建一個選擇模型的。
三、代碼實例
實現兩個視圖共享數據模型和選擇模型。
MainWindow.h
#pragma once#include <QMainWindow>class QTableView;
class QItemSelection;
class QModelIndex;QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow {
Q_OBJECTpublic: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 ¤t, const QModelIndex &previous);
private:Ui::MainWindow *ui;QTableView* tableView;QTableView* tableView2;
};
MainWindow.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 ¤t, 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版 (霍亞飛 著)