文档章节

Qt 实现QTableView单元格多按钮效果

剑是SR剑
 剑是SR剑
发布于 02/27 00:50
字数 1039
阅读 189
收藏 0

实现效果预览:

思路:

使用Qt的委托类独自渲染表格的某一列,如上图字段2的那一列。该一个单元格放入多个按钮,重写 QStyledItemDelegate  委托类 paint 方法即可实现上图效果。

代码:

委托类: CButtonDelegate:

#ifndef BUTTONSDELEGATE_H
#define BUTTONSDELEGATE_H

#include <QPushButton>
#include <QStyledItemDelegate>
#include <QStyleOptionButton>
#include <QVector>

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

public:
    QWidget *createEditor(QWidget *parent,
                          const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

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

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

signals:
    void SIGNAL_Clicked(int _iRow, int _iColumn, QString _sButtonContent);

private:
    QPushButton* m_pbtn = nullptr;
    QMap<QModelIndex, QVector<QStyleOptionButton*>> m_mapStyleBtn;  ///< 每个单元格管理的按钮
    QPair<bool, QStyleOptionButton*> m_pairClickedButton;    ///< 记录被按压的按钮坐标

    int m_iBtnHeight = 40;      ///< 按钮的高度
    int m_iBtnWidth = 100;      ///< 按钮的宽度
    int m_iIntervalWidth = 20;  ///< 按钮间隔距离
};
#endif // BUTTONSDELEGATE_H
#include <QDebug>
#include <QString>
#include <QPainter>
#include <QMouseEvent>

CButtonsDelegate::CButtonsDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
    m_pbtn = new QPushButton();

    QStringList qss;
    qss.append(QString("QPushButton{background-color:green;border:1px solid black; border-radius:6px;color:black;font-size:15px;}"));
    qss.append(QString("QPushButton:pressed{background-color:red;}"));
    qss.append(QString("QPushButton:!enabled{border:1px solid gray;color:gray;}"));
    m_pbtn->setStyleSheet(qss.join(""));
}

QWidget *CButtonsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                                        const QModelIndex &index) const
{
    return nullptr;
}

void CButtonsDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QVector<QStyleOptionButton*> vtStyleButton;
    if (!m_mapStyleBtn.contains(index) || m_mapStyleBtn[index].size() == 0)
    {
        for (int i = 0; i < 3; i++)
        {
            QStyleOptionButton* pbtnStyle = new QStyleOptionButton();
            pbtnStyle->text = "按钮" + QString::number(i);
            vtStyleButton.push_back(pbtnStyle);
        }
        (const_cast<CButtonsDelegate*>(this))->m_mapStyleBtn[index] = vtStyleButton;
    }
    else{
        vtStyleButton = m_mapStyleBtn[index];
    }

    int iTopSpaceHeight = static_cast<float>(option.rect.height() - m_iBtnHeight) / 2;
    int iX = m_iIntervalWidth + option.rect.x();
    int iY = option.rect.y() + iTopSpaceHeight;
    for (auto pBtn : vtStyleButton)
    {
        pBtn->rect = QRect(iX, iY, m_iBtnWidth, m_iBtnHeight);
        pBtn->state |= QStyle::State_Enabled;
        iX = iX + m_iBtnWidth + m_iIntervalWidth;
    }

//    painter->save();
//    if (option.state & QStyle::State_Selected)
//    {
//        painter->fillRect(option.rect, option.palette.highlight());
//    }
//    painter->restore();

    for (auto pBtn : vtStyleButton)
    {
        m_pbtn->style()->drawControl(QStyle::CE_PushButton, pBtn, painter, m_pbtn);
    }
}

bool CButtonsDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (event->type() == QEvent::MouseButtonPress)  // 鼠标按下
    {
        QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(event);
        if (m_mapStyleBtn.contains(index))
        {
            const QVector<QStyleOptionButton*> vtStyleBtns = m_mapStyleBtn[index];
            for (auto& pBtn : vtStyleBtns)
            {
                if (pBtn->rect.contains(pMouseEvent->x(), pMouseEvent->y()))
                {
                    pBtn->state |= QStyle::State_Sunken;
                    m_pairClickedButton = {true, pBtn};
                }
            }
        }
        return true;
    }
    else if (event->type() == QEvent::MouseButtonRelease) // 鼠标松开或者移出
    {
        QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(event);

        if (m_mapStyleBtn.contains(index))
        {
            const QVector<QStyleOptionButton*> vtStyleBtns = m_mapStyleBtn[index];
            for (auto& pBtn : vtStyleBtns)
            {
                if (pBtn->rect.contains(pMouseEvent->x(), pMouseEvent->y()))
                {
                    pBtn->state &= (~QStyle::State_Sunken);
                    qDebug() << "####################SIGNAL_Clicked" << index.row() << index.column() << pBtn->text;
                    emit SIGNAL_Clicked(index.row(), index.column(), pBtn->text);
                    m_pairClickedButton = {false, nullptr};
                }
            }
        }
        return true;
    }
    else if (event->type() == QEvent::MouseMove)        // 鼠标移出, 使被点击按钮恢复状态
    {
        QMouseEvent* pMouseEvent = dynamic_cast<QMouseEvent*>(event);

        if (m_pairClickedButton.first && m_pairClickedButton.second)
        {
            if (!m_pairClickedButton.second->rect.contains(pMouseEvent->x(), pMouseEvent->y()))
            {
                m_pairClickedButton.second->state &= (~QStyle::State_Sunken);
                m_pairClickedButton = {false, nullptr};
            }
        }
        return true;
    }

    return false;
}

QTableView的数据模型CTestModel, 继承自 QAbstractTableModel 必须重写的方法有 datarowCountcolumnCount,其他方法根据需求实现,这里只是实现一个简单的model 

#ifndef TESTITEMMODEL_H
#define TESTITEMMODEL_H

#include <QAbstractTableModel>
#include <QStringList>

class CTestItemModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    explicit CTestItemModel(QObject *parent = nullptr);
    void SetHeader(QStringList &_lstHeader)
    {
        m_lstHeader = std::move(_lstHeader);
    }

    void SetData(QVector<QStringList> &_vtData)
    {
        m_vtData = std::move(_vtData);
    }

public:
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;

private:
    QStringList m_lstHeader;    ///< 表字段
    QVector<QStringList> m_vtData;  ///< 文本二维数据
};

#endif // TESTITEMMODEL_H
CTestItemModel::CTestItemModel(QObject *parent) : QAbstractTableModel(parent)
{

}

QVariant CTestItemModel::data(const QModelIndex &index, int role) const
{
    if (index.row() < m_vtData.size() && index.column() < m_vtData[index.row()].size())
    {
        if (role == Qt::DisplayRole)
        {
            return QVariant(m_vtData[index.row()][index.column()]);
        }

        if (role == Qt::TextAlignmentRole)
        {
            return QVariant(Qt::AlignCenter);
        }
    }

    return QVariant();
}

Qt::ItemFlags CTestItemModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
    {
        return Qt::NoItemFlags;
    }

//    if (index.column() == 2)
//    {
//        return Qt::ItemIsEditable | Qt::ItemIsEnabled;
//    }
    Qt::ItemFlags flag = QAbstractItemModel::flags(index);
    return flag;
}

QVariant CTestItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (Qt::Horizontal == orientation)
    {
        if (Qt::DisplayRole == role)
        {
            if (section < m_lstHeader.size())
            {
                return QVariant(m_lstHeader[section]);
            }
        }

        if (Qt::TextAlignmentRole == role)
        {
            return QVariant(Qt::AlignCenter);
        }
    }
    return QVariant();
}

int CTestItemModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_vtData.size();
}

int CTestItemModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_lstHeader.size();
}

调用, 我就不贴全部代码了,只贴部分:

// 初始化就省略了……

ui->tableView->setItemDelegateForColumn(1, m_pButtonsDelegate);
QStringList lstHeader = {"字段1", "字段2", "字段3"};
QStringList lstRow1 = {"第一行1", "第一行2", "第一行3"};
QStringList lstRow2 = {"第二行1", "第二行2", "第二行3"};
QStringList lstRow3 = {"第三行1", "第三行2"};    
QVector<QStringList> vtData;    
vtData.push_back(std::move(lstRow1));
vtData.push_back(std::move(lstRow2));
vtData.push_back(std::move(lstRow3));
m_pDataModel->SetHeader(lstHeader);

ui->tableView->setModel(m_pDataModel);
m_pDataModel->SetData(vtData);
ui->tableView->horizontalHeader()->setFixedHeight(20);
ui->tableView->verticalHeader()->setDefaultSectionSize(100);
ui->tableView->setColumnWidth(0, 200);
ui->tableView->setColumnWidth(1, 400);
ui->tableView->setColumnWidth(3, 200);

参考:

https://www.itread01.com/content/1546976188.html

https://www.cnblogs.com/li-peng/p/4029885.html

 

© 著作权归作者所有

剑是SR剑
粉丝 0
博文 3
码字总数 2074
作品 0
厦门
私信 提问
加载中

评论(0)

QTableView表格控件区域选择-自绘选择区域

目录 一、开心一刻 二、概述 三、效果展示 四、实现思路 五、相关文章 原文链接:QTableView表格控件区域选择-自绘选择区域 一、开心一刻 陪完客户回到家,朦胧之中,看到我妈正在拖地,我掏...

朝十晚八
2019/08/01
0
0
QTableWidget 基本概念及功能使用

QTableWidget是QT程序中常用的显示数据表格的空间。QTableWidget是QTableView的子类,主要的区别是QTableView可以使用自定义的数据模型来显示内容(也就是先要通过setModel来绑定数据源),而Q...

barsoom
2012/10/24
985
0
《Qt 5.9 C++开发指南》第5章 Model/View结构【详细摘要】

Model/View(模型/视图)结构是Qt中用界面组件显示与编辑数据的一种结构,视图(View)是显示和编辑数据的界面组件,模型(Model)是视图与原始数据之间的接口。Model/View结构的典型应用是在...

HongAndYi
前天
0
0
Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等

目录 一、概述 二、效果展示 三、定制表头 四、设置属性 五、相关文章 原文链接:Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等 一、概述 最近在研究QTableView支持多...

朝十晚八
2019/07/11
0
0
QRowTable表格控件-支持hover整行、checked整行、指定列排序等

目录 一、开心一刻 二、嘴一嘴 三、效果展示 四、浅谈实现 五、自定义数据源 六、自定义视图 七、测试 八、相关文章 原文链接:QRowTable表格控件-支持hover整行、checked整行、局部列排序等...

朝十晚八
2019/07/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

打造Worktile敏捷开发管理工具的思与惑

从2019年初,我们团队准备开发一款适合研发团队使用的敏捷开发管理工具,那时候我们也在思考,到底什么样的工具才算是优秀的研发管理工具,研发管理的场景、方法和流派有很多,市面上关于研发...

Worktile
2分钟前
0
0
Android知识体系总结2020之Android部分自定义View篇

1.自定义View需要具备的知识点 View坐标系 Android的角度与弧度 View绘制机制 View事件分发 Android动画机制 Paint类 Canvas类 Path类 贝塞尔曲线 ...... 2.自定义View的几种方式 《Android...

ClAndEllen
4分钟前
7
0
星云精准测试有力提升金融复杂系统的测试能效

随着国内大数据、云计算、人工智能等新技术的发展,银行业的前中后台正面临着全面改造,金融科技是业务转型发展的一个核心发力点。金融行业信息系统集中度高、规模庞大、多系统之间关联性强、...

星云测试
7分钟前
7
0
Leech for mac(轻量级mac下载工具) v3.14

Leech一个轻量级但功能强大的下载管理器。掌握你的浏览器下载,让你轻松下载,不再受浏览器限速带宽,以及下载多个数据,Leech让你最高最快的速度下载,不再受限于浏览器下载 软件特点 必须....

麦克W
11分钟前
7
0
js 迅雷 批量下载

参考 http://open.thunderurl.com/#/ 效果 点击下载按钮, 会弹出批量下载对话框 主要函数 function f2() { let link = [ // 'magnet:?xt=urn:btih:884DDE11B387DD22248B4F3......

阿豪boy
19分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部