Qt6.0開發(fā) 第五章 模型-視圖結(jié)構(gòu)
第五章 模型-視圖結(jié)構(gòu)
模型-視圖結(jié)構(gòu)概述
模型-視圖結(jié)構(gòu)是一種將數(shù)據(jù)存儲(chǔ)和界面分離的編程方法.模型存儲(chǔ)數(shù)據(jù),視圖組件顯示模型中的數(shù)據(jù),在視圖組件里修改的數(shù)據(jù)會(huì)被自動(dòng)保存到模型里.
GUI程序的主要功能是可由用戶在界面上編輯和修改數(shù)據(jù),典型的如數(shù)據(jù)庫應(yīng)用程序.在數(shù)據(jù)庫應(yīng)用程序中,界面上的數(shù)據(jù)來源于數(shù)據(jù)庫,用戶在界面上修改數(shù)據(jù),修改后的數(shù)據(jù)又保存到數(shù)據(jù)庫.
模型-視圖的基本結(jié)構(gòu)包括以下幾個(gè)部分:
- 源數(shù)據(jù)(data):原始數(shù)據(jù),如數(shù)據(jù)庫的一個(gè)數(shù)據(jù)表或SQL查詢結(jié)果等
- 視圖(view):也稱視圖組件,是界面組件,視圖從模型獲得數(shù)據(jù)然后將其顯示在界面上.
- 模型(model):也稱數(shù)據(jù)模型,與源數(shù)據(jù)通信,并為視圖組件提供數(shù)據(jù)接口.它從源數(shù)據(jù)提取需要的數(shù)據(jù),用于視圖組件進(jìn)行顯示和編輯.
- 代理(delegate):為視圖與模型之間交互操作提供臨時(shí)的編輯器.模型向視圖提供數(shù)據(jù)一般是單向的,一般僅用于顯示.當(dāng)需要在視圖上編輯數(shù)據(jù)時(shí),代理會(huì)為編輯數(shù)據(jù)提供一個(gè)編輯器.
模型
所有基于項(xiàng)(item)的模型類都是基于QAbstractItemModel類的,這個(gè)類定義了試圖組件和代理存取數(shù)據(jù)的接口.
模型只是在內(nèi)存中臨時(shí)存儲(chǔ)數(shù)據(jù),模型的數(shù)據(jù)來源可以是其他類,文件,數(shù)據(jù)庫或任何數(shù)據(jù)源.
模型類繼承關(guān)系:
- QAbstractItemModel:抽象模型類父類
- QFileSystemModel:用于表示計(jì)算機(jī)上文件系統(tǒng)的模型類
- QAbstractListModel:抽象列表類
- QStringListModel:用于表示字符串列表數(shù)據(jù)的模型類
- QStandardItemModel:標(biāo)準(zhǔn)的基于項(xiàng)的模型類,每個(gè)項(xiàng)都為QStandardItem對(duì)象
- QAbstractTableModel:抽象表類
- QSqlQueryModel:用于表示數(shù)據(jù)庫SQL查詢結(jié)果的模型類
- QSqlTableModel:用于表示數(shù)據(jù)庫的一個(gè)數(shù)據(jù)表的模型類
- QSqlQueryModel:用于表示數(shù)據(jù)庫SQL查詢結(jié)果的模型類
視圖
視圖就是用于顯示模型中的數(shù)據(jù)的界面組件,Qt提供的視圖組件主要有以下幾個(gè):
- QListView:用于顯示單列的列表結(jié)構(gòu),適用于一維數(shù)據(jù)
- QTreeView:用于顯示樹狀結(jié)構(gòu)的數(shù)據(jù),適用于樹狀結(jié)構(gòu)數(shù)據(jù)的操作
- QTableView:用于顯示表格數(shù)據(jù),適用于二維表格數(shù)據(jù)的操作
- QColumnView:用多個(gè)QListView顯示樹狀結(jié)構(gòu)數(shù)據(jù),樹狀結(jié)構(gòu)的一層用一個(gè)QListView顯示
- QUndoView:用于顯示undo指令棧內(nèi)數(shù)據(jù)的視圖組件,是QListView的子類
只需調(diào)用視圖類的setModel()函數(shù)為視圖組件設(shè)置一個(gè)模型,模型的數(shù)據(jù)就可以顯示在視圖組件上.在視圖組件上修改數(shù)據(jù)后,數(shù)據(jù)可以自動(dòng)保存到模型.
視圖組件的數(shù)據(jù)來源于模型,視圖組件不存儲(chǔ)數(shù)據(jù).便利類則為組件的每個(gè)節(jié)點(diǎn)或單元格創(chuàng)建一個(gè)項(xiàng),用項(xiàng)存儲(chǔ)數(shù)據(jù).
因此,便利類缺乏對(duì)大型數(shù)據(jù)源進(jìn)行靈活處理的能力,只適用于小型數(shù)據(jù)的顯示和編輯,而視圖組件則會(huì)根據(jù)模型的數(shù)據(jù)內(nèi)容自動(dòng)顯示.
代理
代理能夠在視圖組件上為編輯數(shù)據(jù)提供臨時(shí)的編輯器.代理負(fù)責(zé)從模型獲取相應(yīng)的數(shù)據(jù),然后將其顯示在編輯器里,修改數(shù)據(jù)又將編輯器里的數(shù)據(jù)保存到模型中.
QAbstractItemDelegate是所有代理類的基類,作為抽象類,它不能直接用于創(chuàng)建對(duì)象.它有兩個(gè)子類,即QItemDelegate和QStyledItemDelegate,這兩個(gè)類基本相同,而QStyledItemDelegate能使用Qt樣式表定義的當(dāng)前樣式繪制代理組件.
對(duì)于一些特殊的數(shù)據(jù)編輯需求,則可以從QStyledItemDelegate中繼承.
一些基本概念
在模型-視圖結(jié)構(gòu)中,模型為視圖組件和代理提供存取數(shù)據(jù)的標(biāo)準(zhǔn)接口.
QAbstractItemModel是所有模型類的基類,不管底層的數(shù)據(jù)結(jié)構(gòu)是如何組織數(shù)據(jù)的,QAbstractItemModel的子類都以表格的層次結(jié)構(gòu)展示數(shù)據(jù),視圖組件按照這種規(guī)則規(guī)則來存取模型中的數(shù)據(jù),但是展示給用戶的形式不一樣.
模型的三種常見展示形式:
- 列表模型(list model)
- 表格模型(table model)
- 樹狀模型(tree model)
不管模型的表現(xiàn)形式是怎樣的,模型中存儲(chǔ)數(shù)據(jù)的基本單元都是項(xiàng)(item),每個(gè)項(xiàng)有一個(gè)行號(hào)和一個(gè)列號(hào),還有一個(gè)父項(xiàng)(parent item).三個(gè)模型都有一個(gè)隱藏的根項(xiàng)(item).
為了確保數(shù)據(jù)的展示與數(shù)據(jù)存取方式分離,模型中引入了模型索引(model index)的概念.通過模型索引,視圖組件和代理都通過模型索引來獲取數(shù)據(jù).
QModelIndex是表示模型索引的類.模型索引提供訪問數(shù)據(jù)的臨時(shí)指針,用于通過模型提取或修改數(shù)據(jù).
因?yàn)槟P蛢?nèi)部組織數(shù)據(jù)的結(jié)構(gòu)可能隨時(shí)改變,所以模型索引是臨時(shí)的.
模型的基本形式是用行和列定義的表格數(shù)據(jù),但這并不意味著底層的數(shù)據(jù)是用二維數(shù)據(jù)存儲(chǔ)的,是用行與列只是為了組件之間交互方便.一個(gè)模型索引包含行號(hào)與列號(hào).
在創(chuàng)建模型索引的函數(shù)中需要傳遞行號(hào),列號(hào)和父項(xiàng)的模型索引.
當(dāng)模型為列表或表格結(jié)構(gòu)時(shí),是用行號(hào)、列號(hào)訪問數(shù)據(jù)比較直觀,所有項(xiàng)的父項(xiàng)就是頂層項(xiàng).
當(dāng)模型為樹狀結(jié)構(gòu)時(shí)情況比較復(fù)雜,一個(gè)節(jié)點(diǎn)有父節(jié)點(diǎn),其他可以是其他節(jié)點(diǎn)的父節(jié)點(diǎn),在構(gòu)造節(jié)點(diǎn)的模型索引時(shí),需要指定正確的行號(hào),列號(hào)和父節(jié)點(diǎn).
在為模型的一個(gè)項(xiàng)設(shè)置數(shù)據(jù)時(shí),可以為項(xiàng)設(shè)置不同的角色數(shù)據(jù).QAbstractItemModel定義了設(shè)置項(xiàng)的數(shù)據(jù)的函數(shù)setData():
- bool QAbstractItemModel:::setData(const QModelIndex& index,const
QVariant &value,int role =Qt::EditRole)
其中,index是項(xiàng)的模型索引,value是需要設(shè)置的數(shù)據(jù),role是設(shè)置數(shù)據(jù)的角色.
可以為一個(gè)項(xiàng)設(shè)置不同角色的數(shù)據(jù),角色參數(shù)role用枚舉類型Qt::ItemDataRole的枚舉類型表示.枚舉類型Qt::ItemDataRole常用的一些枚舉值及其含義如表所示,表中的橘色數(shù)據(jù)類型用于相應(yīng)數(shù)據(jù)的數(shù)據(jù)的類型:
- bool QAbstractItemModel::setData(const QModelIndex &index,const QVariant &value,int role=Qt::EditRole)
| 枚舉值 | 角色數(shù)據(jù)類型 | 含義 |
|---|---|---|
| Qt::DisplayRole | QString | 界面上顯示的字符串 |
| Qt::DecorationRole | QIcon,QColor | 在界面上期裝飾作用的數(shù)據(jù) |
| Qt::EditRole | QString | 界面上適合在編輯器中顯示的數(shù)據(jù) |
| Qt::ToolTipRole | QString | 項(xiàng)的toolTip |
| Qt::StatusTipRole | QString | 項(xiàng)的statusTip |
| Qt::FontRole | QFont | 項(xiàng)的字體 |
| Qt::TextAlignmentRole | Qt::Alignment | 項(xiàng)的對(duì)齊方式 |
| Qt::BackgroundRole | QBrush | 項(xiàng)的背景色 |
| Qt::ForegroundRole | QBrush | 項(xiàng)的前景色 |
| Qt::CheckStateRole | Qt::CheckState | 項(xiàng)的復(fù)選狀態(tài) |
| Qt::UserRole | QVariant | 自定義的用戶數(shù)據(jù) |
在獲取一個(gè)項(xiàng)的數(shù)據(jù)時(shí)也需要指定角色,以獲取不同角色的數(shù)據(jù).QAbstractItemModel定義了函數(shù)data()可以返回一個(gè)項(xiàng)的不同角色的數(shù)據(jù).
- QVariant QAbstractItemModel::data(const QModelIndex &index,int role=Qt::DisplayRole)
通過為一個(gè)項(xiàng)的不同角色定義數(shù)據(jù),可以告知試圖組件和代理如何展示數(shù)據(jù).
QAbstractItemModel類
QAbstractItemModel是所有模型類的字節(jié)或間接父類,它定義了模型的通用接口函數(shù),作為抽象類不能直接用來創(chuàng)建對(duì)象實(shí)例.
由于QAbstractItemModel是抽象類,所以它的很多函數(shù)都是虛函數(shù).
下面給出一些關(guān)于QAbstractItemModel的接口函數(shù):
- int rowCount(const QModelIndex &parent=QModelIndex())//返回行數(shù)
- int columnCount(const QModelIndex &parent=QModelIndex())//返回列數(shù)
- bool insertRow(int row,const QModelIndex &parent=QModelIndex())//插入行
- bool insertRows(int row,int count,const QModelIndex &parent=QModelIndex())//插入多行
- bool removeRow(int row,const QModelIndex &parent=QModelIndex())//移除行
- bool removeRows(int row,int count,const QModelIndex &parent=QModelIndex())//移除多行
- bool insertColumn(int column,const QModelIndex &parent=QModelIndex())//插入列
- bool insertColumns(int column,int count,const QModelIndex &parent=QModelIndex())//插入多列
- bool removeColumn(int column,const QModelIndex &parent=QModelIndex())//移除列
- bool removeColumns(int column,int count,const QModelIndex &parent=QModelIndex())//移除多列
- bool moveRow(const QModelIndex &sourceParent,int sourceRow,const QModelIndex &destinationParent,int destinationChild)//移動(dòng)行
- bool moveColumn(const QModelIndex &sourceParent,int sourceColumn,const QModelIndex &destinationParent,int destinationChild)//移動(dòng)列
- void sort(int column,Qt::SortOrder order=Qt::AscendingOrder)//排序項(xiàng)
- bool setData(const QModelIndex &index,const QVariant &value,int role=Qt::EditRole)//設(shè)置數(shù)據(jù)
- QVariant data(const QModelIndex &index,int role=Qt::EditRole)//返回?cái)?shù)據(jù)
- bool clearItemData(const QModelIndex &index)//清楚項(xiàng)的數(shù)據(jù)
QAbstractItemView類
QAbstractView類是所有視圖組件類的父類,它定義了視圖組件類共有的一些接口.
下面是其常用的一些函數(shù):
- void setModel(QAbstractItemModel *model)//設(shè)置數(shù)據(jù)模型
- QAbstractItemModel *model()//返回關(guān)聯(lián)模型的數(shù)據(jù)對(duì)象指針
- void setSelectionModel(QItemSelectionModel *selectionModel)//設(shè)置選擇模型
- QItemSelectionModel *selectionModel()//返回選擇模型的對(duì)象指針
- QModelIndex currentIndex()//返回當(dāng)前項(xiàng)的模型索引
- void setCurrentIndex(const QModelIndex &index)//設(shè)置模型索引為index的項(xiàng)為當(dāng)前項(xiàng)
- void selectAll()//選擇視圖中的所有項(xiàng)
- void clearSelection()//清除所有選擇
下面是一些常用的屬性:
- editTriggers屬性:表示視圖組件是否可以編輯數(shù)據(jù)以及進(jìn)入編輯狀態(tài)的方式等.
- 常用函數(shù)有:
- void setEditTriggers(QAbstractItemView::EditTriggers triggers)//
- QAbstractItemView::EditTriggers editTriggers()
- QAbstractItemView::EditTriggers的值有:
- NoEditTriggers:不允許編輯
- CurrentChanged:當(dāng)前項(xiàng)變化時(shí)進(jìn)入編輯狀態(tài)
- DoubleClicked:點(diǎn)擊一個(gè)已選擇的項(xiàng)時(shí)進(jìn)入編輯狀態(tài)
- SelectedClicked:點(diǎn)擊一個(gè)已選擇的項(xiàng)時(shí)進(jìn)入編輯狀態(tài)
- EditKeyPressed:當(dāng)平臺(tái)的編輯按鍵被按下時(shí)進(jìn)入編輯狀態(tài)
- AnyKeyPressed:任何鍵被按下時(shí)進(jìn)入編輯狀態(tài)
- AllEditTriggers:發(fā)生以上任何動(dòng)作時(shí)進(jìn)入編輯狀態(tài)
- 常用函數(shù)有:
- alternatingRowColors屬性:這個(gè)屬性設(shè)置各行是否需要交替使用不同背景色.
- selectionMode屬性:這個(gè)屬性表示在視圖組件上選擇項(xiàng)的操作模式,該屬性值一般為SelectionMode類型
- QAbstractItemView::SelectionMode的值有:
- SingleSelection:單選
- ContiguousSelection:連續(xù)選擇,如按住shift
- ExtendedSelection:擴(kuò)展選擇,如按住ctrl
- MultiSelection:多選
- NoSelection:不允許選擇
- QAbstractItemView::SelectionMode的值有:
- selectionBehavior屬性:這個(gè)屬性表示點(diǎn)擊鼠標(biāo)時(shí)操作的行為,屬性值為SelectionBehavior類型.
- QAbstractItemView::SelectionBehavior的值有:
- SelectItems:選擇單個(gè)項(xiàng)
- SelectRows:選擇行,點(diǎn)擊單個(gè)單元格選擇對(duì)應(yīng)行
- SelectColumns:選擇列,點(diǎn)擊單個(gè)單元格選擇對(duì)應(yīng)列
- QAbstractItemView::SelectionBehavior的值有:
QAbstractItemView常用的信號(hào)有:
- void clicked(const QModelIndex &index)//點(diǎn)擊某個(gè)項(xiàng)時(shí)
- void doubleClicked(const QModelIndex &index)//雙擊某個(gè)項(xiàng)時(shí)
- void entered(const QModelIndex &index)//鼠標(biāo)移動(dòng)到某個(gè)項(xiàng)時(shí)
- void pressed(const QModelIndex &index)//鼠標(biāo)單擊某個(gè)項(xiàng)時(shí)
QStringListModel和QListView
QStringListModel是處理字符串列表的模型類,其實(shí)例可以作為QListView組件的數(shù)據(jù)模型.
QStringListModel內(nèi)部存儲(chǔ)了一個(gè)字符串列表,通過字符串列表內(nèi)容自動(dòng)顯示在關(guān)聯(lián)容器上.
QStringListModel類
QStringListModel類有兩種參數(shù)形式的構(gòu)造函數(shù),定義如下:
- QStringListModel(const QStringList&strings,QObject *parent=nullptr)
- QStringListModel(QObject *parent=nullptr)
作為字符串列表數(shù)據(jù)的模型,QStringListModel對(duì)象內(nèi)部有一個(gè)字符串列表,對(duì)模型數(shù)據(jù)的修改就是對(duì)QStringListModel對(duì)象內(nèi)部字符串的修改.
QStringListModel繼承于QAbstractItemModel,新增加的函數(shù)只有兩個(gè):
- void setStringList(const QStringList &strings)//設(shè)置字符串列表
- QStringList stringList()//返回模型內(nèi)部的字符串列表
下面給出一個(gè)使用例子用于方便理解:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_list<<"北京"<<"上海"<<"成都"<<"長沙"<<"廣州";
m_lisModel= new QStringListModel(m_list,this);
m_lisModel->setStringList(m_list);
ui->ltvView1->setModel(m_lisModel);
ui->ltvView1->setEditTriggers(QAbstractItemView::DoubleClicked|
QAbstractItemView::SelectedClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnReliveList_clicked()
{
m_lisModel->setStringList(m_list);
}
void MainWindow::on_btnClearList_clicked()
{
m_lisModel->removeRows(0,m_lisModel->rowCount());
}
void MainWindow::on_chkEditable_clicked(bool checked)
{
if(checked)
ui->ltvView1->setEditTriggers(QAbstractItemView::DoubleClicked|
QAbstractItemView::SelectedClicked);
else
ui->ltvView1->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
void MainWindow::on_btnAddItem_clicked()
{
m_lisModel->insertRow(m_lisModel->rowCount());
QModelIndex index= m_lisModel->index(m_lisModel->rowCount()-1);
m_lisModel->setData(index,QString("new item %1").arg(m_lisModel->rowCount()),Qt::DisplayRole);
ui->ltvView1->setCurrentIndex(index);
}
void MainWindow::on_btnInsertItem_clicked()
{
QModelIndex index= ui->ltvView1->currentIndex();
m_lisModel->insertRow(index.row());
m_lisModel->setData(index,QString("new item %1").arg(index.row()),Qt::DisplayRole);
ui->ltvView1->setCurrentIndex(index);
}
void MainWindow::on_btnDeleteItem_clicked()
{
QModelIndex index= ui->ltvView1->currentIndex();
m_lisModel->removeRow(index.row());
}
void MainWindow::on_btnUpper_clicked()
{
QModelIndex index;
int curRow= ui->ltvView1->currentIndex().row();
m_lisModel->moveRow(index,curRow,index,curRow-1);
}
void MainWindow::on_btnLower_clicked()
{
QModelIndex index;
int curRow= ui->ltvView1->currentIndex().row();
m_lisModel->moveRow(index,curRow,index,curRow+2);
}
void MainWindow::on_btnSort_clicked(bool checked)
{
if(checked)
m_lisModel->sort(Qt::AscendingOrder);
else
m_lisModel->sort(Qt::DescendingOrder);
}
void MainWindow::on_btnClearPlt_clicked()
{
ui->pltEdit->clear();
}
void MainWindow::on_btnShowSL_clicked()
{
QStringList tempList= m_lisModel->stringList();
ui->pltEdit->clear();
for(auto iter:tempList)
ui->pltEdit->appendPlainText(iter);
}

QStanardItemModel和QTableView
QStandardItemModel是基于項(xiàng)的模型類,每個(gè)項(xiàng)是一個(gè)QStandardItem對(duì)象,可以存儲(chǔ)各種數(shù)據(jù).QStandardItemModel通常與QTableView組成模型-視圖結(jié)構(gòu),實(shí)現(xiàn)二維數(shù)據(jù)的管理.
QTableView類
QTableView繼承自QAbstractItemView類,其新定義的屬性主要用于控制顯示效果,在UI可視化設(shè)計(jì)時(shí)就可以設(shè)置QTableView組件的各種屬性.
QTableView組件有水平表頭和垂直表頭,都是QHeaderView對(duì)象,可以設(shè)置和返回表頭對(duì)象.
相關(guān)函數(shù)定義如下:
- void QTableView::setHorizontalHeader(QHeaderView *header)//設(shè)置水平表頭
- void QTableView::setVerticalHeader(QHeaderView *header)//設(shè)置垂直表頭
- QHeaderView *QTableView::horizontalHeader()//返回水平表頭對(duì)象指針
- QHeaderView *QTableView::verticalHeaer()//返回垂直表頭對(duì)象指針
當(dāng)QTableView使用一個(gè)QStandardItemModel對(duì)象作為數(shù)據(jù)模型時(shí),它會(huì)自動(dòng)創(chuàng)建表頭對(duì)象,垂直表頭一般顯示行號(hào),水平表頭一般顯示列的標(biāo)題.
QStandardItemModel類
QStandardItemModel是以項(xiàng)為基本數(shù)據(jù)單元的模型類,每個(gè)項(xiàng)是一個(gè)QStandardItem對(duì)象.
項(xiàng)可以存儲(chǔ)各種角色的數(shù)據(jù),如文字,字體,對(duì)齊方式,圖標(biāo),復(fù)選狀態(tài)等.
如果以多行多列的二維數(shù)組形式存儲(chǔ)項(xiàng),就是表格模型;如果表格模型只有一列,就是列表模型;如果存儲(chǔ)項(xiàng)時(shí)為項(xiàng)指定父項(xiàng),就構(gòu)成樹狀模型.
如果一個(gè)QStandardItemModel對(duì)象是表格模型,將它設(shè)置為QTableView組件的數(shù)據(jù)模型后,視圖組件就用表格的形式顯示模型的數(shù)據(jù),并且根據(jù)每個(gè)單元格的項(xiàng)定義各種角色數(shù)據(jù)控制顯示效果.
QStandardItemModel的接口主要有:
- void setRowCount(int rows)
- void setColumnCount(int columns)
- void setItem(int row,int column,QStandardItem *item)
- void setItem(int row,QStandardItem *item)
- QStandardItem *item(int row,int column=0)
- QStandardItem *itemFromIndex(const QModelIndex &index)
- QModelIndex indexFromItem(const QStandardItem *item)
- void appendRow(const QList<QStandardItem *>&items)
- void appendRow(QStandardItem *item)
- void appednColumn(cosnt QList<QStandardItem*>&items)
- void insertRow(int row,const QList<QStandardItem *>&items)
- void insertRow(int row,QStandardItem *item)
- bool insertRow(int row,const QModelIndex &parent=QModelIndex())
- void insertColumn(int column,const QList<QStandardItem *>&items)
- bool insertColumn(int column,const QModelIndex &parent=QModelIndex())
- QList<QStandardItem*>takeRow(int row)
- QList<QStandardItem*>takeColumn(int column)
- QStandardItem *takeItem(int row,int column=0)
- void setHorizontalHeaderItem(int column,QStandardItem *item)
- void setHorizontalHeaderLabels(const QStringList &labels)
- QStandardItem *horizontalHeaderItem(int column)
- QStandardItem takeHorizontalHeaderItem(int column)
- void setVerticalHeaderItem(int row,QStandardItem *item)
- void setVerticalHeaderLabels(const QStringList &labels)
- QStandardItem *verticalHeaderItem(int row)
- QStandardItem *takeVerticalHeaderItem(int row)
- void clear()
QStandardItemModel新定義了一個(gè)信號(hào),在任何一個(gè)項(xiàng)的數(shù)據(jù)變化時(shí)該信號(hào)就會(huì)發(fā)射:
- void itemChanged(QStandardItem *item)
QStandardItem類
QStandardItemModel數(shù)據(jù)模型中每個(gè)項(xiàng)都是一個(gè)QStandardItem對(duì)象.QStandardItem對(duì)象存儲(chǔ)了一個(gè)項(xiàng)的各種特性參數(shù),還可以存儲(chǔ)用戶自定義數(shù)據(jù).一個(gè)項(xiàng)可以添加子項(xiàng),子項(xiàng)也是QStandardItem類型的對(duì)象.
QStandardItem類沒有父類,它存儲(chǔ)項(xiàng)的各種特性內(nèi)容與設(shè)置各種特性.
| 讀取函數(shù) | 設(shè)置函數(shù) | 設(shè)置函數(shù)的功能 |
|---|---|---|
| text() | setText() | 設(shè)置項(xiàng)的顯示文字 |
| toolTip() | setToolTip() | 設(shè)置toolTip文字 |
| statusTip() | setStatusTip() | 設(shè)置statusTip文字 |
| icon() | setIcon() | 設(shè)置項(xiàng)的圖標(biāo) |
| font() | setFont() | 設(shè)置文字的字體 |
| textAlignment() | setTextAlignment() | 設(shè)置文字的對(duì)齊方式 |
| foreground() | setForeground() | 設(shè)置項(xiàng)的前景色 |
| background() | setBackground() | 設(shè)置項(xiàng)的背景色 |
| isEnabled() | setEnabled() | 設(shè)置項(xiàng)是否能夠交互 |
| isEditable() | setEditable() | 設(shè)置項(xiàng)是否可以被編輯 |
| isSelectable() | setSelectable() | 設(shè)置項(xiàng)是否可以被選擇 |
| isCheckable() | setCheckable() | 設(shè)置項(xiàng)是否可以復(fù)選 |
| checkState() | setCheckable() | 設(shè)置項(xiàng)的復(fù)選狀態(tài) |
| isAutoTristate() | setAutoTristate() | 設(shè)置是否自動(dòng)改變?nèi)N復(fù)選狀態(tài) |
| isUserTristate() | setUserTristate() | 設(shè)置是否由用戶決定三種復(fù)選狀態(tài) |
| flags() | setFlags() | 設(shè)置項(xiàng)的標(biāo)志 |
| row() | -- | 返回自身父項(xiàng)的所有子項(xiàng)的行號(hào) |
| column() | -- | 返回自身父項(xiàng)的所有子項(xiàng)的列號(hào) |
此外,還有一些函數(shù)很重要:
- void QStandardItem::setFlags(Qt::ItemFlags flags)//設(shè)置標(biāo)志
- void QStandardItem::setData(const QVariant &value,int role=Qt::UserRole+1)//設(shè)置自定義數(shù)據(jù)
- QVariant QStandardItem::data(int role=Qt::UserRole+1)//返回用戶自定義數(shù)據(jù)
- void QStandardItem::clearData()//清除用戶自定義數(shù)據(jù)
QStandard管理子項(xiàng)的函數(shù)有:
- void appendRow(const QList<QStandardItem *>&items)
- void appendRow(QStandardItem *item)
- void appednColumn(cosnt QList<QStandardItem*>&items)
- void insertRow(int row,const QList<QStandardItem *>&items)
- void insertRow(int row,QStandardItem *item)
- bool insertRow(int row,const QModelIndex &parent=QModelIndex())
- void insertColumn(int column,const QList<QStandardItem*>&items)
- void insertColumns(int column,int count)
- void removeColumn(int column)
- void removeColumns(int column,int count)
- int rowCount()
- int columnCount()
- bool hasChildren()
- QStandardItem *child(int row,int column=0)
如果采用樹狀模型,那么QStandardItemModel管理所有的頂層項(xiàng),而各個(gè)一級(jí)節(jié)點(diǎn)的字節(jié)子節(jié)點(diǎn)則通過QStandardItem類來管理.
QItemSelectionModel類
一個(gè)視圖組件需要設(shè)置一個(gè)數(shù)據(jù)模型,還可以設(shè)置一個(gè)選擇模型.QItemSelectionModel是選擇模型類,它的功能是跟蹤視圖組件上的選擇操作,給出選擇范圍.
為了設(shè)置視圖模型,需要用到函數(shù)setModell()為模型設(shè)置數(shù)據(jù)模型,函數(shù)定義如下:
- void QItemSelecitonModel::setModel(QAbstractItemModel *model)//為選擇模型設(shè)置數(shù)據(jù)模型
將數(shù)據(jù)模型,選擇模型,視圖模型這3種對(duì)象做好關(guān)聯(lián)設(shè)置后,在視圖組件上進(jìn)行選擇操作時(shí),選擇模型就可以跟蹤視圖組件上的選擇操作.
QItemSelectionModel有一些接口函數(shù):
- bool hasSelection()//是否有被選擇的項(xiàng)
- QModelIndex currentIndex()//返回當(dāng)前項(xiàng)的模型索引
- bool isSelected(const QModelIndex &index)//模型索引為index的項(xiàng)是否被選中
- QModelIndexList selectedIndexes()//返回所有被選擇項(xiàng)的模型索引列表
- QModelIndexList selectedRows(int column=0)//返回column列所有被選擇項(xiàng)的模型索引列表
- QModelIndexList selectedColumns(int row=0)//返回row行所有被選擇項(xiàng)的模型索引表
- void clear()//清除選擇模型
- void clearCurrentIndex()//清除當(dāng)前索引
- void clearSelection()//清除所有選擇
QItemSelectionModel有幾個(gè)信號(hào):
- void currentChanged(const QModelIndex ¤t,const QModelIndex &previous)//當(dāng)前項(xiàng)改變
- void selectionChanged(const QItemSelection &selected,const QItemSelection &deselected)
//選擇多個(gè)單元格或取消選擇一些單元格
下面給出一個(gè)綜合使用上述知識(shí)的例子:
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QFileDialog>
#include <QString>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QLabel *labCurFile,*labCellPos,*labCellText;
const int fixedColum=6;
QStandardItemModel *m_model;
QItemSelectionModel *m_selection;
void iniModelData(QStringList &fileContent);
private slots:
void do_currentChanged(const QModelIndex¤t,const QModelIndex&previous);
void on_actOpen_triggered();
void on_actAddRow_triggered();
void on_actInsertRow_triggered();
void on_actDeleteRow_triggered();
void on_actModelData_triggered();
void on_actAlignLeft_triggered();
void on_actAlignCenter_triggered();
void on_actAlignRight_triggered();
void on_actFontBold_triggered(bool checked);
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
labCurFile=new QLabel("當(dāng)前文件",this);
labCurFile->setMinimumWidth(200);
labCellPos=new QLabel("當(dāng)前單元格",this);
labCellPos->setMinimumWidth(200);
labCellText=new QLabel("單元格內(nèi)容",this);
labCellText->setMinimumWidth(200);
ui->statusbar->addWidget(labCurFile);
ui->statusbar->addWidget(labCellPos);
ui->statusbar->addWidget(labCellText);
m_model= new QStandardItemModel(2,fixedColum,this);
m_selection= new QItemSelectionModel(m_model,this);
ui->tableView->setModel(m_model);
ui->tableView->setSelectionModel(m_selection);
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::iniModelData(QStringList &fileContent)
{
int rowCount=fileContent.size();
m_model->setRowCount(rowCount-1);
QString header=fileContent.at(0);
QStringList headerList=header.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
m_model->setHorizontalHeaderLabels(headerList);
for(int i=1;i<rowCount;i++){
QString lineText=fileContent.at(i);
QStringList tempList=lineText.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
QStandardItem *item= new QStandardItem(tempList.at(0));
item->setCheckable(true);
item->setBackground(QBrush(Qt::yellow));
if(tempList.at(0)=="0")
item->setCheckState(Qt::Unchecked);
else
item->setCheckState(Qt::Checked);
m_model->setItem(i-1,0,item);
for(int j=1;j<=5;j++){
item=new QStandardItem(tempList.at(j));
m_model->setItem(i-1,j,item);
}
}
}
void MainWindow::do_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
if(current.isValid()){
labCellPos->setText(QString::asprintf("當(dāng)前單元格:%d行,%d列",current.row(),current.column()));
QStandardItem *tempItem= m_model->itemFromIndex(current);
labCellText->setText(QString("單元格內(nèi)容:%1").arg(tempItem->text()));
ui->actFontBold->setChecked(tempItem->font().bold());
}
}
void MainWindow::on_actOpen_triggered()
{
QString curPath= QCoreApplication::applicationDirPath();
QString fileName= QFileDialog::getOpenFileName(this,"打開一個(gè)文件",curPath,"數(shù)據(jù)文件(*.txt);所有文件(*.*)");
if(fileName.isEmpty())
return;
QFile fileObject(fileName);
if(!fileObject.open(QIODevice::ReadOnly|QIODevice::Text))
return;
QStringList fileContent;
ui->plainTextEdit->clear();
QTextStream tempStream(&fileObject);
while(!tempStream.atEnd()){
QString str=tempStream.readLine();
ui->plainTextEdit->appendPlainText(str);
fileContent.append(str);
}
fileObject.close();
labCurFile->setText("當(dāng)前文件:"+fileName);
ui->actAddRow->setEnabled(true);
ui->actInsertRow->setEnabled(true);
ui->actDeleteRow->setEnabled(true);
iniModelData(fileContent);
}
void MainWindow::on_actAddRow_triggered()
{
QList<QStandardItem*>itemList;
QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
QStandardItem *item=new QStandardItem(str);
item->setColumnCount(6);
item->setCheckable(true);
item->setCheckState(Qt::Checked);
item->setBackground(Qt::yellow);
itemList<<item;
for(int i=0;i<m_model->columnCount()-1;i++){
item=new QStandardItem("0");
itemList<<item;
}
m_model->appendRow(itemList);
m_selection->clearSelection();
m_selection->setCurrentIndex(m_model->index(m_model->rowCount()-1,0),QItemSelectionModel::Select);
}
void MainWindow::on_actInsertRow_triggered()
{
QModelIndex index= ui->tableView->currentIndex();
QList<QStandardItem*>itemList;
QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
QStandardItem *item=new QStandardItem(str);
item->setColumnCount(6);
item->setCheckable(true);
item->setCheckState(Qt::Checked);
item->setBackground(Qt::yellow);
itemList<<item;
for(int i=0;i<m_model->columnCount()-1;i++){
item=new QStandardItem("0");
itemList<<item;
}
m_model->insertRow(index.row(),itemList);
m_selection->clearSelection();
m_selection->setCurrentIndex(m_model->index(index.row(),0),QItemSelectionModel::Select);
}
void MainWindow::on_actDeleteRow_triggered()
{
QModelIndex index= ui->tableView->currentIndex();
m_model->removeRow(index.row());
}
void MainWindow::on_actModelData_triggered()
{
ui->plainTextEdit->clear();
QStandardItem *item;
QString str="";
for(int i=0;i<m_model->columnCount();i++){
item=m_model->horizontalHeaderItem(i);
str+=item->text();
str+='\t';
}
ui->plainTextEdit->appendPlainText(str);
for(int i=0;i<m_model->rowCount();i++){
str.clear();
item=m_model->item(i,0);
if(item->checkState()==Qt::Checked)
str+="是 ";
else
str+="否 ";
for(int j=0;j<m_model->columnCount();j++){
item=m_model->item(i,j);
str+=item->text();
str+='\t';
}
ui->plainTextEdit->appendPlainText(str);
}
}
void MainWindow::on_actAlignLeft_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actAlignCenter_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actAlignRight_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actFontBold_triggered(bool checked)
{
QStandardItem *item;
QModelIndexList indexList;
QFont font;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
font=item->font();
font.setBold(checked);
item->setFont(font);
m_model->setItem(index.row(),index.column(),item);
}
}

自定義代理
在模型-視圖結(jié)構(gòu)中,代理的作用就是在視圖組件進(jìn)入編輯狀態(tài)編輯某個(gè)項(xiàng)時(shí),提供一個(gè)臨時(shí)的編輯器用于數(shù)據(jù)編輯,編輯完成后再把數(shù)據(jù)提交給數(shù)據(jù)模型.
QTableView等視圖組件的代理默認(rèn)提供QLineEdit編輯框,在這個(gè)編輯框里可以輸入任何數(shù)據(jù),所以比較通用.
但是某些情況,我們也希望根據(jù)數(shù)據(jù)類型不同使用不同的編輯器,若要使用這樣的功能,就需要使用自定義代理.
若要替換QTableView組件提供的默認(rèn)代理組件,就需要為QTableView組件的某列或某個(gè)單元格設(shè)置自定義代理.
自定義代理類需要從QStyledItemDelegate類繼承,創(chuàng)建自定義代理類的實(shí)例后,再將其設(shè)置為整個(gè)視圖組件或視圖組件的某行或某列的代理.
QAbstractItemView類定義了設(shè)置自定義代理的3個(gè)函數(shù),定義如下:
- void setItemDelegate(QAbstractItemDelegate*delegate)
- void setItemDelegateForColumn(int column,QAbstractItemDelegate*delegate)
- void setItemDelegateForRow(int row,QAbstractItemDelegate*delegate)
其中,delegate是創(chuàng)建的自定義代理類的實(shí)例對(duì)象.
QStyledItemDelegate是視圖組件使用的默認(rèn)代理類,自定義代理類需要從QStyledItemDelegate類繼承.
要自定義一個(gè)代理類,必須重新實(shí)現(xiàn)QStyledItemDelegate中定義的4個(gè)虛函數(shù).
- QWidget*QStyledItemDelegate::createEditor(QWidget*parent,const QStyleOptionViewItem &option,const QModelIndex &index)//用于創(chuàng)建代理編輯器
- void QStyledItemDelegate::setEditorData(QWidget *editor,const QModelIndex &index)
//用于從數(shù)據(jù)模型獲取項(xiàng)的某個(gè)角色(一般為EditRole),然后將其設(shè)置為代理編輯器上顯示的數(shù)據(jù) - void QStyledItemDelegate::setModelData(QWidget*editor,QAbstractItemModel *model,const QModelIndex &index)//完成對(duì)當(dāng)前單元格的編輯
- void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index)//為臨時(shí)組件設(shè)置合適的大小
下面提供一個(gè)使用自定義代理的例子,改編自上面QStandardItemModel綜合使用例子:
tcombobox_delegate.h
#ifndef TCOMBOBOXDELEGATE_H
#define TCOMBOBOXDELEGATE_H
#include <QObject>
#include <QStyledItemDelegate>
#include <QComboBox>
class TComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit TComboBoxDelegate(QObject *parent = nullptr);
// QAbstractItemDelegate interface
public:
void setItems(QStringList items,bool editable);
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
QStringList m_itemList;
bool m_editable;
};
#endif // TCOMBOBOXDELEGATE_H
tfloat_spinbox_delegate.h
#ifndef TFLOATSPINBOXDELEGATE_H
#define TFLOATSPINBOXDELEGATE_H
#include <QObject>
#include <QStyledItemDelegate>
#include <QDoubleSpinBox>
class TFloatSpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit TFloatSpinBoxDelegate(QObject *parent = nullptr);
// QAbstractItemDelegate interface
public:
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // TFLOATSPINBOXDELEGATE_H
tspinbox_delegate.h
#ifndef TSPINBOXDELEGATE_H
#define TSPINBOXDELEGATE_H
#include <QObject>
#include <QSpinBox>
#include <QQmlEngine>
#include <QStyledItemDelegate>
class TSpinBoxDelegate : public QStyledItemDelegate
{
QML_ELEMENT
public:
explicit TSpinBoxDelegate(QObject *parent=nullptr);
// QAbstractItemDelegate interface
public:
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // TSPINBOXDELEGATE_H
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QFileDialog>
#include <QString>
#include <QStyledItemDelegate>
#include "tspinbox_delegate.h"
#include "tfloat_spinbox_delegate.h"
#include "tcombobox_delegate.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QLabel *labCurFile,*labCellPos,*labCellText;
const int fixedColum=6;
QStandardItemModel *m_model;
QItemSelectionModel *m_selection;
TSpinBoxDelegate* intSpinDelegate;
TFloatSpinBoxDelegate* doubleSpinDelegate;
TComboBoxDelegate* comboBoxDelegate;
void iniModelData(QStringList &fileContent);
private slots:
void do_currentChanged(const QModelIndex¤t,const QModelIndex&previous);
void on_actOpen_triggered();
void on_actAddRow_triggered();
void on_actInsertRow_triggered();
void on_actDeleteRow_triggered();
void on_actModelData_triggered();
void on_actAlignLeft_triggered();
void on_actAlignCenter_triggered();
void on_actAlignRight_triggered();
void on_actFontBold_triggered(bool checked);
};
#endif // MAINWINDOW_H
tcombobox_delegate.cpp
#include "tcombobox_delegate.h"
TComboBoxDelegate::TComboBoxDelegate(QObject *parent)
: QStyledItemDelegate{parent}
{
}
void TComboBoxDelegate::setItems(QStringList items, bool editable)
{
m_itemList=items;
m_editable=editable;
}
QWidget *TComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QComboBox *editor=new QComboBox(parent);
editor->setEditable(m_editable);
for(auto iter:m_itemList)
editor->addItem(iter);
return editor;
}
void TComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox *comboBox=dynamic_cast<QComboBox*>(editor);
QString str=index.model()->data(index,Qt::EditRole).toString();
comboBox->setCurrentText(str);
}
void TComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *comboBox=dynamic_cast<QComboBox*>(editor);
QString str=comboBox->currentText();
model->setData(index,str,Qt::EditRole);
}
void TComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
tfloat_spinbox_delegate.cpp
#include "tfloat_spinbox_delegate.h"
TFloatSpinBoxDelegate::TFloatSpinBoxDelegate(QObject *parent)
: QStyledItemDelegate{parent}
{
}
QWidget *TFloatSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QDoubleSpinBox *editor=new QDoubleSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(50000);
return editor;
}
void TFloatSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QDoubleSpinBox *spinBox=dynamic_cast<QDoubleSpinBox*>(editor);
double value=index.model()->data(index,Qt::EditRole).toDouble();
spinBox->setValue(value);
}
void TFloatSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QDoubleSpinBox *spinBox=dynamic_cast<QDoubleSpinBox*>(editor);
double value=spinBox->value();
model->setData(index,value);
}
void TFloatSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
tspinbox_delegate.cpp
#include "tspinbox_delegate.h"
TSpinBoxDelegate::TSpinBoxDelegate(QObject *parent)
:QStyledItemDelegate(parent)
{
}
QWidget *TSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
QSpinBox *editor=new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(50000);
return editor;
}
void TSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QSpinBox *spinBox=dynamic_cast<QSpinBox*>(editor);
int value=index.model()->data(index,Qt::EditRole).toInt();
spinBox->setValue(value);
}
void TSpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QSpinBox *spinBox=dynamic_cast<QSpinBox*>(editor);
int value=spinBox->value();
model->setData(index,value);
}
void TSpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
labCurFile=new QLabel("當(dāng)前文件",this);
labCurFile->setMinimumWidth(200);
labCellPos=new QLabel("當(dāng)前單元格",this);
labCellPos->setMinimumWidth(200);
labCellText=new QLabel("單元格內(nèi)容",this);
labCellText->setMinimumWidth(200);
ui->statusbar->addWidget(labCurFile);
ui->statusbar->addWidget(labCellPos);
ui->statusbar->addWidget(labCellText);
m_model= new QStandardItemModel(2,fixedColum,this);
m_selection= new QItemSelectionModel(m_model,this);
ui->tableView->setModel(m_model);
ui->tableView->setSelectionModel(m_selection);
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
intSpinDelegate= new TSpinBoxDelegate;
ui->tableView->setItemDelegateForColumn(1,intSpinDelegate);
doubleSpinDelegate= new TFloatSpinBoxDelegate;
ui->tableView->setItemDelegateForColumn(3,doubleSpinDelegate);
ui->tableView->setItemDelegateForColumn(4,doubleSpinDelegate);
ui->tableView->setItemDelegateForColumn(5,doubleSpinDelegate);
comboBoxDelegate= new TComboBoxDelegate;
QStringList strList;
strList<<"名字1"<<"名字2"<<"名字3";
comboBoxDelegate->setItems(strList,true);
ui->tableView->setItemDelegateForColumn(0,comboBoxDelegate);
connect(m_selection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(do_currentChanged(QModelIndex,QModelIndex)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::iniModelData(QStringList &fileContent)
{
int rowCount=fileContent.size();
m_model->setRowCount(rowCount-1);
QString header=fileContent.at(0);
QStringList headerList=header.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
m_model->setHorizontalHeaderLabels(headerList);
for(int i=1;i<rowCount;i++){
QString lineText=fileContent.at(i);
QStringList tempList=lineText.split(QRegularExpression(R"(\s+)"),Qt::SkipEmptyParts);
QStandardItem *item= new QStandardItem(tempList.at(0));
item->setCheckable(true);
item->setBackground(QBrush(Qt::yellow));
if(tempList.at(0)=="0")
item->setCheckState(Qt::Unchecked);
else
item->setCheckState(Qt::Checked);
m_model->setItem(i-1,0,item);
for(int j=1;j<=5;j++){
item=new QStandardItem(tempList.at(j));
m_model->setItem(i-1,j,item);
}
}
}
void MainWindow::do_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{
if(current.isValid()){
labCellPos->setText(QString::asprintf("當(dāng)前單元格:%d行,%d列",current.row(),current.column()));
QStandardItem *tempItem= m_model->itemFromIndex(current);
labCellText->setText(QString("單元格內(nèi)容:%1").arg(tempItem->text()));
ui->actFontBold->setChecked(tempItem->font().bold());
}
}
void MainWindow::on_actOpen_triggered()
{
QString curPath= QCoreApplication::applicationDirPath();
QString fileName= QFileDialog::getOpenFileName(this,"打開一個(gè)文件",curPath,"數(shù)據(jù)文件(*.txt);所有文件(*.*)");
if(fileName.isEmpty())
return;
QFile fileObject(fileName);
if(!fileObject.open(QIODevice::ReadOnly|QIODevice::Text))
return;
QStringList fileContent;
ui->plainTextEdit->clear();
QTextStream tempStream(&fileObject);
while(!tempStream.atEnd()){
QString str=tempStream.readLine();
ui->plainTextEdit->appendPlainText(str);
fileContent.append(str);
}
fileObject.close();
labCurFile->setText("當(dāng)前文件:"+fileName);
ui->actAddRow->setEnabled(true);
ui->actInsertRow->setEnabled(true);
ui->actDeleteRow->setEnabled(true);
iniModelData(fileContent);
}
void MainWindow::on_actAddRow_triggered()
{
QList<QStandardItem*>itemList;
QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
QStandardItem *item=new QStandardItem(str);
item->setColumnCount(6);
item->setCheckable(true);
item->setCheckState(Qt::Checked);
item->setBackground(Qt::yellow);
itemList<<item;
for(int i=0;i<m_model->columnCount()-1;i++){
item=new QStandardItem("0");
itemList<<item;
}
m_model->appendRow(itemList);
m_selection->clearSelection();
m_selection->setCurrentIndex(m_model->index(m_model->rowCount()-1,0),QItemSelectionModel::Select);
}
void MainWindow::on_actInsertRow_triggered()
{
QModelIndex index= ui->tableView->currentIndex();
QList<QStandardItem*>itemList;
QString str=m_model->headerData(0,Qt::Horizontal).toString()+QString::number(m_model->rowCount());
QStandardItem *item=new QStandardItem(str);
item->setColumnCount(6);
item->setCheckable(true);
item->setCheckState(Qt::Checked);
item->setBackground(Qt::yellow);
itemList<<item;
for(int i=0;i<m_model->columnCount()-1;i++){
item=new QStandardItem("0");
itemList<<item;
}
m_model->insertRow(index.row(),itemList);
m_selection->clearSelection();
m_selection->setCurrentIndex(m_model->index(index.row(),0),QItemSelectionModel::Select);
}
void MainWindow::on_actDeleteRow_triggered()
{
QModelIndex index= ui->tableView->currentIndex();
m_model->removeRow(index.row());
}
void MainWindow::on_actModelData_triggered()
{
ui->plainTextEdit->clear();
QStandardItem *item;
QString str="";
for(int i=0;i<m_model->columnCount();i++){
item=m_model->horizontalHeaderItem(i);
str+=item->text();
str+='\t';
}
ui->plainTextEdit->appendPlainText(str);
for(int i=0;i<m_model->rowCount();i++){
str.clear();
item=m_model->item(i,0);
if(item->checkState()==Qt::Checked)
str+="是 ";
else
str+="否 ";
for(int j=0;j<m_model->columnCount();j++){
item=m_model->item(i,j);
str+=item->text();
str+='\t';
}
ui->plainTextEdit->appendPlainText(str);
}
}
void MainWindow::on_actAlignLeft_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actAlignCenter_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actAlignRight_triggered()
{
QStandardItem *item;
QModelIndexList indexList;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
m_model->setItem(index.row(),index.column(),item);
}
}
void MainWindow::on_actFontBold_triggered(bool checked)
{
QStandardItem *item;
QModelIndexList indexList;
QFont font;
if(!m_selection->hasSelection())
return;
indexList=m_selection->selectedIndexes();
for(auto index:indexList){
item= m_model->item(index.row(),index.column());
font=item->font();
font.setBold(checked);
item->setFont(font);
m_model->setItem(index.row(),index.column(),item);
}
}

QFileSystemModel和QTreeView
QFileSystemModel為文件系統(tǒng)提供一個(gè)模型,綜合使用QFileSystemModel和QTreeView可以以目錄樹的形式顯示本機(jī)的文件系統(tǒng),如同Windows的資源管理器一樣.
QFileSystemModel類
要通過QFileSystemModel獲得本機(jī)的文件系統(tǒng),需要用QFileSystemModel的函數(shù)setRootPath()設(shè)置一個(gè)根目錄.如果需要使用條件過濾,則還需要設(shè)置過濾器.
QFileSystemModel類的主要成員函數(shù)如下:
- QModelIndex setRootPath(const QString &newPath)//設(shè)置根目錄
- QDir rootDirectory()//以QDir類返回目錄
- QString rootPath()//以QString類返回目錄
- QModelIndex index(const QString &path,int column=0)//返回目錄或文件的模型索引
- void setFilter(QDir::Filters filters)//設(shè)置模型數(shù)據(jù)項(xiàng)過濾器
- void setNameFilters(const QStringList &filters)//設(shè)置文件名過濾器
- void setNameFilterDisables(bool enable)//設(shè)置文件名過濾器
- void setOption(QFileSystemModel::Option option,bool on=true)//設(shè)置模型選項(xiàng)
- QIcon fileIcon(const QModelIndex &index)//返回項(xiàng)的圖標(biāo)
- QFileInfo fileInfo(const QModelIndex &index)//返回項(xiàng)的文件信息
- QString fileName(const QModelIndex &index)//返回不含路徑的文件名或最后一級(jí)文件夾名稱
- QString filePath(const QModelIndex &index)//返回項(xiàng)的路徑或包含路徑的文件名
- QDateTime lastModified(const QModelIndex &index)//返回項(xiàng)的最后修改日期
- bool isDir(const QModelIndex &index)//判斷項(xiàng)是不是一個(gè)文件夾
- qint64 size(const QModelIndex &index)//返回文件的大小字節(jié)數(shù)
- QString type(const QModelIndex &index)//返回項(xiàng)的類型描述文字
- QModelIndex mkdir(const QModelIndex &parent,const QString &name)//創(chuàng)建文件夾
- bool rmdir(const QModelIndex &index)//刪除文件夾
- bool remove(const QModelIndex &index)//刪除文件
相關(guān)的一些內(nèi)容:
- QDir的枚舉值:
- QDir::AllDirs:列出所有目錄
- QDir::Files:列出文件
- QDir::Drives:列出驅(qū)動(dòng)器
- QDir::NoDotAndDotDot:不列出目錄下的'.'和'..'特殊項(xiàng)
- QDir::Hidden:列出隱藏的文件
- QDir::System:列出系統(tǒng)文件
- QFileSystemModel::Option的枚舉值:
- QFileSystemModel::Option::DontWatchForChanges:不監(jiān)視文件系統(tǒng)的變化
- QFileSystemModel::Option::DontResolveSymlinks:不解析文件系統(tǒng)的符號(hào)連接項(xiàng)
- QFileSystemModel::Option::DontUseCustomDirectoryIcons:不使用自定義的目錄圖標(biāo)
QTreeView類
QTreeView類是用于顯示樹狀模型的視圖組件,其與QFileSystemModel模型結(jié)合就可以顯示本機(jī)的文件系統(tǒng).
如果QTreeView組件允許多選操作,就需要使用QItemSelectionModel選擇模型.
關(guān)于文件系統(tǒng)有關(guān)內(nèi)容,后面會(huì)進(jìn)一步深入,因而暫時(shí)不用太過在意.
下面是一個(gè)綜合使用的樣例:
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileSystemModel>
#include <QFileDialog>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_actSetRootDir_triggered();
void on_radOnlyDirAndFile_clicked();
void on_radOnlyDir_clicked();
void on_chkFliterFile_clicked(bool checked);
void on_btnApplicate_clicked();
void on_treeView_clicked(const QModelIndex &index);
private:
Ui::MainWindow *ui;
QFileSystemModel *m_model;
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->radOnlyDirAndFile->setChecked(true);
m_model=new QFileSystemModel(this);
ui->treeView->setModel(m_model);
ui->listView->setModel(m_model);
ui->tableView->setModel(m_model);
m_model->setRootPath(QDir::currentPath());
//ui->treeView->setRootIndex(m_model->index(QDir::currentPath()));
connect(ui->treeView,SIGNAL(clicked(QModelIndex)),ui->listView,SLOT(setRootIndex(QModelIndex)));
connect(ui->treeView,SIGNAL(clicked(QModelIndex)),ui->tableView,SLOT(setRootIndex(QModelIndex)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actSetRootDir_triggered()
{
QString dir=QFileDialog::getExistingDirectory(this,"選擇目錄",QDir::currentPath());
if(dir.isEmpty())
return;
ui->treeView->setRootIndex(m_model->index(dir));
ui->listView->setRootIndex(m_model->index(dir));
ui->tableView->setRootIndex(m_model->index(dir));
}
void MainWindow::on_radOnlyDirAndFile_clicked()
{
ui->gbFilter->setEnabled(true);
m_model->setFilter(QDir::AllDirs|QDir::Files|QDir::NoDotAndDotDot);
}
void MainWindow::on_radOnlyDir_clicked()
{
ui->gbFilter->setEnabled(false);
m_model->setFilter(QDir::AllDirs|QDir::NoDotAndDotDot);
}
void MainWindow::on_chkFliterFile_clicked(bool checked)
{
ui->comFileType->setEnabled(checked);
ui->btnApplicate->setEnabled(checked);
m_model->setNameFilterDisables(!checked);
}
void MainWindow::on_btnApplicate_clicked()
{
QStringList filter= ui->comFileType->currentText().trimmed().split(';',Qt::SkipEmptyParts);
m_model->setNameFilters(filter);
}
void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
ui->lbFileName->setText(m_model->fileName(index));
ui->lbDirName->setText(m_model->filePath(index));
ui->lbFileType->setText(m_model->type(index));
ui->chkIsDir->setChecked(m_model->isDir(index));
int sz=m_model->size(index)/1024;
if(sz<1024)
ui->lbFileSize->setText(QString("%1 KB").arg(sz));
else
ui->lbFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}



浙公網(wǎng)安備 33010602011771號(hào)