目录
1、官方例程赏析
2、自定义委托编程方法
3、编程示例
本文由【暴躁的野生猿】发表于CSDN,如有非法转载请帮忙举报谢谢。
QT提供的表格视图QTabelView或者表格控件QTableWidget中,我们一般用于显示一些文字内容,但是在官方教程中搜索delegate,可以看到QT官方例程,使用委托搞出来的一些什么花样操作:
(1)官方例程pixelDelegate。下图所示的东西竟然是个表格视图QTableView,而且并没有自定义视图(本系列教程后面会有自定义视图的博文,请关注)!表格的每个单元格里都放了一个黑色圆,这个黑色圆的半径代表了一个灰度值,灰度值来源于载入的一个jpg图片。鼠标框选,其实就是在框选单元格。
(2)官方例程starDelegate,把几首音乐的评分展示为星级,并支持星级编辑。这个委托支持读写。
(3)官方例程SpinboxDelegate。这个程序实现了单元格中只允许用户输入[0, 100]之间的整数。
可见:委托的作用是让程序员实现视图中内容的自定义展示方式和编辑方式。
下面列举几个场景,这些场景适合都适合用委托:
(1)表格中要显示或编辑一列日期,默认的表格的单元格的输入框都是类似QLineEdit的一个小控件,程序员想要监控用户输入的合法性,实现起来较为繁琐。
类似场景还有:① 某一列单元格只允许输入[1,120]之间的正整数,② 只允许从下拉列表中选择数据。等等。
(2)表格中有一列内容是学生的成绩,为了便于观察成绩的分布,用户希望把成绩大小展现为进度条样式,或者类似上图的星级,如果学生的成绩低于60分,则显示为红色。
以上场景就是涉及到了单元格内容的展示方式和编辑方式,就是把单元格要显示和编辑的内容,用QT内置的各种QWidget或者自定义的Widget来进行输出显示和输入处理。
查询帮助文档可知,所以委托的祖宗类是QAbstractItemDelegate,QT内置了它的两个子类QItemDelegate and QStyledItemDelegate,其中QStyledItemDelegate会使用当前样式绘制内容,支持样式表,推荐程序员继承这个来实现自定义委托。
如官方例所示,如果我们期望通过画星星数,来显示数据,而QT内置的widget是没有这种功能的,这时有两种方案可选:
(1) 直接重写QT内置的祖宗类委托QAbstractItemDelegate的【绘制函数paint()】,以及【推荐尺寸函数sizeHint()】:
void QStyledItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
(2) 自定义widget,并使用这个widget进行委托,帮助手册指出,这时必须覆盖至少4个虚函数:
下面讲一下以上两种方法的优劣,以及最适合的使用场景:
方法(1)适合于纯自由绘制图形,以及图形长久保持在界面上时。(例子见我的下一篇博文)
方法(2)更适合于你打算使用QT内置的QWidget或自定义的Widget时,例如QSpinbox、QComboBox、QDateEdit及其子类等。
什么叫“图形长久保持在界面上”呢?比如文章开头的官方示例中,星标就是保持显示的,而另一个例子SpinBox就不是,spinbox只在用户双击单元格进入编辑态以后才会显示,再比如使用QComboBox下拉列表控件委托时,只有在进入编辑态,才会显示下拉箭头,如果用户不双击这个单元格,他甚至可能不会意识到这个单元格是可下拉的;再比如下面我写的日历控件的例子,只有进入编辑态,日历才会显示。
基于上述方法(2),在表格中使用QT内置的日期控件来编辑日期。
先来个动图看看效果:当双击单元格时,会弹出日历控件,用户单击日历上的日期时,日历隐藏,同时用户单击的日期会显示在单元格上,并通过Model被存了下来。
在下面这个代码实例中,我不仅覆盖了官方手册所要求的的4个虚函数,还写了一个单击的槽函数,用于快速退出日历控件(通过hide实现的),要不然只有用户点击其他单元格后,这个日历控件才消失,体验不好。
#ifndef CUSTOMDELEGATE_H
#define CUSTOMDELEGATE_H
#include
#include
#include
class CustomDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
CustomDelegate(QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
private slots:
void when_calendarClicked(const QDate &date);
private:
};
#endif // CUSTOMDELEGATE_H
#include "customDelegate.h"
#include
#include
CustomDelegate::CustomDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
QWidget *CustomDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index)
QCalendarWidget *calendar = new QCalendarWidget(parent);
calendar->resize(200, 150);//设置弹出的日历控件初始大小
connect(calendar, SIGNAL(clicked(QDate)), this, SLOT(when_calendarClicked(QDate)));
qDebug() << __func__;
return calendar;
}
/*
设置刚弹出日历时,日历上要显示的日期,一般会将其设置为对应model中的当前值
*/
void CustomDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString dateStr = index.model()->data(index).toString();
QCalendarWidget *calendarWidget = static_cast(editor);
calendarWidget->setSelectedDate(QDate::fromString(dateStr, "yyyy.MM.dd"));
qDebug() << __func__;
}
/*
设置日历控件弹出时显示的位置、以及修改日历控件的大小(如果需要修改的话)
*/
void CustomDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index)
editor->move(option.rect.x(), option.rect.y() + option.rect.height());//把日历放到了被编辑的单元格的正下方
//editor->move(option.rect.x(), option.rect.y());//把日历左上角与单元格左上角对齐(会遮住单元格,看起来不爽)
// editor->resize(50,50);//修改日历显示的大小
qDebug() << __func__;
}
/*
把用户单击的日期,通过Model记录下来
*/
void CustomDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QCalendarWidget *calendarWidget = static_cast(editor);
model->setData(index, QVariant(calendarWidget->selectedDate().toString("yyyy.MM.dd")));
qDebug() << __func__;
}
/*
当日历被单击时
*/
void CustomDelegate::when_calendarClicked(const QDate &date)
{
Q_UNUSED(date)
QCalendarWidget *calendar =static_cast(QObject::sender());
qDebug() << calendar->selectedDate();
//calendar->hide();
calendar->close();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
MyTableModel *model = new MyTableModel(this);
ui->tableView->setModel(model);
//给第1列(出生日期)设置日历控件代理
ui->tableView->setItemDelegateForColumn(1, new CustomDelegate(this));
}
其中,tableView->setItemDelegateForColumn 可以给某一列单元格设置代理
void setItemDelegateForRow(int row, QAbstractItemDelegate *delegate); 可以给某一行单元格设置代理
void setItemDelegate(QAbstractItemDelegate *delegate);可以给所有单元格设置统一的代理
我们发现,QT没有提供对单独某个单元格的代理,可能是因为大家几乎都不会有这种需求。