QT自定义QTableView的Model/View

文章目录

    • 0. 参考网址
    • 1. 展示效果图
    • 2. 介绍
    • 3. 实现步骤
      • ① 界面设计
      • ② Model实现
        • 实现原理
        • 实现步骤
        • 展示类customDatastructure实现
        • customtableview.h
        • customtableview.cpp
      • ③ Views
      • ④ Delegates
        • 实现原理
        • 实现步骤
        • customitemdelegate.h
        • customitemdelegate.cpp
      • ⑤ 应用实现的类
        • 实现方式

0. 参考网址

  1. Adding custom style to button added in QTableview
  2. QTreeView & QStyledItemDelegate & QPushButton/Checkbox etc?
  3. QStyledItemDelegate paint QPushButton with stylesheet
  4. Adding Button to QTableView
  5. add buttons in tablewidget’s row
  6. Qt 之 QTableView 添加复选框(QAbstractItemDelegate)
  7. Qt 之模型/视图(自定义按钮)
  8. Qt 之模型/视图(自定义进度条)
  9. Model/View之子类化QAbstractItemModel实现QTreeView的复选框
  10. 关于QStyledItemDelegate在tableview控件中的使用心得
  11. QTableView使用自定义委托(QItemDelegate)
  12. 【译】Qt模型/视图(Model/View)指南
  13. QML 中使用 QAbstractListModel 作为 ListView 的 model 实例
  14. Qt之QListView使用

1. 展示效果图

  • 完整QT工程例子下载链接已在上方提供

2. 介绍

目前有一个项目需要定义自己的显示方式,通过搜索和查询官方文档,了解到使用QT的Model/View模式,通过继承QT自带的类可以实现想要的效果;

3. 实现步骤

① 界面设计

  • QT设计师面板中有QTableWidget和QTableView,其中QTableView就是用来让用户使用自定义的显示模式,同理,QListView和QTreeView也是一样的
  • 因此首先在QT设计师界面添加一个QTableView,并根据需要修改属性QT自定义QTableView的Model/View_第1张图片

② Model实现

实现原理

  • 根据官方文档所述,Model需要继承自QAbstractItemModel、QAbstractTableModel、QAbstractListModel…其中之一,如图所示;
    QT自定义QTableView的Model/View_第2张图片
  • 其中QAbstractItemModel虚基类可以实现最复杂的显示方式,一般QTreeView使用这种Model,当然其他如QAbstractTableModel都是继承自这个类,所以也可以用于QTableView或QListView,一般QTableView使用QAbstractTableModel虚基类,QListView使用QAbstractListModel虚基类;
  • 根据文档的要求,继承自QAbstractTableModel必须要实现rowCount(),columnCount()和data()三个函数;官方建议自定义头部显示内容,所以需要实现headerData()函数;如果显示内容需要实时编辑,那么又要实现 insertRows(),removeRows(),insertColumns()和removeColumns()等函数,还有一些其他可以重写的函数,如图所示
    QT自定义QTableView的Model/View_第3张图片

实现步骤

  1. 这里新建一个继承自QAbstractTableModel的类,选择Qt Item Model选项,选择继承的类并可以根据需要勾选要实现的函数
    QT自定义QTableView的Model/View_第4张图片
    QT自定义QTableView的Model/View_第5张图片
  2. 新建后的头文件内容
    QT自定义QTableView的Model/View_第6张图片
  3. 根据需要,重写里面的函数

展示类customDatastructure实现

class customDatastructure
{
private:
    bool isDownload;
    QString name;
    long id;
    double allSize;

public:
    double processScale = 0.0;
    double transferSpeed = 0;
    double curSize = 0;
    bool isFinished = false;
    bool isTransmitting = false;

public:
    customDatastructure();
    customDatastructure(bool isDownload, long id = 0, QString name = "", double allSize = 0);

    QString objName() const;
    QString objSize() const;
    QString objProcSize() const;
    QString objTransSpeed() const;
    double objTransScale() const;
    bool objIsDownload() const;

    void setCurSize(double size);
    void appendCurSize(double size);
    void setFinished(bool is_finish);
    void setTransmitting(bool is_trans);

};

customtableview.h

#ifndef CUSTOMTABLEVIEW_H
#define CUSTOMTABLEVIEW_H

#include 
#include 
#include 

#include "customdatastructure.h"

class customTableView : public QAbstractTableModel
{
    Q_OBJECT

public: // 默认建立后自带函数

    explicit customTableView(QObject* parent = nullptr);

    // Header:
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

    // Basic functionality:
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    int columnCount(const QModelIndex& parent = QModelIndex()) const override;

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;

public: // 新增函数

    // 数据类型,用于标识显示哪一个数据
    enum
    {
        NameRole = 0,
        InfoRole_1,
        InfoRole_2,
        InfoRole_3,
        OperatorRole_1,
        OperatorRole_2,
        OperatorRole_3,
    };

    // 重写析构函数
    ~customTableView() override;
    // 添加数据函数
    void addData(customDatastructure*);
	// 
    Qt::ItemFlags flags(const QModelIndex& index) const override;

private:
    // 要显示的数据List
    QList<customDatastructure*> m_datum;
};

#endif // CUSTOMTABLEVIEW_H

customtableview.cpp

#include "customtableview.h"

customTableView::customTableView(QObject* parent)
    : QAbstractTableModel(parent)
{
}

QVariant customTableView::headerData(int section, Qt::Orientation orientation, int role) const
{
    // FIXME: Implement me!

    if (role == Qt::SizeHintRole)
        return QSize(0, 0);
    return QVariant();
}

int customTableView::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;
    // FIXME: Implement me!

    return m_datum.count();
}

int customTableView::columnCount(const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;

    // FIXME: Implement me!

    return 7; // 返回列的个数,这里与头文件定义的枚举体类型个数一致
}

QVariant customTableView::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || index.row() > m_datum.count())
    {
        return QVariant();
    }
    int nColumn = index.column();
    switch(role) // 返回的数据类型
    {
        case Qt::FontRole:          // 6
            return QVariant();
        case Qt::TextAlignmentRole: // 7
            return QVariant();
        case Qt::TextColorRole:     // 9
            return QColor(Qt::black);
        case Qt::CheckStateRole:    // 10
            return QVariant();
        case Qt::DecorationRole:    // 1
            return QVariant();
        case Qt::ToolTipRole:
            {
                switch(nColumn)
                {
                    case NameRole:      // 0 名称
                        {
                            return tr("文件名称");
                        }
                        break;
                    case InfoRole_1:    // 1 [已下载大小/]总大小
                        {
                            if(m_datum[index.row()]->isFinished)
                                return tr("总大小");
                            else if(m_datum[index.row()]->objIsDownload())
                                return tr("已下载大小/总大小");
                            else
                                return tr("已上传大小/总大小");
                        }
                        break;
                    case InfoRole_2:    // 2 [下载速度]
                        {
                            if(m_datum[index.row()]->isFinished)
                                return "";
                            else
                                return tr("下载速度");
                        }
                        break;
                    case InfoRole_3:    // 3  下载进度
                        {
                            if(m_datum[index.row()]->isFinished)
                                if(m_datum[index.row()]->objIsDownload())
                                    return tr("下载完成");
                                else
                                    return tr("上传完成");
                            else
                                return tr("下载进度");
                        }
                        break;
                    case OperatorRole_1:// 4
                        {
                            if(m_datum[index.row()]->isFinished)
                                return tr("打开文件");
                            else if(m_datum[index.row()]->isTransmitting)
                                return tr("暂停");
                            else
                                return tr("开始");
                        }
                        break;
                    case OperatorRole_2:// 5
                        {
                            if(m_datum[index.row()]->isFinished)
                                return tr("打开所在文件夹");
                            else
                                return tr("删除");
                        }
                        break;
                    case OperatorRole_3:// 6
                        {
                            if(m_datum[index.row()]->isFinished)
                                return tr("删除");
                            else
                                return tr("打开所在文件夹");
                        }
                        break;
                }
            }
            break;
        case Qt::DisplayRole:       // 0
            {
                switch(nColumn)
                {
                    case NameRole:      // 0 名称
                        {
                            return m_datum[index.row()]->objName();
                        }
                        break;
                    case InfoRole_1:    // 1 [(]已下载大小/]总大小
                        {
                            if(m_datum[index.row()]->isFinished)
                                return m_datum[index.row()]->objSize();
                            else
                                return m_datum[index.row()]->objProcSize();
                        }
                        break;
                    case InfoRole_2:    // 2 [下载速度]
                        {
                            if(m_datum[index.row()]->isFinished == false)
                                return m_datum[index.row()]->objTransSpeed();
                        }
                        break;
                    case InfoRole_3:    // 3 下载进度
                        {
                            if(m_datum[index.row()]->isFinished == false)
                                return m_datum[index.row()]->objTransScale();
                        }
                        break;
                    case OperatorRole_1:// 4
                        {

                        }
                        break;
                    case OperatorRole_2:// 5
                        {

                        }
                        break;
                    case OperatorRole_3:// 6
                        {

                        }
                        break;
                }
            }
        case Qt::BackgroundColorRole:   // 8
            return QVariant();
    }

    // FIXME: Implement me!
    return QVariant();
}

customTableView::~customTableView()
{
    for(auto d : m_datum)
        delete(d);
}

void customTableView::addData(customDatastructure* dataObj) // 自定义添加函数类型,需要添加两段代码
{
    beginResetModel();
    m_datum.push_back(dataObj);
    endResetModel();
}

Qt::ItemFlags customTableView::flags(const QModelIndex& index) const
{
    if (!index.isValid())
        return QAbstractItemModel::flags(index);
    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

③ Views

  • 一般不需要重写;
    在这里插入图片描述

④ Delegates

实现原理

  • 可以改变显示内容的样式,有两个类,一个是QAbstractItemDelegate另一个是QStyledItemDelegate,官方推荐使用QStyledItemDelegate;继承自QStyledItemDelegate需要重写sizeHint()和paint()函数,如果需要处理事件,则需要重写editorEvent()函数;
    在这里插入图片描述

实现步骤

  • 添加一个新类,继承自QStyledItemDelegate;
  • 修改为如下代码
#include 
#include 

class customItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    customItemDelegate(QObject* parent = nullptr);

    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;

    QSize sizeHint(const QStyleOptionViewItem& option,
                   const QModelIndex& index) const override;
    bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
                     const QModelIndex& index) override;
}

customitemdelegate.h

#ifndef CUSTOMITEMDELEGATE_H
#define CUSTOMITEMDELEGATE_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include "customtableview.h"

// 自定义Button属性
class ItemButton
{
public:
    static const int posx = 15;
    static const int poxy = 5;
    static const int width = 20;
    static const int height = width;
    enum BUTTON_STATUS
    {
        NONE, HOVER, PRESSED, RELEASED
    };

private:
    QString buttonIconName;
    QScopedPointer<QPushButton> buttonWidget;
    BUTTON_STATUS buttonStatus = NONE;

public:
    ItemButton(QString iconName);
    void paintButton(QPainter* painter, const QStyleOptionViewItem& option, const QPoint& pos );

    void inline setStatusNone();
    void inline setStatusHover();
    void inline setStatusPressed();
    void inline setStatusReleased();

    static bool withinButtonRegion(const QPoint, const QStyleOptionViewItem& option);

};

// 自定义ProgressBar属性
class ItemProgressBar
{
public:
    static const int left = 2;
    static const int right = left;
    static const int top = 6;
    static const int bottom = top;

private:
    QScopedPointer<QProgressBar> progressBarWidget;

public:
    ItemProgressBar();
    void paintQProgressBar(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index );

};

class customItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    customItemDelegate(QObject* parent = nullptr);

    void paint(QPainter* painter, const QStyleOptionViewItem& option,
               const QModelIndex& index) const override;

    QSize sizeHint(const QStyleOptionViewItem& option,
                   const QModelIndex& index) const override;
    bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
                     const QModelIndex& index) override;

private:
    QScopedPointer<ItemButton> itemButton1;
    QScopedPointer<ItemButton> itemButton2;
    QScopedPointer<ItemButton> itemButton3;
    QScopedPointer<ItemProgressBar> itemProgressBar;
    QPoint mousePos;

signals:
    void signals1( const QModelIndex& index);
    void signals2( const QModelIndex& index);
    void signals3( const QModelIndex& index);

public slots:
    void showMessage1( const QModelIndex& index);
    void showMessage2( const QModelIndex& index);
    void showMessage3( const QModelIndex& index);
};

#endif // CUSTOMITEMDELEGATE_H

customitemdelegate.cpp

#include "customitemdelegate.h"


customItemDelegate::customItemDelegate(QObject* parent): QStyledItemDelegate(parent)
    , itemButton1(new ItemButton("play"))
    , itemButton2(new ItemButton("cancle"))
    , itemButton3(new ItemButton("folder"))
    , itemProgressBar(new ItemProgressBar())
{
    connect(this, SIGNAL(signals1( const QModelIndex&)), this, SLOT(showMessage1( const QModelIndex&)));
    connect(this, SIGNAL(signals2( const QModelIndex&)), this, SLOT(showMessage2( const QModelIndex&)));
    connect(this, SIGNAL(signals3( const QModelIndex&)), this, SLOT(showMessage3( const QModelIndex&)));
}

void customItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (option.state.testFlag(QStyle::State_HasFocus))
        viewOption.state = viewOption.state ^ QStyle::State_HasFocus;

    switch(index.column())
    {
        case customTableView::NameRole:      // 0 名称
            {
            }
            break;
        case customTableView::InfoRole_1:    // 1 [已下载大小/]总大小
            {
                viewOption.displayAlignment = Qt::AlignCenter;
            }
            break;
        case customTableView::InfoRole_2:    // 2 [下载速度]
            {
                viewOption.displayAlignment = Qt::AlignCenter;


            }
            break;
        case customTableView::InfoRole_3:    // 3  下载进度
            {
                itemProgressBar->paintQProgressBar(painter, option, index);
                viewOption.text = "";
            }
            break;
        case customTableView::OperatorRole_1:// 4
            {
                itemButton1->paintButton(painter, option, mousePos);
            }
            break;
        case customTableView::OperatorRole_2:// 5
            {
                itemButton2->paintButton(painter, option, mousePos);
            }
            break;
        case customTableView::OperatorRole_3:// 6
            {
                itemButton3->paintButton(painter, option, mousePos);
            }
            break;
        default:
            {
            }
    }
    QStyledItemDelegate::paint(painter, viewOption, index);
}

QSize customItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    switch(index.column())
    {
        case customTableView::NameRole:      // 0 名称
        case customTableView::InfoRole_1:    // 1 [(]已下载大小/]总大小
        case customTableView::InfoRole_2:    // 2 [下载速度]
            {
                return QSize(100, 30);
            }
            break;
        case customTableView::InfoRole_3:    // 3  下载进度
            {
                return QSize(200, 30);
            }
            break;
        case customTableView::OperatorRole_1:// 4
        case customTableView::OperatorRole_2:// 5
        case customTableView::OperatorRole_3:// 6
            {
                return QSize(50, 30);
            }
            break;
    }
    return QStyledItemDelegate::sizeHint(option, index);
}

bool customItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
    QMouseEvent* pEvent = static_cast<QMouseEvent*> (event);
    mousePos = pEvent->pos();

    if (ItemButton::withinButtonRegion(mousePos, option))
    {
        switch (event->type())
        {
            case QEvent::MouseMove:
                {
                    switch(index.column())
                    {
                        case customTableView::OperatorRole_1:// 4
                            {
                                itemButton1->setStatusHover();
                            }
                            break;
                        case customTableView::OperatorRole_2:// 5
                            {
                                itemButton2->setStatusHover();
                            }
                            break;
                        case customTableView::OperatorRole_3:// 6
                            {
                                itemButton3->setStatusHover();
                            }
                            break;
                    }
                    return true;
                }
                break;
            case QEvent::MouseButtonPress:
                {
                    switch(index.column())
                    {
                        case customTableView::OperatorRole_1:// 4
                            {
                                itemButton1->setStatusPressed();
                            }
                            break;
                        case customTableView::OperatorRole_2:// 5
                            {
                                itemButton2->setStatusPressed();
                            }
                            break;
                        case customTableView::OperatorRole_3:// 6
                            {
                                itemButton3->setStatusPressed();
                            }
                            break;
                    }
                    return false;
                }
                break;
            case QEvent::MouseButtonRelease:
                {
                    switch(index.column())
                    {
                        case customTableView::OperatorRole_1:// 4
                            {
                                itemButton1->setStatusReleased();
                                emit signals1(index);
                            }
                            break;
                        case customTableView::OperatorRole_2:// 5
                            {
                                itemButton2->setStatusReleased();
                                emit signals2(index);
                            }
                            break;
                        case customTableView::OperatorRole_3:// 6
                            {
                                itemButton3->setStatusReleased();
                                emit signals3(index);
                            }
                            break;
                    }
                    return true;
                }
                break;
            default:
                {
                    switch(index.column())
                    {
                        case customTableView::OperatorRole_1:// 4
                            {
                                itemButton1->setStatusNone();
                            }
                            break;
                        case customTableView::OperatorRole_2:// 5
                            {
                                itemButton2->setStatusNone();
                            }
                            break;
                        case customTableView::OperatorRole_3:// 6
                            {
                                itemButton3->setStatusNone();
                            }
                            break;
                    }
                    QStyledItemDelegate::editorEvent(event, model, option, index);
                    return false;
                }
                break;
        }
    }
    else
    {
        switch(index.column())
        {
            case customTableView::OperatorRole_1:// 4
                {
                    itemButton1->setStatusNone();
                }
                break;
            case customTableView::OperatorRole_2:// 5
                {
                    itemButton2->setStatusNone();
                }
                break;
            case customTableView::OperatorRole_3:// 6
                {
                    itemButton3->setStatusNone();
                }
                break;
        }
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }

    return true;
}

void customItemDelegate::showMessage1(const QModelIndex& index)
{
    qDebug() << "showMessage1     --" << index;
    QMessageBox::information(NULL, "触发消息", QString("showMessage1 开启"));
}

void customItemDelegate::showMessage2(const QModelIndex& index)
{
    qDebug() << "showMessage2     --" << index;
    QMessageBox::information(NULL, "触发消息", QString("showMessage2 删除"));
}

void customItemDelegate::showMessage3(const QModelIndex& index)
{
    qDebug() << "showMessage3     --" << index;
    QMessageBox::information(NULL, "触发消息", QString("showMessage3 打开"));
}

ItemButton::ItemButton(QString iconName): buttonWidget(new QPushButton())
{
    buttonIconName = iconName;
    buttonWidget->setStyleSheet("\
                            QPushButton{\
                                border: none;\
                                background-color: transparent;\
                                image: url(:/icon/icon/" + iconName + ".png);\
                            }\
                            QPushButton:hover {\
                                image: url(:/icon/icon/" + iconName + "-hover.png);\
                            }\
                            QPushButton:pressed {\
                                image: url(:/icon/icon/" + iconName + "-pressed.png);\
                            }\
                          ");
}

void ItemButton::paintButton(QPainter* painter, const QStyleOptionViewItem& option, const QPoint& pos)
{
    QStyleOptionButton buttonOption;
    buttonOption.rect = QRect(option.rect.x() + posx, option.rect.y() + poxy, width, height);
    buttonOption.state = QStyle::State_Enabled;
    if(buttonOption.rect.contains(pos))
    {
        if(buttonStatus == BUTTON_STATUS::HOVER)
            buttonOption.state |= QStyle::State_MouseOver;
        else if(buttonStatus == BUTTON_STATUS::PRESSED)
            buttonOption.state |= QStyle::State_Sunken;
    }
    
// 根据需要选择使用的绘制函数
//    QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, buttonWidget.data());
//    option.widget->style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, buttonWidget.data());
    buttonWidget.data()->style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, buttonWidget.data());
}

void ItemButton::setStatusNone()
{
    buttonStatus = NONE;
}

void ItemButton::setStatusHover()
{
    buttonStatus = HOVER;
}

void ItemButton::setStatusPressed()
{
    buttonStatus = PRESSED;
}

void ItemButton::setStatusReleased()
{
    buttonStatus = RELEASED;
}

bool ItemButton::withinButtonRegion(const QPoint pos, const QStyleOptionViewItem& option)
{
    QStyleOptionButton buttonOption;
    buttonOption.rect = QRect(option.rect.x() + posx, option.rect.y() + poxy, width, height);
    return buttonOption.rect.contains(pos);
}

ItemProgressBar::ItemProgressBar(): progressBarWidget(new QProgressBar())
{
    progressBarWidget->setStyleSheet("\
                                        QProgressBar{\
                                            border: 2px solid #347;\
                                            border-radius:4px;\
                                        }\
                                        QProgressBar::chunk {\
                                            background-color: #77AAEE;\
                                        }\
                                     ");
}

void ItemProgressBar::paintQProgressBar(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index)
{

    int progress = index.data().toDouble() * 100;

    QStyleOptionProgressBar progressBarOption;
    progressBarOption.initFrom(option.widget);
    progressBarOption.rect = QRect(option.rect.x() + left, option.rect.y() + top, option.rect.width() - left - right, option.rect.height() - top - bottom);
//    progressBarOption.rect = option.rect;
    progressBarOption.minimum = 0;
    progressBarOption.maximum = 100;
    progressBarOption.progress = progress;
    progressBarOption.text = QString::number(progress) + "%";
    progressBarOption.textVisible = true;
    progressBarOption.textAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
    
// 根据需要选择使用的绘制函数
//    QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, progressBarWidget.data());
//    option.widget->style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, progressBarWidget.data());
    progressBarWidget.data()->style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, progressBarWidget.data());

}

⑤ 应用实现的类

实现方式

  • 在mainwindow.cpp添加代码
MainWindow::MainWindow(QWidget* parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    table_view = new customTableView(this);
    ui->tableView->setModel(table_view);

    customItemDelegate* processingDelegate = new customItemDelegate(this);
    ui->tableView->setItemDelegate(processingDelegate);
    ui->tableView->setMouseTracking(true);


    // 测试
    customDatastructure* test = new customDatastructure(true, 1, "hello1.jpg",  100);
    test->setCurSize(39);
    table_view->addData(test);
    table_view->addData(new customDatastructure(true, 2, "hello2.jpg",  150));

    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
    ui->tableView->resizeColumnsToContents();
}

你可能感兴趣的:(QT,c++)