代理的基本使用
一般代理只需要继承QStyledItemDelegate类,然后重写createEditor,setEditorData和setModelData接口即可。
// COpratorDelegate.h class COpratorDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit COpratorDelegate(QObject *parent = nullptr, const ColType &type=STRING); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; private: CModel *_pModel = nullptr; };
在createEditor中创建控件,然后返回,在setEditorData中从模型中获取数据,然后填充到控件中,setModelData从控件中获取数据设置到model中。
在view中使用代理,如下:
COpratorDelegate *pCOD = new COpratorDelegate(_pTableView, COpratorDelegate::MULBTN); _pTableView->setItemDelegateForColumn(1, pCOD);
在代理中使用复杂控件
在代理中使用复杂控件,比如使用布局添加多个控件,如下:
// 创建一个多按键的QWidget页面, MulBtnWiget.h class MulBtnWiget : public QWidget { Q_OBJECT public: explicit MulBtnWiget(const QModelIndex &index, QWidget *parent = nullptr); void setBtnData(const QVariant &data); signals: void btnColor(const QString &color, const int &row); private: QPushButton *_pButton1; QPushButton *_pButton2; QPushButton *_pButton3; QModelIndex _index; };
此页面包含三个按钮,分别为红色,绿色和重置,用来改变颜色,如下:
MulBtnWiget::MulBtnWiget(const QModelIndex &index, QWidget *parent/* = nullptr*/) : QWidget(parent), _index(index) { QHBoxLayout *pLayout = new QHBoxLayout(); _pButton1 = new QPushButton("red"); pLayout->addWidget(_pButton1); _pButton2 = new QPushButton("green"); pLayout->addWidget(_pButton2); _pButton3 = new QPushButton("reset"); pLayout->addWidget(_pButton3); pLayout->setContentsMargins(0, 0, 0, 0); this->setLayout(pLayout); this->setContentsMargins(0, 0, 0, 0); _pButton1->setEnabled(false); _pButton2->setEnabled(false); _pButton3->setEnabled(false); _pButton1->setStyleSheet("color: red;"); _pButton2->setStyleSheet("color: green;"); _pButton3->setStyleSheet("color: black;"); connect(_pButton1, &QPushButton::clicked, this, [this](){ emit btnColor("red", _index.row()); }); connect(_pButton2, &QPushButton::clicked, this, [this](){ emit btnColor("green", _index.row()); }); connect(_pButton3, &QPushButton::clicked, this, [this](){ emit btnColor("black", _index.row()); }); }
创建了这个页面之后,可以使用这个页面作为代理,使用的方式非常简单,直接new出来,然后返回即可:
QWidget *COpratorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { MulBtnWiget *pMulBtnWidget = new MulBtnWiget(index, parent); return pMulBtnWidget; }
使用这种方式既可以使用复杂的控件代理,如果需要使用数据进行设置,只需要定义一个数据类,然后注册,如下:
struct Person { int id; QString name; qint8 age; }; Q_DECLARE_METATYPE(Person)
但是这种情况只有在双击的时候代理才会显示,如果需要一直显示,或者在标题栏中是无法实现的。
让代理一直显示
使用自绘的方式
使用自绘的方式主要是paint和editorEvent两个接口,自己手动绘制,这种方式最原始的方案,但是面对复杂的布局和多个控件的时候就比较麻烦。如下:
还可以参考Qt的例子: Star Delegate example 的实现,网上大部分添加一直显示的控件就是使用此方式实现的。
使用View的接口设置一直显示
针对代理一直显示,Qt的View中三个接口能完成此事,分别为: openPersistentEditor(), closePersistentEditor() 和 isPersistentEditorOpen()方法。使用方法非常简单:
_pTableView->openPersistentEditor(pModel->index(1, 1));
比如需要给某一列进行设置就可以使用循环遍历所有列进行设置即可。但是,在使用排序过滤QSortFilterProxyModel会导致设置的一直显示失效, 主要原因在于通过_pTableView->model()获取的model已经是QSortFilterProxyModel,因此设置的索引不对,排序过滤后导致,需要将真正的数据类模型指针传递到代理中,并且过滤时手动调用openPersistentEditor接口。
最终实现的结果展示
至于自定义QHeaderView参考: https://blog.csdn.net/beichen_yx/article/details/105599995
示例代码
// CDelegate.h #ifndef CDELEGATE_H #define CDELEGATE_H #include <QObject> #include <QStyledItemDelegate> #include <QWidget> #include <QComboBox> #include <QProgressBar> class QWidget; class QVariant; class QPushButton; class CModel; class MulBtnWiget : public QWidget { Q_OBJECT public: explicit MulBtnWiget(const QModelIndex &index, QWidget *parent = nullptr); void setBtnData(const QVariant &data); signals: void btnColor(const QString &color, const int &row); private: QPushButton *_pButton1; QPushButton *_pButton2; QPushButton *_pButton3; QModelIndex _index; }; class COpratorDelegate : public QStyledItemDelegate { Q_OBJECT public: enum ColType { STRING, COMBOBOX, PROGRESSBAR, MULBTN }; explicit COpratorDelegate(QObject *parent = nullptr, const ColType &type=STRING); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void setCModel(CModel * const pModel) { _pModel = pModel; } signals: void btnColor(const QString &color, const QModelIndex &index); private: CModel *_pModel = nullptr; ColType _colType = STRING; }; #endif // CDELEGATE_H
#include "CDelegate.h" #include <QWidget> #include <QLabel> #include <QPushButton> #include <QHBoxLayout> #include <QVariant> #include "CModel/CModel.h" MulBtnWiget::MulBtnWiget(const QModelIndex &index, QWidget *parent/* = nullptr*/) : QWidget(parent), _index(index) { QHBoxLayout *pLayout = new QHBoxLayout(); _pButton1 = new QPushButton("red"); pLayout->addWidget(_pButton1); _pButton2 = new QPushButton("green"); pLayout->addWidget(_pButton2); _pButton3 = new QPushButton("reset"); pLayout->addWidget(_pButton3); pLayout->setContentsMargins(0, 0, 0, 0); this->setLayout(pLayout); this->setContentsMargins(0, 0, 0, 0); _pButton1->setEnabled(false); _pButton2->setEnabled(false); _pButton3->setEnabled(false); _pButton1->setStyleSheet("color: red;"); _pButton2->setStyleSheet("color: green;"); _pButton3->setStyleSheet("color: black;"); connect(_pButton1, &QPushButton::clicked, this, [this](){ emit btnColor("red", _index.row()); }); connect(_pButton2, &QPushButton::clicked, this, [this](){ emit btnColor("green", _index.row()); }); connect(_pButton3, &QPushButton::clicked, this, [this](){ emit btnColor("black", _index.row()); }); } void MulBtnWiget::setBtnData(const QVariant &data) { int type = qvariant_cast<int>(data); if (type <= 3) { _pButton1->setEnabled(true); _pButton2->setEnabled(true); _pButton3->setEnabled(true); _pButton1->setStyleSheet("color: red;"); _pButton2->setStyleSheet("color: green;"); } else if (type >3 && type <=4) { _pButton1->setEnabled(false); _pButton1->setStyleSheet("color: black;"); _pButton2->setEnabled(true); _pButton3->setEnabled(true); } else { _pButton1->setEnabled(true); _pButton2->setEnabled(false); _pButton2->setStyleSheet("color: black;"); _pButton3->setEnabled(true); } } COpratorDelegate::COpratorDelegate(QObject *parent/* = nullptr*/, const ColType &type/*=STRING*/) : QStyledItemDelegate(parent), _colType(type) { } QWidget *COpratorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (_colType == STRING) { return QStyledItemDelegate::createEditor(parent, option, index); } else if (_colType == COMBOBOX) { auto *pCbx = new QComboBox(parent); pCbx->addItems({"test1", "test1", "test2", "test3"}); return pCbx; } else if (_colType == PROGRESSBAR) { auto *pProgressbar = new QProgressBar(parent); pProgressbar->setRange(0, 10); return pProgressbar; } else if (_colType == MULBTN) { MulBtnWiget *pMulBtnWidget = new MulBtnWiget(index, parent); connect(pMulBtnWidget, &MulBtnWiget::btnColor, this, [this](const QString &color, const int &row){ // index.model()返回的模型不一定是自定义的数据模型,可能是代理模型 if (_pModel == nullptr) { return; } _pModel->setRowColor(row, color); }); return pMulBtnWidget; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } void COpratorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (_colType == STRING) { } else if (_colType == COMBOBOX) { QComboBox *pWidget = qobject_cast<QComboBox*>(editor); auto tmp = QString::number(index.data().toInt()); if (pWidget->findText(tmp) < 0) { pWidget->addItem(tmp); } pWidget->setCurrentText(tmp); } else if (_colType == PROGRESSBAR) { QProgressBar *pWidget = qobject_cast<QProgressBar*>(editor); pWidget->setValue(index.data().toInt()); } else if (_colType == MULBTN) { MulBtnWiget *pWidget = qobject_cast<MulBtnWiget*>(editor); pWidget->setBtnData(index.data()); } else { } } void COpratorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QStyledItemDelegate::setModelData(editor, model, index); }
#include "CDelegate.h" #include <QWidget> #include <QLabel> #include <QPushButton> #include <QHBoxLayout> #include <QVariant> #include "CModel/CModel.h" MulBtnWiget::MulBtnWiget(const QModelIndex &index, QWidget *parent/* = nullptr*/) : QWidget(parent), _index(index) { QHBoxLayout *pLayout = new QHBoxLayout(); _pButton1 = new QPushButton("red"); pLayout->addWidget(_pButton1); _pButton2 = new QPushButton("green"); pLayout->addWidget(_pButton2); _pButton3 = new QPushButton("reset"); pLayout->addWidget(_pButton3); pLayout->setContentsMargins(0, 0, 0, 0); this->setLayout(pLayout); this->setContentsMargins(0, 0, 0, 0); _pButton1->setEnabled(false); _pButton2->setEnabled(false); _pButton3->setEnabled(false); _pButton1->setStyleSheet("color: red;"); _pButton2->setStyleSheet("color: green;"); _pButton3->setStyleSheet("color: black;"); connect(_pButton1, &QPushButton::clicked, this, [this](){ emit btnColor("red", _index.row()); }); connect(_pButton2, &QPushButton::clicked, this, [this](){ emit btnColor("green", _index.row()); }); connect(_pButton3, &QPushButton::clicked, this, [this](){ emit btnColor("black", _index.row()); }); } void MulBtnWiget::setBtnData(const QVariant &data) { int type = qvariant_cast<int>(data); if (type <= 3) { _pButton1->setEnabled(true); _pButton2->setEnabled(true); _pButton3->setEnabled(true); _pButton1->setStyleSheet("color: red;"); _pButton2->setStyleSheet("color: green;"); } else if (type >3 && type <=4) { _pButton1->setEnabled(false); _pButton1->setStyleSheet("color: black;"); _pButton2->setEnabled(true); _pButton3->setEnabled(true); } else { _pButton1->setEnabled(true); _pButton2->setEnabled(false); _pButton2->setStyleSheet("color: black;"); _pButton3->setEnabled(true); } } COpratorDelegate::COpratorDelegate(QObject *parent/* = nullptr*/, const ColType &type/*=STRING*/) : QStyledItemDelegate(parent), _colType(type) { } QWidget *COpratorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (_colType == STRING) { return QStyledItemDelegate::createEditor(parent, option, index); } else if (_colType == COMBOBOX) { auto *pCbx = new QComboBox(parent); pCbx->addItems({"test1", "test1", "test2", "test3"}); return pCbx; } else if (_colType == PROGRESSBAR) { auto *pProgressbar = new QProgressBar(parent); pProgressbar->setRange(0, 10); return pProgressbar; } else if (_colType == MULBTN) { MulBtnWiget *pMulBtnWidget = new MulBtnWiget(index, parent); connect(pMulBtnWidget, &MulBtnWiget::btnColor, this, [this](const QString &color, const int &row){ // index.model()返回的模型不一定是自定义的数据模型,可能是代理模型 if (_pModel == nullptr) { return; } _pModel->setRowColor(row, color); }); return pMulBtnWidget; } else { return QStyledItemDelegate::createEditor(parent, option, index); } } void COpratorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (_colType == STRING) { } else if (_colType == COMBOBOX) { QComboBox *pWidget = qobject_cast<QComboBox*>(editor); auto tmp = QString::number(index.data().toInt()); if (pWidget->findText(tmp) < 0) { pWidget->addItem(tmp); } pWidget->setCurrentText(tmp); } else if (_colType == PROGRESSBAR) { QProgressBar *pWidget = qobject_cast<QProgressBar*>(editor); pWidget->setValue(index.data().toInt()); } else if (_colType == MULBTN) { MulBtnWiget *pWidget = qobject_cast<MulBtnWiget*>(editor); pWidget->setBtnData(index.data()); } else { } } void COpratorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QStyledItemDelegate::setModelData(editor, model, index); }
#include "CModel/CModel.h" #include <ctime> #include <algorithm> #include <QDebug> CModel::CModel(QAbstractItemModel *parent) : QAbstractItemModel(parent) { for (int i=0; i<10; ++i) { m_stocks.append({i, i*100, rand() % 100, rand() % 300, i, i}); _colors.append(QColor(Qt::black)); } } void CModel::setRowColor(const int &row, const QString &color) { QColor txtColor; if (color == "red") { txtColor = Qt::red; } else if (color == "green") { txtColor = Qt::green; } else { txtColor = Qt::black; } _colors[row] = txtColor; auto start = this->index(row, 0); auto end = this->index(row, this->columnCount(QModelIndex()) - 1); emit dataChanged(start, end); } int CModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_stocks.length(); } int CModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_names.length(); } QVariant CModel::data(const QModelIndex &index, int role) const { if(!index.isValid()){ qDebug() << "index is valid: " << index.row() << ":" << index.column(); return QVariant(); } if(index.row() > m_stocks.length() || index.column() > m_names.length()){ qDebug() << "m_stocks len: " << m_stocks.length() << " " << index.row() << ":" << index.column(); return QVariant(); } if(role == Qt::DisplayRole){ auto stock = m_stocks.at(index.row()); return (index.column() > stock.length() || index.column() < 0) ? QVariant() : stock.at(index.column()); } else if(role == Qt::EditRole){ auto stock = m_stocks.at(index.row()); return (index.column() > stock.length() || index.column() < 0) ? QVariant() : stock.at(index.column()); } else if (role == Qt::TextColorRole) { return QVariant(_colors.at(index.row())); } else if (role == Qt::TextAlignmentRole) { return QVariant(Qt::AlignCenter|Qt::AlignVCenter); } return QVariant(); } bool CModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()){ qDebug() << "index is valid: " << index.row() << ":" << index.column(); return false; } if(role == Qt::EditRole){ auto stock = m_stocks.at(index.row()); stock[index.column()] = value; emit dataChanged(index, index); return true; } return false; } QVariant CModel::headerData(int section, Qt::Orientation orientation, int role) const { // if(role != Qt::DisplayRole){ // return QVariant(); // } // if(orientation == Qt::Horizontal){ // if(section < 0 || section > m_names.length()){return QVariant();} // return QVariant(m_names.at(section)); // }else { // return QVariant(QString::number(section)); // } if(orientation == Qt::Horizontal){ if (role == Qt::DisplayRole) { return QVariant(m_names.at(section)); } else { qDebug() << role; } } else if (orientation == Qt::Vertical) { if (role == Qt::DisplayRole) { return QVariant(QString::number(section)); } } return QVariant(); } bool CModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if(role != Qt::EditRole){ return false; } if(orientation == Qt::Horizontal){ m_names.replace(section, value.toString()); emit headerDataChanged(Qt::Horizontal, section, section); return true; }else{ return false; } } Qt::ItemFlags CModel::flags(const QModelIndex &index) const { // if(index.isValid()){ // Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren | Qt::ItemIsEditable; // return flags; // } // return QAbstractItemModel::flags(index); if(index.isValid()){ Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren | Qt::ItemIsEditable; return flags | QAbstractItemModel::flags(index); } return QAbstractItemModel::flags(index); } QModelIndex CModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || column >= columnCount(parent) || column > m_names.length()) return QModelIndex(); return createIndex(row,column); } QModelIndex CModel::parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); }
#include "mainwindow.h" #include "ui_mainwindow.h" #include "CView/CTableView.h" #include "CModel/CModel.h" #include "CDelegate/CDelegate.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), _pTableView(new CTableView(this)), _pModel(new CModel()) { ui->setupUi(this); this->setWindowTitle("Custom MVC"); this->setCentralWidget(_pTableView); this->setContentsMargins(0, 0, 0, 0); _pTableView->setModel(_pModel); this->initUi(); this->resize(1024, 768); } MainWindow::~MainWindow() { delete ui; delete _pModel; delete _pTableView; } void MainWindow::initUi() { int colCount = _pModel->columnCount(_pModel->index(0, 0)); _pTableView->resizeColumnsToContents(); for (int i=0; i<colCount; ++i) { _pTableView->setColumnWidth(i, 300); } COpratorDelegate *pCOD = new COpratorDelegate(_pTableView, COpratorDelegate::MULBTN); pCOD->setCModel(_pModel); _pTableView->setItemDelegateForColumn(colCount - 1, pCOD); auto *pModel = _pTableView->model(); int row = pModel->rowCount(); for (int i=0; i<row; ++i) { _pTableView->openPersistentEditor(pModel->index(i, colCount - 1)); } _columns.append(colCount - 1); COpratorDelegate *pProcessCOD = new COpratorDelegate(_pTableView, COpratorDelegate::PROGRESSBAR); pProcessCOD->setCModel(_pModel); _pTableView->setItemDelegateForColumn(colCount - 2, pProcessCOD); for (int i=0; i<row; ++i) { _pTableView->openPersistentEditor(pModel->index(i, colCount - 2)); } _columns.append(colCount - 2); COpratorDelegate *pCbxCOD = new COpratorDelegate(_pTableView, COpratorDelegate::COMBOBOX); pCbxCOD->setCModel(_pModel); _pTableView->setItemDelegateForColumn(colCount - 3, pCbxCOD); for (int i=0; i<row; ++i) { _pTableView->openPersistentEditor(pModel->index(i, colCount - 3)); } _columns.append(colCount - 3); connect(_pTableView, &CTableView::filterUpdate, this, &MainWindow::openPersistentForColumns ); } void MainWindow::openPersistentForColumns() { auto *pModel = _pTableView->model(); int row = _pModel->rowCount(); for (int i=0; i<row; ++i) { for (int &col : _columns) { _pTableView->openPersistentEditor(pModel->index(i, col)); } } }