深入解析视图与委托

深度的思考

委托是视图的构成部分,那么委托是否帮助视图显示具体数据呢?

分析

Qt 中的委托作为视图的内部组件而存在,因此,委托是视图的一部分;必然,委托需要承担数据显示的部分工作。

实验结论

视图负责确定数据项的组织显示方式 (列表,树形,表格)

委托负责具体数据项的显示和编辑 (数据值,编辑器)

视图和委托共同完成数据显示功能和数据编辑功能

拓展的思考

如何改变视图默认的数据显示方式?

自定义委托的默认数据显示方式

1. 重写 paint 成员函数

2. 在 paint 中自定义数据显示方式

3. 重写 editorEvent 成员函数

4. 在 editorEvent 中处理交互事件

在 paint 中自定义数据显示方式

在 editorEvent 中处理交互事件

自定义数据项显示 

CustomizedDelegate.h

#ifndef CustomizedDelegate_H
#define CustomizedDelegate_H

#include <QObject>
#include <QItemDelegate>
#include <QModelIndex>

class CustomizedDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    explicit CustomizedDelegate(QObject *parent = nullptr);

    QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void setEditorData(QWidget *editor, const QModelIndex &index) const;
    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);

signals:

};

#endif // CustomizedDelegate_H

CustomizedDelegate.cpp

#include "CustomizedDelegate.h"
#include <QVariant>
#include <QComboBox>
#include <QStyleOptionButton>
#include <QApplication>
#include <QMouseEvent>

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

}

QWidget* CustomizedDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QWidget* w = NULL;

    if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = new QComboBox(parent);

        cb->addItem("A");
        cb->addItem("B");
        cb->addItem("C");
        cb->addItem("D");

        w = cb;
    }
    else
    {
        w = QItemDelegate::createEditor(parent, option, index);
    }

    return w;
}

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

void CustomizedDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            for(int i = 0; i < cb->count(); i++)
            {
                if(cb->itemText(i) == index.data().toString())
                {
                    cb->setCurrentIndex(i);
                    break;
                }
            }
        }
    }
    else
    {
        QItemDelegate::setEditorData(editor, index);
    }
}

void CustomizedDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    if (index.data().type() == QVariant::Char)
    {
        QComboBox* cb = dynamic_cast<QComboBox*>(editor);

        if(cb != NULL)
        {
            model->setData(index, cb->currentText(), Qt::DisplayRole);
        }
    }
    else
    {
        QItemDelegate::setModelData(editor, model, index);
    }
}

void CustomizedDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.data(Qt::DisplayRole).type() == QVariant::Bool)
    {
        bool data = index.data(Qt::DisplayRole).toBool();
        QStyleOptionButton checkBoxStyle;

        checkBoxStyle.state = data ? QStyle::State_On : QStyle::State_Off;
        checkBoxStyle.state |= QStyle::State_Enabled;
        checkBoxStyle.rect = option.rect;
        checkBoxStyle.rect.setX(option.rect.x() + option.rect.width() / 2 - 6);

        QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkBoxStyle, painter);
    }
    else
    {
        QItemDelegate::paint(painter, option, index);
    }
}


bool CustomizedDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    bool ret = true;

    if(index.data(Qt::DisplayRole).type() == QVariant::Bool)
    {
        QMouseEvent* me = dynamic_cast<QMouseEvent*>(event);

        if((event->type() == QEvent::MouseButtonPress) && (option.rect.contains(me->pos())))
        {
            bool data = index.data(Qt::DisplayRole).toBool();

            model->setData(index, !data, Qt::DisplayRole);
        }
    }
    else
    {
        ret = QItemDelegate::editorEvent(event, model, option, index);
    }

    return ret;
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTableView>
#include <QPushButton>
#include <QStandardItemModel>
#include <CustomizedDelegate.h>

class Widget : public QWidget
{
    Q_OBJECT
private:
    QStandardItemModel m_model;
    QTableView m_view;
    CustomizedDelegate m_delegate;

    void initModel();
    void initView();

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

Widget.cpp

#include "Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    initModel();
    initView();

    m_view.setModel(&m_model);

    for(int i = 0; i < m_model.columnCount(); i++)
    {
        m_view.setColumnWidth(i, 150);
    }
}

void Widget::initModel()
{
    QStandardItem *root = m_model.invisibleRootItem();
    QStandardItem *itemA1 = new QStandardItem();
    QStandardItem *itemA2 = new QStandardItem();
    QStandardItem *itemA3 = new QStandardItem();
    QStandardItem *itemB1 = new QStandardItem();
    QStandardItem *itemB2 = new QStandardItem();
    QStandardItem *itemB3 = new QStandardItem();

    QStringList list;

    list.append("Language");
    list.append("Level");
    list.append("Script");

    m_model.setHorizontalHeaderLabels(list);

    itemA1->setData("C++", Qt::DisplayRole);
    itemA2->setData(QChar('A'), Qt::DisplayRole);
    itemA3->setData(false, Qt::DisplayRole);
    itemB1->setData("Python", Qt::DisplayRole);
    itemB2->setData(QChar('B'), Qt::DisplayRole);
    itemB3->setData(true, Qt::DisplayRole);

    root->setChild(0, 0, itemA1);
    root->setChild(0, 1, itemA2);
    root->setChild(0, 2, itemA3);
    root->setChild(1, 0, itemB1);
    root->setChild(1, 1, itemB2);
    root->setChild(1, 2, itemB3);
}

void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(600, 400);

    m_view.setItemDelegate(&m_delegate);
};

Widget::~Widget()
{
}

程序运行结果如下所示:

一个实例的分析与改进 

思考:

如何改进程序界面使其拥有更好的用户体验?

改进思路 

将 Progress 从纯文本的显示方式改变为 进度条 + 文本显示 的方式;可以直观的让用户感受到当前的任务进度。

解决方案

1. 自定义新的委托类

2. 在 paint 函数中绘制进度条显示方式

3. 在 editorEvent 成员中禁止数据编辑操作

在 paint 成员函数中绘制进度条显示

在 editorEvent 成员函数中禁止数据编辑操作 

委托中编辑器的双击事件将触发委托进入数据编辑状态。

任务进度模拟

1. 定义计时器用于模拟任务进度

2. 定义计时器槽函数 void timerTimeout()

3. 在槽函数中修改模型中的数据

任务进度模拟

CustomizedItemDelegate.h

#ifndef CustomizedItemDelegate_H
#define CustomizedItemDelegate_H

#include <QObject>
#include <QItemDelegate>

class CustomizedItemDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    explicit CustomizedItemDelegate(QObject* parent = nullptr);

    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;

    bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index);

signals:

};

#endif // CustomizedItemDelegate_H

CustomizedItemDelegate.cpp

#include "CustomizedItemDelegate.h"
#include <QVariant>
#include <QStyleOptionProgressBar>
#include <QApplication>
#include <QDebug>
#include <QEvent>

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

}

void CustomizedItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if(index.data().type() == QVariant::Int)
    {
        const int DELTA = 4;
        int data = index.data().toInt();
        QStyleOptionProgressBar progressBarOption;
        int top = option.rect.top() + DELTA;
        int left = option.rect.left() + DELTA;
        int width = option.rect.width() - 2 * DELTA;
        int height = option.rect.height() - 2 * DELTA;

        progressBarOption.rect = QRect(left, top, width, height);
        progressBarOption.maximum = 100;
        progressBarOption.minimum = 0;
        progressBarOption.progress = data;
        progressBarOption.textVisible = true;
        progressBarOption.text = QString().sprintf("%d", data) + "%";
        progressBarOption.textAlignment = Qt::AlignCenter;

        QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
    }
    else
    {
        QItemDelegate::paint(painter, option, index);
    }
}

bool CustomizedItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    bool ret = true;

    if(event->type() != QEvent::MouseButtonDblClick)
    {
        ret = QItemDelegate::editorEvent(event, model, option, index);
    }

    return ret;
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTableView>
#include <QStandardItemModel>
#include "CustomizedItemDelegate.h"
#include <QTimer>

class Widget : public QWidget
{
    Q_OBJECT
private:
    QStandardItemModel m_model;
    QTableView m_view;
    CustomizedItemDelegate m_delegate;
    QTimer m_timer;

    void initModel();
    void initView();

private slots:
    void timerTimeout();

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QModelIndex>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    initModel();
    initView();

    m_view.setModel(&m_model);

    for(int i = 0; i < m_model.columnCount(); i++)
    {
        m_view.setColumnWidth(i, 220);
    }

    connect(&m_timer, &QTimer::timeout, this, &Widget::timerTimeout);

    m_timer.start(500);
}

void Widget::initModel()
{
    QStandardItem* root = m_model.invisibleRootItem();
    QStandardItem* itemA1 = new QStandardItem();
    QStandardItem* itemA2 = new QStandardItem();
    QStandardItem* itemB1 = new QStandardItem();
    QStandardItem* itemB2 = new QStandardItem();

    itemA1->setData("C++", Qt::DisplayRole);
    itemA2->setData(70, Qt::DisplayRole);
    itemB1->setData("Java", Qt::DisplayRole);
    itemB2->setData(50, Qt::DisplayRole);

    root->setChild(0, 0, itemA1);
    root->setChild(0, 1, itemA2);
    root->setChild(1, 0, itemB1);
    root->setChild(1, 1, itemB2);

    QStringList list;
    list.append("Task");
    list.append("Progress");

    m_model.setHorizontalHeaderLabels(list);

}

void Widget::initView()
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(600, 200);

    m_view.setItemDelegate(&m_delegate);
}

void Widget::timerTimeout()
{
    QModelIndex i1 = m_model.index(0, 1, QModelIndex());
    QModelIndex i2 = m_model.index(1, 1, QModelIndex());

    QVariant v1 = (i1.data().toInt() + 1) % 100;
    QVariant v2 = (i2.data().toInt() + 3) % 100;

    m_model.setData(i1, v1, Qt::DisplayRole);
    m_model.setData(i2, v2, Qt::DisplayRole);
}

Widget::~Widget()
{
}

程序运行结果如下所示:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值