这是我对 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
-
- /**
- @projectName ItemView
- @author qiaowei
- @date 2018-12-22
- @version 1.0
- @description 保存的音频数据,包括音频名称和时长
- **/
- class Track
- {
- public:
- explicit Track(const QString& title = "", int duration = 0);
-
- QString getTitle() const;
- void setTitle(const QString& title);
-
- int getDuration() const;
- void setDuration(int duration);
-
- private:
- /**
- @author qioawei
- @date 2018-12-22
- @description 音频名称
- **/
- QString title_;
-
- /**
- @author qioawei
- @date 2018-12-22
- @description 音频时长(单位:秒)
- **/
- int duration_;
- };
-
- #endif // TRACK_H
Track.cpp
- #include "track.h"
-
- Track::Track(const QString &title, int duration) :
- title_(title),
- duration_(duration)
- {
-
- }
-
- QString Track::getTitle() const
- {
- return title_;
- }
-
- void Track::setTitle(const QString &title)
- {
- title_ = title;
- }
-
- int Track::getDuration() const
- {
- return duration_;
- }
-
- void Track::setDuration(int duration)
- {
- duration_ = duration;
- }
TrackModel.h
- #ifndef TRACKMODEL_H#define TRACKMODEL_H
-
- #include
- #include
- #include
- #include "track.h"
- #include
-
- /**
- @brief 继承的模型类。
- */
- class TrackModel : public QAbstractTableModel
- {
- Q_OBJECT
-
- public:
- explicit TrackModel(QList;
- ~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
- };
-
- #endif // TRACKMODEL_H
TrackEditor h 文件
- #ifndef TRACKEDITOR_H#define TRACKEDITOR_H
-
- #include
-
- #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;
- ~TrackEditor();
-
- private:
- Ui::TrackEditor *ui;
- QTableView* tableView;
- TrackModel* model;
- //QAbstractTableModel* model;
- };
-
- #endif // TRACKEDITOR_H
TrackDelegate h 文件
- #ifndef TRACKDELEGATE_H#define TRACKDELEGATE_H
-
- #include
- #include
-
- 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
- #include
- #include
-
- #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
(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
(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
(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 得出的数据是原来保存的,而不是用户修改的。