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

相关推荐
yuyanjingtao35 分钟前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
闻缺陷则喜何志丹1 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie1145141911 小时前
C++ STL CookBook
开发语言·c++·stl·c++20
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
倔强的石头1061 小时前
【C++指南】类和对象(九):内部类
开发语言·c++
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导3 小时前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香3 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3054 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻4 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++