Qt学习之路(48): 自定义委托

2023-11-16

好久没有来写文章了,由于家里面宽带断了,所以一直没能更新,今天现在写上一篇。
 
还是继续前面的内容。前面我们分三次把自定义model说完了,其实主要还是那三个实例。在 model/view 架构中,与model同等重要的就是 view。
 
我们知道,在经典的 MVC 模型中,view用于向用户展示 model 的数据。但是,Qt提供的不是 MVC 三层架构,而是一个 model/view 设计。这种设计并没有包含一个完整而独立的组件用于管理用户的交互。一般来说,view仅仅是用作对model数据的展示和对用户输入的处理,而不应该去做其他的工作。在这种结构中,为了获得对用户输入控制的灵活性,这种交互工作交给了delegate,也就是“委托”,去完成。简单来说,就像它们的名字一样,view 将用户输入委托给 delegate 处理,而自己不去处理这种输入。这些组件提供一种输入能力,并且能够在某些 view 中提供这种交互情形下的渲染,比如在 table 中通过双击单元格即可编辑内容等。对这种控制委托的标准接口被定义在 QAbstractItemDelegate 类中。
 
delegate 可以用于渲染内容,这是通过 paint() 和 sizeHint() 函数来完成的。但是,对于一些简单的基于组件的delegate,可以通过继承 QItemDelegate 或者 QStyledItemDelegate 来实现。这样就可以避免要完全重写 QAbstractItemDelegate 中所需要的所有函数。对于一些相对比较通用的函数,在这两个类中已经有了一个默认的实现。
 
Qt提供的标准组件使用 QItemDelegate 提供编辑功能的支持。这种默认的实现被用在 QListView,QTableView 和 QTreeView 之中。view 实用的delegate可以通过 itemDelegate() 函数获得。setItemDelegate() 函数则可以为一个标准组件设置自定义的 delegate。
 
Qt 4.4版本之后提供了两个可以被继承的delegate类:QItemDelegate 和 QStyledItemDelegate。默认的delegate是 QStyledItemDelegate。这两个类可以被相互替代,用于给view 组件提供绘制和编辑的功能。它们之间的主要区别在于,QStyledItemDelegate 使用当前的风格(style)去绘制组件。所以,在自定义delegate或者需要使用 Qt style sheets 时,建议使用 QStyledItemDelegate 作为父类。使用这两个类的代码通常是一样的,除了需要使用style进行绘制的部份。如果你希望为view item自定义绘制函数,最好实现一个自定义的style。这个你可以通过QStyle类来实现。
 
如果delegate没有支持为你的数据类型进行绘制,或者你希望自己绘制item,那么就可以继承 QStyledItemDelegate 类,并且重写 paint() 或者还需要重写 sizeHint() 函数。paint() 函数会被每一个item独立调用,而sizeHint()函数则可以定义每一个item 的大小。在重写 paint() 函数的时候,通常需要用 if 语句找到你需要进行渲染的数据类型并进行绘制,其他的数据类型需要调用父类的实现进行绘制。
 
一个自定义的delegate也可以直接提供一个编辑器,而不是使用内置的编辑器工厂(editor item factory)。如果你需要这种功能,那么需要实现一下几个函数:
  • createEditor(): 返回修改数据的组件;
  • setEditorData(): 为editor提供编辑的原始数据;
  • updateEditorGeometry(): 保证editor显示在 item view 的合适位置以及大小;
  • setModelData(): 根据editor 的数据更新model的数据。
好了,这就是一个自定义delegate的实现了。下面来看一个例子。
 
这是一个歌曲及其时间的例子。使用的是QTableWidget,一共有两列,第一列是歌曲名字,第二列是歌曲持续的时间。为了表示这个数据,我们建立一个Track类:
 
track.h
InBlock.gif#ifndef TRACK_H
InBlock.gif#define TRACK_H
InBlock.gif
InBlock.gif#include <QtCore>
InBlock.gif
InBlock.gif class Track
InBlock.gif{
InBlock.gif public:
InBlock.gif        Track( const QString &title = "", int duration = 0);
InBlock.gif
InBlock.gif        QString title;
InBlock.gif         int duration;
InBlock.gif};
InBlock.gif
InBlock.gif#endif // TRACK_H
 
track.cpp
InBlock.gif#include "track.h"
InBlock.gif
InBlock.gifTrack::Track( const QString &title, int duration)
InBlock.gif        : title(title), duration(duration)
InBlock.gif{
InBlock.gif}
 
这个类的构造函数没有做任何操作,只是把title和duration这两个参数通过构造函数初始化列表赋值给内部的成员变量。注意,现在这两个成员变量都是public的,在正式的程序中应该声明为private的才对。然后来看TrackDelegate类:
 
trackdelegate.h
InBlock.gif#ifndef TRACKDELEGATE_H
InBlock.gif#define TRACKDELEGATE_H
InBlock.gif
InBlock.gif#include <QtGui>
InBlock.gif
InBlock.gif class TrackDelegate : public QStyledItemDelegate
InBlock.gif{
InBlock.gif        Q_OBJECT
InBlock.gif
InBlock.gif public:
InBlock.gif        TrackDelegate( int durationColumn, QObject *parent = 0);
InBlock.gif
InBlock.gif         void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
InBlock.gif        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
InBlock.gif         void setEditorData(QWidget *editor, const QModelIndex &index) const;
InBlock.gif         void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
InBlock.gif
InBlock.gif private slots:
InBlock.gif         void commitAndCloseEditor();
InBlock.gif
InBlock.gif private:
InBlock.gif         int durationColumn;
InBlock.gif};
InBlock.gif
InBlock.gif
InBlock.gif#endif // TRACKDELEGATE_H
 
trackdelegate.cpp
InBlock.gif#include "trackdelegate.h"
InBlock.gif
InBlock.gifTrackDelegate::TrackDelegate( int durationColumn, QObject *parent)
InBlock.gif        : QStyledItemDelegate(parent)
InBlock.gif{
InBlock.gif         this->durationColumn = durationColumn;
InBlock.gif}
InBlock.gif
InBlock.gif void TrackDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
InBlock.gif{
InBlock.gif         if (index.column() == durationColumn) {
InBlock.gif                 int secs = index.model()->data(index, Qt::DisplayRole).toInt();
InBlock.gif                QString text = QString( "%1:%2").arg(secs / 60, 2, 10, QChar('0')).arg(secs % 60, 2, 10, QChar('0'));
InBlock.gif                QTextOption o(Qt::AlignRight | Qt::AlignVCenter);
InBlock.gif                painter->drawText(option.rect, text, o);
InBlock.gif        } else {
InBlock.gif                QStyledItemDelegate::paint(painter, option, index);
InBlock.gif        }
InBlock.gif}
InBlock.gif
InBlock.gifQWidget *TrackDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
InBlock.gif{
InBlock.gif         if (index.column() == durationColumn) {
InBlock.gif                QTimeEdit *timeEdit = new QTimeEdit(parent);
InBlock.gif                timeEdit->setDisplayFormat( "mm:ss");
InBlock.gif                connect(timeEdit, SIGNAL(editingFinished()), this, SLOT(commitAndCloseEditor()));
InBlock.gif                 return timeEdit;
InBlock.gif        } else {
InBlock.gif                 return QStyledItemDelegate::createEditor(parent, option, index);
InBlock.gif        }
InBlock.gif}
InBlock.gif
InBlock.gif void TrackDelegate::commitAndCloseEditor()
InBlock.gif{
InBlock.gif        QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender());
InBlock.gif        emit commitData(editor);
InBlock.gif        emit closeEditor(editor);
InBlock.gif}
InBlock.gif
InBlock.gif void TrackDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
InBlock.gif{
InBlock.gif         if (index.column() == durationColumn) {
InBlock.gif                 int secs = index.model()->data(index, Qt::DisplayRole).toInt();
InBlock.gif                QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
InBlock.gif                timeEdit->setTime(QTime(0, secs / 60, secs % 60));
InBlock.gif        } else {
InBlock.gif                QStyledItemDelegate::setEditorData(editor, index);
InBlock.gif        }
InBlock.gif}
InBlock.gif
InBlock.gif void TrackDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
InBlock.gif{
InBlock.gif         if (index.column() == durationColumn) {
InBlock.gif                QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
InBlock.gif                QTime time = timeEdit->time();
InBlock.gif                 int secs = (time.minute() * 60) + time.second();
InBlock.gif                model->setData(index, secs);
InBlock.gif        } else {
InBlock.gif                QStyledItemDelegate::setModelData(editor, model, index);
InBlock.gif        }
InBlock.gif}
 
正如前面所说的,这个类继承了QStyledItemDelegate,覆盖了其中的四个函数。通过前面的讲解,我们已经了解到这些函数的作用。至于实现,我们前面也说过,需要通过QModelIndex选择我们需要进行渲染的列,然后剩下的数据类型仍然需要显式地调用父类的相应函数。由于我们在Track里面存储的是歌曲的秒数,所以在paint()里面需要用除法计算出分钟数,用%60计算秒数。其他的函数都比较清楚,请注意代码。
 
最后写一个使用的类:
 
trackeditor.h
InBlock.gif#ifndef TRACKEDITOR_H
InBlock.gif#define TRACKEDITOR_H
InBlock.gif
InBlock.gif#include <QtGui>
InBlock.gif#include "track.h"
InBlock.gif
InBlock.gif class TrackEditor : public QDialog
InBlock.gif{
InBlock.gif        Q_OBJECT
InBlock.gif
InBlock.gif public:
InBlock.gif        TrackEditor(QList<Track> *tracks, QWidget *parent);
InBlock.gif
InBlock.gif private:
InBlock.gif        QList<Track> *tracks;
InBlock.gif        QTableWidget *tableWidget;
InBlock.gif};
InBlock.gif
InBlock.gif#endif // TRACKEDITOR_H
 
trackeditor.cpp
InBlock.gif#include "trackeditor.h"
InBlock.gif#include "trackdelegate.h"
InBlock.gif
InBlock.gifTrackEditor::TrackEditor(QList<Track> *tracks, QWidget *parent)
InBlock.gif        : QDialog(parent)
InBlock.gif{
InBlock.gif         this->tracks = tracks;
InBlock.gif
InBlock.gif        tableWidget = new QTableWidget(tracks->count(), 2);
InBlock.gif        tableWidget->setItemDelegate( new TrackDelegate(1));
InBlock.gif        tableWidget->setHorizontalHeaderLabels(QStringList() << tr( "Track") << tr( "Duration"));
InBlock.gif
InBlock.gif         for ( int row = 0; row < tracks->count(); ++row) {
InBlock.gif                Track track = tracks->at(row);
InBlock.gif
InBlock.gif                QTableWidgetItem *item0 = new QTableWidgetItem(track.title);
InBlock.gif                tableWidget->setItem(row, 0, item0);
InBlock.gif
InBlock.gif                QTableWidgetItem *item1 = new QTableWidgetItem(QString::number(track.duration));
InBlock.gif                item1->setTextAlignment(Qt::AlignRight);
InBlock.gif                tableWidget->setItem(row, 1, item1);
InBlock.gif        }
InBlock.gif
InBlock.gif        QVBoxLayout *mainLayout = new QVBoxLayout;
InBlock.gif        mainLayout->addWidget(tableWidget);
InBlock.gif         this->setLayout(mainLayout);
InBlock.gif}
 
其实也并没有很大的不同,只是我们使用setItemDelegate()函数设置了一下delegate。然后写main()函数:
 
InBlock.gif#include <QtGui>
InBlock.gif#include "trackeditor.h"
InBlock.gif
InBlock.gif int main( int argc, char *argv[])
InBlock.gif{
InBlock.gif        QApplication a(argc, argv);
InBlock.gif        QList<Track> tracks;
InBlock.gif        Track t1( "Song 1", 200);
InBlock.gif        Track t2( "Song 2", 150);
InBlock.gif        Track t3( "Song 3", 120);
InBlock.gif        Track t4( "Song 4", 210);
InBlock.gif        tracks << t1 << t2 << t3 << t4;
InBlock.gif        TrackEditor te(&tracks, NULL);
InBlock.gif        te.show();
InBlock.gif         return a.exec();
InBlock.gif}
 
好了,运行一下看看效果吧!
 

转载于:https://blog.51cto.com/devbean/271255

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt学习之路(48): 自定义委托 的相关文章

  • rac集群节点级联重启故障分析

    author skate time 2012 07 16 无意中发现以前处理故障写的一篇文章 记录下来以备查找 rac集群节点级联重启故障分析 环境 os linux db rac10g ocfs2 rac数据库环境实际包含两个集群 一个是
  • 测试用例设计方法——等价类

    等价类 思路 输入的集合是无穷的 不能全部都覆盖到 依据需求将输入 特殊情况下会考虑输出 划分为若干个等价类 从等价类中挑选一个测试用例 如果这个测试用例通过 则认为所代表的等价类通过 这样就可以用较少的测试用例达到尽可能多的功能覆盖 解决
  • (服务计算)在centos上编写golang的库,并进行测试

    首先是在centos上按照老师给的教程安装golang的相关内容 安装成功后进行后面的操作 首先是创建了一个hello go的文件 然后执行结果如下 可知安装基本正确 然后编写第一个库 首先创建包路径 然后创建名为reverse go的文件
  • 用户注册及登录测试用例小记

    用户注册及登录测试用例
  • Servlet+JSP+JavaBean开发模式(MVC)介绍

    好伤心 写登陆注册之前看见一篇很好的博文 没有收藏 然后找不到了 前几天在知乎上看见一个问题 什么时候感觉最无力 前两天一直想回答 尝试过google到的所有solve case 结果bug依然在 今天想回答 明明遇见过 就是找不到那篇文的
  • Ubuntu18.04环境下Gtest框架安装测试

    准备 在安装Gtest框架之前在linux上安装好cmke g 工具 sudo apt get install cmake sudo apt get install g 方法一 下载Gtest压缩包解压安装 最新版本的Gtest下载链接 h
  • Bicubic Interpolation (双三次插值)

    在Wikipedia http en wikipedia org wiki Bicubic interpolation 上找到了bicubic的描述 不过它只给出了知道导数情况下的公式 后来在CSDN上找到了C语言的算法描述 http to
  • 导入 导出 jeesite框架封装

    为什么80 的码农都做不了架构师 gt gt gt JeeSite的Excel导入 导出 支持大数据量 使用annotation最小化配置 介绍 对Apache POI 3 9的简单封装 实现Excel的导出导入功能 使用Annotatio
  • 微软和Facebook推出Docs.com

    来源 德胜网 4月22日 据国外媒体报道 Facebook和微软在联手一项在线文件共享服务 看起来多一点像Google Docs 周三在Facebook的F8会议Docs com上线 该网站允许Facebook用户使用Facebook连接登
  • 常用电子元器件简介

    一 电阻器 电阻器 一般情况下也称电阻 是一种阻碍电流在电路中流动的线性元件 也是组成电子电路的主要元件之一 1 电阻器的作用及电路图形符号 1 电阻器的作用 电阻器主要用于控制电路中的电压和电流 除了具有降压 分压 限流和分流作用外 还具
  • 从零开始写一个Javascript解析器

    最近在研究 AST 之前有一篇文章 面试官 你了解过 Babel 吗 写过 Babel 插件吗 答 没有 卒 为什么要去了解它 因为懂得 AST 真的可以为所欲为 简单点说 使用 Javascript 运行Javascript代码 这篇文章
  • HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException:...

    1 HTTP Status 500 Request processing failed nested exception is java lang IllegalArgumentException Control character in
  • 高效程序员的40个好习惯和行为方式

    每一个好的习惯 开头都会相应有一个唱反调的句子哦 1 做事 出了问题 第一重要的是确定元凶 找到那个人 一旦证实了是他的错误 就可以保证这样的问题永远也不会再发生了 指责不会修复bug 把矛头对准问题的解决办法 而不是人 这是真正有用处的正
  • 金融类测试的总结

    金融测试前后端 前端 执行页面级测试用例 验证应用层基本功能 能是否和需求一致 页面风格是否一致 金额 利息 以及对应的状态是否正确等 后端 通过测试页面 录入测试用例 比对结果 为了看数字金额的准确性 也是确认金融底层的正确性以及逻辑性
  • 使用SpringSecurity

    前几天写了一个SpringBoot对拦截器的使用 在实际项目中 对一些情况需要做一些安全验证 比如在没有登录的情况下访问特定的页面应该解释的拦截处理 这一篇介绍使用SpringSecurity来做简单的安全控制 由于SpringSecuri
  • 基于 LoadRunner 实现企业级服务器端性能测试的实践

    后端性能测试工具首先通过虚拟用户脚本生成器生成基于协议的虚拟用户脚本 然后根据性能测试场景设计的要求 通过压力控制器控制协调各个压力产生器以并发的方式执行虚拟用户脚本 并且在测试执行过程中 通过系统监控器收集各种性能指标以及系统资源占用率
  • 软件测试 app自动化02 Appium常用的元素定位工具 元素的属性 元素定位方法

    文章目录 1 Appium常用的元素定位工具 1 1 uiautomatorviewer 1 2 Appium Inspector 1 3 Weditor 2 元素的属性 3 元素定位方法 小结 1 Appium常用的元素定位工具 1 1
  • 重命名文件或目录(renameTo)

    File or directory with old name File file new File oldname File or directory with new name File file2 new File newname R
  • 如何从零开始搭建公司自动化测试框架?

    搭建的自动化测试框架要包括API测试 UI测试 APP测试三类 以上三类其实可以简化为两类 那就是 1 接口自动化测试框架搭建 2 UI自动化测试框架搭建 没问题 安排 且是手把手教你如何搭建以上两类自动化测试框架 回到这篇主题 刷到这个问
  • ASTM D6147测定压缩情况下硫化橡胶和热塑弹性体作用力衰减 (应力松弛) 的标准试验方法

    标准名称 ASTM D6147 Standard Test Method for Vulcanized Rubber and Thermoplastic Elastomer Determination of Force Decay Stre

随机推荐