深度的思考
委托是视图的构成部分,那么委托是否帮助视图显示具体数据呢?
分析
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()
{
}
程序运行结果如下所示: