关于重写 QComboBox 后下拉菜单显示不正常的问题
文章目录问题描述问题分析临时解决方案代码整理1. 错误案例:Qt 组合框QComboBox定制颜色选择框2. 错误案例:自定义多选QComboBox3. 正确案例:教你如何实现带复选框的ComboBox(自定义QComboBox)问题描述这两天在开发过程中,重写了 QComboBox 这个控件,参考这篇博客 Qt 组合框QComboBox定制颜色选择框 实现的但是发现了一个奇怪的问题,就是在加载数
文章目录
问题描述
这两天在开发过程中,重写了 QComboBox 这个控件,参考这篇博客 Qt 组合框QComboBox定制颜色选择框 实现的
但是发现了一个奇怪的问题,就是在加载数据后,初始化是正确的
但是在多次点击下拉按钮后,或者是从某次点击某一 item 开始,下拉菜单的显示便开始错乱,如下图所示
这个图显示的是末尾几项缺失
再或者如下图
这个图显示的是开始几项缺失
与这个类似的,自定义多选QComboBox 这篇博客也存在同样的问题
问题分析
导致出现该问题可能的操作:多次点击下拉菜单 或者是 从某一次点击开始后就错乱
但是这两个操作都是最普通不过的操作,所以感觉很诡异
猜测可能是因为 QListWidget 的问题,换成 QListView 好像就没问题了
但是核心问题是什么,尚且不清楚,欢迎读者指出问题所在
临时解决方案
好在网上大神多,我又找了几篇博客,找到了一篇刻印临时解决的方案
具体见博客 教你如何实现带复选框的ComboBox(自定义QComboBox)
或者采用以下简单的方法,在不提升 QComboBox 的情况下直接使用
QPixmap image(30, 30);
image.fill(QColor(255, 0, 0));
combobox->addItem(QIcon(image),
QString::fromLocal8Bit("Content"));
代码整理
以下类的使用都相同,都是首先在界面拖拽一个 QComboBox,然后将其提升为以下类中的某一个,然后添加数据进行测试
1. 错误案例:Qt 组合框QComboBox定制颜色选择框
QColorCombobox.h
#pragma once
#include <QLineEdit>
#include <QCombobox>
class QLabel;
class QListWidget;
class QColorWidget : public QLineEdit {
Q_OBJECT
public:
QColorWidget(QWidget *parent = Q_NULLPTR);
~QColorWidget();
void updateColor(const QColor &color);
void mousePressEvent(QMouseEvent *event);
signals:
void click(const QColor &color);
private:
QLabel *m_pLabel;
QLabel *m_pRgbLabel;
QColor m_color;
};
class QColorCombobox : public QComboBox {
Q_OBJECT
public:
QColorCombobox(QWidget *parent = Q_NULLPTR);
~QColorCombobox();
void appendItem(const QColor &color);
private slots:
void onClickColorWidget(const QColor &color);
private:
QColorWidget *m_pLineEdit;
QListWidget *m_pListWidget;
};
QColorCombobox.cpp
#include "QColorCombobox.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QListWidget>
QColorWidget::QColorWidget(QWidget *parent /*= Q_NULLPTR*/)
: QLineEdit(parent)
, m_color(255, 0, 0) {
m_pLabel = new QLabel(this);
m_pRgbLabel = new QLabel(this);
m_pLabel->setFixedSize(15, 15);
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(m_pLabel);
layout->addWidget(m_pRgbLabel);
layout->setContentsMargins(5, 0, 0, 2);
setLayout(layout);
setReadOnly(true);
setStyleSheet("QLineEdit{border: none;}QLineEdit:hover{background-color:rgb(0,125,255);}");
}
QColorWidget::~QColorWidget() {
}
void QColorWidget::updateColor(const QColor &color) {
m_color = color;
QString strstyle = QString("border:1px solid black;background-color:rgb(%1,%2,%3);").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()));
QString strText = QString("%1,%2,%3").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()));
m_pLabel->setStyleSheet(strstyle);
m_pRgbLabel->setText(strText);
}
void QColorWidget::mousePressEvent(QMouseEvent *event) {
emit click(m_color);
}
QColorCombobox::QColorCombobox(QWidget *parent /*= Q_NULLPTR*/)
: QComboBox(parent) {
m_pLineEdit = new QColorWidget(this);
m_pListWidget = new QListWidget(this); m_pLineEdit->setStyleSheet("");
setContextMenuPolicy(Qt::NoContextMenu);//禁用菜单
m_pListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//禁用垂直滚动条
m_pListWidget->setStyleSheet("QListWidget::Item:hover{background-color:rgb(0,125,255);}");
setLineEdit(m_pLineEdit);
setModel(m_pListWidget->model());
setView(m_pListWidget);
}
QColorCombobox::~QColorCombobox() {
}
void QColorCombobox::appendItem(const QColor &color) {
QColorWidget *pWid = new QColorWidget(this);
pWid->updateColor(color);
connect(pWid, SIGNAL(click(const QColor &)), this, SLOT(onClickColorWidget(const QColor &)));
QListWidgetItem *pItem = new QListWidgetItem(m_pListWidget);
m_pListWidget->addItem(pItem);
m_pListWidget->setItemWidget(pItem, pWid);
}
void QColorCombobox::onClickColorWidget(const QColor &color) {
m_pLineEdit->updateColor(color);
hidePopup();
}
MainWindow.cpp
ui.comboBox->appendItem(QColor(255, 0, 0));
ui.comboBox->appendItem(QColor(255, 255, 0));
ui.comboBox->appendItem(QColor(255, 0, 255));
ui.comboBox->appendItem(QColor(0, 255, 255));
ui.comboBox->appendItem(QColor(0, 255, 0));
2. 错误案例:自定义多选QComboBox
MutiComboBox.h
#ifndef MUTICOMBOBOX_H
#define MUTICOMBOBOX_H
#include <QWidget>
#include <QListWidget>
#include <QLineEdit>
#include <QComboBox>
class MutiComboBox : public QComboBox
{
Q_OBJECT
public:
MutiComboBox(QWidget *parent = 0);
MutiComboBox(const QMap<int,QString> &mapData,QWidget *parent = 0);
~MutiComboBox();
public:
void setModelData(const QMap<int,QString> &mapData);
void setCheckedItems(const QString &data);
QString getSelectedItemDatas();
private slots:
void stateChanged(int state);
void textChanged(const QString &text);
private:
void clear();
private:
QListWidget *pListWidget;
QLineEdit *pLineEdit;
QString strSelectedText;
bool bSelected;
};
#endif // MUTICOMBOBOX_H
MutiComboBox.cpp
#include "muticombobox.h"
#include <QCheckBox>
#include <QDebug>
MutiComboBox::MutiComboBox(QWidget *parent)
: QComboBox(parent)
{
pListWidget = new QListWidget(this);
pListWidget->setObjectName("MutiComboBoxListWidget");
pLineEdit = new QLineEdit(this);
//this->setModel(pListWidget->model());
//this->setView(pListWidget);
this->setLineEdit(pLineEdit);
pLineEdit->setReadOnly(true);
connect(pLineEdit,SIGNAL(textChanged(const QString &)),this,SLOT(textChanged(const QString &)));
}
MutiComboBox::MutiComboBox(const QMap<int,QString> &mapData,QWidget *parent)
:QComboBox(parent)
{
pListWidget = new QListWidget(this);
pLineEdit = new QLineEdit(this);
QMapIterator<int,QString> it(mapData);
while(it.hasNext()){
it.next();
QListWidgetItem *pItem = new QListWidgetItem(pListWidget);
pListWidget->addItem(pItem);
pItem->setData(Qt::UserRole,it.key());
QCheckBox *pCheckBox = new QCheckBox(this);
pCheckBox->setText(it.value());
pListWidget->addItem(pItem);
pListWidget->setItemWidget(pItem,pCheckBox);
connect(pCheckBox,SIGNAL(stateChanged(int)),this,SLOT(stateChanged(int)));
}
this->setModel(pListWidget->model());
this->setView(pListWidget);
this->setLineEdit(pLineEdit);
pLineEdit->setReadOnly(true);
connect(pLineEdit,SIGNAL(textChanged(const QString &)),this,SLOT(textChanged(const QString &)));
}
MutiComboBox::~MutiComboBox()
{
delete pListWidget;
delete pLineEdit;
}
void MutiComboBox::setModelData(const QMap<int,QString> &mapData)
{
int count = pListWidget->count();
for (int row = 0; row < count;++row)
{
QListWidgetItem *pItem = pListWidget->takeItem(0);
delete pItem;
}
QMapIterator<int,QString> it(mapData);
while(it.hasNext()){
it.next();
QListWidgetItem *pItem = new QListWidgetItem(pListWidget);
pListWidget->addItem(pItem);
pItem->setData(Qt::UserRole,it.key());
QCheckBox *pCheckBox = new QCheckBox(this);
pCheckBox->setText(it.value());
pListWidget->addItem(pItem);
pListWidget->setItemWidget(pItem,pCheckBox);
connect(pCheckBox,SIGNAL(stateChanged(int)),this,SLOT(stateChanged(int)));
}
this->setModel(pListWidget->model());
this->setView(pListWidget);
}
void MutiComboBox::setCheckedItems(const QString &data)
{
clear();
if(data.isEmpty())
return;
int count = pListWidget->count();
QStringList list(data.split(";"));
QStringListIterator it(list);
while(it.hasNext())
{
QString da = it.next();
for(int i=0;i<count;++i)
{
QListWidgetItem *pItem = pListWidget->item(i);
if(pItem->data(Qt::UserRole).toString() == da)
{
QWidget *pWidget = pListWidget->itemWidget(pItem);
QCheckBox *pCheckBox = (QCheckBox*) pWidget;
pCheckBox->setChecked(true);
break;
}
}
}
}
QString MutiComboBox::getSelectedItemDatas()
{
int nCount = pListWidget->count();
QString strSeltectedData("");
for(int i=0; i < nCount; ++i)
{
QListWidgetItem *pItem = pListWidget->item(i);
QWidget *pWidget = pListWidget->itemWidget(pItem);
QCheckBox *pCheckBox = (QCheckBox*) pWidget;
if(pCheckBox->isChecked()){
QString strText = pItem->data(Qt::UserRole).toString().trimmed();
strSeltectedData.append(strText).append(";");
}
}
if(strSeltectedData.endsWith(";"))
{
strSeltectedData.remove(strSeltectedData.count()-1,1);
}
return strSeltectedData;
}
void MutiComboBox::stateChanged(int state)
{
bSelected = true;
QString strSelectedData("");
strSelectedText.clear();
QObject *object= QObject::sender();
QCheckBox *pSenderCheckBox = static_cast<QCheckBox*>(object);
int nCount = pListWidget->count();
for(int i=0; i < nCount; ++i)
{
QListWidgetItem *pItem = pListWidget->item(i);
QWidget *pWidget = pListWidget->itemWidget(pItem);
QCheckBox *pCheckBox = (QCheckBox*) pWidget;
if(pCheckBox->isChecked()){
QString strText = pCheckBox->text();
strSelectedData.append(strText).append(";");
}
}
if(strSelectedData.endsWith(";"))
{
strSelectedData.remove(strSelectedData.count()-1,1);
}
if(!strSelectedData.isEmpty())
{
strSelectedText = strSelectedData;
pLineEdit->setText(strSelectedData);
pLineEdit->setToolTip(strSelectedData);
}else{
pLineEdit->clear();
}
bSelected = false;
}
void MutiComboBox::textChanged(const QString &text)
{
if(!bSelected)
{
pLineEdit->setText(strSelectedText);
}
}
void MutiComboBox::clear(){
int nCount = pListWidget->count();
for(int i=0; i < nCount; ++i)
{
QListWidgetItem *pItem = pListWidget->item(i);
QWidget *pWidget = pListWidget->itemWidget(pItem);
QCheckBox *pCheckBox = (QCheckBox*) pWidget;
if(pCheckBox->isChecked()){
pCheckBox->setChecked(false);
}
}
strSelectedText = "";
pLineEdit->setText("");
}
MainWindow.cpp
QMap<int, QString> qmap = {
{1, "111"},
{2, "222"},
{3, "333"},
{4, "444"},
{5, "555"},
{11, "111"},
{12, "222"},
{13, "333"},
{14, "444"},
{15, "555"},
{21, "111"},
{22, "222"},
{23, "333"},
{24, "444"},
{25, "555"},
};
ui->combobox->setModelData(qmap);
3. 正确案例:教你如何实现带复选框的ComboBox(自定义QComboBox)
XComboBox.h
#pragma once
#include <QComboBox>
#include <QStandardItemModel>
#include <QListView>
#include <QKeyEvent>
class QLineEdit;
class QListView;
struct ItemInfo
{
int idx;
QString str;
QVariant userData;
bool bChecked;
ItemInfo()
{
idx = -1;
str = QString("");
userData = QVariant();
bChecked = false;
}
};
// 事件过滤器
class KeyPressEater : public QObject
{
Q_OBJECT
public:
KeyPressEater(QObject* parent=nullptr):QObject(parent) {}
~KeyPressEater() {}
signals:
void sigActivated(int idx);
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Space)
{
QListView* lstV = qobject_cast<QListView*>(obj);
if (nullptr != lstV)
{
int idx = lstV->currentIndex().row();
if (-1 != idx)
{
emit sigActivated(idx);
}
}
}
else if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)
{
return QObject::eventFilter(obj, event);
}
return true;
}
else {
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
class XComboBox : public QComboBox
{
Q_OBJECT
public:
XComboBox(QWidget *parent = Q_NULLPTR);
~XComboBox();
// 添加item
void AddItem(const QString& str, bool bChecked = false, QVariant &userData = QVariant());
void AddItems(const QList<ItemInfo>& lstItemInfo);
void AddItems(const QMap<QString, bool>& mapStrChk);
void AddItems(const QList<QString>& lstStr);
// 删除item
void RemoveItem(int idx);
// 清空item
void Clear();
// 获取选中的数据字符串列表
QStringList GetSelItemsText();
// 获取选中item的信息
QList<ItemInfo> GetSelItemsInfo();
// 获取item文本
QString GetItemText(int idx);
// 获取item信息
ItemInfo GetItemInfo(int idx);
signals:
// popup显示信号
void showingPopup();
// popup隐藏信号
void hidingPopup();
protected:
void showPopup();
// 重写QComboBox的hidePopup函数
// 目的选择过程中,不隐藏listview
void hidePopup();
virtual void mousePressEvent(QMouseEvent * event);
virtual void mouseReleaseEvent(QMouseEvent * event);
virtual void mouseMoveEvent(QMouseEvent * event);
private:
void UpdateText();
private slots:
void sltActivated(int idx);
private:
QLineEdit* pLineEdit;
QListView* pListView;
QStandardItemModel m_model;
};
XComboBox.cpp
#include "XComboBox.h"
#include <QLineEdit>
#include <QMouseEvent>
#include <QDebug>
XComboBox::XComboBox(QWidget *parent)
: QComboBox(parent)
{
pLineEdit = new QLineEdit(this);
pLineEdit->setReadOnly(true);
this->setLineEdit(pLineEdit);
this->lineEdit()->disconnect();
KeyPressEater *keyPressEater = new KeyPressEater(this);
pListView = new QListView(this);
pListView->installEventFilter(keyPressEater);
this->setView(pListView);
this->setModel(&m_model);
connect(this, SIGNAL(activated(int)), this, SLOT(sltActivated(int)));
connect(keyPressEater, SIGNAL(sigActivated(int)), this, SLOT(sltActivated(int)));
}
XComboBox::~XComboBox()
{
}
void XComboBox::AddItem(const QString& str, bool bChecked /*= false*/, QVariant &userData /*= QVariant()*/)
{
QStandardItem* item = new QStandardItem(str);
item->setCheckable(true);
item->setCheckState(bChecked ? Qt::Checked : Qt::Unchecked);
item->setData(userData, Qt::UserRole + 1);
m_model.appendRow(item);
UpdateText();
}
void XComboBox::AddItems(const QList<ItemInfo>& lstItemInfo)
{
for (auto a : lstItemInfo)
{
AddItem(a.str, a.bChecked, a.userData);
}
}
void XComboBox::AddItems(const QMap<QString, bool>& mapStrChk)
{
for (auto it = mapStrChk.begin(); it != mapStrChk.end(); ++it)
{
AddItem(it.key(), it.value());
}
}
void XComboBox::AddItems(const QList<QString>& lstStr)
{
for (auto a : lstStr)
{
AddItem(a, false);
}
}
void XComboBox::RemoveItem(int idx)
{
m_model.removeRow(idx);
UpdateText();
}
void XComboBox::Clear()
{
m_model.clear();
UpdateText();
}
QStringList XComboBox::GetSelItemsText()
{
QStringList lst;
QString str = pLineEdit->text();
if (str.isEmpty())
{
return lst;
}
else
{
return pLineEdit->text().split(",");
}
}
QList<ItemInfo> XComboBox::GetSelItemsInfo()
{
QList<ItemInfo> lstInfo;
for (int i = 0; i < m_model.rowCount(); i++)
{
QStandardItem* item = m_model.item(i);
if (item->checkState() == Qt::Unchecked) continue;
ItemInfo info;
info.idx = i;
info.str = item->text();
info.bChecked = true;
info.userData = item->data(Qt::UserRole + 1);
lstInfo << info;
}
return lstInfo;
}
QString XComboBox::GetItemText(int idx)
{
if (idx < 0 || idx >= m_model.rowCount())
{
return QString("");
}
return m_model.item(idx)->text();
}
ItemInfo XComboBox::GetItemInfo(int idx)
{
ItemInfo info;
if (idx < 0 || idx >= m_model.rowCount())
{
return info;
}
QStandardItem* item = m_model.item(idx);
info.idx = idx;
info.str = item->text();
info.bChecked = (item->checkState() == Qt::Checked);
info.userData = item->data(Qt::UserRole + 1);
return info;
}
void XComboBox::showPopup()
{
emit showingPopup();
QComboBox::showPopup();
}
void XComboBox::hidePopup()
{
int width = this->view()->width();
int height = this->view()->height();
int x = QCursor::pos().x() - mapToGlobal(geometry().topLeft()).x() + geometry().x();
int y = QCursor::pos().y() - mapToGlobal(geometry().topLeft()).y() + geometry().y();
QRect rectView(0, this->height(), width, height);
if (!rectView.contains(x, y))
{
emit hidingPopup();
QComboBox::hidePopup();
}
}
void XComboBox::mousePressEvent(QMouseEvent * event)
{
QComboBox::mousePressEvent(event);
event->accept();
}
void XComboBox::mouseReleaseEvent(QMouseEvent * event)
{
QComboBox::mouseReleaseEvent(event);
event->accept();
}
void XComboBox::mouseMoveEvent(QMouseEvent * event)
{
QComboBox::mouseMoveEvent(event);
event->accept();
}
void XComboBox::UpdateText()
{
QStringList lstTxt;
for (int i = 0; i < m_model.rowCount(); ++i)
{
QStandardItem* item = m_model.item(i);
if (item->checkState() == Qt::Unchecked) continue;
lstTxt << item->text();
}
pLineEdit->setText(lstTxt.join(","));
pLineEdit->setToolTip(lstTxt.join("\n"));
}
void XComboBox::sltActivated(int idx)
{
QStandardItem* item = m_model.item(idx);
if (nullptr == item) return;
Qt::CheckState state = (item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
item->setCheckState(state);
UpdateText();
}
MainWindow.cpp
QStringList lstStr;
for (int i = 0; i < 20; ++i) {
lstStr << QString("item %1").arg(i);
}
ui->combobox->AddItems(lstStr);
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)