Qt model/view 理解 2

这是我对 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 个类。

cpp 复制代码
/**  
@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 文件

cpp 复制代码
#ifndef TRACK_H
#define TRACK_H

#include <QString>

/**
  @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

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

cpp 复制代码
#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 文件

cpp 复制代码
#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 文件

cpp 复制代码
#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 文件

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 得出的数据是原来保存的,而不是用户修改的。

相关推荐
烦躁的大鼻嘎24 分钟前
模拟算法实例讲解:从理论到实践的编程之旅
数据结构·c++·算法·leetcode
IU宝27 分钟前
C/C++内存管理
java·c语言·c++
fhvyxyci28 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
C++忠实粉丝41 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
古月居GYH1 小时前
在C++上实现反射用法
java·开发语言·c++
Betty’s Sweet1 小时前
[C++]:IO流
c++·文件·fstream·sstream·iostream
敲上瘾1 小时前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
不会写代码的ys1 小时前
【类与对象】--对象之舞,类之华章,共绘C++之美
c++
兵哥工控1 小时前
MFC工控项目实例三十二模拟量校正值添加修改删除
c++·mfc
长弓聊编程1 小时前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++