Qt 實現QTableView單元格多按鈕效果

實現效果預覽:

思路:

使用Qt的委託類獨自渲染表格的某一列,如上圖字段2的那一列。該一個單元格放入多個按鈕,重寫 QStyledItemDelegate  委託類 paint 方法便可實現上圖效果。html

代碼:

委託類: CButtonDelegate:app

#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);
                }
            }

            if (m_pairClickedButton.first && m_pairClickedButton.second)
            {
                m_pairClickedButton.second->state &= (~QStyle::State_Sunken);
                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 ide

#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

// 初始化就省略了……

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.htmlthis

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

相關文章
相關標籤/搜索