Qt QStandardItemModel用法小結(jié)
QStandardItemModel 是標準的以項數(shù)據(jù)(item data)為基礎的標準數(shù)據(jù)模型類,通常與 QTableView 組合成 Model/View 結(jié)構,實現(xiàn)通用的二維數(shù)據(jù)的管理功能。
本節(jié)介紹 QStandardltemModel 的使用,主要用到以下 3 個類:
- QStandardItemModel:基于項數(shù)據(jù)的標準數(shù)據(jù)模型,可以處理二維數(shù)據(jù)。維護一個二維的項數(shù)據(jù)數(shù)組,每個項是一個 QStandardltem 類的變量,用于存儲項的數(shù)據(jù)、字體格式、對齊方式等。
- QTableView:二維數(shù)據(jù)表視圖組件,有多個行和多個列,每個基本顯示單元是一個單元格,通過 setModel() 函數(shù)設置一個 QStandardItemModel 類的數(shù)據(jù)模型之后,一個單元格顯示 QStandardItemModel 數(shù)據(jù)模型中的一個項。
- QItemSelectionModel:一個用于跟蹤視圖組件的單元格選擇狀態(tài)的類,當在 QTableView 選擇某個單元格,或多個單元格時,通過 QItemSelectionModel 可以獲得選中的單元格的模型索引,為單元格的選擇操作提供方便。
這幾個類之間的關系是:QTableView 是界面視圖組件,其關聯(lián)的數(shù)據(jù)模型是 QStandardItem Model,關聯(lián)的項選擇模型是 QItemSelectionModel,QStandardItemModel 的數(shù)據(jù)管理的基本單元是 QStandardItem。
實例 samp5_3 演示 QStandardItemModel 的使用,其運行時界面如圖 1 所示。

圖 1 實例 samp5_3 的運行時界面
該實例具有如下功能:
- 打開一個純文本文件,該文件是規(guī)則的二維數(shù)據(jù)文件,通過字符串處理獲取表頭和各行各列的數(shù)據(jù),導入到一個 QStandardItemModel 數(shù)據(jù)模型。
- 編輯修改數(shù)據(jù)模型的數(shù)據(jù),可以插入行、添加行、刪除行,還可以在 QTableView 視圖組件中直接修改單元格的數(shù)據(jù)內(nèi)容。
- 可以設置數(shù)據(jù)模型中某個項的不同角色的數(shù)據(jù),包括文字對齊方式、字體是否粗體等。
- 通過 QItemSelectionModel 獲取視圖組件上的當前單元格,以及選擇單元格的范圍,對選擇的單元格進行操作。
- 將數(shù)據(jù)模型的數(shù)據(jù)內(nèi)容顯示到 QPlainTextEdit 組件里,顯示數(shù)據(jù)模型的內(nèi)容,檢驗視圖組件上做的修改是否與數(shù)據(jù)模型同步。
- 將修改后的模型數(shù)據(jù)另存為一個文本文件。
界面設計與主窗口類定義
本實例的主窗口從 QMainWindow 繼承而來,中間的 TableView 和 PlainTextEdit 組件采用水平分割條布局。在 Action 編輯器中創(chuàng)建如圖 2 所示的一些 Action,并由 Action 創(chuàng)建主工具欄上的按鈕,下方的狀態(tài)欄設置了幾個 QLabel 組件,顯示當前文件名稱、當前單元格行號、列號,以及相應內(nèi)容。

圖 2 實例中創(chuàng)建的 Action
主窗口類 MainWindow 里新增的定義如下(省略了 UI 設計器生成的界面組件的槽函數(shù)的聲明):
#define FixedColumnCount 6 //文件固定 6 列
class MainWindow : public QMainWindow
{
Q_OBJECT private:
QLabel *LabCurFile; //當前文件
QLabel *LabCellPos; //當前單元格行列號
QLabel *LabCellText; //當前單元格內(nèi)容
QStandardItemModel * theModel; //數(shù)據(jù)模型
QItemSelectionModel *theSelection; //選擇模型
void iniModelFromStringList (QStringList&) ; //從 StringList 初始化數(shù)據(jù)模型
public:
explicit MainWindow(QWidget *parent = 0);
private slots:
//當前選擇單元格發(fā)生變化
void on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
private:
Ui::MainWindow *ui;
};
這里定義了數(shù)據(jù)模型變量 theModel,項數(shù)據(jù)選擇模型變量 theSelection。
定義的私有函數(shù) iniModelFromStringList() 用于在打開文件時,從一個 QStringList 變量的內(nèi)容創(chuàng)建數(shù)據(jù)模型。
自定義槽函數(shù) on_currentChanged() 用于在 TableView 上選擇單元格發(fā)生變化時,更新狀態(tài)欄的信息顯示,這個槽函數(shù)將會與項選擇模型 theSelection 的 currentChanged() 信號關聯(lián)。
QStandardltemModel的使用
系統(tǒng)初始化
在 MainWindow 的構造函數(shù)中進行界面初始化,數(shù)據(jù)模型和選擇模型的創(chuàng)建,以及與視圖組件的關聯(lián),信號與槽的關聯(lián)等設置,代碼如下:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui (new Ui::MainWindow)
{
ui->setupUi(this);
setCentralWidget(ui->splitter);
theModel = new QStandardltemModel (2, FixedColumnCount, this) ; //數(shù)據(jù)模型
theSelection = new QItemSelectionModel (theModel) ;//選擇模型
connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)), this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
ui->tableView->setModel (theModel) ; //設置數(shù)據(jù)模型
ui->tableVi.evi-> setSelectionModel(theSelection) ; //設置選擇模型
ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
//創(chuàng)建狀態(tài)欄組件,代碼略
}
在構造函數(shù)里首先創(chuàng)建數(shù)據(jù)模型 theModel,創(chuàng)建數(shù)據(jù)選擇模型時需要傳遞一個數(shù)據(jù)模型變量作為其參數(shù)。這樣,數(shù)據(jù)選擇模型 theSelection 就與數(shù)據(jù)模型 theModel 關聯(lián),用于表示 theModel 的項數(shù)據(jù)選擇操作。
創(chuàng)建數(shù)據(jù)模型和選擇模型后,為 TableView 組件設置數(shù)據(jù)模型和選擇模型:
ui->tableView->setModel (theModel) ; //設置數(shù)據(jù)模型 ui->tableView->setSelectionModel (theSelection) ; //設置選擇模型
構造函數(shù)里還將自定義的槽函數(shù) on_currentChanged() 與 theSelection 的 currentChanged() 信號關聯(lián),用于界面上 tableView 選擇單元格發(fā)生變化時,顯示單元格的行號、列號、內(nèi)容等信息,槽函數(shù)代碼如下:
void MainWindow::on_currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
{ //選擇單元格變化時的響應
if (current.isValid())
{
LabCellPos->setText (QString::asprintf ("當前單元格:%d 行,%d 列", current.row(),current.column()));
QStandardItem* aItem=theModel->itemFromIndex(current);
this->LabCellText->setText ("單元格內(nèi)容:"+aItem->text());
QFont font=aItem->font();
ui->actFontBold->setChecked(font.bold());
}
}
從文本文件導入數(shù)據(jù)
QStandardItemModel 是標準的基于項數(shù)據(jù)的數(shù)據(jù)模型,以類似于二維數(shù)組的形式管理內(nèi)部數(shù)據(jù),適合于處理表格型數(shù)據(jù),其顯示一般采用 QTableView。
QStandardItemModel 的數(shù)據(jù)可以是程序生成的內(nèi)存中的數(shù)據(jù),也可以來源于文件。例如,在實際數(shù)據(jù)處理中,有些數(shù)據(jù)經(jīng)常是以純文本格式保存的,它們有固定的列數(shù),每一列是一項數(shù)據(jù),實際構成一個二維數(shù)據(jù)表。圖 3 是本實例程序要打開的一個純文本文件的內(nèi)容,文件的第 1 行是數(shù)據(jù)列的文字標題,相當于數(shù)據(jù)表的表頭,然后以行存儲數(shù)據(jù),以 TAB 鍵間隔每列數(shù)據(jù)。
當單擊工具欄上的“打開文件”按鈕時,需要選擇一個這樣的文件導入到數(shù)據(jù)模型,并在 tableView 上進行顯示和編輯。圖 3 的數(shù)據(jù)有 6 列,第 1 列是整數(shù),第 2 至 4 列是浮點數(shù),第 5 列是文字,第 6 列是邏輯型變量,“1”表示 true。

圖 3 純文本格式的數(shù)據(jù)文件
下面是“打開文件”按鈕的槽函數(shù)代碼:
void MainWindow::on_actOpen_triggered()
{ //打開文件
//QString str;
QString curPath=QCoreApplication::applicationDirPath(); //獲取應用程序的路徑
//調(diào)用打開文件對話框打開一個文件
QString aFileName=QFileDialog::getOpenFileName(this,"打開一個文件",curPath, "井數(shù)據(jù)文件(*.txt);;所有文件(*.*)");
if (aFileName.isEmpty())
return; //如果未選擇文件,退出
QStringList fFileContent;//文件內(nèi)容字符串列表
QFile aFile(aFileName); //以文件方式讀出
if (aFile.open(QIODevice::ReadOnly | QIODevice::Text)) //以只讀文本方式打開文件
{
QTextStream aStream(&aFile); //用文本流讀取文件
ui->plainTextEdit->clear();//清空
while (!aStream.atEnd())
{
QString str=aStream.readLine();//讀取文件的一行
ui->plainTextEdit->appendPlainText(str); //添加到文本框顯示
fFileContent.append(str); //添加到 StringList
}
aFile.close();//關閉文件
this->LabCurFile->setText("當前文件:"+aFileName);//狀態(tài)欄顯示
ui->actAppend->setEnabled(true); //更新Actions的enable屬性
ui->actInsert->setEnabled(true);
ui->actDelete->setEnabled(true);
ui->actSave->setEnabled(true);
iniModelFromStringList(fFileContent);//從StringList的內(nèi)容初始化數(shù)據(jù)模型
}
}
這段代碼讓用戶選擇所需要打開的數(shù)據(jù)文本文件,然后用只讀和文本格式打開文件,逐行讀取其內(nèi)容,將每行字符串顯示到界面上的 plainTextEdit 里,并且添加到一個臨時的 QStringList 類型的變量 fFileContent 里。
然后調(diào)用自定義函數(shù) iniModelFromStringList(),用 fFileContent 的內(nèi)容初始化數(shù)據(jù)模型。下面是 iniModelFromStringList() 函數(shù)的代碼:
void MainWindow::iniModelFromStringList(QStringList& aFileContent)
{ //從一個StringList 獲取數(shù)據(jù),初始化數(shù)據(jù)Model
int rowCnt=aFileContent.count(); //文本行數(shù),第1行是標題
theModel->setRowCount(rowCnt-1); //實際數(shù)據(jù)行數(shù)
//設置表頭
QString header=aFileContent.at(0);//第1行是表頭
//一個或多個空格、TAB等分隔符隔開的字符串, 分解為一個StringList
QStringList headerList=header.split(QRegExp("\\s+"),QString::SkipEmptyParts);
theModel->setHorizontalHeaderLabels(headerList); //設置表頭文字
//設置表格數(shù)據(jù)
QString aText;
QStringList tmpList;
int j;
QStandardItem *aItem;
for (int i=1;i<rowCnt;i++)
{
QString aLineText=aFileContent.at(i); //獲取數(shù)據(jù)區(qū)的一行
//一個或多個空格、TAB等分隔符隔開的字符串, 分解為一個StringList
QStringList tmpList=aLineText.split(QRegExp("\\s+"),QString::SkipEmptyParts);
for (j=0;j<FixedColumnCount-1;j++) //tmpList的行數(shù)等于FixedColumnCount, 固定的
{ //不包含最后一列
aItem=new QStandardItem(tmpList.at(j));//創(chuàng)建item
theModel->setItem(i-1,j,aItem); //為模型的某個行列位置設置Item
}
aItem=new QStandardItem(headerList.at(j));//最后一列是Checkable,需要設置
//aItem=new QStandardItem();//最后一列是Checkable,設置
aItem->setCheckable(true); //設置為Checkable
//aItem->setTextAlignment(Qt::AlignHCenter);
if (tmpList.at(j)=="0")
aItem->setCheckState(Qt::Unchecked); //根據(jù)數(shù)據(jù)設置check狀態(tài)
else
aItem->setCheckState(Qt::Checked);
theModel->setItem(i-1,j,aItem); //為模型的某個行列位置設置Item
}
}
傳遞來的參數(shù) aFileContent 是文本文件所有行構成的 StringList,文件的每一行是 aFileContent 的一行字符串,第 1 行是表頭文字,數(shù)據(jù)從第 2 行開始。
程序首先獲取字符串列表的行數(shù),然后設置數(shù)據(jù)模型的行數(shù),因為數(shù)據(jù)模型的列數(shù)在初始化時己經(jīng)設置了。
然后獲取字符串列表的第 1 行,即表頭文字,用 QString::split() 函數(shù)分割成一個 QStringList,設置為數(shù)據(jù)模型的表頭標題。
QString::split() 函數(shù)根據(jù)某個特定的符號將字符串進行分割。例如,header 是數(shù)據(jù)列的標題, 每個標題之間通過一個或多個 TAB 鍵分隔,其內(nèi)容是:
測深(m) 垂深(m) 方位(°) 總位移(m) 固井質(zhì)量 測井取樣
那么通過上面的 split() 函數(shù)操作,得到一個字符串列表 headerList,其內(nèi)容是:
測深(m)
垂深(m)
方位(°)
總位移(m)
固井質(zhì)量
測井取樣
也就是分解為一個 6 行的 StringList。然后使用此字符串列表作為數(shù)據(jù)模型,設置表頭標題的函數(shù) setHorizontalHeaderLabels() 的參數(shù),就可以為數(shù)據(jù)模型設置表頭了。
同樣,在逐行獲取字符串后,也采用 split() 函數(shù)進行分解,為每個數(shù)據(jù)創(chuàng)建一個 QStandardltem 類型的項數(shù)據(jù) altem,并賦給數(shù)據(jù)模型作為某行某列的項數(shù)據(jù)。
QStandardItemModel 以二維表格的形式保存項數(shù)據(jù),每個項數(shù)據(jù)對應著 QTableView 的一個單元格。項數(shù)據(jù)不僅可以存儲顯示的文字,還可以存儲其他角色的數(shù)據(jù)。
數(shù)據(jù)文件的最后一列是一個邏輯型數(shù)據(jù),在 tableView 上顯示時為其提供一個 CheckBox 組件,此功能通過調(diào)用 QStandardItem 的 setCheckable() 函數(shù)實現(xiàn)。
數(shù)據(jù)修改
當 TableView 設置為可編輯時,雙擊一個單元格可以修改其內(nèi)容,對于使用 CheckBox 的列,改變 CheckBox 的勾選狀態(tài),就可以修改單元格關聯(lián)項的選擇狀態(tài)。
在實例主窗口工具欄上有“添加行”、“插入行”、“刪除行”按鈕,它們實現(xiàn)相應的編輯操作,這些操作都是直接針對數(shù)據(jù)模型的,數(shù)據(jù)模型被修改后,會直接在 TableView 上顯示出來。
添加行
“添加行”操作是在數(shù)據(jù)表的最后添加一行,其實現(xiàn)代碼如下:
void MainWindow::on_actAppend_triggered()
{ //在表格最后添加行
QList<QStandardItem*> aItemList; //容器類
QStandardItem *aItem;
for(int i=0;i<FixedColumnCount-1;i++) //不包含最后1列
{
aItem=new QStandardItem("0"); //創(chuàng)建Item
aItemList<<aItem; //添加到容器
}
//獲取最后一列的表頭文字
QString str=theModel->headerData(theModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();
aItem=new QStandardItem(str); //創(chuàng)建 "測井取樣"Item
aItem->setCheckable(true);
aItemList<<aItem; //添加到容器
theModel->insertRow(theModel->rowCount(),aItemList); //插入一行,需要每個Cell的Item
QModelIndex curIndex=theModel->index(theModel->rowCount()-1,0);//創(chuàng)建最后一行的ModelIndex
theSelection->clearSelection();//清空選擇項
theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);//設置剛插入的行為當前選擇行
}
使用 QStandardltemModel::insertRow() 函數(shù)插入一行,其函數(shù)原型是:
void insertRow(int row, const QList<QStandardltem *> fiitems)
其中,row 是一個行號,表示在此行號之前插入一行,若 row 等于或大于總行數(shù),則在最后添加一行。QList<QStandardItem *>&items 是一個 QStandardltem 類型的列表類,需要為插入的一行的每個項數(shù)據(jù)創(chuàng)建一個 QStandardltem 類型的項,然后傳遞給 insertRow() 函數(shù)。
在這段程序中,為前 5 列創(chuàng)建 QStandardItem 對象時,都使用文字“0”,最后一列使用表頭的標題,并設置為 Checkable。創(chuàng)建完每個項數(shù)據(jù)對象后,使用 insertRow() 函數(shù)在最后添加一行。
插入行
“插入行”按鈕的功能是在當前行的前面插入一行,實現(xiàn)代碼與“添加行”類似。
刪除行
“刪除行”按鈕的功能是刪除當前行,首先從選擇模型中獲取當前單元格的模型索引,然后從模型索引中獲取行號,調(diào)用 removeRow(int row) 刪除指定的行。
void MainWindow::on_actDelete_triggered()
{ //刪除行
QModelIndex curIndex=theSelection->currentIndex () ;//獲取模型索引
if (curIndex. row () ==theModel->rowCount () -1) //最后一行
theModel->removeRow (curIndex.row () ) ; //刪除最后一行
else {
theModel->removeRow (curIndex.row () );//刪除一行,并重新設置當前選擇行
theSelection->setCurrentIndex (curIndex, QItemSelectionModel::Select);
}
}
單元格格式設置
工具欄上有 3 個設置單元格文字對齊方式的按鈕,還有一個設置字體粗體的按鈕。當在 TableView 中選擇多個單元格時,可以同時設置多個單元格的格式。例如,“居左”按鈕的代碼如下:
void MainWindow::on_actAlignLeft_triggered()
{ //設置文字居左對齊
if (!theSelection->hasSelection())
return;
//獲取選擇的單元格的模型索引列表,可以是多選
QModelIndexList selectedIndex=theSelection->selectedIndexes();
for (int i=0;i<selectedIndex.count();i++)
{
QModelIndex aIndex=selectedIndex.at (i) ; //獲取一個模型索引
QStandardItem* aItem=theModel->itemFromIndex(aIndex);
aItem->setTextAlignment (Qt::AlignLeft) ;//設置文字對齊方式
}
}
QItemSelectionModel::selectedIndexes() 函數(shù)返回選擇單元格的模型索引列表,然后通過此列表獲取每個選擇的單元格的模型索引,再通過模型索引獲取其項數(shù)據(jù),然后調(diào)用 QStandardItem::setTextAlignment() 設置一個項的對齊方式即可。
“居中”和“居右”按鈕的代碼與此類似。
“粗體”按鈕設置單元格的字體是否為粗體,在選擇單元格時,actFontBold 的 check 狀態(tài)根據(jù)當前單元格的字體是否為粗體自動更新。actFontBold 的 triggered(bool) 的槽函數(shù)代碼如下,與設置對齊方式的代碼操作方式類似:
void MainWindow::on_actFontBold_triggered(bool checked)
{//設置字體粗體
if (!theSelection->hasSelection())
return;
//獲取選擇單元格的模型索引列表
QModelIndexList selectedIndex=theSelection->selectedIndexes();
for (int i=0;i<selectedIndex.count();i++)
{
QModelIndex aIndex=selectedIndex.at(i); //獲取一個模型索引
QStandardItem* aItem=theModel->itemFromIndex(aIndex);//獲取項數(shù)據(jù)
QFont font=aItem->font(); //獲取字體
font.setBold(checked); //設置字體是否粗體
aItem->setFont(font); //重新設置字體
}
}
數(shù)據(jù)另存為文件
在視圖組件上對數(shù)據(jù)的修改都會自動更新到數(shù)據(jù)模型里,單擊工具欄上的“模型數(shù)據(jù)預覽” 按鈕,可以將數(shù)據(jù)模型的數(shù)據(jù)內(nèi)容顯示到 PlainTextEdit 里。
數(shù)據(jù)模型里的數(shù)據(jù)是在內(nèi)存中的,工具欄上的“另存文件”按鈕可以將數(shù)據(jù)模型的數(shù)據(jù)另存 為一個數(shù)據(jù)文本文件,同時也顯示在 PlainTextEdit 里,其實現(xiàn)代碼如下:
void MainWindow::on_actSave_triggered()
{ //保存為文件
QString curPath=QCoreApplication::applicationDirPath(); //獲取應用程序的路徑
//調(diào)用打開文件對話框選擇一個文件
QString aFileName=QFileDialog::getSaveFileName(this,tr("選擇一個文件"),curPath,
"井斜數(shù)據(jù)文件(*.txt);;所有文件(*.*)");
if (aFileName.isEmpty()) //未選擇文件,退出
return;
QFile aFile(aFileName);
if (!(aFile.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)))
return; //以讀寫、覆蓋原有內(nèi)容方式打開文件
QTextStream aStream(&aFile); //用文本流讀取文件
QStandardItem *aItem;
int i,j;
QString str;
ui->plainTextEdit->clear();
//獲取表頭文字
for (i=0;i<theModel->columnCount();i++)
{
aItem=theModel->horizontalHeaderItem(i); //獲取表頭的項數(shù)據(jù)
str=str+aItem->text()+"\t\t"; //以TAB見隔開
}
aStream<<str<<"\n"; //文件里需要加入換行符 \n
ui->plainTextEdit->appendPlainText(str);
//獲取數(shù)據(jù)區(qū)文字
for ( i=0;i<theModel->rowCount();i++)
{
str="";
for( j=0;j<theModel->columnCount()-1;j++)
{
aItem=theModel->item(i,j);
str=str+aItem->text()+QString::asprintf("\t\t");
}
aItem=theModel->item(i,j); //最后一列是邏輯型
if (aItem->checkState()==Qt::Checked)
str=str+"1";
else
str=str+"0";
ui->plainTextEdit->appendPlainText(str);
aStream<<str<<"\n";
}
}到此這篇關于Qt QStandardItemModel用法小結(jié)的文章就介紹到這了,更多相關Qt QStandardItemModel用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++將二叉樹轉(zhuǎn)為雙向鏈表及判斷兩個鏈表是否相交
這篇文章主要介紹了C++將二叉樹轉(zhuǎn)為雙向鏈表及判斷兩個鏈表是否相交的方法,文中還給出了求兩個鏈表相交的第一個節(jié)點列的實現(xiàn)方法,需要的朋友可以參考下2016-02-02

