Qt 模型、视图、代理

提示文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

模型和视图的主要作用就是实现数据显示和存储分离;Qt通过MVD(模型、视图、代理)实现了这种功能


一、MVD的理解

  1. 模型 M: 模型类定义了标准的接口,供视图(View)和委托(Delegate)来访问数据。数据本身不需要存储在模型中;它可以保存在由单独的类、文件数据库或其它应用程序组件提供的数据结构或存储库中。
  • 模型对数据进行封装例如封装成List、Tree、Table等,并提供了数据的访问接口,例如data()、setData()等。

  • 有的简单的数据可以保存在Model中,复杂的可以保存在类、文件数据库等组件中。 例如:
    QStringListModel类可以保存QString数据;
    QFileSystemModel类可以组织、封装文件系统数据,并提供访问接口;
    QsqlQueryModel、QsqlTableModel和QsqlRelationalTableModel组织、封装数据库数据,并提供数据的访问接口。

  1. 视图 V: 视图提供了一个标准接口,通过信号和槽机制与模型进交互操作,使子类能够随时更新其模型的更改。
  • 视图类负责人眼能看到的一些属性包括数据的绘制,管理表头、数据项的选择,网格的显示隐藏以及行高列宽的设置等。另外,提供信号,当交互操作时,通知模型更新数据等。
  1. 代理D: 代理为模型/视图架构中的代理提供了接口和通用函数。代理在视图中显示单独的数据项,并且处理模型数据的编辑。
  • 代理提供了访问模型和视图的接口,当要实现复杂功能显示时(例如表格中显示下拉框、按钮、进度条等),只靠模型和视图,不能实现;需要借助代理来实现。代理可以通过创建编辑器(编辑器可以是下拉框、按钮或者进度条等),并实现代理提供的虚函数,来实现这些功能

二、代码

1.实现的功能

该demo使用QTableView、QStandardItemmodel、qstyledItemDelegate类创建了表格,并表格中自定义了QComboBox、QSpinBox、QPushButton、QProgressBar等,其中按钮关联槽函数获取表格item的值并打印;并且创建了定时器,在超时槽函数中更新进度条的值。实现时,代理我选择了qstyledItemDelegate类,因为更新进度条时,需要重绘进度条样式。若只有交互组件,不用重绘组件样式,也可选用QItemDelegate。

2.效果

在这里插入图片描述


在这里插入图片描述

3.代码示例

3.1 Mainwindow.h头文件代码示例

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTableView>
#include <QStandardItemmodel>

#include "Myitemydelegate.h"
#include <QVBoxLayout>
#include <QTextbrowser>
#include <QTimer>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    QString tableInitData[4][5] = {{ "张一","男","21","10","获取"},{ "张二","男","22","20","获取"},
                                   { "张三","女","23","30","获取"},{ "张四","女","24","40","获取"}};
    QTimer *updateBarTimer;
    QWidget *centralWidget;
    QTableView *tableView;
    QStandardItemmodel *itemmodel;
    MyItemyDelegate *delegate;
    QVBoxLayout *widgetVLayout;
    QTextbrowser *displaybrowser;

    void initTable();

public slots:
    void tableBtnClickedSlot( int row );
    void updateProgressBarData();
};

#endif // MAINWINDOW_H

3.2 Mainwindow.cpp文件代码示例

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

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

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

void MainWindow::initTable()
{
    /*新建对象*/
    updateBarTimer = new QTimer;
    centralWidget = new QWidget(this);
    tableView = new QTableView;
    itemmodel = new QStandardItemmodel;
    delegate = new MyItemyDelegate;
    widgetVLayout = new QVBoxLayout;
    displaybrowser = new QTextbrowser;

    /*设置表格格式*/
    itemmodel->setColumnCount(5);
    itemmodel->setRowCount(4);
    itemmodel->setHeaderData(0,Qt::Horizontal,"姓名");
    itemmodel->setHeaderData(1,Qt::Horizontal,"性别");
    itemmodel->setHeaderData(2,Qt::Horizontal,"年龄");
    itemmodel->setHeaderData(3,Qt::Horizontal,"学习进度");
    itemmodel->setHeaderData(4,Qt::Horizontal,"获取进度");

    /*表格设置模型和代理*/
    tableView->setModel(itemmodel);
    tableView->setItemDelegate(delegate);

    /*布局*/
    widgetVLayout->addWidget(tableView);
    widgetVLayout->addWidget(displaybrowser);
    centralWidget->setLayout(widgetVLayout);
    this->setCentralWidget(centralWidget);

    /*表格item初始化*/
    for(int i = 0; i < 4;i++)
    {
        for(int j = 0; j < 5; j++)
        {
            itemmodel->setItem(i,j,new QStandardItem(tableInitData[i][j]));
            itemmodel->item(i,j)->setTextAlignment(Qt::AlignCenter);
        }
    }

    connect(delegate,SIGNAL(clicked(int)),this,SLOT(tableBtnClickedSlot(int)));
    connect(updateBarTimer,SIGNAL(timeout()),this,SLOT(updateProgressBarData()));

    updateBarTimer->start(1000);
}

/*表格中按钮的响应槽函数*/
void MainWindow::tableBtnClickedSlot(int row)
{
    displaybrowser->clear();
    QString itemName[4] = {"姓名:","性别:","年龄:","学习进度:"};

    for(int i = 0; i < 4; i++)
    {
        displaybrowser->append(itemName[i]+itemmodel->item(row,i)->text());
    }
}

/*定时器超时函数:更新进度条*/
void MainWindow::updateProgressBarData()
{
    for(int i = 0; i < 4; i++)
    {
        int setData = (itemmodel->item(i,3)->text().toInt()+5)%100;
        itemmodel->setData(itemmodel->index(i,3),setData);
    }
}

3.3 MyItemyDelegate.h文件代码示例
在使用代理时,有几个虚函数需要在子类中重新实现,具体可根据需要去实现,例如当需要绘制样式时,需要实现paint()函数,否则不用实现。

#ifndef MYITEMYDELEGATE_H
#define MYITEMYDELEGATE_H

#include <QObject>
#include <QPushButton>
#include <QComboBox>
#include <QSpinBox>
#include <QProgressBar>
#include <qstyledItemDelegate>

class MyItemyDelegate : public qstyledItemDelegate
{
    Q_OBJECT
public:
    explicit MyItemyDelegate(QObject *parent = nullptr);
    ~MyItemyDelegate();
	
	/*当需要创建自定义交互编辑器时,需要实现下面这几个函数*/
    QWidget * createEditor(QWidget *parent, const qstyleOptionViewItem &option, const QModelIndex &index) const override;
    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor,qabstractitemmodel *model, const QModelIndex &index) const override;
    void updateEditorGeometry(QWidget *editor,const qstyleOptionViewItem &option, const QModelIndex &index) const override;
	/*当需要绘制样式时,需要重新实现paint()函数*/
    void paint(QPainter* painter,const qstyleOptionViewItem &option, const QModelIndex &index) const;
    /*当需要过滤操作事件(例如按钮点击事件)时,需要重新实现editorEvent()函数*/
    bool editorEvent(QEvent *event, qabstractitemmodel *model, const qstyleOptionViewItem &option, const QModelIndex &index);

   signals:
       void clicked(int row);
};

#endif // MYITEMYDELEGATE_H

3.4MyItemyDelegate.cpp文件代码示例

#include "Myitemydelegate.h"
#include <QApplication>
#include <QMouseEvent>
#include <QDebug>

MyItemyDelegate::MyItemyDelegate(QObject *parent )
    :qstyledItemDelegate(parent)
{

}

MyItemyDelegate::~MyItemyDelegate()
{

}

QWidget * MyItemyDelegate::createEditor(QWidget *parent, const qstyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.column() == 1) /*性别*/
    {
        QComboBox *tableComBox = new QComboBox(parent);
        tableComBox->addItem("男");
        tableComBox->addItem("女");
        return tableComBox;
    }
    else if(index.column() == 2) /*年龄*/
    {
        QSpinBox *tableSpinBox = new QSpinBox(parent);
        tableSpinBox->setFrame(false);
        tableSpinBox->setMinimum(0);
        tableSpinBox->setMaximum(100);
        return tableSpinBox;
    }
    else if(index.column() == 3) /*进度*/
    {
        return nullptr;
    }
    else if(index.column() == 4) /*控制按钮*/
    {
        return nullptr;
    }

    return qstyledItemDelegate::createEditor(parent,option,index);
}

void MyItemyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if(index.column() == 1) /*性别*/
    {
        QString value = index.model()->data(index,Qt::EditRole).toString();
        QComboBox* comboBox = static_cast<QComboBox*>(editor);
        int tindex = comboBox->findText(value);
        comboBox->setCurrentIndex(tindex);
    }
    else if(index.column() == 2) /*年龄*/
    {
        int value = index.model()->data(index,Qt::EditRole).toInt();
        QSpinBox* spinBox = static_cast<QSpinBox*>(editor);
        spinBox->setValue(value);
    }
    else
    {
        qstyledItemDelegate::setEditorData(editor,index);
    }
}

void MyItemyDelegate::setModelData(QWidget *editor,qabstractitemmodel *model, const QModelIndex &index) const
{
    if(index.column() == 1) /*性别*/
    {
        QComboBox* comboBox = static_cast<QComboBox*>(editor);
        QString text = comboBox->currentText();
        model->setData(index,text,Qt::EditRole);
    }
    else if(index.column() == 2) /*年龄*/
    {
        QSpinBox* spinBox = static_cast<QSpinBox*>(editor);
        spinBox->interpretText();
        int value = spinBox->value();
        model->setData(index,value,Qt::EditRole);
    }
    else
    {
        qstyledItemDelegate::setModelData(editor,model,index);
    }
}

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

void MyItemyDelegate::paint(QPainter* painter,const qstyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.isValid() && (index.column() == 3))
    {
        qstyleOptionProgressBar *progressBar = new qstyleOptionProgressBar;

        progressBar->rect = option.rect;

        progressBar->progress = index.data().toInt();
        progressBar->maximum = 100;
        progressBar->minimum = 0;
        progressBar->text = QString::number(progressBar->progress)+"%";
        progressBar->textVisible = true;
        progressBar->textAlignment = Qt::AlignCenter;

        QApplication::style()->drawControl(qstyle::CE_ProgressBar,progressBar,painter);
    }
    else if(index.isValid() && (index.column() == 4))
    {
        qstyleOptionButton  *btnStyle = new qstyleOptionButton;
        btnStyle->text = "获取";
        btnStyle->rect = option.rect;
        btnStyle->state = qstyle::State_Enabled;

        QPushButton btn;
        btn.style()->drawControl(qstyle::CE_PushButton, btnStyle, painter,&btn);
    }
    else
    {
        qstyledItemDelegate::paint(painter,option,index);
    }
}

bool MyItemyDelegate::editorEvent(QEvent *event, qabstractitemmodel *model, const qstyleOptionViewItem &option, const QModelIndex &index)
{
    Q_UNUSED(model);

    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
    if(option.rect.contains(mouseEvent->pos()))
    {
        if((event->type() == QEvent::MouseButtonPress) && (index.column() == 4))
        {
            emit clicked(index.row());
        }
    }

    return qstyledItemDelegate::editorEvent(event, model, option, index);
}



总结

以上就是今天要讲的内容,本文以QTableView为例,仅仅简单介绍模型、视图、代理的简单使用方法;Qt还提供了其它丰富的类,感兴趣可继续深入学习。

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...