Delegate 类 概念 与MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件。一般来讲, view负责把数据展示给用户,也处理用户的输入。为了获得更多的灵性性,交互通过d

Delegate  类

概念 MVC模式不同,model/view结构没有用于与用户交互的完全独立的组件。一般来讲, view负责把数据展示给用户,也处理用户的输入。为了获得更多的灵性性,交互通过delegagte执行。它既提供输入功能又负责渲染view中的每个数据项。 
使用Delegate的原因  Qt中当用到QTreeView和QTableView等用于显示item的视图时,你要编辑一个item用到的编辑工具可能是除了默认文字编辑lineEdit以外的工具,例如button,spinBox,甚至Slider,ProgressBar,也有可能是自定义的widget。所以Qt提供了一个委托类,用来处理View中的数据展示方式。

Delegate类的继承架构见下图,

        自从Qt4.4,出现了两个delegate基类,QStyledItemDelegate vs. QItemDelegate。默认的delegate是QStyledItemDelegate,即你不自己写delegate的时候,默认那个lineEdit是来自QStyledItemDelegate。Qt Assistant建议用户如果自定义delegate或者用到了Qt style sheets的话,最好继承自QStyledItemDelegate,为什么呢?首先这两个类在绘制代理和为item提供编辑器上面是独立的,没什么联系,互不影响;不同的是QStyledItemDelegate使用当前style来绘制item(的代理),即如果程序设置了总体的风格(用QSS或其他定义方式),QStyledItemDelegate会使用这个风格设置。

先看看Qt Demos看了里面spinboxDelegat的例子:
1. 自定义的delegate继承自QItemDelegate。

2. 必须重载的一些函数:
       (1)  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const;
       (2)  void setEditorData(QWidget *editor, const QModelIndex &index) const;
       (3)  void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const;
       (4)  void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const;

3. createEditor创建自定义widget并返回之。
        setEditorData是将model中当前有的数据设置到代理上。自己从model取出数据,自己setValue到editor上。
        setModelData是将editor上的数据保存到Model中。
        updateEditorGeometry就是将editor设置到一定位置,并且有一定大小,使这个editor看起来像是正好嵌入到格子里面一样。用的是option.rect。

4. closeEditor() signal 表明用户完成编辑数据,编辑控件可以销毁。

5. commitData() signal 必须在完成编辑数据之后,发送该信号,将会把新数据写回Model

6. paint() and sizeHint(), QitemDelegate默认继承了该方法,如果需要特殊风格绘制单元项中内容,还需重载这两个函数。


一、

   SpinBoxDelegate例子是Qt Assistant中提供的一个非常优秀的例子,虽然讲的是继承于QItemDelegate的例子。但对于我们理解Delegate-委托这个概念,非常有帮助。

它重载了必须的几个函数:

        (1)  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const;
       (2)  void setEditorData(QWidget *editor, const QModelIndex &index) const;
       (3)  void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const;
       (4)  void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const;

下面把源码附上,并加上部分注释。附件有源码可以下载。

 

Main.cpp

 

Cpp代码   收藏代码
  1. #include <QApplication>  
  2. #include <QHeaderView>  
  3. #include <QItemSelectionModel>  
  4. #include <QStandardItemModel>  
  5. #include <QTableView>  
  6.   
  7. #include "delegate.h"  
  8.   
  9.   
  10. int main(int argc, char *argv[])  
  11. {  
  12.     QApplication app(argc, argv);  
  13.   
  14.     //构建一个4行,2列的项模型  
  15.     QStandardItemModel model(4, 2);  
  16.     //声明一个TableView  
  17.     QTableView tableView;  
  18.     //绑定模型  
  19.     tableView.setModel(&model);  
  20.     //声明一个委托  
  21.     SpinBoxDelegate delegate;  
  22.     //设定视图的委托  
  23.     tableView.setItemDelegate(&delegate);  
  24.     //ensuring that the view does not waste any of the space assigned to it for its header  
  25.     //最后一列全部填充View  
  26.     tableView.horizontalHeader()->setStretchLastSection(true);  
  27.   
  28.     //初始化Model  
  29.     for (int row = 0; row < 4; ++row) {  
  30.         for (int column = 0; column < 2; ++column) {  
  31.             QModelIndex index = model.index(row, column, QModelIndex());  
  32.             model.setData(index, QVariant((row+1) * (column+1)));  
  33.         }  
  34.   
  35.     }  
  36.   
  37.   
  38.     tableView.setWindowTitle(QObject::tr("Spin Box Delegate"));  
  39.     tableView.show();  
  40.     return app.exec();  
  41. }  

 

 

 delegate.h

 

Cpp代码   收藏代码
  1. #ifndef DELEGATE_H  
  2. #define DELEGATE_H  
  3.   
  4. #include <QItemDelegate>  
  5. #include <QModelIndex>  
  6. #include <QObject>  
  7. #include <QSize>  
  8. #include <QSpinBox>  
  9.   
  10.   
  11. class SpinBoxDelegate : public QItemDelegate  
  12. {  
  13.     Q_OBJECT  
  14.   
  15. public:  
  16.     SpinBoxDelegate(QObject *parent = 0);  
  17.   
  18.     //返回一个编辑控件,用来编辑指定项的数据  
  19.     QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,  
  20.                           const QModelIndex &index) const;  
  21.     //将Model中数据赋值到控件上  
  22.     void setEditorData(QWidget *editor, const QModelIndex &index) const;  
  23.     //设定模型数据,根据指定项中对应编辑控件的数据  
  24.     void setModelData(QWidget *editor, QAbstractItemModel *model,  
  25.                       const QModelIndex &index) const;  
  26.     //更新编辑框几何形状  
  27.     void updateEditorGeometry(QWidget *editor,  
  28.         const QStyleOptionViewItem &option, const QModelIndex &index) const;  
  29. };  
  30.   
  31.   
  32. #endif  

 

 

 delegate.cpp

 

Cpp代码   收藏代码
  1. #include <QtGui>  
  2.   
  3. #include "delegate.h"  
  4.   
  5.   
  6. SpinBoxDelegate::SpinBoxDelegate(QObject *parent)  
  7.     : QItemDelegate(parent)  
  8. {  
  9. }  
  10.   
  11. //返回一个编辑控件,用来编辑指定项的数据  
  12. QWidget *SpinBoxDelegate::createEditor(QWidget *parent,  
  13.     const QStyleOptionViewItem &/* option */,  
  14.     const QModelIndex &/* index */const  
  15. {  
  16.     //返回该QSpinBox控件  
  17.     QSpinBox *editor = new QSpinBox(parent);  
  18.     editor->setMinimum(0);  
  19.     editor->setMaximum(100);  
  20.   
  21.     return editor;  
  22. }  
  23. //将Model中数据赋值到控件上  
  24. void SpinBoxDelegate::setEditorData(QWidget *editor,  
  25.                                     const QModelIndex &index) const  
  26. {  
  27.     //返回该索引的模型,继而返回该模型中此索引的编辑角色数据  
  28.     int value = index.model()->data(index, Qt::EditRole).toInt();  
  29.     //给控件赋值  
  30.     QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  
  31.     spinBox->setValue(value);  
  32. }  
  33. //设定模型数据,根据指定项中对应编辑控件的数据  
  34. void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,  
  35.                                    const QModelIndex &index) const  
  36. {  
  37.     QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  
  38.     spinBox->interpretText();  
  39.     int value = spinBox->value();  
  40.     //设置模型的数据  
  41.     model->setData(index, value, Qt::EditRole);  
  42. }  
  43. //更新编辑框几何形状  
  44. void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,  
  45.     const QStyleOptionViewItem &option, const QModelIndex &/* index */const  
  46. {  
  47.     //根据option,设置编辑框位置  
  48.     editor->setGeometry(option.rect);  
  49. }  
  50. 二、
  51.  trackeEditorDelegate例子是《 C++ GUI Programming with Qt 4》中自定义委托的标准例子。


    和上一个SpinBox例子相比更完整:它多了自定义Editor、重载Paint()函数、提交数据信号commitData()、关闭控件信号closeEditor()

    附件中有源码可以下载。

     

    Main.cpp

     

    Cpp代码   收藏代码
    1. #include <QApplication>  
    2.   
    3. #include "trackeditor.h"  
    4.   
    5. int main(int argc, char *argv[])  
    6. {  
    7.     QApplication app(argc, argv);  
    8.   
    9.     QList<Track> tracks;  
    10.     tracks << Track("The Flying Dutchman: Overture", 630)  
    11.            << Track("The Flying Dutchman: Wie aus der Fern laengst "  
    12.                     "vergangner Zeiten", 374)  
    13.            << Track("The Flying Dutchman: Steuermann, lass die Wacht",  
    14.                     152)  
    15.            << Track("Die Walkuere: Ride of the Valkyries", 286)  
    16.            << Track("Tannhaeuser: Freudig begruessen wir die edle "  
    17.                     "Halle", 384)  
    18.            << Track("Tannhaeuser: Wie Todesahnung - O du mein holder "  
    19.                     "Abendstern", 257)  
    20.            << Track("Lohengrin: Treulich gefuert ziehet dahnin", 294)  
    21.            << Track("Lohengrin: In fernem Land", 383)  
    22.            << Track("Die Meistersinger von Nuernberg: Overture", 543)  
    23.            << Track("Die Meistersinger von Nuernberg: Verachtet mir "  
    24.                     "die Meister nicht", 200)  
    25.            << Track("Die Meistersinger von Nuernberg: Ehrt eure "  
    26.                     "deutschen Meister", 112)  
    27.            << Track("Goetterdaemmerung: Funeral Music", 469)  
    28.            << Track("Tristan und Isolde: Mild und leise, wie er "  
    29.                     "laechelt", 375);  
    30.     //自定义编辑控件  
    31.     TrackEditor editor(&tracks);  
    32.     editor.resize(600, 300);  
    33.     editor.show();  
    34.   
    35.     return app.exec();  
    36. }  

     

     TrackEditor.h

     

    Cpp代码   收藏代码
    1. #ifndef TRACKEDITOR_H  
    2. #define TRACKEDITOR_H  
    3.   
    4. #include <QDialog>  
    5. #include <QList>  
    6.   
    7. class QDialogButtonBox;  
    8. class QTableWidget;  
    9.   
    10. class Track  
    11. {  
    12. public:  
    13.     Track(const QString &title = ""int duration = 0);  
    14.   
    15.     QString title;  
    16.     int duration;  
    17. };  
    18.   
    19. class TrackEditor : public QDialog  
    20. {  
    21.     Q_OBJECT  
    22.   
    23. public:  
    24.     TrackEditor(QList<Track> *tracks, QWidget *parent = 0);  
    25.   
    26.     void done(int result);  
    27.   
    28. private slots:  
    29.     void addTrack();  
    30.   
    31. private:  
    32.     QTableWidget *tableWidget;  
    33.     QDialogButtonBox *buttonBox;  
    34.     QList<Track> *tracks;  
    35. };  
    36.   
    37. #endif  

     

    TrackEditor.cpp

     

    Cpp代码   收藏代码
    1. #include <QtGui>  
    2.   
    3. #include "trackdelegate.h"  
    4. #include "trackeditor.h"  
    5.   
    6. Track::Track(const QString &title, int duration)  
    7. {  
    8.     this->title = title;  
    9.     this->duration = duration;  
    10. }  
    11.   
    12. TrackEditor::TrackEditor(QList<Track> *tracks, QWidget *parent)  
    13.     : QDialog(parent)  
    14. {  
    15.     this->tracks = tracks;  
    16.     //视图控件  
    17.     tableWidget = new QTableWidget(tracks->count(), 2);  
    18.     //设置委托  
    19.     tableWidget->setItemDelegate(new TrackDelegate(1));  
    20.     //设置视图列头  
    21.     tableWidget->setHorizontalHeaderLabels(  
    22.             QStringList() << tr("Track") << tr("Duration"));  
    23.     //初始化视图内容  
    24.     for (int row = 0; row < tracks->count(); ++row) {  
    25.         Track track = tracks->at(row);  
    26.   
    27.         QTableWidgetItem *item0 = new QTableWidgetItem(track.title);  
    28.         //设置第0列中所有项  
    29.         tableWidget->setItem(row, 0, item0);  
    30.   
    31.         QTableWidgetItem *item1  
    32.              = new QTableWidgetItem(QString::number(track.duration));  
    33.         item1->setTextAlignment(Qt::AlignRight);  
    34.         //设置第1列所有项  
    35.         tableWidget->setItem(row, 1, item1);  
    36.     }  
    37.     //根据委托的SizeHint,重新设置视图大小  
    38.     tableWidget->resizeColumnToContents(0);  
    39.   
    40.     buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok  
    41.                                      | QDialogButtonBox::Cancel);  
    42.     QPushButton *addTrackButton = buttonBox->addButton(tr("&Add Track"),  
    43.             QDialogButtonBox::ActionRole);  
    44.   
    45.     connect(addTrackButton, SIGNAL(clicked()), this, SLOT(addTrack()));  
    46.     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));  
    47.     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));  
    48.   
    49.     QVBoxLayout *mainLayout = new QVBoxLayout;  
    50.     mainLayout->addWidget(tableWidget);  
    51.     mainLayout->addWidget(buttonBox);  
    52.     setLayout(mainLayout);  
    53.   
    54.     setWindowTitle(tr("Track Editor"));  
    55. }  
    56.   
    57. void TrackEditor::done(int result)  
    58. {  
    59.     //ok  
    60.     if (result == QDialog::Accepted) {  
    61.         tracks->clear();  
    62.         for (int row = 0; row < tableWidget->rowCount(); ++row) {  
    63.             QString title = tableWidget->item(row, 0)->text();  
    64.             QTableWidgetItem *item = tableWidget->item(row, 1);  
    65.             int duration = item ? item->text().toInt() : 0;  
    66.             tracks->append(Track(title, duration));  
    67.         }  
    68.     }  
    69.     QDialog::done(result);  
    70. }  
    71.   
    72. void TrackEditor::addTrack()  
    73. {  
    74.     //在最后新插入一行  
    75.     tableWidget->insertRow(tableWidget->rowCount());  
    76. }  

     

    TrackDelegate.h

     

    Cpp代码   收藏代码
    1. #ifndef TRACKDELEGATE_H  
    2. #define TRACKDELEGATE_H  
    3.   
    4. #include <QItemDelegate>  
    5.   
    6. class TrackDelegate : public QItemDelegate  
    7. {  
    8.     Q_OBJECT  
    9.   
    10. public:  
    11.     TrackDelegate(int durationColumn, QObject *parent = 0);  
    12.     //重新绘制  
    13.     void paint(QPainter *painter, const QStyleOptionViewItem &option,  
    14.                const QModelIndex &index) const;  
    15.     QWidget *createEditor(QWidget *parent,  
    16.                           const QStyleOptionViewItem &option,  
    17.                           const QModelIndex &index) const;  
    18.     void setEditorData(QWidget *editor, const QModelIndex &index) const;  
    19.     void setModelData(QWidget *editor, QAbstractItemModel *model,  
    20.                       const QModelIndex &index) const;  
    21.   
    22. private slots:  
    23.     void commitAndCloseEditor();  
    24.   
    25. private:  
    26.     int durationColumn;  
    27. };  
    28.   
    29. #endif  

     TrackDelegate.cpp

     

    Cpp代码   收藏代码
    1. #include <QtGui>  
    2.   
    3. #include "trackdelegate.h"  
    4.   
    5. TrackDelegate::TrackDelegate(int durationColumn, QObject *parent)  
    6.     : QItemDelegate(parent)  
    7. {  
    8.     this->durationColumn = durationColumn;  
    9. }  
    10.   
    11. void TrackDelegate::paint(QPainter *painter,  
    12.                           const QStyleOptionViewItem &option,  
    13.                           const QModelIndex &index) const  
    14. {  
    15.     //保存音轨的列  
    16.     if (index.column() == durationColumn) {  
    17.         //获得索引对应Model中的数据  
    18.         int secs = index.model()->data(index, Qt::DisplayRole).toInt();  
    19.         //设置时间格式字符串 分:秒  
    20.         QString text = QString("%1:%2")  
    21.                        .arg(secs / 60, 2, 10, QChar('0'))  
    22.                        .arg(secs % 60, 2, 10, QChar('0'));  
    23.         //获取项风格设置  
    24.         QStyleOptionViewItem myOption = option;  
    25.         myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;  
    26.         //绘制文本  
    27.         drawDisplay(painter, myOption, myOption.rect, text);  
    28.         //如果当前有焦点,就绘制一个焦点矩形,否则什么都不做  
    29.         drawFocus(painter, myOption, myOption.rect);  
    30.     } else{  
    31.         //否则默认  
    32.         QItemDelegate::paint(painter, option, index);  
    33.     }  
    34. }  
    35.   
    36. QWidget *TrackDelegate::createEditor(QWidget *parent,  
    37.         const QStyleOptionViewItem &option,  
    38.         const QModelIndex &index) const  
    39. {  
    40.     //音轨时间列  
    41.     if (index.column() == durationColumn) {  
    42.         //时间编辑控件  
    43.         QTimeEdit *timeEdit = new QTimeEdit(parent);  
    44.         //时间编辑控件文本格式  
    45.         timeEdit->setDisplayFormat("mm:ss");  
    46.         //如果编辑结束,激活提交和关闭槽  
    47.         connect(timeEdit, SIGNAL(editingFinished()),  
    48.                 this, SLOT(commitAndCloseEditor()));  
    49.         return timeEdit;  
    50.     } else {  
    51.         //否则使用默认委托处理  
    52.         return QItemDelegate::createEditor(parent, option, index);  
    53.     }  
    54. }  
    55. //设置控件值  
    56. void TrackDelegate::setEditorData(QWidget *editor,  
    57.                                   const QModelIndex &index) const  
    58. {  
    59.     //音轨时间列  
    60.     if (index.column() == durationColumn) {  
    61.         //获得当前索引在Model中的值  
    62.         int secs = index.model()->data(index, Qt::DisplayRole).toInt();  
    63.         //设置时间控件的值  
    64.         QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);  
    65.         timeEdit->setTime(QTime(0, secs / 60, secs % 60));  
    66.     } else {  
    67.         QItemDelegate::setEditorData(editor, index);  
    68.     }  
    69. }  
    70. //设置Model值  
    71. void TrackDelegate::setModelData(QWidget *editor,  
    72.                                  QAbstractItemModel *model,  
    73.                                  const QModelIndex &index) const  
    74. {  
    75.     //音轨列  
    76.     if (index.column() == durationColumn) {  
    77.         //获得时间控件值  
    78.         QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);  
    79.         QTime time = timeEdit->time();  
    80.         int secs = (time.minute() * 60) + time.second();  
    81.         //设置模型值  
    82.         model->setData(index, secs);  
    83.     } else {  
    84.         //否则使用默认委托处理  
    85.         QItemDelegate::setModelData(editor, model, index);  
    86.     }  
    87. }  
    88. //自定义 提交和关闭 槽函数  
    89. void TrackDelegate::commitAndCloseEditor()  
    90. {  
    91.     QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender());  
    92.     //提交该控件的值 否则模型中数据不更改  
    93.     emit commitData(editor);  
    94.     //关闭该控件 可以告知委托去代开下一个控件  
    95.     emit closeEditor(editor);  
    96. }  
    三、

  52.  class StarDelegate : public QStyledItemDelegate
     {
         Q_OBJECT
    
     public:
         StarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
    
         void paint(QPainter *painter, const QStyleOptionViewItem &option,
                    const QModelIndex &index) const;
         QSize sizeHint(const QStyleOptionViewItem &option,
                        const QModelIndex &index) const;
         QWidget *createEditor(QWidget *parent, 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;
    
     private slots:
         void commitAndCloseEditor();
     };

    All public functions are reimplemented virtual functions from QItemDelegate to provide custom rendering and editing.

    StarDelegate Class Implementation

    The paint() function is reimplemented from QItemDelegate and is called whenever the view needs to repaint an item:

     void StarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const
     {
         if (qVariantCanConvert<StarRating>(index.data())) {
             StarRating starRating = qVariantValue<StarRating>(index.data());
    
             if (option.state & QStyle::State_Selected)
                 painter->fillRect(option.rect, option.palette.highlight());
    
             starRating.paint(painter, option.rect, option.palette,
                              StarRating::ReadOnly);
         } else {
             QStyledItemDelegate::paint(painter, option, index);
         }

    The function is invoked once for each item, represented by a QModelIndex object from the model. If the data stored in the item is aStarRating, we paint it ourselves; otherwise, we let QItemDelegate paint it for us. This ensures that the StarDelegate can handle the most common data types.

    In the case where the item is a StarRating, we draw the background if the item is selected, and we draw the item usingStarRating::paint(), which we will review later.

    StartRatings can be stored in a QVariant thanks to the Q_DECLARE_METATYPE() macro appearing in starrating.h. More on this later.

    The createEditor() function is called when the user starts editing an item:

     QWidget *StarDelegate::createEditor(QWidget *parent,
                                         const QStyleOptionViewItem &option,
                                         const QModelIndex &index) const
    
     {
         if (qVariantCanConvert<StarRating>(index.data())) {
             StarEditor *editor = new StarEditor(parent);
             connect(editor, SIGNAL(editingFinished()),
                     this, SLOT(commitAndCloseEditor()));
             return editor;
         } else {
             return QStyledItemDelegate::createEditor(parent, option, index);
         }
     }

    If the item is a StarRating, we create a StarEditor and connect its editingFinished() signal to our commitAndCloseEditor() slot, so we can update the model when the editor closes.

    Here's the implementation of commitAndCloseEditor():

     void StarDelegate::commitAndCloseEditor()
     {
         StarEditor *editor = qobject_cast<StarEditor *>(sender());
         emit commitData(editor);
         emit closeEditor(editor);
     }

    When the user is done editing, we emit commitData() and closeEditor() (both declared in QAbstractItemDelegate), to tell the model that there is edited data and to inform the view that the editor is no longer needed.

    The setEditorData() function is called when an editor is created to initialize it with data from the model:

     void StarDelegate::setEditorData(QWidget *editor,
                                      const QModelIndex &index) const
     {
         if (qVariantCanConvert<StarRating>(index.data())) {
             StarRating starRating = qVariantValue<StarRating>(index.data());
             StarEditor *starEditor = qobject_cast<StarEditor *>(editor);
             starEditor->setStarRating(starRating);
         } else {
             QStyledItemDelegate::setEditorData(editor, index);
         }
     }

    We simply call setStarRating() on the editor.

    The setModelData() function is called when editing is finished, to commit data from the editor to the model:

     void StarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                     const QModelIndex &index) const
     {
         if (qVariantCanConvert<StarRating>(index.data())) {
             StarEditor *starEditor = qobject_cast<StarEditor *>(editor);
             model->setData(index, qVariantFromValue(starEditor->starRating()));
         } else {
             QStyledItemDelegate::setModelData(editor, model, index);
         }
     }

    The sizeHint() function returns an item's preferred size:

     QSize StarDelegate::sizeHint(const QStyleOptionViewItem &option,
                                  const QModelIndex &index) const
     {
         if (qVariantCanConvert<StarRating>(index.data())) {
             StarRating starRating = qVariantValue<StarRating>(index.data());
             return starRating.sizeHint();
         } else {
             return QStyledItemDelegate::sizeHint(option, index);
         }
     }

    We simply forward the call to StarRating.

    StarEditor Class Definition

    The StarEditor class was used when implementing StarDelegate. Here's the class definition:

     class StarEditor : public QWidget
     {
         Q_OBJECT
    
     public:
         StarEditor(QWidget *parent = 0);
    
         QSize sizeHint() const;
         void setStarRating(const StarRating &starRating) {
             myStarRating = starRating;
         }
         StarRating starRating() { return myStarRating; }
    
     signals:
         void editingFinished();
    
     protected:
         void paintEvent(QPaintEvent *event);
         void mouseMoveEvent(QMouseEvent *event);
         void mouseReleaseEvent(QMouseEvent *event);
    
     private:
         int starAtPosition(int x);
    
         StarRating myStarRating;
     };

    The class lets the user edit a StarRating by moving the mouse over the editor. It emits the editingFinished() signal when the user clicks on the editor.

    The protected functions are reimplemented from QWidget to handle mouse and paint events. The private function starAtPosition() is a helper function that returns the number of the star under the mouse pointer.

    StarEditor Class Implementation

    Let's start with the constructor:

     StarEditor::StarEditor(QWidget *parent)
         : QWidget(parent)
     {
         setMouseTracking(true);
         setAutoFillBackground(true);
     }

    We enable mouse tracking on the widget so we can follow the cursor even when the user doesn't hold down any mouse button. We also turn on QWidget's auto-fill background feature to obtain an opaque background. (Without the call, the view's background would shine through the editor.)

    The paintEvent() function is reimplemented from QWidget:

     void StarEditor::paintEvent(QPaintEvent *)
     {
         QPainter painter(this);
         myStarRating.paint(&painter, rect(), this->palette(),
                            StarRating::Editable);
     }

    We simply call StarRating::paint() to draw the stars, just like we did when implementing StarDelegate.

     void StarEditor::mouseMoveEvent(QMouseEvent *event)
     {
         int star = starAtPosition(event->x());
    
         if (star != myStarRating.starCount() && star != -1) {
             myStarRating.setStarCount(star);
             update();
         }
     }

    In the mouse event handler, we call setStarCount() on the private data member myStarRating to reflect the current cursor position, and we call QWidget::update() to force a repaint.

     void StarEditor::mouseReleaseEvent(QMouseEvent * /* event */)
     {
         emit editingFinished();
     }

    When the user releases a mouse button, we simply emit the editingFinished() signal.

     int StarEditor::starAtPosition(int x)
     {
         int star = (x / (myStarRating.sizeHint().width()
                          / myStarRating.maxStarCount())) + 1;
         if (star <= 0 || star > myStarRating.maxStarCount())
             return -1;
    
         return star;
     }

    The starAtPosition() function uses basic linear algebra to find out which star is under the cursor.

    StarRating Class Definition

     class StarRating
     {
     public:
         enum EditMode { Editable, ReadOnly };
    
         StarRating(int starCount = 1, int maxStarCount = 5);
    
         void paint(QPainter *painter, const QRect &rect,
                    const QPalette &palette, EditMode mode) const;
         QSize sizeHint() const;
         int starCount() const { return myStarCount; }
         int maxStarCount() const { return myMaxStarCount; }
         void setStarCount(int starCount) { myStarCount = starCount; }
         void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; }
    
     private:
         QPolygonF starPolygon;
         QPolygonF diamondPolygon;
         int myStarCount;
         int myMaxStarCount;
     };
    
     Q_DECLARE_METATYPE(StarRating)

    The StarRating class represents a rating as a number of stars. In addition to holding the data, it is also capable of painting the stars on aQPaintDevice, which in this example is either a view or an editor. The myStarCount member variable stores the current rating, andmyMaxStarCount stores the highest possible rating (typically 5).

    The Q_DECLARE_METATYPE() macro makes the type StarRating known to QVariant, making it possible to store StarRating values inQVariant.

    StarRating Class Implementation

    The constructor initializes myStarCount and myMaxStarCount, and sets up the polygons used to draw stars and diamonds:

     StarRating::StarRating(int starCount, int maxStarCount)
     {
         myStarCount = starCount;
         myMaxStarCount = maxStarCount;
    
         starPolygon << QPointF(1.0, 0.5);
         for (int i = 1; i < 5; ++i)
             starPolygon << QPointF(0.5 + 0.5 * cos(0.8 * i * 3.14),
                                    0.5 + 0.5 * sin(0.8 * i * 3.14));
    
         diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4)
                        << QPointF(0.6, 0.5) << QPointF(0.5, 0.6)
                        << QPointF(0.4, 0.5);
     }

    The paint() function paints the stars in this StarRating object on a paint device:

     void StarRating::paint(QPainter *painter, const QRect &rect,
                            const QPalette &palette, EditMode mode) const
     {
         painter->save();
    
         painter->setRenderHint(QPainter::Antialiasing, true);
         painter->setPen(Qt::NoPen);
    
         if (mode == Editable) {
             painter->setBrush(palette.highlight());
         } else {
             painter->setBrush(palette.foreground());
         }
    
         int yOffset = (rect.height() - PaintingScaleFactor) / 2;
         painter->translate(rect.x(), rect.y() + yOffset);
         painter->scale(PaintingScaleFactor, PaintingScaleFactor);
    
         for (int i = 0; i < myMaxStarCount; ++i) {
             if (i < myStarCount) {
                 painter->drawPolygon(starPolygon, Qt::WindingFill);
             } else if (mode == Editable) {
                 painter->drawPolygon(diamondPolygon, Qt::WindingFill);
             }
             painter->translate(1.0, 0.0);
         }
    
         painter->restore();
     }

    We first set the pen and brush we will use for painting. The mode parameter can be either Editable or ReadOnly. If mode is editable, we use the Highlight color instead of the Foreground color to draw the stars.

    Then we draw the stars. If we are in Edit mode, we paint diamonds in place of stars if the rating is less than the highest rating.

    The sizeHint() function returns the preferred size for an area to paint the stars on:

     QSize StarRating::sizeHint() const
     {
         return PaintingScaleFactor * QSize(myMaxStarCount, 1);
     }

    The preferred size is just enough to paint the maximum number of stars. The function is called by both StarDelegate::sizeHint() andStarEditor::sizeHint().

    The main() Function

    Here's the program's main() function:

     int main(int argc, char *argv[])
     {
         QApplication app(argc, argv);
    
         QTableWidget tableWidget(4, 4);
         tableWidget.setItemDelegate(new StarDelegate);
         tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked
                                     | QAbstractItemView::SelectedClicked);
         tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows);
    
         QStringList headerLabels;
         headerLabels << "Title" << "Genre" << "Artist" << "Rating";
         tableWidget.setHorizontalHeaderLabels(headerLabels);
    
         populateTableWidget(&tableWidget);
    
         tableWidget.resizeColumnsToContents();
         tableWidget.resize(500, 300);
         tableWidget.show();
    
         return app.exec();
     }

    The main() function creates a QTableWidget and sets a StarDelegate on it. DoubleClicked and SelectedClicked are set as edit triggers, so that the editor is opened with a single click when the star rating item is selected.

    The populateTableWidget() function fills the QTableWidget with data:

     void populateTableWidget(QTableWidget *tableWidget)
     {
         static const struct {
             const char *title;
             const char *genre;
             const char *artist;
             int rating;
         } staticData[] = {
             { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 },
         ...
             { 0, 0, 0, 0 }
         };
    
         for (int row = 0; staticData[row].title != 0; ++row) {
             QTableWidgetItem *item0 = new QTableWidgetItem(staticData[row].title);
             QTableWidgetItem *item1 = new QTableWidgetItem(staticData[row].genre);
             QTableWidgetItem *item2 = new QTableWidgetItem(staticData[row].artist);
             QTableWidgetItem *item3 = new QTableWidgetItem;
             item3->setData(0,
                            qVariantFromValue(StarRating(staticData[row].rating)));
    
             tableWidget->setItem(row, 0, item0);
             tableWidget->setItem(row, 1, item1);
             tableWidget->setItem(row, 2, item2);
             tableWidget->setItem(row, 3, item3);
         }
     }

    Notice the call to qVariantFromValue to convert a StarRating to a QVariant.

    Possible Extensions and Suggestions

    There are many ways to customize Qt's model/view framework. The approach used in this example is appropriate for most custom delegates and editors. Examples of possibilities not used by the star delegate and star editor are:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值