好久沒有來寫文章了,由於家裡面寬帶斷了,所以一直沒能更新,今天現在寫上一篇。
還是繼續前面的內容。前面我們分三次把自定義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
#ifndef TRACK_H
#define TRACK_H
#include <QtCore>
class Track
{
public:
Track(
const QString &title = "",
int duration = 0);
QString title;
int duration;
};
#endif
// TRACK_H
track.cpp
#include
"track.h" Track::Track(
const QString &title,
int duration)
: title(title), duration(duration)
{
}
這個類的構造函數沒有做任何操作,只是把title和duration這兩個參數通過構造函數初始化列表賦值給內部的成員變量。注意,現在這兩個成員變量都是public的,在正式的程序中應該聲明為private的才對。然後來看TrackDelegate類:
trackdelegate.h
#ifndef TRACKDELEGATE_H
#define TRACKDELEGATE_H
#include <QtGui>
class TrackDelegate :
public QStyledItemDelegate
{
Q_OBJECT
public:
TrackDelegate(
int durationColumn, QObject *parent = 0);
void paint(QPainter *painter,
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();
private:
int durationColumn;
};
#endif
// TRACKDELEGATE_H
trackdelegate.cpp
#include
"trackdelegate.h" TrackDelegate::TrackDelegate(
int durationColumn, QObject *parent)
: QStyledItemDelegate(parent)
{
this->durationColumn = durationColumn;
}
void TrackDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const {
if (index.column() == durationColumn) {
int secs = index.model()->data(index, Qt::DisplayRole).toInt();
QString text = QString(
"%1:%2").arg(secs / 60, 2, 10, QChar('0')).arg(secs % 60, 2, 10, QChar('0'));
QTextOption o(Qt::AlignRight | Qt::AlignVCenter);
painter->drawText(option.rect, text, o);
}
else {
QStyledItemDelegate::paint(painter, option, index);
}
}
QWidget *TrackDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const {
if (index.column() == durationColumn) {
QTimeEdit *timeEdit =
new QTimeEdit(parent);
timeEdit->setDisplayFormat(
"mm:ss");
connect(timeEdit, SIGNAL(editingFinished()),
this, SLOT(commitAndCloseEditor()));
return timeEdit;
}
else {
return QStyledItemDelegate::createEditor(parent, option, index);
}
}
void TrackDelegate::commitAndCloseEditor()
{
QTimeEdit *editor = qobject_cast<QTimeEdit *>(sender());
emit commitData(editor);
emit closeEditor(editor);
}
void TrackDelegate::setEditorData(QWidget *editor,
const QModelIndex &index)
const {
if (index.column() == durationColumn) {
int secs = index.model()->data(index, Qt::DisplayRole).toInt();
QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
timeEdit->setTime(QTime(0, secs / 60, secs % 60));
}
else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
void TrackDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index)
const {
if (index.column() == durationColumn) {
QTimeEdit *timeEdit = qobject_cast<QTimeEdit *>(editor);
QTime time = timeEdit->time();
int secs = (time.minute() * 60) + time.second();
model->setData(index, secs);
}
else {
QStyledItemDelegate::setModelData(editor, model, index);
}
}
正如前面所說的,這個類繼承了QStyledItemDelegate,覆蓋了其中的四個函數。通過前面的講解,我們已經了解到這些函數的作用。至於實現,我們前面也說過,需要通過QModelIndex選擇我們需要進行渲染的列,然後剩下的數據類型仍然需要顯式地調用父類的相應函數。由於我們在Track裡面存儲的是歌曲的秒數,所以在paint()裡面需要用除法計算出分鐘數,用%60計算秒數。其他的函數都比較清楚,請注意代碼。
最後寫一個使用的類:
trackeditor.h
#ifndef TRACKEDITOR_H
#define TRACKEDITOR_H
#include <QtGui>
#include
"track.h" class TrackEditor :
public QDialog
{
Q_OBJECT
public:
TrackEditor(QList<Track> *tracks, QWidget *parent);
private:
QList<Track> *tracks;
QTableWidget *tableWidget;
};
#endif
// TRACKEDITOR_H
trackeditor.cpp
#include
"trackeditor.h" #include
"trackdelegate.h" TrackEditor::TrackEditor(QList<Track> *tracks, QWidget *parent)
: QDialog(parent)
{
this->tracks = tracks;
tableWidget =
new QTableWidget(tracks->count(), 2);
tableWidget->setItemDelegate(
new TrackDelegate(1));
tableWidget->setHorizontalHeaderLabels(QStringList() << tr(
"Track") << tr(
"Duration"));
for (
int row = 0; row < tracks->count(); ++row) {
Track track = tracks->at(row);
QTableWidgetItem *item0 =
new QTableWidgetItem(track.title);
tableWidget->setItem(row, 0, item0);
QTableWidgetItem *item1 =
new QTableWidgetItem(QString::number(track.duration));
item1->setTextAlignment(Qt::AlignRight);
tableWidget->setItem(row, 1, item1);
}
QVBoxLayout *mainLayout =
new QVBoxLayout;
mainLayout->addWidget(tableWidget);
this->setLayout(mainLayout);
}
其實也並沒有很大的不同,只是我們使用setItemDelegate()函數設置了一下delegate。然後寫main()函數:
#include <QtGui>
#include
"trackeditor.h" int main(
int argc,
char *argv[])
{
QApplication a(argc, argv);
QList<Track> tracks;
Track t1(
"Song 1", 200);
Track t2(
"Song 2", 150);
Track t3(
"Song 3", 120);
Track t4(
"Song 4", 210);
tracks << t1 << t2 << t3 << t4;
TrackEditor te(&tracks, NULL);
te.show();
return a.exec();
}
好了,運行一下看看效果吧!
本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/271255