Qt开发-MVD架构

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();
        }
    }
}

然后修改绘画函数

// paintEvent()函数具体完成柱状统计图绘制的工作
void HistogramView::paintEvent(QPaintEvent *)
{
    QPainter painter(viewport());
    painter.setPen(Qt::black);

    int x0=40;
    int y0=250;
    //y坐标轴
    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));
    }
    //x 坐标轴
    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

文章源代码

不用积分
源代码

  • 7
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值