MVC编程

0\概念

Qt 中 的 模 型 / 视 图 架 构 用 来 实 现 大 量 的 数 据 存 储 、 处 理 及 显 示 。
MVC(Model-View-Controller)包括了 3 个组件:模型(Model)是应用对象,用来表示数据;视图(View)是模型的用户界面,用来显示数据;控制(Controller)定义了用户界面对用户输入的反应方式。委托(Delegate)用于定制数据的渲染和编辑方式。
在这里插入图片描述

1. 模型

所有的模型都基于 QAbstractItemModel 类,该类提供了十分灵活的接口来处理各种视图,这些视图可以将数据的表现形式为表格(table)、列表(list)、树(tree)。
Qt 提供了一些现成的模型来处理数据项:
QStringListModel 存储简单的 QString 项目列表;
QStandardItemModel 管理复杂的属性结构数据项,每一个数据项可以包含任意的数据;
QFileSystemModel 提供了本地文件系统中文件和目录信息;
QSqlQueryModel、QSqlTableModel 和 QSqlRelationTableModel 用来访问数据库。
标准模型还无法满足需要时,可子类化 QAbstractItemModel、QAbstractListModel 或
QAbstractTableModel 来创建自定义的模型。

为确保数据的表示与数据的获取相分离,Qt 引入了模型索引的概念,输入和委托均可通过模型索引来请求数据并显示。只有模型需要知道怎样获取数据,被模型管理的数据类型可以被广泛的定义。模型索引包含一个指针,指向创建他们的模型,使用多个模型时可避免混淆。模型索引 QModelIndex 类提供对一块数据的临时引用,用来修改或检索模型中的数据,获取一个数据项的模型索引必须指定模型的 3 个属性:行号、列号和父项的模型索引。
在这里插入图片描述

2. 视图

Qt 提供了 QListView、QTableView 视图、QTreeView 视图分别实现列表、表格与树视图效果。QListView 将数据项显示为一个列表;QTableView 将模型中的数据显示在一个表格中;QTreeView 将模型中的数据项显示在具有层次的列表中。QTableView 和 QTreeView 在显示项目的时候同时还可以显示标头,通过 QHeaderView 类实现。自定义视图类是基于
QAbstractItemView 抽象基类,如实现条形图,饼状图等特殊显示方式。
在这里插入图片描述在这里插入图片描述

3. 委托

在模型/视图框架中,QAbstractItemDelegate 是委托类的抽象基类,Qt 默认的委托实现由 QStyledItemDelegate 类 提 供 , 这 也 被 用 作 Qt 标 准 视 图 的 默 认 委 托 , 选 择
QStyledItemDelegate 或 QItemDelegate 中其一来为视图中的项目绘制和提供编辑器。不同的是 QStyledItemDelegate 使用当前的样式来绘制项目,实现自定义委托建议使用
QStyledItemDelegate 作为基类。
Qt 提供了项目试图的便捷类,这些类底层通过模型/视图框架实现。这些部件分别是
QListWidget 提供一个项目列表,QTreeWidget 显示一个多层次的树结构,QTableWidget
提供了一个以项目作为单元的表格。它们每一个类都继承了 QAbstractItemView 类的行为。
之所以成为便捷因其用起来比较简单,使用于少量的数据的存储和显示。因没有将视图与模型分离,所以没有视图类灵活,不能和任意的模型一起使用。

1\基于系统文件

#include<QApplication>
#include<QAbstractItemModel>
#include<QAbstractItemView>
#include<QItemSelectionModel>
#include<QDirModel>
#include<QTreeView>
#include<QListView>
#include<QTableView>
#include<QSplitter>

int main(int argc, char *argv[])
{
    QApplication app(argc,argv);

    //创建模型
    QDirModel model;

    //创建树视图,列表视图,表格视图
    QTreeView tree;
    QListView list;
    QTableView table;

    //视图设置模型
    tree.setModel(&model);
    list.setModel(&model);
    table.setModel(&model);

    //设置视图对象的选择方式为多选
    tree.setSelectionMode(QAbstractItemView::MultiSelection);
    list.setSelectionMode(tree.selectionMode());
    table.setSelectionMode(tree.selectionMode());

    //树图示双击信号发射后,列表及表格视图刷新响应
    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(QString("视图/模型"));
    splitter->show();

    return app.exec();
}

在这里插入图片描述

2\标准模型项

#include<QApplication>
#include<QTreeView>
#include<QStandardItemModel>
#include<QDebug>

int main(int argc,char* argv[]){

    QApplication app(argc,argv);

    //创建标准项模型
    QStandardItemModel model;

    //获取标准项模型的根项,根项不可见
    QStandardItem *parentItem = model.invisibleRootItem();

    //创建标准型item0,设置文本,设置图标,工具提示
    QStandardItem *item0 = new QStandardItem;
    item0->setText("A");

    QPixmap pixmap0(50,50);
    pixmap0.fill(Qt::red);

    item0->setIcon(QIcon(pixmap0));
    item0->setToolTip(QString("A项提示"));

    //将item0作为父项的子项
    parentItem->appendRow(item0);
    parentItem = item0;

    //创建item0的子项
    QStandardItem *item1 = new QStandardItem;
    item1->setText("B");

    QPixmap pixmap1(50,50);
    pixmap1.fill(Qt::yellow);

    item1->setIcon(QIcon(pixmap1));
    item1->setToolTip(QString("B的提示"));
    parentItem->appendRow(item1);

    QStandardItem *item2 = new QStandardItem;

    QPixmap pixmap2(50,50);
    pixmap2.fill(Qt::green);

    item2->setData("C",Qt::EditRole);
    item2->setData("index C",Qt::ToolTipRole);
    item2->setData(QIcon(pixmap2),Qt::DecorationRole);

    parentItem->appendRow(item2);

    //树视图中显示数据
    QTreeView view;
    view.setModel(&model);
    view.show();

    QModelIndex indexA = model.index(0,0,QModelIndex());
    qDebug() << "indexA row coutn"
             <<model.rowCount(indexA);

    QModelIndex indexB = model.index(0,0,indexA);
    qDebug() << "indexB text"
             <<model.data(indexB,Qt::EditRole).toString();
    qDebug() << "indexB tooltip"
             <<model.data(indexB,Qt::ToolTipRole).toString();

    return app.exec();
}

在这里插入图片描述

3\自定义模型

1\WeaponMoedl.h

#ifndef WEAPONMOEDL_H
#define WEAPONMOEDL_H
#include<QAbstractTableModel>

class WeaponMoedl : public QAbstractTableModel
{
public:
    WeaponMoedl(QObject *parent = 0);

    virtual int rowCount(const QModelIndex &parent = QModelIndex())const;
    virtual int columnCount(const QModelIndex &parent = QModelIndex())const;
    QVariant data(const QModelIndex &index,int role)const;
    QVariant headerData(int section,Qt::Orientation orientation,int role)const;

private:
    QVector<short> army; //军队
    QVector<short> weaponType; //武器类型
    QMap<short,QString> armyMap; //军队映射
    QMap<short,QString> weaponMap;//武器映射

    QStringList weapon;//武器
    QStringList header;//表头
    void populateModel();//表格数据的初始化
};

#endif // WEAPONMOEDL_H

2/WeaponMoedl.cpp

#include "weaponmoedl.h"

WeaponMoedl::WeaponMoedl(QObject *parent):QAbstractTableModel(parent)
{
    armyMap[1] = QString("陆军");
    armyMap[2] = QString("空军");
    armyMap[3] = QString("海军");
    armyMap[4] = QString("海军陆战队");

    weaponMap[1] = QString("轰炸机");
    weaponMap[2] = QString("战斗机");
    weaponMap[3] = QString("坦克");
    weaponMap[4] = QString("直升机");
    weaponMap[5] = QString("航空母舰");
    weaponMap[6] = QString("潜水战车");
    weaponMap[7] = QString("核潜艇");
    weaponMap[8] = QString("超音速");

    populateModel();
}

int WeaponMoedl::rowCount(const QModelIndex &parent) const
{
    return army.size();
}

int WeaponMoedl::columnCount(const QModelIndex &parent) const
{
    return 3;
}

//放回指定索引的数据,将数值映射成文字
QVariant WeaponMoedl::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 weaponMap[weaponType[index.row()]];
            break;
        case 2:
            return weapon[index.row()];
            break;
        default:
            return QVariant();
            break;
        }
    }
    return QVariant();

}

QVariant WeaponMoedl::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);
}

void WeaponMoedl::populateModel()
{
    header << QString("军种")
           << QString("种类")
           << QString("武器");
    army<< 1 << 2 << 3 << 4 << 3 << 2 << 1;
    weaponType << 1 << 3 << 5 << 7 << 8 << 6 << 4 << 2;
    weapon << QString("大黄蜂") << QString("华尔兹")
           << QString("扒鸡跑") << QString("尼泊尔")
           << QString("M90") << QString("华飞思")
           << QString("朴良国") << QString("双飞片");
}

3/main

#include<QApplication>
#include<QTableView>
#include"weaponmoedl.h"

int main(int argc, char *argv[])
{
    QApplication app(argc,argv);

    WeaponMoedl model;
    QTableView view;
    view.setModel(&model);
    view.setWindowTitle(QString("表格/视图"));
    view.resize(600,400);
    view.show();
    return app.exec();
}

在这里插入图片描述

4\只读模型

stringListModel.h

#ifndef STRINGLISTMODEL_H
#define STRINGLISTMODEL_H
#include<QAbstractListModel>

class stringListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    stringListModel(const QStringList &strList,QObject *parent = 0)
        :QAbstractListModel(parent),m_stringList(strList){}

    //模型行数
    int rowCount(const QModelIndex &parent = QModelIndex()) const;

    //指定模型索引的数据项
    QVariant data(const QModelIndex &index, int role) const;

    //表头内容(数或者表格)
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const;

    //项目属性
    Qt::ItemFlags flags(const QModelIndex &index) const;

    //编辑数据
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole);

    //插入行 参数(插入位置,插入的行数,父项模型索引)
    bool insertRows(int row, int count,
                    const QModelIndex &parent = QModelIndex());

    //删除行
    bool removeRows(int row, int count,
                    const QModelIndex &parent = QModelIndex());

private:
    QStringList m_stringList;
};

#endif // STRINGLISTMODEL_H

stringListModel.cpp

#include "stringlistmodel.h"

int stringListModel::rowCount(const QModelIndex &parent) const
{
    return m_stringList.count();
}

QVariant stringListModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    if(index.row() == m_stringList.size())
        return QVariant();

    if(role == Qt::DisplayRole || role == Qt::EditRole)
        return m_stringList.at(index.row());
    else
        return QVariant();
}

QVariant stringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role != Qt::DisplayRole)
        return QVariant();

    //水平表头
    if(orientation == Qt::Horizontal)
        return QString("名字%1").arg(section);
    else
        return QString("%1").arg(section);
}

Qt::ItemFlags stringListModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return Qt::ItemIsEnabled;
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

bool stringListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //检验索引有效且可编辑
    if(index.isValid() && role == Qt::EditRole){
        m_stringList.replace(index.row(),value.toString());
        emit dataChanged(index,index);
        return true;
    }
    return false;
}

bool stringListModel::insertRows(int row, int count, const QModelIndex &parent)
{
    //告知其他组件指定的行开始插入操作
    beginInsertRows(parent,row,count + row - 1);
    for(int i=0; i<count; ++i)
        m_stringList.insert(row,QString("地球"));
    //告知其他组件完成操作
    endInsertRows();
    return true;
}

bool stringListModel::removeRows(int row, int count, const QModelIndex &parent)
{
    //告知其他组件指定的行进行删除操作
    beginRemoveRows(parent,row,count + row - 1);
    for(int i=0; i<count; ++i)
        m_stringList.removeAt(row);
    //告知其他组件完成操作
    endRemoveRows();
    return true;
}

main

#include<QApplication>
#include<QListView>
#include<QTableView>
#include"stringlistmodel.h"

int main(int argc, char *argv[])
{
    QApplication app(argc,argv);

    QStringList list;
    list << QString("太阳") << QString("月亮")
         << QString("火星") << QString("木星");

    stringListModel model(list);//创建模型

    model.insertRows(0,2);
    model.removeRows(2,1);

    QListView listView;//创建列表视图
    listView.setModel(&model);//视图设置模型
    listView.show();//视图显示

    QTableView tableView;//创建表格视图
    tableView.setModel(&model);//视图设置模型
    tableView.show();//视图显示

    return app.exec();
}

在这里插入图片描述

5\选择模型与自定义委托

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include<QItemSelection>
#include <QMainWindow>
#include<QTableView>
#include<QModelIndex>


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void getCurrentItemData();//当前选择
    void toggleSection();//切换选择

    //更新选择,selected表新的选择,deselected表以前的选择
    void updateSelection(const QItemSelection &selected,
                         const QItemSelection &deselected);

    //改变当前模型索引
    void changeCurrent(const QModelIndex &current,
                       const QModelIndex &previous);
private:
    Ui::MainWindow *ui;
    QTableView *m_tableView;
    QTableView *m_tableView2;
};

#endif // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QStandardItemModel>
#include<QDebug>
#include<QTableView>
#include <spinboxdelegate.h>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //创建标准模型项,7行4列
    QStandardItemModel *model = new QStandardItemModel(7,4,this);
    for(int row = 0; row < 7; ++row )
        for(int column = 0; column < 4; ++column ){
            QStandardItem *item = new QStandardItem(QString("%1").arg(row * 4 + column));

            //标准模型项设置数据项
            model->setItem(row,column,item);
        }
    m_tableView = new QTableView;
    m_tableView->setModel(model);
    setCentralWidget(m_tableView);//设置主窗口中心部件为表格视图

    //获取视图的选择模式
    QItemSelectionModel *selectionModel = m_tableView->selectionModel();

    QModelIndex topLeft;//左上角模型索引
    QModelIndex bottomRight;//右下角模型索引
    topLeft = model->index(1,1);
    bottomRight = model->index(5,2);

    //创建模型选择
    QItemSelection selection(topLeft,bottomRight);
    //以选择的方式来选择项目
    selectionModel->select(selection,QItemSelectionModel::Select);

    //添加动作Action(动作文本,响应者,槽方法)
//    ui->menuBar->addAction(QString("当前项目"),this,&MainWindow::getCurrentItemData);
//    ui->menuBar->addAction(QString("切换选择"),this,&MainWindow::toggleSection);

    //6.9.6版本,
    QAction *action1 = ui->menuBar->addAction(QString("当前项目"));
    QAction *action2 = ui->menuBar->addAction(QString("切换选择"));
    connect(action1,&QAction::triggered,this,&MainWindow::getCurrentItemData);
    connect(action2,&QAction::triggered,this,&MainWindow::toggleSection);

    //关联选择模型的选择改变,当前项改变信号
    connect(selectionModel,&QItemSelectionModel::selectionChanged,
            this,&MainWindow::updateSelection);
    connect(selectionModel,&QItemSelectionModel::currentChanged,
            this,&MainWindow::changeCurrent);

    m_tableView2 = new QTableView;
    m_tableView2->setWindowTitle("tableView2");
    m_tableView2->resize(600,400);
    m_tableView2->setModel(model);
    m_tableView2->setSelectionModel(selectionModel);
    m_tableView2->show();

    //自定义委托
    spinboxDelegate *dategate = new spinboxDelegate(this);
    m_tableView->setItemDelegate(dategate );
}

MainWindow::~MainWindow()
{
    delete ui;
    delete m_tableView2;
}

void MainWindow::getCurrentItemData()
{
    qDebug() << QString("当前数据: ")
             <<m_tableView->selectionModel()->currentIndex().data().toString();
}

void MainWindow::toggleSection()
{
    //左上角模型索引
    QModelIndex topLeft = m_tableView->model()->index(0,0,QModelIndex());

    //右下角模型索引
    QModelIndex bottomRight = m_tableView->model()->index(
                m_tableView->model()->rowCount(QModelIndex())-1,
                m_tableView->model()->columnCount(QModelIndex())-1,
                QModelIndex());

    //项选择
    QItemSelection curSelection(topLeft,bottomRight);
    m_tableView->selectionModel()->select(curSelection,
                                          QItemSelectionModel::Toggle);
}

void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected)
{
    QModelIndex index;

    //indexes()返回所有选择项的模型
    QModelIndexList list = selected.indexes();

    //给选择项填充数据
    foreach (index, list) {
        QString text = QString("%1,%2").arg(index.row()).arg(index.column());
        m_tableView->model()->setData(index,text);
    }

    //清空上一次的内容
    list = deselected.indexes();
    foreach (index, list) {
        m_tableView->model()->setData(index,"");
    }
}

void MainWindow::changeCurrent(const QModelIndex &current, const QModelIndex &previous)
{
    qDebug() << QString("从(%1,%2)到(%3,%4)")
                .arg(previous.row()).arg(previous.column())
                .arg(current.row()).arg(current.column());
}

spinboxDelegate.h

#ifndef SPINBOXDELEGATE_H
#define SPINBOXDELEGATE_H
#include<QItemDelegate>

class spinboxDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    spinboxDelegate(QObject *parent = 0);

    //创建编辑器
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

    //设置编辑器数据
    void setEditorData(QWidget *editor,const QModelIndex &index) const override;

    //更新编辑器几何属性
    void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,
                              const QModelIndex &index) const override;
};

#endif // SPINBOXDELEGATE_H

spinboxDelegate.cpp

#include "spinboxdelegate.h"
#include<QSpinBox>

spinboxDelegate::spinboxDelegate(QObject *parent):QItemDelegate(parent)
{

}

QWidget *spinboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSpinBox *editor = new QSpinBox(parent);
    editor->setMinimum(0);
    editor->setMaximum(100);
    return editor;
}

void spinboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    int value = index.model()->data(index,Qt::EditRole).toInt();

    //类型转化QWidget转QSpinBox
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);

    //编辑器设置数据
    spinBox->setValue(value);
}

void spinboxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值