QT5自定义下拉框为QTreeView类型(树形分上下级)的下拉框(QComboBox)(超详细步骤)

news2024/11/22 23:35:33
项目开发中,很可能简单的QComboBox满足不了需求,就需要自定义QComboBox。

先看效果。

自定义ComboBox

1、先建立一个project,命名为CustomComboBox,建立一个project的过程不细说了。建立后的工程目录如下图:

在这里插入图片描述

2、在项目名CustomComboBox右击鼠标,新建一个c++class

在这里插入图片描述

然后在弹出的页面设置class的名字为MComboBox,设置class的基类为QComboBox,然后点击下一步,在弹出的页面点击完成即可创建完成。

在这里插入图片描述

3、创建完成后的项目目录结构如下。然后双击widget.ui文件,在UI文件里添加控件QComboBox.

在这里插入图片描述

在这里插入图片描述

4、右键控件QComboBox,点击提升为,在弹出的页面提升的类名称写入MyComboBox,点击添加,就会在提升的类中就会出现已经提升的类MyComboBox。最后点击提升。QComboBox就变成了MyComboBox。

在这里插入图片描述

添加后的页面变成如下所示:

在这里插入图片描述

QComboBox提升前:

在这里插入图片描述

QComboBox提升后:

提升后

5、在mycombobox.h和mycombobox.cpp文件内编写代码。
mycombobox.h



#ifndef MYCOMBOBOX_H
#define MYCOMBOBOX_H

#include <QComboBox>
#include <QTreeView>

class MyComboBox : public QComboBox
{
    Q_OBJECT
public:
    explicit MyComboBox( QWidget *parent = nullptr);
    void showPopup() override;
    void hidePopup() override;


    void setCustomText(const QString &text,QIcon icon = QIcon());
    void setView(QAbstractItemView *itemView);

protected:
    bool eventFilter(QObject *object, QEvent *event) override;
    void paintEvent(QPaintEvent *e) override;

private:
    bool m_isPermitHidePopup;
    QString m_customString;
    QIcon m_customIcon;
    bool m_bCustomLineEdit;
};

#endif // MYCOMBOBOX_H
mycombobox.h



#include "mycombobox.h"
#include <QHeaderView>
#include <QLineEdit>
#include <QMouseEvent>
#include <QStylePainter>
MyComboBox::MyComboBox(QWidget *parent)
    : QComboBox(parent),
    m_isPermitHidePopup(true),
    m_bCustomLineEdit(false)
{
    QTreeView *view = new QTreeView(this);
    view->header()->setVisible(false);
    setView(view);
    connect (view, &QTreeView::doubleClicked, [=](const QModelIndex&){
        m_isPermitHidePopup = true;
        hidePopup();
    });
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    QFrame* frame = this->findChild<QFrame*>();
    if(frame != nullptr)
    {
        frame->installEventFilter(this);
    }
    frame->setFixedHeight(200);
}

void MyComboBox::showPopup()
{
    QComboBox::showPopup();
    m_isPermitHidePopup = false;
}

void MyComboBox::hidePopup()
{
    if(m_isPermitHidePopup)
    {
        QComboBox::hidePopup();
    }
}

void MyComboBox::setCustomText(const QString &text,QIcon icon)
{
    m_bCustomLineEdit = true;
    m_customString = text;
    m_customIcon = icon;
}

bool MyComboBox::eventFilter(QObject *object, QEvent *event)
{
    QFrame* frame = this->findChild<QFrame*>();
    if(frame && frame == object)
    {
        if(event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QPoint globalPos = mouseEvent->globalPos();
            QRect rect(frame->mapToGlobal(QPoint(0,0)), QSize(frame->width(), frame->height()));
            //鼠标按下位置不在下拉框内则关闭下拉框
            if(!rect.contains(globalPos))
            {
                m_isPermitHidePopup = true;
            }
        }
    }
    return QComboBox::eventFilter(object,event);

}

void MyComboBox::paintEvent(QPaintEvent *e)
{
    if (m_bCustomLineEdit)
    {
        QStylePainter painter(this);
        painter.setPen(palette().color(QPalette::Text));
        QStyleOptionComboBox opt;
        initStyleOption(&opt);
        opt.currentText = m_customString;
        opt.currentIcon = m_customIcon;
        painter.drawComplexControl(QStyle::CC_ComboBox, opt);
        painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
    }
    else
    {
        QComboBox::paintEvent(e);
    }
}

void MyComboBox::setView(QAbstractItemView *itemView)
{
    QComboBox::setView(itemView);
    if (itemView == nullptr)
    {
        return;
    }
    itemView->setSelectionMode(QAbstractItemView::ExtendedSelection);
    QWidget *w = itemView->parentWidget();
    if (w != nullptr)
    {
        itemView->removeEventFilter(w);
        itemView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    }
}

6、接着,在工程中再添加数的节点的两个文件:mytreeitem.h,mytreeitem.cpp
mytreeitem.h


#include<QVariant>

class MyTreeItem
{
public:
    explicit MyTreeItem(const QString &data, MyTreeItem *parent = nullptr);

    ~MyTreeItem();

    void appendChild(MyTreeItem *child);

    MyTreeItem *child(int row);

    int childCount() const;

    int columnCount() const;

    QVariant data(int column) const;

    int row() const;

    int setRow() const;

    MyTreeItem *parent();

private:
    QList<MyTreeItem*> childItems;
    QString itemData;
    MyTreeItem *parentItem;
};
mytreeitem.cpp


#include "mytreeitem.h"

MyTreeItem::MyTreeItem(const QString &data, MyTreeItem *parent)
    : parentItem(parent), itemData(data)
{

}


MyTreeItem::~MyTreeItem()
{
    qDeleteAll(childItems);
}

void MyTreeItem::appendChild(MyTreeItem *child)
{
    childItems.append(child);
}

MyTreeItem *MyTreeItem::child(int row)
{
    return childItems.value(row);
}

int MyTreeItem::childCount() const
{
    return childItems.count();
}

int MyTreeItem::columnCount() const
{
    return 1;
}

QVariant MyTreeItem::data(int column) const
{
    return itemData;
}

int MyTreeItem::row() const
{
    if (parentItem)
    {
        return parentItem->childItems.indexOf(const_cast<MyTreeItem*>(this));
    }
    return 0;
}

MyTreeItem *MyTreeItem::parent()
{
    return parentItem;
}
7、接着在工程中添加树的model的文件:mytreemodel.h, mytreemodel.cpp
mytreemodel.h


#include "mytreeitem.h"
#include <QAbstractItemModel>

class MyTreeModel : public QAbstractItemModel
{
public:
    explicit MyTreeModel(QString header, QMap<QString, QStringList>& data, QObject *parent = nullptr);
    ~MyTreeModel();

    QVariant data(const QModelIndex &index, int role) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

private:
    MyTreeItem *rootItem;
};
mytreemodel.cpp


#include "mytreemodel.h"

MyTreeModel::MyTreeModel(const QString header, QMap<QString, QStringList> &data, QObject *parent)
{
    rootItem = new MyTreeItem(header);
    for (auto folder : data.keys())
    {
        MyTreeItem* folderItem = new MyTreeItem(folder, rootItem);
        rootItem->appendChild(folderItem);

        for(auto file: data.value(folder))
        {
            MyTreeItem *fileItem = new MyTreeItem(file, folderItem);
            folderItem->appendChild(fileItem);
        }
    }
}

MyTreeModel::~MyTreeModel()
{
    delete rootItem;
}

int MyTreeModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
    {
        return static_cast<MyTreeItem*>(parent.internalPointer())->columnCount();
    }
    else
    {
        return rootItem->columnCount();
    }
}

QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    if (role != Qt::DisplayRole)
    {
        return QVariant();
    }
    int col = index.column();
    MyTreeItem *item = static_cast<MyTreeItem*>(index.internalPointer());
    return item->data(index.column());
}

QVariant MyTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
    {
        return rootItem->data(section);
    }
    return QVariant();
}

QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
    {
        return QModelIndex();
    }

    MyTreeItem *parentItem;
    if (!parent.isValid())
    {
        parentItem = rootItem;
    }
    else
    {
        parentItem = static_cast<MyTreeItem*>(parent.internalPointer());
    }

    MyTreeItem *childItem = parentItem->child(row);
    if (childItem)
    {
        return createIndex(row, column, childItem);
    }
    else
    {
        return QModelIndex();
    }
}

QModelIndex MyTreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
    {
        return QModelIndex();
    }

    MyTreeItem *childItem = static_cast<MyTreeItem*>(index.internalPointer());
    MyTreeItem *parentItem = childItem->parent();

    if (parentItem == rootItem)
    {
        return QModelIndex();
    }

    return createIndex(parentItem->row(), 0, parentItem);
}



int MyTreeModel::rowCount(const QModelIndex &parent) const
{
    MyTreeItem *parentItem;

    if (parent.column() > 0)
    {
        return 0;
    }

    if (!parent.isValid())
    {
        parentItem = rootItem;
    }
    else
    {
        parentItem = static_cast<MyTreeItem*>(parent.internalPointer());
    }

    return parentItem->childCount();
}

最后,在widget.cpp中写测试代码
widget.cpp


#include "widget.h"
#include "ui_widget.h"
#include <QTreeView>
#include "mytreemodel.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QTreeView *view = new QTreeView(this);
    ui->userComboBox->setView(view);

    QString header;
    header.append("doucument");

    QString child1 = "folder1";
    QStringList grandson1 = {"file1", "file2"};

    QString child2 = "folder2";
    QStringList grandson2 = {"file3", "file4"};


    QMap<QString, QStringList> data;
    data.insert(child1, grandson1);
    data.insert(child2, grandson2);

    MyTreeModel *model = new MyTreeModel(header, data);
    ui->userComboBox->setModel(model);

    connect(view, &QTreeView::pressed, this, [=](QModelIndex index){
        //让ui->userComboBox上显示当前被点击的index的名称
        ui->userComboBox->setCustomText(index.data(Qt::DisplayRole).toString());
    });

}

Widget::~Widget()
{
    delete ui;
}

以上代码复制至对应文件即可运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1044602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React 全栈体系(十六)

第八章 React 扩展 五、Context 1. 代码 /* index.jsx */ import React, { Component } from react import ./index.css//创建Context对象 const MyContext React.createContext() const {Provider,Consumer} MyContext export default class A extends Component {state …

蓝桥杯每日一题20223.9.26

4407. 扫雷 - AcWing题库 题目描述 分析 此题目使用map等都会超时&#xff0c;所以我们可以巧妙的使用哈希模拟散列表&#xff0c;哈希表初始化为-1首先将地雷读入哈希表&#xff0c;找到地雷的坐标在哈希表中对应的下标&#xff0c;如果没有则此地雷的位置第一次出现&#…

2023彩虹商城自助发卡商城+卡卡云模板+wxstore模板

2023彩虹商城自助发卡商城免授权版卡卡云模板wxstore模板 全新SUP模板/知识付费模板/卡卡云模板&#xff0c;首页美化&#xff0c;登陆页美化&#xff0c;修复了pc端购物车页面显示不正常的问题。

RabbitMQ的工作模式——WorkQueues模式

1.工作队列模式 生产者代码 public class Producer_WorkQueues1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133&qu…

动态线程池框架DynamicTp v1.1.4大版本发布,新增若干实用特性

DynamicTp 简介 DynamicTp 是一个基于配置中心实现的轻量级动态线程池监控管理工具&#xff0c;主要功能可以总结为动态调参、通知报警、运行监控、三方包线程池管理等几大类。 DynamicTp 特性 代码零侵入&#xff1a;我们改变了线程池以往的使用姿势&#xff0c;所有配置均放…

【算法】直接插入排序

文章目录 概念实现过程时间复杂度和空间复杂度代码示例 总结 概念 直接插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的基本思想是通过构建有序的子序列&#xff0c;逐步将无序的元素插入到有序序列中&#xff0c;最终实现整体的排序…

【python基础】—内置模块os常用功能介绍

文章目录 前言一、模块变量os.nameos.environ 二、文件与文件夹os.getcwd(path)os.chdir(path)os.listdir(path)os.mkdir(path)os.remove(path)os.rename(src,dst) 三、os的子模块&#xff1a;Path模块os.path.abspath(path)os.path.basename(path)os.path.dirname(path)os.pat…

软件设计模式——桥接模式

摘要 桥接模式(Bridge pattern): 使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变。 一、桥接模式的意图 将抽象与实现分离开来&#xff0c;使它们可以独立变化。 二、桥接模式的类图 Abstraction: 定义抽象类的接口Implementor: 定义实现类接口 …

C++标准模板(STL)- 输入/输出操纵符-(std::resetiosflags,std::setiosflags)

操纵符是令代码能以 operator<< 或 operator>> 控制输入/输出流的帮助函数。 不以参数调用的操纵符&#xff08;例如 std::cout << std::boolalpha; 或 std::cin >> std::hex; &#xff09;实现为接受到流的引用为其唯一参数的函数。 basic_ostream::…

【计算机视觉】3.传统计算机视觉方法

传统计算机视觉方法 一、大纲图像分割人脸检测行人检测 二、图像分割基于阈值检测的方法基于边缘检测的方法基于区域的分割方法基于图论的分割方法 三、人脸检测四、行人检测五、SVM六、DPM 一、大纲 图像分割 基于阈值、基于边缘 基于区域、基于图论 人脸检测 Haar-like 特征…

渗透测试之——信息收集思路

请遵守网络安全法 渗透测试时要和客户定好时间再使用扫描器。 渗透测试切记不要渗透客户所给域名的同级域名&#xff0c;可以渗透所给域名的子域名。信息收集永远是渗透测试的第一步。 1. 测试目标 xiusafe.com 2. 域名与ip 渗透测试中IP比域名更适合做渗透&#xff1b; …

Elasticsearch 集群时的内部结构是怎样的?

Apache Lucene : Flush, Commit Elasticsearch 是一个基于 Apache Lucene 构建的搜索引擎。 它利用 Lucene 的倒排索引、查询处理和返回搜索结果等功能来执行搜索。 它还扩展了 Lucene 的功能&#xff0c;添加分布式处理功能以支持大型数据集的搜索。 让我们看一下 Apache Luc…

网络通信(套接字通信)(C/C++)

1.网络编程必知概念 1.广域网和局域网 广域网:又称外网、公网。是连接不同地区局域网或城域网进行计算机通信的远程公共网络。 局域网:在一定的通信范围内,有很个多计算机组成的私有网络就叫局域网。(这些计算机相互之间是可以通信的,但是不能直接访问外网(可以通过网线…

ElementUI -- Mock.js介绍和使用与首页导航栏左侧菜单搭建

1.1 mockjs介绍 Mock.js是一个用于生成随机数据和模拟接口请求的JavaScript库。它可以帮助开发人员在前端开发过程中模拟后端接口的返回数据&#xff0c;以便进行前端页面的开发和测试。 Mock.js有两个重要的特性风靡前端: 数据类型丰富 Mock.js提供了一套简单易用的API&#x…

FL Studio21编曲软件好不好用?值得下载吗

现在的编曲界&#xff0c;鱼龙混杂&#xff0c;很多垃圾软件都来滥竽充数&#xff0c;能入流的编曲软件真的是屈指可数。而编曲业的缺口却又如此之大&#xff0c;所以各类货色都想入行分一杯羹。然而没有实力注定就没有市场而FL Studio又是一款非常著名&#xff0c;实力超群的编…

CTF_BUUCTF_MIS解题_05大白

题目地址&#xff1a;BUUCTF在线评测 文件下载解压之后发现真是一个大白 但是这个大白越看越不对劲&#xff0c;怎么下半身给整没了呢&#xff1f; 随机考虑到图片高度的隐写&#xff0c;杀手锏winhex上场&#xff0c;高度改高一下看看 flag已出现&#xff1a;flag{He1l0_d4_…

面试打底稿③ 专业技能的第三部分

简历原文 抽查部分 基本了解RabbitMQ、Elasticsearch等微服务技术&#xff0c;对分布式事务、分布式缓存等有所学习&#xff1b;熟悉数据库的基本操作&#xff0c;写过C#与数据库交互的项目&#xff1b; 模拟问答 1.你是如何保障消息可靠性的 从消息生产者到交换机&#x…

C语言 数据类型

变量声明 格式&#xff08;变量类型变量名称&#xff09; 变量类型&#xff1a;整数类型&#xff08;int&#xff09;&#xff0c;浮点数类型&#xff08;float&#xff09; float类型可以存储带小数的数字。 用printf()打印变量&#xff0c;使用%d来处理整数值&#xff0c…

AI指令百科全书:1000条AI指令,一次性全给你!

这是一位&#xff0c;国外博主哈桑 整理的&#xff0c;1000条ChatGPT实用指令&#xff0c;涵盖目前几乎所有的&#xff0c;主流提示需求。 全文超过40000字。 我把它们翻译成更适合大家理解的「中文版Prompt」&#xff0c;并根据具体的内容&#xff0c;拆解成一二级目录&…

【C++STL基础入门】list交换、翻转,排序、合并和拼接操作

文章目录 前言一、交换list二、翻转list三、排序list四、合并list五、拼接list总结 前言 在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;list是一个双向链表容器&#xff0c;提供了丰富的功能和操作。本文将介绍list容器在交换、翻转、排序、合并和拼接等方面的基…