使用代理 在代理处嵌套一层QTableView
目录
3.1.1.1QStyleOption及其子类包含QStyle函数绘制图形元素所需的所有信息。
1展示效果
右键菜单 处理点击事件 以及槽函数
缩小形成滑动条
能够实现单击 和双击事件
2具体实现
结构体类
QiantaoviewMsg.h
#ifndef QIANTAOVIEWMSG_H
#define QIANTAOVIEWMSG_H
#include <QVector>
#include <QString>
#include <QMetaType>
namespace TargetInfoDefine
{
struct GroupTargetInfo
{
int TargetId;//编号
double errorValue;
double gaiLvValue;
bool IsMainId;
GroupTargetInfo():TargetId(0),errorValue(0.00),gaiLvValue(0.00),IsMainId(false)
{
}
};
struct GroupParam
{
int GroupNumber;
int MianGroupID;
QString c;
int ClassNumer;
int TaskType;
int State;
QList<GroupTargetInfo> memberVec;
GroupParam():GroupNumber(0),MianGroupID(0),c(""),ClassNumer(0),TaskType(0),State(0)
{
memberVec.clear();
}
};
}
Q_DECLARE_METATYPE(TargetInfoDefine::GroupTargetInfo)
Q_DECLARE_METATYPE(QList<TargetInfoDefine::GroupTargetInfo>)
Q_DECLARE_METATYPE(TargetInfoDefine::GroupParam)
#endif // QIANTAOVIEWMSG_H
主窗口
main.cpp
#include <QApplication>
#include <QMainWindow>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include "customtablemodel.h"
#include "customdelegate.h"
#include "MessageInfo.h"
Q_DECLARE_METATYPE(QVector<s1>)
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QList<TargetInfoDefine::GroupParam> data;
// 初始化数据
TargetInfoDefine::GroupTargetInfo g1;
g1.TargetId = 1001;
g1.errorValue = 10;
g1.gaiLvValue = 80;
g1.IsMainId = true;
TargetInfoDefine::GroupTargetInfo g2;
g2.TargetId = 1002;
g2.errorValue = 20;
g2.gaiLvValue = 90;
g2.IsMainId = false;
TargetInfoDefine::GroupTargetInfo g3;
g3.TargetId = 1003;
g3.errorValue = 30;
g3.gaiLvValue = 70;
g3.IsMainId = false;
TargetInfoDefine::GroupParam item;
item.GroupNumber = 1;
item.MianGroupID = 2;
item.c = "banzhu1";
item.ClassNumer = 3;
item.TaskType = 4;
item.State = 1;
item.memberVec <<g1 << g2 ;
data.append(item);
TargetInfoDefine::GroupParam item2;
item2.GroupNumber = 2;
item2.MianGroupID = 20;
item2.c = "banzhu2";
item2.ClassNumer = 6;
item2.TaskType = 6;
item2.State = 1;
item2.memberVec <<g1 << g2 << g3;
data.append(item2);
CustomTableModel *model = new CustomTableModel();
model->setDataInfo(data);
QTableView *view = new QTableView;
view->setModel(model);
view->setSelectionMode(QAbstractItemView::NoSelection);
CustomDelegate *delegate = new CustomDelegate();
view->setItemDelegateForColumn(6, delegate);
int row = model->rowCount();
for (int i=0; i<row; ++i) {
view->openPersistentEditor(model->index(i, 6));
}
view->setSortingEnabled(true);
view->horizontalHeader()->setSortIndicatorShown(true);
view->horizontalHeader()->setStretchLastSection(true);
view->verticalHeader()->setDefaultSectionSize(40); // 例如,设置行高为 30 像素
QMainWindow window;
window.setCentralWidget(view);
window.show();
return app.exec();
}
主model类
customtablemodel.h
#ifndef CUSTOMTABLEMODEL_H
#define CUSTOMTABLEMODEL_H
#include <QAbstractTableModel>
#include "QiantaoviewMsg.h"
#include <QList>
class CustomTableModel : public QAbstractTableModel {
Q_OBJECT
public:
explicit CustomTableModel(QObject *parent = nullptr);
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;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void setDataInfo(const QList<TargetInfoDefine::GroupParam> &data);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const ;
private:
QList<TargetInfoDefine::GroupParam> m_data;
};
#endif // CUSTOMTABLEMODEL_H
customtablemodel.cpp
#include "CustomTableModel.h"
CustomTableModel::CustomTableModel(QObject *parent)
: QAbstractTableModel(parent) {
}
int CustomTableModel::rowCount(const QModelIndex &parent) const {
return m_data.size(); // 更改这里的名称
}
int CustomTableModel::columnCount(const QModelIndex &parent) const {
return 7; // 组号, 组长号, 规模, 等级, 任务类型, 状态, 人员
}
QVariant CustomTableModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || role != Qt::DisplayRole) {
return QVariant();
}
const TargetInfoDefine::GroupParam &item = m_data[index.row()];
switch (index.column()) {
case 0: return item.GroupNumber;
case 1: return item.MianGroupID;
case 2: return item.c;
case 3: return item.ClassNumer;
case 4: return item.TaskType;
case 5: return item.State;
case 6: return QVariant::fromValue(item.memberVec); // 返回 QVector<s1>
default: return QVariant();
}
}
bool CustomTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
return true;
}
Qt::ItemFlags CustomTableModel::flags(const QModelIndex &index) const {
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}
void CustomTableModel::setDataInfo(const QList<TargetInfoDefine::GroupParam> &data)
{
beginResetModel();
m_data = data;
endResetModel();
}
QVariant CustomTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole) {
return QVariant();
}
if (orientation == Qt::Horizontal) {
switch (section) {
case 0: return QString::fromLocal8Bit("组号");
case 1: return QString::fromLocal8Bit("组长号");
case 2: return QString::fromLocal8Bit("规模");
case 3: return QString::fromLocal8Bit("等级");
case 4: return QString::fromLocal8Bit("任务类型");
case 5: return QString::fromLocal8Bit("状态");
case 6: return QString::fromLocal8Bit("人员");
default: return QVariant();
}
}
return QVariant();
}
主代理里
customdelegate.h
#ifndef CUSTOMDELEGATE_H
#define CUSTOMDELEGATE_H
#include <QStyledItemDelegate>
class CustomDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit CustomDelegate(QObject *parent = nullptr);
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 updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // CUSTOMDELEGATE_H
customdelegate.cpp
#include "CustomDelegate.h"
#include <QComboBox>
#include <QSpinBox>
#include <QProgressBar>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QDebug>
#include <QPainter>
#include "QiantaoviewMsg.h"
#include <QApplication>
#include "groupnumbertableview.h"
#include "groupnumbertablemodel.h"
CustomDelegate::CustomDelegate(QObject *parent)
: QStyledItemDelegate(parent) {
}
QWidget *CustomDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
if (index.column() == 6) {
GroupNumberTableView * gruopView = new GroupNumberTableView(index,parent);
QList<TargetInfoDefine::GroupTargetInfo> memberVec;
memberVec.clear();
QVariant var = index.data(); // 从模型中获取数据
if (var.canConvert<QList<TargetInfoDefine::GroupTargetInfo>>()) {
memberVec = var.value<QList<TargetInfoDefine::GroupTargetInfo>>();
}
//可以接受信号 传入到model中 代理设置model model 传入到外边 也可以直接在外边进行接受
GroupNumberTableModel * groupModel = new GroupNumberTableModel();
groupModel->setData(memberVec);
gruopView->setModel(groupModel);
return gruopView;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void CustomDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
int value = index.model()->data(index, Qt::EditRole).toInt();
if (QTableWidget *tableWidget = qobject_cast<QTableWidget*>(editor)) {
QList<TargetInfoDefine::GroupTargetInfo> items = index.data().value<QList<TargetInfoDefine::GroupTargetInfo>>();
tableWidget->setColumnCount(items.size());
for (int i = 0; i < items.size(); ++i) {
const TargetInfoDefine::GroupTargetInfo &item = items[i];
QString cellText = QString("%1-%2-%3").arg(item.TargetId).arg(item.errorValue).arg(item.gaiLvValue);
tableWidget->setItem(0, i, new QTableWidgetItem(cellText));
}
}else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
void CustomDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}
void CustomDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
editor->setGeometry(option.rect);
}
void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QStyledItemDelegate::paint(painter, option, index);
}
子表格view
groupnumbertableview.h
#ifndef GROUPNUMBERTABLEVIEW_H
#define GROUPNUMBERTABLEVIEW_H
#include <QTableView>
#include <QMenu>
#include <QAction>
class CellPainterDelegate;
class GroupNumberTableModel;
// 组号表格视图类
class GroupNumberTableView : public QTableView {
Q_OBJECT
public:
explicit GroupNumberTableView(const QModelIndex &index, QWidget *parent = nullptr);
void setModel(GroupNumberTableModel *model);
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
private slots:
void onCustomContextMenuRequested(const QPoint &pos);
void onAction1Triggered();
void onAction2Triggered();
private:
QMenu *contextMenu;
QAction *action1;
QAction *action2;
};
#endif // GROUPNUMBERTABLEVIEW_H
groupnumbertableview.cpp
#include "groupnumbertableview.h"
#include "cellpainterdelegate.h"
#include "groupnumbertablemodel.h"
#include <QMouseEvent>
#include <QDebug>
#include <QApplication>
#include <QHeaderView>
GroupNumberTableView::GroupNumberTableView(const QModelIndex &index, QWidget *parent)
: QTableView(parent), contextMenu(new QMenu(this)), action1(new QAction(QString::fromLocal8Bit("操作1"), this)), action2(new QAction(QString::fromLocal8Bit("操作2"), this)) {
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onCustomContextMenuRequested(const QPoint &)));
contextMenu->addAction(action1);
contextMenu->addAction(action2);
connect(action1, SIGNAL(triggered()), this, SLOT(onAction1Triggered()));
connect(action2, SIGNAL(triggered()), this, SLOT(onAction2Triggered()));
setItemDelegate(new CellPainterDelegate(this));
horizontalHeader()->setVisible(false);
verticalHeader()->setVisible(false);
//setShowGrid(false);
// 使用样式表设置无边框
setStyleSheet("GroupNumberTableView { border: none; outline: none; }");
//setStyleSheet("GroupNumberTableView::item { border-left: none; border-top: none; border-bottom: none; border-right: 1px solid black; }");
//setSelectionMode(QAbstractItemView::NoSelection);
setSelectionMode(QAbstractItemView::ExtendedSelection);
verticalHeader()->setDefaultSectionSize(40); // 例如,设置行高为 30 像素
}
void GroupNumberTableView::setModel(GroupNumberTableModel *model) {
QTableView::setModel(model);
}
void GroupNumberTableView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
QModelIndex index = indexAt(event->pos());
if (index.isValid()) {
// 获取数据
QVariant data = index.data(Qt::UserRole);
TargetInfoDefine::GroupTargetInfo info = data.value<TargetInfoDefine::GroupTargetInfo>();
qDebug() << "单击单元格1111111111111111 info" << info.TargetId << info.errorValue <<info.gaiLvValue << "是否是主" << info.IsMainId << endl;
//qDebug() << "单击单元格:" << index.row();
}
}
QTableView::mousePressEvent(event);
}
void GroupNumberTableView::mouseDoubleClickEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
QModelIndex index = indexAt(event->pos());
if (index.isValid()) {
qDebug() << "双击单元格:" << index.row();
}
}
QTableView::mouseDoubleClickEvent(event);
}
void GroupNumberTableView::onCustomContextMenuRequested(const QPoint &pos) {
QModelIndex index = indexAt(pos);
if (index.isValid()) {
contextMenu->exec(mapToGlobal(pos));
}
}
void GroupNumberTableView::onAction1Triggered() {
qDebug() << "操作1被触发";
}
void GroupNumberTableView::onAction2Triggered() {
qDebug() << "操作2被触发";
}
子表格model
groupnumbertablemodel.h
#ifndef GROUPNUMBERTABLEMODEL_H
#define GROUPNUMBERTABLEMODEL_H
#include <QAbstractTableModel>
#include <QList>
#include "QiantaoviewMsg.h"
// 组号表格模型类
class GroupNumberTableModel : public QAbstractTableModel {
Q_OBJECT
public:
// 默认构造函数
explicit GroupNumberTableModel(QObject *parent = nullptr);
void setData(const QList<TargetInfoDefine::GroupTargetInfo> &data);
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;
private:
QList<TargetInfoDefine::GroupTargetInfo> dataList;
};
#endif // GROUPNUMBERTABLEMODEL_H
groupnumbertablemodel.cpp
#include "groupnumbertablemodel.h"
#include <QVariant>
#include <QDebug>
GroupNumberTableModel::GroupNumberTableModel(QObject *parent)
: QAbstractTableModel(parent) {}
void GroupNumberTableModel::setData(const QList<TargetInfoDefine::GroupTargetInfo> &data) {
beginResetModel();
dataList = data;
endResetModel();
}
int GroupNumberTableModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return 1; // 单行
}
int GroupNumberTableModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
qDebug() << "GroupNumberTableModel::rowCount " << dataList.size() << endl;
return dataList.size();
}
QVariant GroupNumberTableModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
if (role == Qt::UserRole) {
return QVariant::fromValue(dataList.at(index.column()));
}
return QVariant();
}
子表格代理
cellpainterdelegate.h
#ifndef CELLPANTERDELEGATE_H
#define CELLPANTERDELEGATE_H
#include <QStyledItemDelegate>
#include <QPainter>
#include <QStyleOptionViewItem>
#include <QModelIndex>
#include "QiantaoviewMsg.h"
// 单元格绘制代理类
class CellPainterDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit CellPainterDelegate(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;
};
#endif // CELLPANTERDELEGATE_H
cellpainterdelegate.cpp
#include "cellpainterdelegate.h"
#include <QApplication>
#include <QIcon>
#include <QDebug>
CellPainterDelegate::CellPainterDelegate(QObject *parent)
: QStyledItemDelegate(parent) {}
void CellPainterDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
painter->save();
// 获取数据
QVariant data = index.data(Qt::UserRole);
TargetInfoDefine::GroupTargetInfo info = data.value<TargetInfoDefine::GroupTargetInfo>();
qDebug() << " GroupTargetInfo info" << info.TargetId << info.errorValue <<info.gaiLvValue << "是否是主" << info.IsMainId << endl;
// 绘制背景
// painter->fillRect(option.rect, option.state & QStyle::State_Selected ? option.palette.highlight() : option.palette.base());
// 绘制文本信息
QRect textRect = option.rect;
textRect.adjust(5, 5, -5, -5);
QString text = QString::number(info.TargetId);
if (info.IsMainId) {
// 如果是主要标志,添加图标
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_ArrowRight);
icon.paint(painter, textRect, Qt::AlignLeft);
textRect.adjust(20, 0, 0, 0);
}
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
// 绘制errorValue和LvValue
QString errorText = QString::number(info.errorValue);
QString lvText = QString::number(info.gaiLvValue);
painter->drawText(textRect, Qt::AlignRight | Qt::AlignTop, errorText);
painter->drawText(textRect, Qt::AlignRight | Qt::AlignBottom, lvText);
painter->restore();
}
QSize CellPainterDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
return QSize(100, 50);
}
3心得总结
创作灵感来源:参考博客CSDN编程社区
Qt的复杂代理使用总结
3.1疑难点汇总
3.1.1自定义控件的显示 只有在双击的时候代理才会显示
一般代理只需要继承QStyledItemDelegate类,然后重写createEditor,setEditorData和setModelData接口 这种情况只有在双击的时候代理才会显示,如果需要一直显示,或者在标题栏中是无法实现的
能 paint 里用 QStyle 的 drawControl,但是支持的组件有限(毕竟 Qt 的 view 只在单元格处于可见区域时才进行渲染,所以没有提供这项功能
QStyleOption是风格的设置类,定义了最基本的绘制控件所需的信息。
绘制不同控件时,控件所使用的设置类继承QStyleOption,且OptionType值不同。
如绘制按钮的风格设置类QStyleOptionButton继承QStyleOption时,Type = SO_Button表明是要绘制按钮,且添加了一些按钮才有的属性。
3.1.1.1QStyleOption及其子类包含QStyle函数绘制图形元素所需的所有信息。
出于性能考虑,成员函数很少,对成员变量的访问是直接的(即使用。或者->运算符)。这种低级的感觉使结构易于使用,并强调它们只是样式函数使用的参数。
QStyle函数的调用者通常在堆栈上创建QStyleOption对象。这与Qt对QString、QPalette和QColor等类型的隐式共享的广泛使用相结合,确保了没有不必要的内存分配
解决办法:可以用 view 的 openPersistentEditor 接口使某个 index 的 editor 一直处于编辑状态,直到调用 closePersistentEditor 关闭
view 只有一个 setIndexWidget 的接口,用于展示静态数据 对于动态数据 并不能有效的替代
3.1.2点击控件 响应事件
使用自绘的方式主要是paint和editorEvent两个接口,自己手动绘制,这种方式最原始的方案,但是面对复杂的布局和多个控件的时候就比较麻烦。如下:
void cpBtnDelegate:: paint(0Painter *painter, const 0style0ptionviewItem &option, const 0ModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
QRect rect =option.rect;
NetworkModel *pModel = static cast<NetworkModel*>(const cast<QAbstractItemModel*>(index.model()));
if(pModel->isPlcIndex(index))
{
QRect btnRect = 0Rect(rect.x()+ rect.width()-m width, rect.y(),m width, rect.height()).0style0ptionButton btnoption;
btnoption.rect = btnRect;
btnOption.text=m text;
btnOption.state = option.state;
btnOption.palette = option.palette;
QApplication::style()->drawcontrol(Qstyle::cE ItemViewItem, &option, painter);
QApplication::style()->drawcontrol(Qstyle::CE PushButton, &btnoption, painter);
}
}
CPBtnDelegate::editorEvent(QEvent *event,0AbstractItemModel:*model,const Qstyle0ptionViewItem&option, const&
{
NetworkModel *pModel= static cast<NetworkModel*>(model);
bool isPlc = pModel->isPlcIndex(index);
if(isPlc && event->type()== QEvent::MouseButtonRelease)
{
QRect rect = option.rect;
QRect btnRect = ORect(rect.x()+ rect.width()-m width, rect.y(),m width, rect.height());
QMouseEvent*e=(OMouseEvent*)event;
if(btnRect.contains(e->x(),e->y())){
// 发送信号
emit btnclicked();
return ostyledItemDelegate::editorEvent(event, model, option, index);
}
}
}
3.2代理的基本使用
一般代理只需要继承QStyledItemDelegate类,然后重写createEditor,setEditorData和setModelData接口即可。
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);
3.3在代理中使用复杂控件
在代理中使用复杂控件,比如使用布局添加多个控件,如下:
// 创建一个多按键的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)
但是这种情况只有在双击的时候代理才会显示,如果需要一直显示,或者在标题栏中是无法实现的。
3.4让代理一直显示
使用自绘的方式使用自绘的方式主要是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参考:
问题汇总
频繁的调用setDataInfo emit dataChanged 造成界面卡顿
beginResetModel();
m_data = data;
endResetModel();
这种调用很消耗内存 造成界面卡卡顿 这种只有内部结构数据全部改变适合使用
一般适合 整体map赋值时 会考虑用到这个
同理 QModelIndex topLeft = index(0, 0);
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(topLeft, bottomRight);
这个信号也是 这个信号相当于整个都遍历一遍 但对上面好一点
在使用自定义控件一直显示编辑模式 数据不更新问题
常规的代理 使用自定义控件时 是通过双击进入编辑模式setEditorData 在进入setModelData 最后更新 updateEditorGeometry
但由于是一直显示编辑模式 不能通过编辑模式进行修改数据
通过beginResetModel(); 更新能同步数据 但是频繁调用会导致内存泄漏
最后发现 通过 新增一行或者删除一行 这种在内存上会好很多
//新增 一行的方式
void CustomTableModel::addDataInfo(ClassNumberInfoDefine::GroupParam data)
{
beginInsertRows(QModelIndex(), 0, 0);
m_data.prepend(data);
endInsertRows();
}
void CustomTableModel::removeDataInfo(int id)
{
for(int i=0; i<m_data.size(); i++)
{
if( m_data.at(i).GroupNumber == id)
{
beginRemoveRows(QModelIndex(), i, i);
m_data.removeAt(i);
endRemoveRows();
}
}
}
外侧去控制删除和新添加
void testtableviewqiantaoviewqplugin::checkDataTimeout()
{
//model->removeDataInfo(QDateTime::currentMSecsSinceEpoch());
qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
int sizemap = m_TargetGroupMap.size();
{
QMutexLocker Locker(&updataMutex);
auto itr = m_TargetGroupMap.begin();
while(itr != m_TargetGroupMap.end())
{
qint64 timeDiff = currentTime - itr.value().recvtTime;
if(timeDiff > 3000)
{
model->removeDataInfo(itr.key());
itr = m_TargetGroupMap.erase(itr);
}
else
{
itr++;
}
}
}
}
void testtableviewqiantaoviewqplugin::onTargetInfoSlot(const TargetInfoDefine::GroupParam& info)
{
QMutexLocker locker(&updataMutex);
if(m_TargetGroupMap.contains(info.GroupNumber))
{
m_TargetGroupMap[info.GroupNumber] = info;
model->updateDataInfo(info);
}
else
{
if(info.GroupNumber !=0)
{
m_TargetGroupMap.insert(info.GroupNumber,info);
model->addDataInfo(info);
}
}
int row = model->rowCount();
for (int i=0; i<row; ++i) {
view->openPersistentEditor(model->index(i, 6));
}
}
通过这些代码发现 数据同步到data上了 但是最后一列数据没有更新 由于是一直显示编辑模式 不能通过编辑模式进行修改数据 setModelData 这个函数一直没有进入 也就没有更新model的值
正常是能更新到全部item上的 但是 自定义控件是在item上有设置了一个控件 数据同步不上去
测试发现 setEditorData是周期在调用 于是在想能否通过这个函数手动更新数据
//不建议这样做 我是想要这样的效果不得已这样做的
void CustomDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
GroupNumberTableView *gruopView = qobject_cast<GroupNumberTableView *>(editor);
if(gruopView)
{
QList<TargetInfoDefine::GroupTargetInfo> memberVec;
memberVec.clear();
QVariant var = index.data(); // 从模型中获取数据
if (var.canConvert<QList<TargetInfoDefine::GroupTargetInfo>>()) {
memberVec = var.value<QList<TargetInfoDefine::GroupTargetInfo>>();
gruopView->setData(memberVec);
}
qDebug() << "memberVec11111111111111 = " << memberVec.size();
}
QStyledItemDelegate::setEditorData(editor, index);
}
通过index 是能获取自定义控件的 因为在createEditor 创建时返回的时当时创建的指针return gruopView; 我这里返回的子view指针 其实可以自定义QWidget 里面包括view model delegate
可以把三者分离开来 我目前时把model delegate 放到 view里面 通过index 获取view指针并获取最新数据 通过setdata 进行数据驱动
****************这样效果体验不太好 但是确实驱动数据了*********************
接下来发下 在数据跟新过程中 发现界面数据没有变 发现一直在更新数据 view数据界没同步
只有在删除和插入的适合通过beginInsertRows beginRemoveRows会导致界面更新
在更新时 没有发送跟新信号导致数据没同步 (但是跟新频频太快 且改变一个item就要遍历整个view 太消耗内存)
选择了一个定时器一秒更新界面
void CustomTableModel::updateUI()
{
QModelIndex topLeft = index(0, 0);
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);
emit dataChanged(topLeft, bottomRight);
}
最后观察数据 发现内存增长降了下来 且满足自己的预想 后期在进行优化
这种自定义控件 大部门是用在静态的 不经常更新的场景下
如果经常更新则 不建议使用这种 使用paint进行定制绘制会更好 自己实现事件等