这是我对Qt的model/view内容理解的第二篇blog,在第一篇文章中,介绍QTableView和QAbstractTableModel,实现显示了对数据源的显示,但是显示的格式和修改的模式都是按照View控件的自显示方式。在此,使用Qt自带的QStyledItemDelegate类实现对特定行/列的显示/修改模式实现,实现过程中不出现对item的代码生成,对item的生成由程序自动完成。
在此同样以《c++ gui programming with Qt4》中的trackEditor例子作一讲解。
在此,我们首先应当考虑以下几个问题:
1)有一个代理类加到View中,处理特定的View内容。
2)代理类要完成以下几项工作:a)当用户修改数据时生成用户要求的控件(用户每次修改数据时在相应的位置都会生成控件,所以当控件用完后,应del释放资源)。b)设置在生成的修改控件中显示的内容。c)设置要写到model中的数据内容。d)设置当结束修改数据后,View显示的内容。
在此我们需要实现以下4个类。
/** @brief 保存显示数据的类。 */ class Track /** @brief 继承的委托类。 */ class TrackDelegate : public QStyledItemDelegate /** @brief 继承的模型类。 */ class TrackModel : public QAbstractTableModel /** @brief 用于组装显示的控件类。 */ class TrackEditor : public QDialog
在此,Track TrackModel TrackEditor的功能在Qt model/view理解1中做过介绍,在此只列出code,不再过多介绍。在此主要介绍TrackDelegate。
Track h文件
#ifndef TRACK_H#define TRACK_H #include <QString> /** @brief 保存显示数据的类。 */ class Track { public: explicit Track(const QString &title = "", int duration = 0); ~Track(); QString getTitle() const; int getDuration() const; void setTitle(QString title); void setDuration(int duration); private: QString title; int duration; }; #endif // TRACK_H
TrackModel h文件
#ifndef TRACKMODEL_H#define TRACKMODEL_H #include <QWidget> #include <QAbstractTableModel> #include <QList> #include "track.h" #include <QObject> /** @brief 继承的模型类。 */ class TrackModel : public QAbstractTableModel { Q_OBJECT public: explicit TrackModel(QList<Track>* tracks, QObject* parent = 0); ~TrackModel(); virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual Qt::ItemFlags flags(const QModelIndex &index) const; private: QList<Track>* tracks; }; #endif // TRACKMODEL_H
TrackEditor h文件
#ifndef TRACKEDITOR_H#define TRACKEDITOR_H #include <QDialog> #include "track.h" QT_BEGIN_NAMESPACE class QTableView; class TrackModel; class QAbstractTableModel; QT_END_NAMESPACE namespace Ui { class TrackEditor; } /** @brief 用于组装显示的控件类。 */ class TrackEditor : public QDialog { Q_OBJECT public: explicit TrackEditor(QList<Track>* tracks, QWidget *parent = 0); ~TrackEditor(); private: Ui::TrackEditor *ui; QTableView* tableView; TrackModel* model; //QAbstractTableModel* model; }; #endif // TRACKEDITOR_H
TrackDelegate h文件
#ifndef TRACKDELEGATE_H#define TRACKDELEGATE_H #include <QObject> #include <QStyledItemDelegate> QT_BEGIN_NAMESPACE class QPainter; QT_END_NAMESPACE /** @brief 继承的委托类。 */ class TrackDelegate : public QStyledItemDelegate { public: explicit TrackDelegate(QObject* parent = 0); ~TrackDelegate(); virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual void setEditorData(QWidget* parent, const QModelIndex& index) const; virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; private: bool isRightColumn(const QModelIndex& index, const int column) const; private slots: void commitAndCloseEditor(); private: static const int columnNumber; }; #endif // TRACKDELEGATE_H
TrackDelegate cpp文件
#include "trackdelegate.h" #include <QTimeEdit> #include <QPainter> #include <QApplication> #include "trackmodel.h" const int TrackDelegate::columnNumber = 1; TrackDelegate::TrackDelegate(QObject* parent) : QStyledItemDelegate(parent) { } TrackDelegate::~TrackDelegate() { } QWidget* TrackDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (isRightColumn(index, TrackDelegate::columnNumber)) { QTimeEdit *timeEdit = new QTimeEdit(parent); timeEdit->setDisplayFormat("hh:mm"); //当控件结束编辑内容时,触发释放资源 connect(timeEdit, &QTimeEdit::editingFinished, this, &TrackDelegate::commitAndCloseEditor); //int secs = index.model()->data(index, Qt::DisplayRole).toInt(); int secs = index.model()->data(index, Qt::EditRole).toInt(); QTime time(secs / 60, secs % 60); timeEdit->setTime(time); return timeEdit; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } void TrackDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if ( !index.isValid()) { return; } QTimeEdit* timeEditor = qobject_cast<QTimeEdit*>(editor); if ( !timeEditor) { return; } if (isRightColumn(index, TrackDelegate::columnNumber)) { int secs = index.model()->data(index, Qt::EditRole).toInt(); QTime time(secs / 60, secs % 60); timeEditor->setTime(time); } else { QStyledItemDelegate::setEditorData(editor, index); } } void TrackDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if ( !index.isValid()) { return; } QTimeEdit* timeEditor = qobject_cast<QTimeEdit*>(editor); if ( !timeEditor) { return; } if (isRightColumn(index, TrackDelegate::columnNumber)) { QTime time = timeEditor->time(); int secs = time.hour() * 60 + time.minute(); model->setData(index, secs, Qt::EditRole); } else { QStyledItemDelegate::setModelData(editor, model, index); } } void TrackDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); editor->setGeometry(option.rect); } void TrackDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (isRightColumn(index, TrackDelegate::columnNumber)) { int secs = index.model()->data(index, Qt::EditRole).toInt(); QString text = QString("%1:%2") .arg(secs / 60, 2, 10, QChar('0')) .arg(secs % 60, 2, 10, QChar('0')); //获取项风格设置 QStyleOptionViewItem myOption = option; myOption.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; painter->drawText(option.rect, text); } else { QStyledItemDelegate::paint(painter, option, index); } } QSize TrackDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return option.rect.size(); } void TrackDelegate::commitAndCloseEditor() { QTimeEdit* editor = qobject_cast<QTimeEdit*>(sender()); emit commitData(editor); emit closeEditor(editor); } bool TrackDelegate::isRightColumn(const QModelIndex &index, const int column) const { if ( !index.isValid()) { return false; } if (index.column() == column) { return true; } else { return false; } }
createEditor用于创建用户自己需要的显示数据控件。
setEditorData用于设置显示控件中显示的具体数据信息。
setModelData用户设置模型的数据,也可理解为当显示的数据发生变化后,用户update模型数据,保持显示/储存内容一致。
paint由用户自己绘制要显示的内容信息(很重要,当你点击时间框修改时间,要改的内容为原显示内容并允许你修改,而不是数据变为00:00,让你修改)。
commitAndCloseEditor 创建关于commitData和closeEditor的信号槽链接,保证当代理控件的数据修改完成后,释放信号,保存数据(保存数据步骤由系统自动完成)。
在此要求注意内容:
在TrackModel类中的setData方法应当注意,data的值应从value得出而不是通过model的data得出,model得出的数据是原来保存的,而不是用户修改的。
完整代码见http://www.oschina.net/code/snippet_48549_45036