QT-对于MVC中典型QTreeView简单使用参考记录(二)
上一个记录地址:(https://www.cnblogs.com/NekoBlog/p/17869939.html)
在TreeView视图中呼出菜单(右键)
Qt为QWidget提供了单独的菜单信号
void customContextMenuRequested(const QPoint &pos); //此为信号
但要先设置TreeView的菜单策略,调用以下接口:
setContextMenuPolicy(Qt::CustomContextMenu);
实际代码:
QTreeView *view = ui->treeView; view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(slotTreeMenu(QPoint))); //绑定菜单信号槽
SLOT:
void Widget::slotTreeMenu(const QPoint &pos) { QString qss = "QMenu{color:#E8E8E8;background:#4D4D4D;margin:2px;}\ QMenu::item{padding:3px 20px 3px 20px;}\ QMenu::indicator{width:13px;height:13px;}\ QMenu::item:selected{color:#E8E8E8;border:0px solid #575757;background:#1E90FF;}\ QMenu::separator{height:1px;background:#757575;}"; QMenu menu; menu.setStyleSheet(qss); //可以给菜单设置样式 QModelIndex curIndex = ui->treeView->indexAt(pos); //获取指针位置所在的model数据序号 QModelIndex index = curIndex.sibling(curIndex.row(),0); //sibling->兄弟的意思,获取该节点的指定列元素对应序号 if(index.isValid()) { menu.addAction(QStringLiteral("展开"),this,SLOT(slotTreeMenuExpand(bool))); menu.addSeparator(); menu.addAction(QStringLiteral("折叠"),this,SLOT(slotTreeMenuCollapse(bool))); } menu.exec(QCursor::pos()); //此处开始显示菜单,等待菜单事件循环 }
菜单实现效果:
菜单图标和多级菜单:
菜单图标设置:
menu.addAction(QIcon(":/images/images/menu.png"),QStringLiteral("展开"),this,SLOT(slotTreeMenuExpand(bool))); menu.addSeparator(); menu.addAction(QIcon(":/images/images/mini.png"),QStringLiteral("折叠"),this,SLOT(slotTreeMenuCollapse(bool)));
效果:
多级菜单:
QAction * actionParent = menu.addAction(QStringLiteral("移动此栏")); //父菜单 QMenu *subMenu = new QMenu(&menu); //设置子菜单 subMenu->addAction(QStringLiteral("1"),this,SLOT(slotTreeMenuExpand(bool))); subMenu->addAction(QStringLiteral("2"),this,SLOT(slotTreeMenuExpand(bool))); subMenu->addAction(QStringLiteral("3"),this,SLOT(slotTreeMenuExpand(bool))); subMenu->addAction(QStringLiteral("4"),this,SLOT(slotTreeMenuExpand(bool))); actionParent->setMenu(subMenu); //添加子菜单到父菜单
效果展示:
自定义委托代理:
自定义新的类,需继承自QStyledItemDelegate,并重写:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index); void setEditorData(QWidget *editor, const QModelIndex &index) ; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index); void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index);
createEditor: 当item激活编辑状态时,显示的内容。
setEditorData:用以初始化createEditor里创建的控件内容。这里直接把当前item的text设置为选中项。
setModelData:应用编辑后,修改model的data。这里把当前选中项文本设置为item的显示文本。
updateEditorGeometry:更新控件位置状态。
具体实现的代码(此处参考原文:QTreeView使用总结9,使用委托,定制item输入效果_qtreeview 委托-CSDN博客):
#include "mydelegate.h" MyDelegate::MyDelegate(QObject *parent):QStyledItemDelegate(parent) { } QWidget *MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); if(index.column()) { QComboBox * box = new QComboBox(parent); box->addItems(QStringList()<<"5"<<"4"<<"3"); return box; } return NULL; } void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index,Qt::EditRole).toString(); QComboBox *box = static_cast<QComboBox*>(editor); box->setCurrentText(value); } void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *box = static_cast<QComboBox *>(editor); model->setData(index,box->currentText(),Qt::EditRole); } void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); }
使用方法:
QTreeView *view =ui->treeView; MyDelegate *delegate = new MyDelegate(this); view->setEditTriggers(QTreeView::DoubleClicked); //设置其中一种可写状态的即可,否则代理失效 view->setItemDelegate(delegate);
展示效果:
数据过滤:
实现流程:
QTreeView->QSortFilterProxyModel->Model
将QSortFilterProxyModel类型 new 为 Model 为父类的对象,并设置view的model为该proxymodel,通过这种方式,view将访问proxymodel再根据过滤条件选择显示再model中的数据;
void MainWindow::on_btn1_clicked() { //正则表达式 //包含a、b、c中任意一个字符就满足 QRegExp regExp("[abc]", Qt::CaseInsensitive, QRegExp::RegExp); mProxyModel->setFilterRegExp(regExp); } void MainWindow::on_btn2_clicked() { //通配符 //有bc的满足 QRegExp regExp("bc*", Qt::CaseInsensitive, QRegExp::Wildcard); mProxyModel->setFilterRegExp(regExp); } void MainWindow::on_btn3_clicked() { //文本 //包含文本e的满足 QRegExp regExp("e", Qt::CaseInsensitive, QRegExp::FixedString); mProxyModel->setFilterRegExp(regExp); }
原代码参考:QTreeView使用总结11,数据过滤,使用代理model,简单过滤_filteracceptsrow-CSDN博客
自定义过滤方式:
核心代码:(对行进行过滤,所以重写filterAcceptsRow方法,其他方法请另查)
bool MyProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if(sourceParent == QModelIndex()) //只对一级节点 { if(RowInRange(sourceRow)) //节点是否在指定行数内 { return true; } else { return false; } } else { return true; } } bool MyProxyModel::RowInRange(const int &row) const { if( mMin == 0 && mMax == 0 ) { //未初始化过滤条件 return true; } if(row >= mMin && row <= mMax) { return true; } return false; }
自定义Model&&Item:
包括自定义MVC中model,view控制且可访问的对象Item
Item类内容/功能分析:
Item本身作为节点,具有节点的信息(data)->该信息由QList/QVector等抽象类型存储
在TreeItem中还需设置父节点指针(parentPointer)->该信息由TreeItem * parent类似存储
在TreeItem中还需设置子节点指针集合(childPointer)->该信息由QList/QVector等抽象类型存储,例如QList<TreeItem *> childItems
Item需要知道自己在父节点下的哪一个位置(row)->该信息由Int存储即可
初始设置:
若存在指定的parent,则设置->构造函数
若存在指定的data,则设置->构造函数
子节点指针集合,在父节点对应的子节点位置(row)由对应的set函数设置->优先级低于parent和data
子节点管理:
appendChild(Item *child)->设置新增的子节点指针,加入子节点列表中(append)
removeChild(...)->参数根据需求变化,删除子节点,可以是特定的也可以是清空子节点队列
应上层Model需求:
int childCount(); ->返回子节点个数
int columnCount(); ->返回data的列数(该节点存储的信息个数)
其余函数方法皆根据需求添加.
Model类内容/功能分析:
model本身作为数据的接触容器,需持有最基本的可访问数据的权限->设置根节点(root,此节点不可见)的指针
model需完成对Item的管理->访问和使用Item的权限(主要是访问)->对Item->data的访问
model本身不存储item,无需QList等存储结构.
model可根据一定条件访问Item的位置->引入QModelIndex->index函数查找对应item
初始设置:
设置根节点root,此处也可以开始在model内设置root下或平行table,list的view信息(tree则是添加子节点,table则是appendRow)
析构时要删除tree的根节点及其他在此model申请内存的数据item等.
方法功能:
QModelIndex index(int row, int column, const QModelIndex &parent) const override;//返回给定parent节点下的(row,column)子节点索引. QModelIndex parent(const QModelIndex &child) const override;//返回指定索引的item的父节点索引 int rowCount(const QModelIndex &parent) const override;//返回指定父节点的子节点数目 int columnCount(const QModelIndex &parent) const override;//返回指定父节点的数据列数 QVariant data(const QModelIndex &index, int role) const override;//核心函数,对给定的index访问对应的role值,由role值决定访问index对应item的哪个存储值.
item/model实现代码(cpp):
Item:
#include "treeitem.h" TreeItem::TreeItem(QList<QVariant> data,TreeItem *parentItem):mParentItem(parentItem) ,mItemData(data) { mItemData = data; } TreeItem::~TreeItem() { qDeleteAll(mChildItems); } TreeItem * TreeItem::child(int row) { if(row < 0 || row >= mChildItems.size()) return nullptr; return mChildItems.value(row); } TreeItem * TreeItem::parent() { return mParentItem; } int TreeItem::childCount() const { return mChildItems.size(); } int TreeItem::columnCount() const { //int Msize = mItemData.size(); return mItemData.size(); } int TreeItem::row() const { if(mParentItem) return mParentItem->mChildItems.indexOf(const_cast<TreeItem*>(this)); else return 0; } QVariant TreeItem::data(int column) const { if(column < 0 || column >= mItemData.size()) return QVariant(); else return mItemData.value(column); } void TreeItem::appendChild(TreeItem *child) { mChildItems.append(child); } void TreeItem::removeChilds() { for(int index = 0;index < mChildItems.size(); index++) { mChildItems[index]->removeChilds(); } qDeleteAll(mChildItems); mChildItems.clear(); }
model:
#include "treemodel.h" TreeModel::TreeModel(QObject *parent) { mRootItem = new TreeItem({"root"},nullptr); TreeItem * item1 = new TreeItem({"testItem"},mRootItem); TreeItem * item2 = new TreeItem({"testChild"},item1); item1->appendChild(item2); mRootItem->appendChild(item1); } TreeModel::~TreeModel() { if(NULL != mRootItem) delete mRootItem; } QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if(!hasIndex(row,column,parent)) return QModelIndex(); TreeItem *parentItem; if(!parent.isValid()) parentItem = mRootItem; else parentItem = static_cast<TreeItem *>(parent.internalPointer()); TreeItem *TargetItem = parentItem->child(row); if(TargetItem) return createIndex(row,column,TargetItem); else return QModelIndex(); } QModelIndex TreeModel::parent(const QModelIndex &child) const { if(!child.isValid()) return QModelIndex(); TreeItem * childItem = static_cast<TreeItem *>(child.internalPointer()); TreeItem * parentItem = childItem->parent(); if(parentItem != mRootItem) return createIndex(parentItem->row(),0,parentItem); else return QModelIndex(); } int TreeModel::rowCount(const QModelIndex &parent) const { TreeItem * parentItem; if(!parent.isValid()) parentItem = mRootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); return parentItem->childCount(); } int TreeModel::columnCount(const QModelIndex &parent) const { return 2; if(!parent.isValid()) return static_cast<TreeItem*>(parent.internalPointer())->columnCount(); else return mRootItem->columnCount(); } QVariant TreeModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); if(role == Qt::DisplayRole) { return item->data(index.column()); } else return QVariant(); }
本文作者:Neko_Code
本文链接:https://www.cnblogs.com/NekoBlog/p/17873291.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!