QT-模型檢視之自定義委託
阿新 • • 發佈:2019-01-03
檢視委託(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);
}