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());	//此处开始显示菜单,等待菜单事件循环

}

菜单实现效果:

image-20231201144648178

参考:QTreeView使用总结7,右键菜单-CSDN博客

菜单图标和多级菜单:

菜单图标设置:

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

效果:

image-20231201145212382

多级菜单:

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);	//添加子菜单到父菜单

效果展示:

image-20231201145708816

自定义委托代理:

自定义新的类,需继承自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);

展示效果:

image-20231201152608136

数据过滤:

实现流程:

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

posted @ 2023-12-03 15:50  Neko_Code  阅读(156)  评论(0编辑  收藏  举报