当前位置:   article > 正文

Qt开发-MVD架构_qt mvd

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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

例二

自定义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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

例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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

然后是对柱状图点击效果的修改。
首先,我再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();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

然后修改绘画函数

// 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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

文件
一部,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();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

文件
Tom,1997-01-05,工人,1500
Jack,1978-12-23,医生,3000
Alice,1980-04-06,军人,2500
John,1983-09-25,律师,5000

文章源代码

不用积分
源代码

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/831160
推荐阅读
相关标签
  

闽ICP备14008679号