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