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();
}