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

项目开发中,很可能简单的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;
}

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

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

commonbelive

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值