当前位置: 代码迷 >> 综合 >> 关于重写 QComboBox 后下拉菜单显示不正常的问题
  详细解决方案

关于重写 QComboBox 后下拉菜单显示不正常的问题

热度:47   发布时间:2023-10-14 22:04:34.0

文章目录

    • 问题描述
    • 问题分析
    • 临时解决方案
    • 代码整理
      • 1. 错误案例:Qt 组合框QComboBox定制颜色选择框
      • 2. 错误案例:自定义多选QComboBox
      • 3. 正确案例:教你如何实现带复选框的ComboBox(自定义QComboBox)

问题描述


这两天在开发过程中,重写了 QComboBox 这个控件,参考这篇博客 Qt 组合框QComboBox定制颜色选择框 实现的

关于重写 QComboBox 后下拉菜单显示不正常的问题

但是发现了一个奇怪的问题,就是在加载数据后,初始化是正确的

但是在多次点击下拉按钮后,或者是从某次点击某一 item 开始,下拉菜单的显示便开始错乱,如下图所示

关于重写 QComboBox 后下拉菜单显示不正常的问题
这个图显示的是末尾几项缺失

再或者如下图

关于重写 QComboBox 后下拉菜单显示不正常的问题

这个图显示的是开始几项缺失

与这个类似的,自定义多选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_OBJECTpublic: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 processingreturn QObject::eventFilter(obj, event);}}
};class XComboBox : public QComboBox
{
    Q_OBJECTpublic:XComboBox(QWidget *parent = Q_NULLPTR);~XComboBox();// 添加itemvoid 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);// 删除itemvoid RemoveItem(int idx);// 清空itemvoid 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函数// 目的选择过程中,不隐藏listviewvoid 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);
  相关解决方案