在上一章學習 25.QT-模型視圖 后,本章接着學習視圖委托
視圖委托(Delegate)簡介
由於模型負責組織數據,而視圖負責顯示數據,所以當用戶想修改顯示的數據時,就要通過視圖中的委托來完成
視圖委托類似於傳統的MVC設計模式里的Controller(控制器)角色
- Model(模型) - 負責數據組織
- View(視圖) - 負責數據顯示
- Controller(控制器) - 負責用戶輸入,並處理數據
初探自定義委托類
- 委托屬於視圖的子功能
- 視圖主要負責組織具體數據項的顯示方式(是列表方式,還是樹形方式,還是表格方式)
- 委托主要負責具體數據項的顯示和編輯,比如用戶需要編輯某個數據時,則需要彈出編輯框
- 視圖可以通過 itemDelegate() ,setItemDelegate ( )成員函數來 獲得/設置當前委托對象
- QAbstractItemDelegate類是所有委托的父類,用來 負責提供通用接口
- 在模型視圖中,會默認提供一個QStyledItemDelegate類,供用戶編輯數據
- 也可以通過繼承QItemDelegate父類,實現自定義委托功能
QAbstractItemDelegate類中的關鍵虛函數
QWidget * createEditor( QWidget * parent, QStyleOptionViewItem & option, QModelIndex & index ) ; //創建編輯器,並返回該編輯器, option包含了該數據項的具體信息(比如:數據項窗口大小,字體格式,對齊方式,圖標位於字體的哪個位置等)、index 包含了該數據項的內容(比如:text信息,背景色等) void updateEditorGeometry ( QWidget * editor, QStyleOptionViewItem & option, QModelIndex &index ); //該函數里,可以通過editor->setGeometry()更新編輯組件大小,保證editor顯示的位置及大小 //大小可以通過option.rect獲取數據項窗口大小 void setEditorData ( QWidget * editor, const QModelIndex & index ); //通過索引值,將模型里的數據提取到編輯器內容里 void setModelData ( QWidget * editor, QAbstractItemModel * model, QModelIndex & index ); //通過索引值, 根據editor 的數據更新model的數據。 void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) ; //復制繪畫數據項的顯示和編輯
QAbstractItemDelegate類中的關鍵信號
void closeEditor ( QWidget * editor, QAbstractItemDelegate::EndEditHint hint = NoHint ); //當用戶關閉編輯器后,就會發出這個信號。 // hint 參數用來指定當用戶完成編輯后,應該顯示什么標記,用來提示用戶已完成編輯 void commitData ( QWidget * editor ) ; //當完成編輯數據后,發送該信號,表示有新數據提交到模型中
我們以編輯某個數據項為例:
- 視圖首先會調用createEditor()函數生成編輯器
- 調用updateEditorGeometry()函數設置編輯器組件大小
- 調用setEditorData()函數,將模型里的數據提取到編輯器中
- 等待用戶編輯... ...
- 當用戶編輯完成后, 系統將會發送commitData信號函數
- 然后調用setModelData()函數,設置模型數據,以及setEditorData()函數,更新編輯器
- 視圖最后發送closeEditor()信號函數,表示已關閉編輯器
接下來,我們重寫上面函數,來自定義一個QCostomizedDelegate委托類
效果如下
QCustomizedDelegate.h:
#ifndef QCUSTOMIZEDDELEGATE_H #define QCUSTOMIZEDDELEGATE_H #include <QItemDelegate> #include <QtGui> class QCustomizedDelegate : public QItemDelegate { Q_OBJECT public: explicit QCustomizedDelegate(QObject *parent = 0); 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; void updateEditorGeometry( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const; }; #endif // QCUSTOMIZEDDELEGATE_H
QCustomizedDelegate.cpp:
#include "QCustomizedDelegate.h"
QCustomizedDelegate::QCustomizedDelegate(QObject *parent) : QItemDelegate(parent) { } QWidget* QCustomizedDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(index.column()==1) //第1列 班級 { QComboBox *Cbox = new QComboBox(parent); Cbox->addItems(QStringList()<<"1班"<<"2班"<<"3班"<<"4班"<<"5班"); return Cbox; } else if(index.column()==2) //第2列 分數 { QSpinBox *Sbox = new QSpinBox(parent); Sbox->setRange(0,150); return Sbox; } return QItemDelegate::createEditor(parent, option, index); //第0列,則選擇默認編輯器 } void QCustomizedDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const { if(index.column()==1) //第1列 班級 { QComboBox *Cbox = dynamic_cast<QComboBox*>(editor); Cbox->setCurrentIndex(Cbox->findText( index.data(Qt::DisplayRole).toString())); } else if(index.column()==2) //第2列 分數 { QSpinBox *Sbox = dynamic_cast<QSpinBox*>(editor); Sbox->setValue(index.data(Qt::DisplayRole).toInt()); } else QItemDelegate::setEditorData(editor, index); } void QCustomizedDelegate::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const { if(index.column()==1) //第1列 班級 { QComboBox *Cbox = dynamic_cast<QComboBox*>(editor); model->setData(index,Cbox->currentText(),Qt::DisplayRole); } else if(index.column()==2) //第2列 分數 { QSpinBox *Sbox = dynamic_cast<QSpinBox*>(editor); model->setData(index,Sbox->value(),Qt::DisplayRole); } else QItemDelegate::setModelData(editor, model, index); } void QCustomizedDelegate::updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const { editor->setGeometry(option.rect); }
然后,再通過視圖的setItemDelegate(QAbstractItemDelegate * delegate )成員函數設置我們自定義的委托類對象即可
深入自定義委托類
之前我們寫的自定義委托,每次都需要雙擊某個數據項,才能彈出編輯器
那如何讓委托一直呈現在視圖顯示上呢?
步驟如下:
- 重寫委托類的paint成員函數
- 在paint()中,通過QApplication::style()->drawControl()來自定義數據顯示方式,比如繪制按鈕
- 重寫委托類的editorEvent成員函數
- 在editorEvent中處理交互事件,比如判斷鼠標是否雙擊,以及更改模型數據等
其中QApplication::style()->drawControl()函數參數如下所示:
QApplication::style()->drawControl (ControlElement element,
constQStyleOption * option,
QPainter *painter, const QWidget * widget = 0 ) ; //繪畫組件 // element: 元素,用來指定控件樣式,比如: QStyle::CE_CheckBox 表示繪畫的widget是一個text文本的復選框 // option:選項,用來繪制控件所需的所有參數比如option.rect(設置組件大小位置), option.state(設置組件狀態)
//其中option. state成員值常見的有: QStyle::State_Enabled //表示該組件是激活的,可以被用戶操作 QStyle::State_On //表示該組件樣式是被選上的 QStyle::State_Off //表示該組件樣式是未被選中的 QStyle::State_MouseOver //表示表示該組件樣式是:鼠標停留在組件上面的樣子 QStyle::State_Sunken //表示該組件樣式是:鼠標按壓下的組件樣子 QStyle::State_HasEditFocus //表示該組件是否有編輯焦點 // painter:誰來繪畫 // widget = 0:如果該widget為0,則表示使用QT自帶的風格
示例-自定義一個QCostomizedDelegate委托類
效果如下
代碼如下
QCustomizedDelegate.h:
#ifndef QCUSTOMIZEDDELEGATE_H #define QCUSTOMIZEDDELEGATE_H #include <QItemDelegate> #include <QtGui> #include "ProgressBar.h" class QCustomizedDelegate : public QItemDelegate { Q_OBJECT //m_bar:溫度台的當前溫度進度條 QScopedPointer<QProgressBar> m_bar ; public: explicit QCustomizedDelegate(QObject *parent = 0); void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; bool editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ); }; #endif // QCUSTOMIZEDDELEGATE_H
QCustomizedDelegate.cpp:
#include "QCustomizedDelegate.h" #include "ProgressBar.h" QCustomizedDelegate::QCustomizedDelegate(QObject *parent) : QItemDelegate(parent), m_bar(new QProgressBar()) { m_bar->setStyleSheet(qApp->styleSheet()); //設置風格 } void QCustomizedDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { if(index.column()==1 && index.data().type() == QVariant::Int) //判斷列數,並判斷索引值是否int型(溫度是通過int型值表示的) { int radio=14; int top = option.rect.top() + radio; int left = option.rect.left() + radio; int width = option.rect.width() - 2 * radio; int height = option.rect.height() - 2 * radio; QStyleOptionProgressBar bar; //設置參數 bar.rect.setRect(left, top, width, height); bar.state = QStyle::State_Enabled; bar.progress = index.data().toInt(); bar.maximum = 100; bar.minimum = 0; bar.textVisible = true; bar.text = QString("當前溫度:%1°").arg(bar.progress); bar.textAlignment = Qt::AlignCenter; QApplication::style()->drawControl(QStyle::CE_ProgressBar,&bar,painter, m_bar.data()); } else { QItemDelegate::paint(painter,option,index); } } bool QCustomizedDelegate::editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ) { if(event->type() == QEvent::MouseButtonDblClick) //禁止雙擊編輯 { return true; } return QItemDelegate::editorEvent(event,model,option,index); }