使用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 必須重寫的方法有 data、rowCount、columnCount,其餘方法根據需求實現,這裏只是實現一個簡單的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);