Qt开发-MVD架构
模型/视图类
MVC是一种我们熟知的架构模式,它的全称是 Model View Controller。MVC具体的概念及应用,网上已经非常非常详尽了,这里就不赘述了,今天要了解的是其在Qt中的具体应用。Qt中的MVC并不叫MVC,而是叫MVD,Qt中没有Controller的说法,而是使用了另外一种抽象: Delegate (委托) ,其行为和传统的MVC是相同的。
模型(Model)
基于抽象基类QAbstractItemModel类,其继承类有:
视图(View)
基于抽象基类QAbstractItemView类,其继承类有:
代理(Delegate)
基于抽象基类QAbstractItemDelegate类,其继承类有:
☆例子
例子是电子工业出版社的《Qt5开发及实例》一书中的内容。
例1:
① 使用QDirModel基类,将QTreeView、QTableView、QListView的模型设为QDirModel实现下图:
主体代码
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDirModel model;
QTreeView tree;
QListView list;
QTableView table;
tree.setModel(&model);
list.setModel(&model);
table.setModel(&model);
tree.setSelectionMode(QAbstractItemView::SingleSelection);
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 *splitter = new QSplitter;
splitter->addWidget(&tree);
splitter->addWidget(&list);
splitter->addWidget(&table);
splitter->setWindowTitle(QObject::tr("Model/View"));
splitter->show();
return a.exec();
}
例二
自定义QAbstractTableModel类,将QtableView的模型设置为自定义模型,实现:
主体代码
modelx是一个继承于QAbstractTableModel的类,modelx.cpp如下:
#include "modelex.h"
#pragma execution_character_set("utf-8")
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();
}
void ModelEx::populateModel()
{
header<<tr("军种")<<tr("种类")<<tr("武器");
army<<1<<2<<3<<4<<2<<4<<3<<1;
weaponType<<1<<3<<5<<7<<4<<8<<6<<2;
weapon<<tr("B-2")<<tr("尼米兹级")<<tr("阿帕奇")<<tr("黄蜂级")<<tr("阿利伯克级")<<tr("AAAV")<<tr("M1A1")<<tr("F-22");
}
int ModelEx::columnCount(const QModelIndex &parent) const
{
return 3;
}
int ModelEx::rowCount(const QModelIndex &parent) const
{
return army.size();
}
QVariant ModelEx::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role==Qt::DisplayRole)
{
switch(index.column())
{
case 0:
return armyMap[army[index.row()]];
break;
case 1:
return weaponTypeMap[weaponType[index.row()]];
break;
case 2:
return weapon[index.row()];
default:
return QVariant();
}
}
return QVariant();
}
QVariant ModelEx::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
return header[section];
return QAbstractTableModel::headerData(section,orientation,role);
}
例3
(☆)表格是QTableView,不多做讲解。重点是柱状统计图绘制,这里使用的自定义的QAbstractItemView,model使用的是标准的QStandardItemModel,效果如图:这里还使用了QItemSelectionModel来实现表格与柱状图的联动。 这里不论是网上给的还是书上给的教程都有点问题,源代码需要进行修正。其一,一些重载函数虽然没有具体实现,但是需要给返回值;其二,在定义柱状图的点击事件时,出现了问题(点击柱状图时没有预期的反应)。自己定义的histogramview类中的用来存储各个柱状的位置信息的链表构建有误需要进行修改。
主体代码
这里只放出我的修改部分。 首先是重载函数的返回值部分。 由于这些重载函数没有被使用到,其返回值不重要,创建一些空对象即可。
QRect HistogramView::visualRect(const QModelIndex &index)const{
return QRect();
}
void HistogramView::scrollTo(const QModelIndex &index,ScrollHint){}
QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers){
return QModelIndex();
}
int HistogramView::horizontalOffset()const{
return 0;
}
int HistogramView::verticalOffset()const{
return 0;
}
bool HistogramView::isIndexHidden(const QModelIndex &index)const{
return true;
}
QRegion HistogramView::visualRegionForSelection(const QItemSelection &selection)const{
return QRegion();
}
然后是对柱状图点击效果的修改。 首先,我再mainwindow.cpp文件中声明了一个全局变量标志位g_regFlag,初始化为false;每一次打开新文件时置为true;
void MainWindow::openFile(QString path)
{
if (!path.isEmpty())
{
QFile file(path);
if (file.open(QFile::ReadOnly|QFile::Text))
{
QTextStream stream(&file);
QString line;
model->removeRows(0,model->rowCount(QModelIndex()),QModelIndex());
g_regFlag=true;
int row = 0;
do
{
line = stream.readLine();
if (!line.isEmpty())
{
model->insertRows(row, 1, QModelIndex());
QStringList pieces=line.split(",",QString::SkipEmptyParts);
model->setData(model->index(row, 0, QModelIndex()),pieces.value(0));
model->setData(model->index(row, 1, QModelIndex()),pieces.value(1));
model->setData(model->index(row, 2, QModelIndex()),pieces.value(2));
model->setData(model->index(row, 3, QModelIndex()),pieces.value(3));
row++;
}
}while (!line.isEmpty());
file.close();
}
}
}
然后修改绘画函数
void HistogramView::paintEvent(QPaintEvent *)
{
QPainter painter(viewport());
painter.setPen(Qt::black);
int x0=40;
int y0=250;
painter.drawLine(x0,y0,40,30);
painter.drawLine(38,32,40,30);
painter.drawLine(40,30,42,32);
painter.drawText(20,30,tr("人数"));
for(int i=1;i<5;i++)
{
painter.drawLine(40,i*50,41,i*50);
painter.drawText(20,i*50,tr("%1").arg((5-i)*5));
}
painter.drawLine(x0,y0,540,250);
painter.drawLine(538,248,540,250);
painter.drawLine(540,250,538,252);
painter.drawText(545,250,tr("部门"));
int posD=x0+20;
int row;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
QModelIndex index=model()->index(row,0,rootIndex());
QString dep=model()->data(index).toString();
painter.drawText(posD,y0+20,dep);
posD+=50;
}
if(g_regFlag)
{
MRegionList.clear();
FRegionList.clear();
SRegionList.clear();
}
int posM=x0+20;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
QModelIndex index=model()->index(row,1,rootIndex());
int male=model()->data(index).toDouble();
int width=10;
if(selections->isSelected(index))
painter.setBrush(QBrush(Qt::blue,Qt::Dense3Pattern));
else
painter.setBrush(Qt::blue);
painter.drawRect(QRect(posM,y0-male*10,width,male*10));
if(g_regFlag)
{
QRegion regionM(posM,y0-male*10,width,male*10);
MRegionList<<regionM;
}
posM+=50;
}
int posF=x0+30;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
QModelIndex index=model()->index(row,2,rootIndex());
int female=model()->data(index).toDouble();
int width=10;
if(selections->isSelected(index))
painter.setBrush(QBrush(Qt::red,Qt::Dense3Pattern));
else
painter.setBrush(Qt::red);
painter.drawRect(QRect(posF,y0-female*10,width,female*10));
if(g_regFlag)
{
QRegion regionF(posF,y0-female*10,width,female*10);
FRegionList<<regionF;
}
posF+=50;
}
int posS=x0+40;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
QModelIndex index=model()->index(row,3,rootIndex());
int retire=model()->data(index).toDouble();
int width=10;
if(selections->isSelected(index))
painter.setBrush(QBrush(Qt::green,Qt::Dense3Pattern));
else
painter.setBrush(Qt::green);
painter.drawRect(QRect(posS,y0-retire*10,width,retire*10));
if(g_regFlag)
{
QRegion regionS(posS,y0-retire*10,width,retire*10);
SRegionList<<regionS;
if(row==model()->rowCount(rootIndex())-1)
g_regFlag=false;
}
posS+=50;
}
}
文件 一部,12,3,5 二部,16,4,0 三部,18,4,2 四部,10,3,1 五部,11,4,3 六部,12,2,4 七部,14,3,5 八部,9,1,1
例4
自定义QAbstractItemDelegate,实现控件只有在需要编辑数据项时才会显示。实现效果如图: 这里自己定义了三个委托类,分别是Date、Combo、Spin类型。
主体代码
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStandardItemModel model(4,4);
QTableView tableView;
tableView.setModel(&model);
DateDelegate dateDelegate;
tableView.setItemDelegateForColumn(1,&dateDelegate);
ComboDelegate comboDelegate;
tableView.setItemDelegateForColumn(2,&comboDelegate);
SpinDelegate spinDelegate;
tableView.setItemDelegateForColumn(3,&spinDelegate);
model.setHeaderData(0,Qt::Horizontal,QObject::tr("姓名"));
model.setHeaderData(1,Qt::Horizontal,QObject::tr("生日"));
model.setHeaderData(2,Qt::Horizontal,QObject::tr("职业"));
model.setHeaderData(3,Qt::Horizontal,QObject::tr("收入"));
QFile file("C:/Users/Bruce/Desktop/456.txt");
if(file.open(QFile::ReadOnly|QFile::Text))
{
QTextStream stream(&file);
QString line;
model.removeRows(0,model.rowCount(QModelIndex()),QModelIndex());
int row =0;
do{
line = stream.readLine();
if(!line.isEmpty())
{
model.insertRows(row,1,QModelIndex());
QStringList pieces = line.split(",",QString::SkipEmptyParts);
model.setData(model.index(row,0,QModelIndex()),pieces.value(0));
model.setData(model.index(row,1,QModelIndex()),pieces.value(1));
model.setData(model.index(row,2,QModelIndex()),pieces.value(2));
model.setData(model.index(row,3,QModelIndex()),pieces.value(3));
row++;
}
}while(!line.isEmpty());
file.close();
}
tableView.setWindowTitle(QObject::tr("Delegate"));
tableView.show();
return a.exec();
}
文件 Tom,1997-01-05,工人,1500 Jack,1978-12-23,医生,3000 Alice,1980-04-06,军人,2500 John,1983-09-25,律师,5000
文章源代码
不用积分 源代码
|