1. 程式人生 > >Qt中模型/檢視結構

Qt中模型/檢視結構

8.1 概述 MVC設計模式是起源於Smalltalk的一種與使用者介面相關的設計模式。通過使用此模型,可以有效地分離資料和使用者介面。MVC設計模式包括三個元素:表示資料的模型 (Model)、表示使用者介面的檢視(View)和定義了使用者在介面上的操作控制(Controller)。 與MVC設計模式類似,Qt引入了模型/檢視結構用於完成資料與介面的分離,即InterView框架。但不同的是,Qt的InterView框架中把檢視和控制部件結合在一起,使得  框架更為簡潔。為了靈活地處理使用者輸入,InterView框架引入了代理(Delegate)。通過使用代理,能夠自定義資料條目(item)的顯示和編輯方式。 Qt的模型/檢視結構分為了三種部分:模式(Model)、檢視(View)和代理(Delegate)。其中,模型與資料通訊,併為了其它部件提供介面;而檢視從模型中獲得用來 引用資料條目的模型索引(Model Index)。在檢視中,代理負責繪製資料條目,當編輯條目時,代理和模型直接進行通訊。模型/檢視/代理之間通過訊號和槽進行通訊。 它們之間的關係如下:      資料發生改變時,模型發出訊號通知檢視。      使用者對介面進行操作,檢視發生訊號。      代理髮出訊號告知模型和檢視編輯器目前的狀態。
基本概念 1、模型(Model)      InterView框架中的所有模型都基於抽象基類QAbstractItemModel類,此類由QProxyModel、QAbstractListModel、QAbstractTableModel、QAbstractProxyModel、 QDirModel、QFileSystemModel、QHelpContentModel 和 QStandardItemModel類繼承。 2、檢視(View)      InterView框架中的所有檢視都基於抽象基類QAbstractItemView類,此類由QColumnView、QHeaderView、QListView、QTableView和QTreeView類繼承。 3、代理(Delegate)      InterView框架中的所有代理都基於抽象基類QAbstractItemDelegate類,此類由QItemDelegate 和 QStyledItemDelegate類繼承。  執行效果

main.cpp
#include"mainwindow.h"
#include<QAbstractItemModel>
#include<QAbstractItemView>
#include<QItemSelectionModel>
#include<QApplication>
#include<QDirModel>
#include<QTreeView>
#include<QListView>
#include<QTableView>
#include
<QSplitter>
/*
*新建一個QDirModel物件,對資料訪問做準備,QDirModel的建立還可以設定過濾器,即只有符合條件的檔案或目錄才能被訪問
*QDirModel類繼承自QAbstractItemModel類,為訪問本地檔案系統提供資料模型,它提供了新建、刪除、建立目錄等一系列與
*檔案操作相關的函式,此處只是用來顯示本地檔案系統
*
*新建三種不同的View物件,以便檔案目錄可以以三種不同的方式顯示
*
*tree.setModel(&model):呼叫setModel()函式設定View物件的Model為QDirModel物件的model。
*
*tree.setSelectionMode(QAbstractItemView::MultiSelection):設定QTreeView物件的選擇方式為多選
*
*list.setSelectionModel(tree.selectionModel()):設定QListView物件與QTreeView物件使用相同的選擇型別
*
*為了實現雙擊QTreeView物件中的某個目錄時,QListView物件和QTableView物件中顯示此選定目錄下的所有檔案和目錄,
*需要連結QTreeView物件的doubleClicked()訊號與QListView物件和QTableView物件的setRootIndex()槽函式
*/
intmain(intargc,char*argv[])
{
QApplicationa(argc,argv);
QDirModelmodel;
QTreeViewtree;
QListViewlist;
QTableViewtable;
tree.setModel(&model);
list.setModel(&model);
table.setModel(&model);
tree.setSelectionMode(QAbstractItemView::MultiSelection);
list.setSelectionModel(tree.selectionModel());
table.setSelectionModel(tree.selectionModel());
QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),
&list,SLOT(setRootIndex(QModelIndex)));
QObject::connect(&tree,SIGNAL(doubleClicked(QModelIndex)),
&table,SLOT(setRootIndex(QModelIndex)));
QSplitter*spliter=newQSplitter;
spliter->addWidget(&tree);
spliter->addWidget(&list);
spliter->addWidget(&table);
spliter->setWindowTitle(QObject::tr("Model/View"));
spliter->show();
returna.exec();
}
8.2 模型(Model) 實現自定義模型可以通過QAbstractItemModel類繼承,也可以通過QAbstractListModel和QAbstractTableModel類繼承實現列表模型或表格模型。在數值程式碼儲存,然後 通過外來鍵關聯操作來查詢其真實的含義,這一方法為了避免冗餘。 Item主要角色及其描述
常量 描述
Qt::DisplayRole 顯示文字
Qt::DecorationRole 繪製裝飾資料(通常是圖示)
Qt::EditRole 在編輯器中編輯的資料
Qt::ToolTipRole 工具提示
 Qt::StatusTipRole 狀態列提示
Qt::WhatsThisRole What's This文字
Qt::SizeHintRole 尺寸提示
Qt::FontRole 預設代理的繪製使用的字型
Qt::TextAlignmentRole 預設代理的對齊方式
Qt::BackgroundRole 預設代理的背景畫刷
Qt::ForegroundRole 預設代理的前景畫刷
Qt::CheckStateRole 預設代理的檢查框狀態
Qt::UserRole 使用者自定義的資料的起始位置
執行畫面
modelex.h
#ifndefMODELEX_H
#defineMODELEX_H
#include<QAbstractTableModel>
#include<QMap>
#include<QVector>
#include<QStringList>
/*
*QMap<Key,T>提供了一個從型別為Key的鍵到型別為T的值的對映。
*rowCount()、columnCount()、data()和返回表頭資料的headerData()函式
*是QAbstractTableModel類的純數函式
*
*純虛擬函式是一種特殊的虛擬函式,在許多情況下,在基類中不能對虛擬函式給出有意義的實現,
*而把它宣告為純虛擬函式,它的實現留給該基類的派生類去做。這就是純虛擬函式的作用
*/
classModelEx:publicQAbstractTableModel
{
public:
explicitModelEx(QObject*parent=0);
virtualintrowCount(constQModelIndex&parent)const;
virtualintcolumnCount(constQModelIndex&parent)const;
QVariantdata(constQModelIndex&index,introle)const;
QVariantheaderData(intsection,Qt::Orientationorientation,introle)const;
signals:
publicslots:
private:
QVector<short>army;//定義一個短型軍種型別容器
QVector<short>weaponType;//定義一個短型武器型別容器
QMap<short,QString>armyMap;//定義一個從short型別的鍵值到型別QString的值得對映
QMap<short,QString>weaponTypeMap;//定義一個從short型別的鍵值到型別QString的值得對映
QStringListweapon;//定義一個字串武器連結串列
QStringListheader;//定義一個頭結點連結串列
voidpopulateModel();//完成表格資料的初始化填充
};
#endif//MODELEX_H
modelex.cpp
#include"modelex.h"
ModelEx::ModelEx(QObject*parent):
QAbstractTableModel(parent)
{
armyMap[1]=tr("空軍");
armyMap[2]=tr("海軍");
armyMap[3]=tr("陸軍");
armyMap[4]=tr("海軍陸戰隊");
weaponTypeMap[1]=tr("轟炸機");
weaponTypeMap[2]=tr("航空母艦");
weaponTypeMap[3]=tr("直升機");
weaponTypeMap[4]=tr("兩棲攻擊艦");
weaponTypeMap[5]=tr("驅逐艦");
weaponTypeMap[6]=tr("兩棲戰艦");
weaponTypeMap[7]=tr("坦克");
weaponTypeMap[8]=tr("戰鬥機");
populateModel();
}
voidModelEx::populateModel()
{
header<<tr("軍種")<<tr("種類")<<tr("武器");
army<<1<<2<<3<<4<<4<<2<<3<<1;
weaponType<<1<<2<<3<<4<<5<<6<<7<<8;
weapon<<tr("B-2")<<tr("尼米茲級")<<tr("阿帕奇")<<tr("黃蜂級")
<<tr("阿利伯克級")<<tr("AAAV")<<tr("MIAI")<<("F-22");
}
/*
*返回行
*/
intModelEx::rowCount(constQModelIndex&parent)const
{
returnarmy.size();
}
/*
*返回列
*/
intModelEx::columnCount(constQModelIndex&parent)const
{
return3;
}
/*
*QVariant類類似於C++的聯合(union)資料型別,它不僅能夠儲存很多Qt型別的值,包括QColor、QBrush、QFont、
*QPen、QRect、QString和QSize等,也能夠存放容器類的值。
*data()函式返回指定索引的資料,即將數值對映為文字
*/
QVariantModelEx::data(constQModelIndex&index,introle)const
{
if(!index.isValid())
{
returnQVariant();
}
/*Qt::DisplayRole用來存取檢視中顯示的文字*/
if(role==Qt::DisplayRole)
{
switch(index.column())
{
case0:
returnarmyMap[army[index.row()]];
break;
case1:
returnweaponTypeMap[weaponType[index.row()]];
break;
case2:
returnweapon[index.row()];
break;
default:
returnQVariant();
break;
}
}
returnQVariant();
}
/*
*headerData()函式返回固定的表頭資料,設定水平表頭的標題
*/
QVariantModelEx::headerData(intsection,Qt::Orientationorientation,introle)const
{
if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
returnheader[section];
returnQAbstractTableModel::headerData(section,orientation,role);
}
main.cpp
#include<QApplication>
#include"modelex.h"
#include<QTableView>
intmain(intargc,char*argv[])
{
QApplicationa(argc,argv);
ModelExmodelEx;
QTableViewview;
view.setModel(&modelEx);
view.setWindowTitle(QObject::tr("modelEx"));
view.resize(400,400);
view.show();
returna.exec();
}
8.3 檢視(View) 實現自定義的View,可繼承自QAbstractItemView類,對所需的純虛擬函式進行重定義與實現,對於QAbstractItemView類中的純虛擬函式,在子類中必須進行重定義, 但不一定要實現,可根據需要選擇實現。 顯示結果
mainwindow.h
#ifndefMAINWINDOW_H
#defineMAINWINDOW_H
#include<QMainWindow>
#include<QStandardItemModel>
#include<QTableView>
#include<QMenuBar>
#include<QMenu>
#include<QAction>
#include<QSplitter>
#include"histogramview.h"
classMainWindow:publicQMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget*parent=0);
~MainWindow();
voidcreateAction();
voidcreateMenu();
voidsetupModel();
voidsetupView();
voidopenFile(QString);
private:
QMenu*fileMenu;
QAction*openAct;
QStandardItemModel*model;
QTableView*table;
QSplitter*splitter;
HistogramView*histogram;
publicslots:
voidslotOpen();
};
#endif//MAINWINDOW_H
mainwindow.cpp
#include"mainwindow.h"
#include<QFileDialog>
#include<QFile>
#include<QTextStream>
#include<QStringList>
MainWindow::MainWindow(QWidget*parent)
:QMainWindow(parent)
{
createAction();
createMenu();
setupModel();
setupView();
setWindowTitle(tr("ViewExample"));
resize(600,660);
}
MainWindow::~MainWindow()
{
}
voidMainWindow::createAction()
{
openAct=newQAction(tr("開啟"),this);
connect(openAct,SIGNAL(triggered(bool)),this,SLOT(slotOpen()));
}
/*
*建立一個檔案選單欄
*將openAct動作插入到這個選單欄裡
*/
voidMainWindow::createMenu()
{
fileMenu=newQMenu(tr("檔案"),this);
fileMenu->addAction(openAct);
menuBar()->addMenu(fileMenu);
}
voidMainWindow::setupModel()
{
model=newQStandardItemModel(4,4,this);
model->setHeaderData(0,Qt::Horizontal,tr("部門"));
model->setHeaderData(1,Qt::Horizontal,tr("男"));
model->setHeaderData(2,Qt::Horizontal,tr("女"));
model->setHeaderData(3,Qt::Horizontal,tr("退休"));
}
voidMainWindow::setupView()
{
splitter=newQSplitter;
splitter->setOrientation(Qt::Vertical);
histogram=newHistogramView(splitter);
histogram->setModel(model);
table=newQTableView;//新建一個QTableView物件
table->setModel(model);//為QTableView物件設定相同的Model
/*新建一個QItemSelectModel物件作為QTableView物件使用的選擇模型*/
QItemSelectionModel*selectionModel=newQItemSelectionModel(model);
table->setSelectionModel(selectionModel);
histogram->setSelectionModel(selectionModel);
splitter->addWidget(table);
splitter->addWidget(histogram);
setCentralWidget(splitter);
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),table,
SLOT(selectionChanged(QItemSelection,QItemSelection)));
connect(selectionModel,SIGNAL(selectionChanged(QItemSelection,QItemSelection)),histogram,
SLOT(selectionChanged(QItemSelection,QItemSelection)));
}
/*
*開啟一個*.txt檔案
*如果檔案非空讀取檔案資訊
*/
voidMainWindow::slotOpen()
{
QStringname;
name=QFileDialog::getOpenFileName(this,"開啟",".","histogramfiles(*.txt)");
if(!name.isEmpty())
{
openFile(name);
}
}
/*
*一行行讀取*.txt檔案內容,當遇到“,”分割資料
*QFile類提供了一個介面,用於讀取或寫入檔案
*QTextStream類可以方便地讀取單詞、行和數字。
*在迴圈中一次讀取檔案中的一行資料,然後插入到表格對應的一行資料中
*/
voidMainWindow::openFile(QStringpath)
{
if(!path.isEmpty())
{
QFilefile(path);
if(file.open(QFile::ReadOnly|QFile::Text))
{
QTextStreamstream(&file);
QStringline;
/*從文字中移除一行的資料*/
model->removeRows(0,model->rowCount(QModelIndex()),QModelIndex());
introw=0;
do
{
line=stream.readLine();
if(!line.isEmpty())
{
/*設定一行插入多個數據,一次插入一個數據*/
model->insertRows(row,1,QModelIndex());
QStringListpieces=line.split(",",QString::SkipEmptyParts);
/*將資料設定到row行、0、1、2、3列*/
model->setData(model->index(row,0,QModelIndex()),pieces.value(0)