程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Qt學習之路(48): 自定義委托

Qt學習之路(48): 自定義委托

編輯:關於C語言

好久沒有來寫文章了,由於家裡面寬帶斷了,所以一直沒能更新,今天現在寫上一篇。   還是繼續前面的內容。前面我們分三次把自定義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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved