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

简介:
好久没有来写文章了,由于家里面宽带断了,所以一直没能更新,今天现在写上一篇。
 
还是继续前面的内容。前面我们分三次把自定义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
 
好了,运行一下看看效果吧!
 

本文转自 FinderCheng 51CTO博客,原文链接:http://blog.51cto.com/devbean/271255


相关文章
|
30天前
|
存储 机器学习/深度学习 人工智能
Qt魔法书:打造自定义鼠标键盘脚本(二)
Qt魔法书:打造自定义鼠标键盘脚本
34 0
|
3月前
QT自定义信号,信号emit,信号参数注册
使用signals声明返回值是void在需要发送信号的地方使用emit 信号名字(参数)进行发送在需要链接的地方使用connect进行链接ct进行链接。
19 0
QT自定义信号,信号emit,信号参数注册
|
3月前
Qt提升控件类为自定义类
Qt提升控件类为自定义类
|
4月前
|
搜索推荐 C++ 索引
C++ Qt开发:QItemDelegate自定义代理组件
在Qt中,`QStyledItemDelegate` 类是用于创建自定义表格视图(如`QTableView`和`QTableWidget`)的委托类,允许你自定义表格中每个单元格的外观和交互。`QStyledItemDelegate` 是`QItemDelegate` 的子类,提供了更现代、更易用的接口。此处我们将实现对`QTableView`表格组件的自定义代理功能,例如默认情况下表格中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重写编辑框实现选择的效果,代理组件常用于个性化定制表格中的字段类型。
37 0
C++ Qt开发:QItemDelegate自定义代理组件
|
3月前
Qt6学习笔记五(自定义对话框、QMessageBox、QColorDialog、QFileDialog、QFontDialog)
Qt6学习笔记五(自定义对话框、QMessageBox、QColorDialog、QFileDialog、QFontDialog)
40 0
|
30天前
|
开发框架 Linux API
Qt魔法书:打造自定义鼠标键盘脚本(一)
Qt魔法书:打造自定义鼠标键盘脚本
23 0
|
1月前
使用代码实现QT自定义布局
使用代码实现QT自定义布局
|
3月前
Qt6自定义QML控件的方式
Qt6自定义QML控件的方式
67 1
|
3月前
|
Ubuntu iOS开发 MacOS
Qt5标题栏自定义QHeaderView自定义
Qt5标题栏自定义QHeaderView自定义
91 0
|
4月前
|
C++
C++ Qt开发:自定义Dialog对话框组件
在之前的文章中笔者已经为大家展示了默认`Dialog`组件的使用方法,虽然内置组件支持对数据的输入,但有时候我们需要一次性输入多个数据,此时如果之使用默认模态对话框似乎不太够用,此时我们需要自己创建一个自定义对话框,需要说明的是此类对话框也是一种窗体,所以可以在其上面放置任何通用组件,以实现更多复杂的开发需求。自定义对话框需要解决的问题是,如何让父窗体与子窗体进行数据交换,要实现数据的交换有两种方式,第一种方式是通过动态加载模态对话框,当用户点击确定后通过`GetValue()`来拿到数据,而第二种方式则是通过发送信号的方式将数据投递给父窗体,这两种方式都可以,读者可根据自身需求来选择不同的通
36 1
C++ Qt开发:自定义Dialog对话框组件

推荐镜像

更多