QT模型视图MVC系列教程(5)---自定义委托Delegate编辑时显示

目录

1、官方例程赏析

2、自定义委托编程方法

3、编程示例


本文由【暴躁的野生猿】发表于CSDN,如有非法转载请帮忙举报谢谢。

1、官方例程赏析

QT提供的表格视图QTabelView或者表格控件QTableWidget中,我们一般用于显示一些文字内容,但是在官方教程中搜索delegate,可以看到QT官方例程,使用委托搞出来的一些什么花样操作:

(1)官方例程pixelDelegate。下图所示的东西竟然是个表格视图QTableView,而且并没有自定义视图(本系列教程后面会有自定义视图的博文,请关注)!表格的每个单元格里都放了一个黑色圆,这个黑色圆的半径代表了一个灰度值,灰度值来源于载入的一个jpg图片。鼠标框选,其实就是在框选单元格。

(2)官方例程starDelegate,把几首音乐的评分展示为星级,并支持星级编辑。这个委托支持读写。

(3)官方例程SpinboxDelegate。这个程序实现了单元格中只允许用户输入[0, 100]之间的整数。

可见:委托的作用是让程序员实现视图中内容的自定义展示方式和编辑方式。

下面列举几个场景,这些场景适合都适合用委托:

(1)表格中要显示或编辑一列日期,默认的表格的单元格的输入框都是类似QLineEdit的一个小控件,程序员想要监控用户输入的合法性,实现起来较为繁琐。

类似场景还有:① 某一列单元格只允许输入[1,120]之间的正整数,② 只允许从下拉列表中选择数据。等等。

(2)表格中有一列内容是学生的成绩,为了便于观察成绩的分布,用户希望把成绩大小展现为进度条样式,或者类似上图的星级,如果学生的成绩低于60分,则显示为红色。

以上场景就是涉及到了单元格内容的展示方式和编辑方式,就是把单元格要显示和编辑的内容,用QT内置的各种QWidget或者自定义的Widget来进行输出显示和输入处理。

2、自定义委托编程方法

查询帮助文档可知,所以委托的祖宗类是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个虚函数:

  • ① QWidget *QStyledItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    功能:创建一个widget并返回。该函数会在用户双击单元格时,被自动调起,并显示出来供用户编辑数据,用户编辑完之后会自动析构。在这里可以设置widget的初状态,大小,显示的初值等。
    参数表:
    parent为View对象,例如QTableWidget、QTableView、QListWidget、QListView等;
    option为视图送进来的对应单元格的样式,包括单元格在View的坐标、单元格的尺寸、文字对齐方式、字体等;
    index为单元格的对应的模型索引,没什么好说的。
  • ② void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    功能:当双击单元格时,widget会显示出来,然后本函数会被调起,程序员应当在这里设置刚弹出的widget要显示的东西(一般是要对应显示当前model里的值,比较符合人的直觉逻辑)。
    参数表:editor弹出的widget
  • ③ void QStyledItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    功能:该函数会在两种情形下被调起:一是用户双击单元格,widget控件弹出的瞬间,二是在widget正在显示时,用户更改了单元格的位置。因此在以上两种情况下,我们都需要个根据形参会传入的单元格的信息option,重新设置widget的位置或者大小。
    参数表:editor 、option如前所述。
  • ④ void QStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    功能:把用户编辑完的数据通过model存储起来。当用户在widget里编辑完数据之后,该函数会被自动调起。何时会发生“编辑完数据”事件?一般用户在别的单元格单击时会触发,有的控件会在用户回车时自动触发(例如QSpinBox),另外程序员还可以通过强制hide这个widget来人为触发这一事件(下文的例子就是这么做的)。

下面讲一下以上两种方法的优劣,以及最适合的使用场景:
方法(1)适合于纯自由绘制图形,以及图形长久保持在界面上时。(例子见我的下一篇博文)
方法(2)更适合于你打算使用QT内置的QWidget或自定义的Widget时,例如QSpinbox、QComboBox、QDateEdit及其子类等。

什么叫“图形长久保持在界面上”呢?比如文章开头的官方示例中,星标就是保持显示的,而另一个例子SpinBox就不是,spinbox只在用户双击单元格进入编辑态以后才会显示,再比如使用QComboBox下拉列表控件委托时,只有在进入编辑态,才会显示下拉箭头,如果用户不双击这个单元格,他甚至可能不会意识到这个单元格是可下拉的;再比如下面我写的日历控件的例子,只有进入编辑态,日历才会显示。

3、编程示例

基于上述方法(2),在表格中使用QT内置的日期控件来编辑日期。 

先来个动图看看效果:当双击单元格时,会弹出日历控件,用户单击日历上的日期时,日历隐藏,同时用户单击的日期会显示在单元格上,并通过Model被存了下来。

在下面这个代码实例中,我不仅覆盖了官方手册所要求的的4个虚函数,还写了一个单击的槽函数,用于快速退出日历控件(通过hide实现的),要不然只有用户点击其他单元格后,这个日历控件才消失,体验不好。

#ifndef CUSTOMDELEGATE_H
#define CUSTOMDELEGATE_H

#include <QObject>
#include <QStyledItemDelegate>
#include <QCalendarWidget>

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 <QDate>
#include <QDebug>
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<QCalendarWidget*>(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<QCalendarWidget*>(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<QCalendarWidget*>(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没有提供对单独某个单元格的代理,可能是因为大家几乎都不会有这种需求。

  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: Qt Model/View Delegate是一个用于实现自定义视图的框架。它可以将数据模型视图分离,从而简化了数据可视化的实现过程,并且遵循了MVC设计模式。Qt Model/View Delegate框架由三个组件组成:模型视图和代理,它们分别负责每个领域的功能。 模型负责存储和管理数据,以及通知视图数据的修改。模型可以是一个简单的列表模型,也可以是一个具有层级结构和多个属性的复杂模型视图负责在屏幕上显示数据。Qt为我们提供了多种不同的视图类型,以满足不同的需求。不同类型的视图可以以许多不同的方式呈现模型数据,例如表格、树形、列表、图形化等等。 代理负责管理视图中的单元格的外观和行为。通过代理,我们可以自定义单元格的显示方式、编辑方式、数据验证和拖放等等。代理可以获得有关模型的完整信息,从而使开发人员可以轻松地为单元格提供自定义视图。 总之,使用Qt模型视图框架可以帮助我们快速简单地实现数据可视化,将数据与界面分离,提高代码的可维护性和可重用性。在Qt中,这个框架被广泛地应用于各种领域的应用程序开发。 ### 回答2: Qt Model-View-DelegateMVC)是一种基于模型视图委托的设计模式,用于实现Qt框架中的表格、列表和树形等具有良好交互效果的UI控件。其主要思想是将底层模型和数据逻辑与表现形式分离开来,使得用户可以用统一的接口访问数据,而不必了解其内部实现细节。 MVC模式中,模型(Model)是数据源,它负责存储和管理数据,可以是数据库、服务器或者本地文件等;视图(View)负责呈现模型中的数据,并与用户进行交互,用户所见到的界面就是视图呈现出来的结果;委托Delegate)作为模型视图之间的桥梁,负责处理鼠标、键盘事件等用户的操作,并将其反映到底层的模型中。 Qt框架中的QAbstractItemModel类提供了表格、列表和树形控件所需的模型接口,而QAbstractItemView类则提供了对应的视图接口。QItemDelegate类则是一个通用的委托类,它可以创建和管理单元格、行和列的编辑显示,为数据提供不同的可视化效果。 使用Qt Model-View-Delegate框架可以帮助开发者简化数据管理和界面设计的任务,提高用户体验,同增加了代码的可维护性和可扩展性。 ### 回答3: Qt Model-View-Delegate(MVD)是一个重要的框架,它是用于管理和展示大量数据的高效方式。该框架可以将数据与视图分离,从而使得用户可以更加灵活地处理数据和视图的交互。 Model是指QT提供的数据模型,View是指数据展示的视图Delegate则是视图的代理,主要负责对视图的各种单元格进行渲染,由Delegate完成格式化和显示控件等。 代理可以被用来实现视图和数据的进一步定制,改变视图对数据的显示方式或者增加行为特性。代理可以在显示表格视图、树视图等多种类型视图候,改变它们的样式和行为特性。 通过使用代理,我们可以自定义复选框、按钮、进度条等控件,添加到视图中以进行用户交互。我们还可以定制排序、筛选、背景颜色等视觉效果,从而创建一个更有效的用户界面。 总之,Qt MVD框架具有高度的灵活性和可扩展性,允许用户对数据和视图进行个性化的定制,提高应用程序的用户体验和效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值