Model/View之子类化QAbstractItemModel实现QTreeView的复选框
来源:互联网 发布:英翻汉软件 编辑:程序博客网 时间:2024/06/11 21:37
引言
- 先上效果图:
最近想要实现上图所示的一个数据展示列表,最先使用的QTreeWidget组件进行展示,但是遇到了当数据量过大(10000以上),第一次点击TabPage加载数据时,总是有很卡顿的感觉,得隔一段时间才能加载显示出数据。汗!偷懒偷不成了,效果自己都不能忍,更何况别人。因此使用了Model/View框架,自己实现了数据项和数据模型,最后效果还算满意。
需求是这样的,当点击表头时,可以全部选中或者全部不选中视图中的数据,而点击数据时,表头能展示选中状态的三态效果。配合键盘的Ctrl和Shift键,实现区域选中,多块选中(效果图见文章最后)
实现
一、 子类化QAbstractItemModel,自定义QTreeView的数据模型
QAbstractItemModel类是虚基类,子类化该类,得需要实现所有的纯虚函数才能实例化自定义的数据模型类。
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);//设置数据virtual Qt::ItemFlags flags(const QModelIndex & index) const;//返回Item项的可选,可用户点击等标识virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;//返回每个数据项的indexvirtual QModelIndex parent(const QModelIndex &index) const;//本需求树只有一层,parent返回NULLvirtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;//返回相应role的表头数据
以上函数是为了实现本需求,必须实现的方法。可以说是自定义QTreeView模型的核心。
- 自定义Model头文件
#ifndef TREEVIEWMODEL_H#define TREEVIEWMODEL_H#include <QAbstractItemModel>struct FlashIndexData{ FlashIndexData():is_be_checked(false){ } bool is_be_checked; quint32 unix_time; quint16 addr;};class TreeViewModel:public QAbstractItemModel{ Q_OBJECTpublic: explicit TreeViewModel(QObject *parent=NULL); void setFlashData(QList<FlashIndexData> &flash_data); void clear(); void getSelectedFlashData(QMap<quint32,quint16> &selected_list); virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; virtual int columnCount(const QModelIndex & parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); virtual Qt::ItemFlags flags(const QModelIndex & index) const; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &index) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;signals: void stateChanged(Qt::CheckState state);private slots: void slot_stateChanged(Qt::CheckState state);private: QList<FlashIndexData> m_flash_index; //flash 索引 void onStateChanged(); enum{ CHECK_BOX_COLUMN = 0, UNIX_TIME_COLUMN, FLASH_ADDR_COLUMN };};#endif // TREEVIEWMODEL_H
- 首先需要定义自己的数据容器,本需求的容器如下所示:
struct FlashIndexData{ FlashIndexData():is_be_checked(false){ } bool is_be_checked; quint32 unix_time; quint16 addr; };
- 返回数据源的个数,即 即将展示的数据的行数.列数固定,直接返回想要显示的列数即可。
int TreeViewModel::rowCount(const QModelIndex &parent) const{ Q_UNUSED(parent); return m_flash_index.count();}int TreeViewModel::columnCount(const QModelIndex &parent) const{ Q_UNUSED(parent); return 3;}
- 获取role的数据,设置role的数据
QVariant TreeViewModel::data(const QModelIndex &index, int role) const{ if(!index.isValid()) return QVariant(); int row = index.row(); int column = index.column(); FlashIndexData index_data = m_flash_index.at(row); switch (role) { case Qt::DisplayRole: if(column == UNIX_TIME_COLUMN) return QDateTime::fromTime_t(index_data.unix_time).toString("yyyy-MM-dd hh:mm:ss"); else if(column == FLASH_ADDR_COLUMN) return index_data.addr; return ""; break; case Qt::CheckStateRole: if(column == CHECK_BOX_COLUMN) return index_data.is_be_checked?Qt::Checked:Qt::Unchecked; break; case Qt::TextAlignmentRole: if(column == CHECK_BOX_COLUMN) return QVariant(Qt::AlignLeft|Qt::AlignVCenter); else return Qt::AlignCenter; break; case Qt::TextColorRole: return QColor(Qt::black); break; case Qt::SizeHintRole: return QSize(100,30); break; case Qt::FontRole: return QFont("SimSun", 11); break; default: break; } return QVariant();}//可编辑,只提供可选不可选的编辑,不提供对数据源的编辑bool TreeViewModel::setData(const QModelIndex &index, const QVariant &value, int role){ if(!index.isValid()) return false; int column = index.column(); FlashIndexData index_data = m_flash_index.at(index.row()); switch (role) { case Qt::UserRole: //根据表头的复选框选择 case Qt::UserRole+1: //根据鼠标点击 if(column == CHECK_BOX_COLUMN) { index_data.is_be_checked = (((Qt::CheckState)value.toInt()) == Qt::Checked); m_flash_index.replace(index.row(),index_data); emit dataChanged(index,index); if(role == Qt::UserRole+1) //点击鼠标,更新表头复选框状态 onStateChanged(); return true; } break; default: return false; break; } return false;}
- 使Item显示复选框
//可选Qt::ItemFlags TreeViewModel::flags(const QModelIndex &index) const{ if (!index.isValid()) return QAbstractItemModel::flags(index); Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren; if (index.column() == CHECK_BOX_COLUMN) flags |= Qt::ItemIsUserCheckable; return flags;}
- 设置(row,column)的index
QModelIndex TreeViewModel::index(int row, int column, const QModelIndex &parent) const{ if (row < 0 || column < 0 || column >= columnCount(parent)) return QModelIndex(); return createIndex(row,column);}
- 只有一层树形结构,parent为空
QModelIndex TreeViewModel::parent(const QModelIndex &index) const{ Q_UNUSED(index); return QModelIndex();}
- 返回表头的一些数据
QVariant TreeViewModel::headerData(int section, Qt::Orientation orientation, int role) const{ switch (role) { case Qt::DisplayRole: if(section == CHECK_BOX_COLUMN) { return QString("全选"); }else if(section == UNIX_TIME_COLUMN) { return QString("时间"); }else if(section == FLASH_ADDR_COLUMN) { return QString("地址"); } return ""; break; case Qt::FontRole: return QFont("SimSun", 12); break; case Qt::TextAlignmentRole: return Qt::AlignCenter; break; case Qt::TextColorRole: return QColor(Qt::black); break; case Qt::SizeHintRole: return QSize(100,40); break; case Qt::BackgroundRole: return QBrush(Qt::black); break; default: break; } return QVariant();}
- 用于与表头HeaderView交互的信号和槽,可以协调表头和Item的复选框状态
void TreeViewModel::slot_stateChanged(Qt::CheckState state){ for(int i = 0;i < rowCount();++i) { setData(index(i,CHECK_BOX_COLUMN),state,Qt::UserRole); }}void TreeViewModel::onStateChanged(){ int select_total = 0; for(int i = 0;i < rowCount();++i) { if(m_flash_index.at(i).is_be_checked) ++select_total; } if(select_total == 0) { emit stateChanged(Qt::Unchecked); }else if(select_total < rowCount()) { emit stateChanged(Qt::PartiallyChecked); }else { emit stateChanged(Qt::Checked); }}
- 用于更新Model和清空Model
TreeViewModel::TreeViewModel(QObject *parent): QAbstractItemModel(parent){ m_flash_index.clear();}void TreeViewModel::setFlashData(QList<FlashIndexData> &flash_data){ m_flash_index = flash_data; beginResetModel(); endResetModel(); emit stateChanged(Qt::Unchecked);}void TreeViewModel::clear(){ m_flash_index.clear(); beginResetModel(); endResetModel(); emit stateChanged(Qt::Unchecked);}void TreeViewModel::getSelectedFlashData(QMap<quint32, quint16> &selected_list){ selected_list.clear(); for(int i = 0;i < rowCount();++i) { if(m_flash_index.at(i).is_be_checked) { selected_list.insert(m_flash_index.at(i).unix_time,m_flash_index.at(i).addr); } }}
—————————————————————————–
二、 自定义QTreeView的HeaderView,实现表头复选框
- 头文件
#ifndef ATHEADERVIEW_H#define ATHEADERVIEW_H#include <QHeaderView>class ATHeaderView : public QHeaderView{ Q_OBJECTpublic: explicit ATHeaderView(QWidget *parent = 0); ~ATHeaderView(){}protected: virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; virtual void mousePressEvent(QMouseEvent *e); virtual void mouseReleaseEvent(QMouseEvent *e);signals: void stateChanged(Qt::CheckState state);private slots: void slot_stateChanged(Qt::CheckState state);private: Qt::CheckState m_state; bool m_is_pressed;};#endif // ATHEADERVIEW_H
- 实现表头背景的绘制,Section文本的绘制和复选框的绘制
//默认水平表头ATHeaderView::ATHeaderView(QWidget *parent): QHeaderView(Qt::Horizontal,parent){ m_state = Qt::Unchecked; m_is_pressed = false; setSectionsClickable(true);}void ATHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const{ painter->save(); QHeaderView::paintSection(painter,rect,logicalIndex);//绘制其他Section painter->restore();//绘制背景色 painter->save(); painter->setBrush(QBrush(Qt::gray)); painter->setPen(Qt::NoPen); painter->drawRect(rect);//绘制Section的Text painter->setFont(QFont("SimSun", 12)); painter->setPen(QColor("#000000")); painter->drawText(rect, Qt::AlignCenter, model()->headerData(logicalIndex,Qt::Horizontal).toString()); painter->restore();//为第一列绘制Checkbox if(logicalIndex == 0) { QStyleOptionButton option; option.initFrom(this); if(m_state == Qt::Unchecked) { option.state |= QStyle::State_Off; } else if(m_state == Qt::PartiallyChecked) { option.state |= QStyle::State_NoChange; } else if(m_state == Qt::Checked) { option.state |= QStyle::State_On; } option.iconSize = QSize(20, 20); option.rect = QRect(QPoint(rect.left()+5,rect.top()+(rect.height()-20)/2),QPoint(rect.left()+25,rect.bottom()-(rect.height()-20)/2)); style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); }}void ATHeaderView::mousePressEvent(QMouseEvent *e){ int nColumn = logicalIndexAt(e->pos()); if ((e->buttons() & Qt::LeftButton) && (nColumn == 0)) { m_is_pressed = true; e->accept(); } e->ignore();}void ATHeaderView::mouseReleaseEvent(QMouseEvent *e){ if(m_is_pressed) { if(m_state == Qt::Unchecked) { m_state = Qt::Checked; }else { m_state = Qt::Unchecked; } updateSection(0); emit stateChanged(m_state); //状态改变 } m_is_pressed = false; e->accept();}//根据Item的复选框状态,表头复选框状态更新void ATHeaderView::slot_stateChanged(Qt::CheckState state){ m_state = state; updateSection(0);}
—————————————————————————–
三、使用Model和HeaderView,实现复选框的协调,同时选择Item,Model和HeaderView作出响应
- 初始化
m_headerView = new ATHeaderView(this); m_treeModel = new TreeViewModel(this); ui->treeView_flashindex->setModel(m_treeModel); ui->treeView_flashindex->setHeader(m_headerView); ui->treeView_flashindex->setSelectionMode(QAbstractItemView::ExtendedSelection); ui->treeView_flashindex->setExpandsOnDoubleClick(false); ui->treeView_flashindex->setIndentation(5); ui->treeView_flashindex->setColumnWidth(0,150); ui->treeView_flashindex->setColumnWidth(1,400); ui->treeView_flashindex->header()->setStretchLastSection(true); connect(m_headerView,SIGNAL(stateChanged(Qt::CheckState)),m_treeModel,SLOT(slot_stateChanged(Qt::CheckState))); connect(m_treeModel,SIGNAL(stateChanged(Qt::CheckState)),m_headerView,SLOT(slot_stateChanged(Qt::CheckState))); connect(ui->treeView_flashindex->selectionModel(),SIGNAL(selectionChanged(QItemSelection,QItemSelection)),this,SLOT(slot_selectionChanged(QItemSelection,QItemSelection)));
- 根据选中状态,多选或者单选,CheckBox作出响应
void NodeObjectFlashExport::slot_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected){ for(int i = 0;i < selected.indexes().count();++i) { m_treeModel->setData(selected.indexes().at(i),Qt::Checked,Qt::UserRole+1); } for(int i = 0;i < deselected.indexes().count();++i) { m_treeModel->setData(deselected.indexes().at(i),Qt::Unchecked,Qt::UserRole+1); }}
效果图如图所示:
阅读全文
1 1
- Model/View之子类化QAbstractItemModel实现QTreeView的复选框
- QTreeView实现复选框
- QTreeView model/View
- QTreeView/QAbstractItemModel用法总结
- QT中QTreeView与QAbstractItemModel使用中QTreeViwe的美化
- QTreeView 复选框样式修改
- 控件QtreeView的实现
- Qt树形控件QTreeView使用2——复选框的设置
- Qt表格之Model/View实现
- Qt QTreeView 加 自定义Model实现自定义树形控件
- Qt继承QAbstractItemModel实现自定义结构的关键问题记录
- Qt中使用QDirModel / QFileSystemModel + QTreeView显示Checkbox复选框
- pyqt 在QTreeView里使用复选框checkbox
- Qt:在TreeModel+QTreeView中使用复选框(checkbox)
- Qt:在TreeModel+QTreeView中使用复选框问题
- Qt:在TreeModel+QTreeView中使用复选框(checkbox)
- Qt:在TreeModel+QTreeView中使用复选框(checkbox)
- Qt树形控件QTreeView使用1——节点的添加删除操作 复选框的设置
- 软件架构的触碰
- 是的,这是致远兄弟的第一篇文章,我们在创造一个全新的职业。
- gcc内置函数
- Ubuntu16.04 安装Nodejs
- Elasticsearch API 基本使用
- Model/View之子类化QAbstractItemModel实现QTreeView的复选框
- 一直都用忙来懒惰自己
- 剑指offer——第一个只出现一次的字符(思路很基础,但有个方法比较巧妙)
- SpringMVC重要接口(一)HandlerMethodArgumentResolver
- iOS条件编译
- SystemError: could not open configuration file `/etc/libuser.conf': 没有那个文件或目录
- Spark将数据压缩存储
- 系统菜单的分层设计
- 不要再使用NSUserDefaults存储隐私信息了