QComboBox下拉框给选项增加删除按钮

QComboBox下拉框给选项增加删除按钮0 前言给下拉框增加按钮是常见的功能 如账号输入框的下拉 网上有不少 Qt 实现的例子 实现方式也很多 在参照了别人的思路后我也实现了选项带按钮的下拉框 中间遇到不少坑 最后效果也不完美 1 实现过程我选的是 QListWidget QListWidgetI 的方式 因为感觉相对简单点 而且和样式表更好搭配 这种方式主要是把 QListWidget 作为 QComboBox 的 View List 的 model 也设置为 QComboBox 的 model 这样展现出

0.前言

给下拉框增加按钮是常见的功能,如 账号输入框的下拉:

QComboBox下拉框给选项增加删除按钮

网上有不少 Qt 实现的例子,实现方式也很多,在参照了别人的思路后我也实现了选项带按钮的下拉框。中间遇到不少坑,最后效果也不完美,后来没用到就不了了之了,仅供参考。

1.实现过程

本文选的是 QListWidget + QListWidgetItem 的方式,因为感觉相对简单点,而且和样式表更好搭配。这种方式主要是把 QListWidget 作为 QComboBox 的 View ,List 的 model 也设置为 QComboBox 的 model,这样展现出来的选项就是一个 QListWidgetItem,我们只需要把按钮设置到 QListWidgetItem 的 widget 中就行了。后来感觉 QComboBox 和 QListWidget 太多内置的规则,细节完善任重道远。

首先,要让 QComboBox 读取到选项的文本,需要给 QListWidgetItem 设置 DisplayRole:

QListWidgetItem* item_widget = new QListWidgetItem(); //设置显示的data,这样combox才有文字 item_widget->setData(Qt::DisplayRole,"text");

然后来看一下比较简单的实现:

 QListWidget *item_list=new QListWidget(this); ui->comboBoxA->setModel(item_list->model()); ui->comboBoxA->setView(item_list); //添加选项 for(int i=0;i<5;i++) { //组合一个带按钮的widget QWidget *item_widget=new QWidget(); QHBoxLayout *layout=new QHBoxLayout(item_widget); layout->addStretch(); //弹簧 QPushButton *btn=new QPushButton(item_widget); layout->addWidget(btn); layout->setMargin(0); layout->setSpacing(0); QListWidgetItem* item_wrap = new QListWidgetItem(item_list); //测试长文字 QString text=(i==0)?"text long long long":"text"; //设置显示的data,这样combox才有文字 item_wrap->setData(Qt::DisplayRole,text); item_list->setItemWidget(item_wrap,item_widget); connect(btn,&QPushButton::clicked,this,[=](){ ui->comboBoxA->hidePopup(); //没有刷新弹框大小 item_list->takeItem(item_list->row(item_wrap)); delete item_wrap; }); }

很快就发现了问题,文字足够长时,选项整体宽度被拉长了,导致按钮位置超出了显示范围(下图蓝色框部分):

 QComboBox下拉框给选项增加删除按钮

(按钮位置我是配合样式表的 margin 调整的,可见源代码) 

索性我就继承了 QListWidget,重写了他的 visualRect 接口,在有滚动条时,rect 就去掉滚动条的宽度:

QRect ComboView::visualRect(const QModelIndex &index) const { QRect rect=QListWidget::visualRect(index); int width=this->width(); if(verticalScrollBar()->isVisible()){ width-=verticalScrollBar()->width(); } rect.setWidth(width); return rect; }

这下就能把选项挤过来了:

QComboBox下拉框给选项增加删除按钮

(2022-09-04 补充)之前有评论指出下拉滚动条滚动后,再次弹出时按钮和选项没对齐,拖动窗口大小后弹出又能恢复对齐,应该是WidgetItem的坐标同步问题。

QComboBox下拉框给选项增加删除按钮

目前想的简单的解决方法有两种,一是弹出时执行一遍resize的逻辑,二是隐藏时恢复弹出时的位置,以思路二为例:

void MyComboBox::hidePopup() { QStyle * const style = this->style(); QStyleOptionComboBox opt; initStyleOption(&opt); view()->scrollTo(view()->currentIndex(), style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ? QAbstractItemView::PositionAtCenter : QAbstractItemView::EnsureVisible); QComboBox::hidePopup(); }

其他问题:

1.滚动条占位宽度计算的问题,我的想法是显示或增删选项时去判断是否有滚动条,然后调整按钮的 margin。 

2.点击按钮删除选项后,下次弹出可能某个选项处于hover状态。

2.完整代码

github 链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/MyComboBox

(demo 没怎么写注释,而且没有应用到自己的项目):

#pragma once #include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         class ComboView : public QListWidget { Q_OBJECT public: explicit ComboView(QWidget * parent=nullptr); QRect visualRect(const QModelIndex &index) const override; }; class ComboItem : public QWidget { Q_OBJECT public: explicit ComboItem(const QString &text,QWidget *parent = nullptr); ~ComboItem(); QString text() const; signals: void itemClicked(const QString &text); private: QString textValue; QPushButton *btn; }; class ComboDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit ComboDelegate(QObject *parent=nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; class MyComboBox : public QComboBox { Q_OBJECT public: explicit MyComboBox(QWidget *parent = nullptr); //设置下拉选项 void setRemovableItems(const QStringList &items); //显示弹框 void showPopup() override; void hidePopup() override; signals: void itemRemoved(const QString &text); private: QListWidget *itemList; }; 
        
       
      
     
    
  
#include "MyComboBox.h" #include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        #include 
       
         ComboView::ComboView(QWidget *parent) : QListWidget(parent) { } QRect ComboView::visualRect(const QModelIndex &index) const { QRect rect=QListWidget::visualRect(index); int width=this->width(); if(verticalScrollBar()->isVisible()){ width-=verticalScrollBar()->width(); } rect.setWidth(width); return rect; } ComboItem::ComboItem(const QString &text, QWidget *parent) : QWidget(parent), textValue(text), btn(new QPushButton(text,this)) { QHBoxLayout *layout=new QHBoxLayout(this); layout->addStretch(); layout->addWidget(btn); layout->setMargin(0); layout->setSpacing(0); connect(btn,&QPushButton::clicked,[this]{ emit itemClicked(textValue); }); } ComboItem::~ComboItem() { qDebug()<<"~delete"< 
        
          setTextElideMode(Qt::ElideNone); setModel(itemList->model()); setView(itemList); setItemDelegate(new ComboDelegate(this)); } void MyComboBox::setRemovableItems(const QStringList &items) { //combox的additem insertitem不是虚函数 //实现里时调用的model->insertRow,但是懒得再去重写listmodel-view,就新增一个接口 itemList->clear(); if(items.isEmpty()) return; for(int i=0;i 
         
           setData(Qt::DisplayRole,items.at(i)); //widget_item->setData(Qt::TextAlignmentRole,int(Qt::AlignRight|Qt::AlignVCenter)); //itemList->addItem(widget_item); itemList->setItemWidget(widget_item,item); connect(item,&ComboItem::itemClicked,this,[this,item,widget_item](){ //take移除item后没有刷新弹框大小,干脆隐藏掉先 hidePopup(); itemList->takeItem(itemList->row(widget_item)); delete widget_item; emit itemRemoved(item->text()); }); } } void MyComboBox::showPopup() { QComboBox::showPopup(); } void MyComboBox::hidePopup() { QStyle * const style = this->style(); QStyleOptionComboBox opt; initStyleOption(&opt); view()->scrollTo(view()->currentIndex(), style->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ? QAbstractItemView::PositionAtCenter : QAbstractItemView::EnsureVisible); QComboBox::hidePopup(); } 
          
         
        
       
      
     
    
  
#include "mainwindow.h" #include "ui_mainwindow.h" #include "MyComboBox.h" #include 
  
    #include 
   
     #include 
    
      MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //准备下拉选项串 QStringList str_list; for(int i=0;i<20;i++) { str_list.push_back(QString("%1 item").arg(i)); } //测试长文本显示 str_list[0]="0 item: text long long long long long long"; //【1】 QListWidget *item_list=new QListWidget(this); ui->comboBoxA->setModel(item_list->model()); ui->comboBoxA->setView(item_list); //添加选项 for(int i=0;i 
     
       addStretch(); //弹簧 QPushButton *btn=new QPushButton(str_list.at(i),item_widget); layout->addWidget(btn); layout->setMargin(0); layout->setSpacing(0); QListWidgetItem* item_wrap = new QListWidgetItem(item_list); //设置显示的data,这样combox才有文字 item_wrap->setData(Qt::DisplayRole,str_list.at(i)); //item_list->addItem(item_wrap); item_list->setItemWidget(item_wrap,item_widget); connect(btn,&QPushButton::clicked,this,[=](){ ui->comboBoxA->hidePopup(); //没有刷新弹框大小 item_list->takeItem(item_list->row(item_wrap)); delete item_wrap; }); } //【2】 //QStringList str_list; ui->comboBoxB->setRemovableItems(str_list); //ui->comboBoxB->setMaxVisibleItems(20); connect(ui->comboBoxB,&MyComboBox::itemRemoved,this,[=](const QString &text){ qDebug()<<"item remove"< 
      
        comboBox_2->clear(); }); } MainWindow::~MainWindow() { delete ui; } 
       
      
     
    
  
/*下拉框*/ QComboBox{ min-width:1px; min-height:26px; padding-left: 5px; padding-right: 2px; color: white; border:1px solid rgb(128, 128, 128); background-color: rgb(100 ,100 ,100); } QComboBox:on{ /*弹出为on*/ } QComboBox:hover{ border:1px solid rgb(255, 170, 0); } QComboBox:disabled{ color: rgb(230, 230, 230); background-color:rgb(150, 150, 150); } QComboBox:editable{ background-color:rgb(100, 100, 100); } QComboBox:editable:disabled{ background-color:rgb(150, 150, 150); } /*下拉按钮-配合贴图*/ QComboBox::drop-down{ min-width:24px; } /*下拉框弹出项*/ QComboBox QAbstractItemView{ font: 15px "宋体"; background-color:rgb(110, 110, 110); } QComboBox QAbstractItemView::item{ height:24px; color:white; } QComboBox QAbstractItemView::item:hover{ background-color: rgb(255, 170, 0); } QComboBox QAbstractItemView::item:selected{ background-color: rgb(255, 170, 0); } /*按钮样式*/ QComboBox QAbstractItemView QPushButton{ max-width:40px; min-width:40px; max-height:20px; min-height:20px; border-radius:2px; border:0; margin-right:20px; background-color:white; } QComboBox QAbstractItemView QPushButton:hover{ background-color:cyan; }

 3.参考

博客:QComboBox的代理(订制QComboBox组合框)_Always0nTheWay的博客-CSDN博客

博客:新浪博客

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/209113.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午10:00
下一篇 2026年3月19日 上午10:00


相关推荐

  • 电脑怎么连接信捷plc_信捷plc怎么连接电脑

    电脑怎么连接信捷plc_信捷plc怎么连接电脑1、 以XC系列为例,需要专用的信捷PLC下载线2、 打开PLC编程软件,把串口线连接PC和PLC。,并查看端口:我的电脑—》设备管理—》串口(端口),以端口5为例3、 连接PLC:选项—》软件串口设置,选择实际的串口,默认是modbus通信,点击检测,会自动搜索波特率校验位之类的,直到找到和PLC配置相同参数说明连接成功…

    2022年10月9日
    6
  • docker端口映射不起作用的解决办法有哪些_docker 端口映射

    docker端口映射不起作用的解决办法有哪些_docker 端口映射今天初学Docker和宿主机的端口映射,根据网上的博客进行了操作。执行命令:dockerrun-d-p50070:50070-p8088:8088–namehadoop1ubuntu:java但是发现命令可以正常执行,但端口映射就是不成功,PORTS一直为空,就像下图这样:然后输入命令dockerportc526发现输出也为空这就奇了怪了…

    2022年10月17日
    11
  • Python集成开发环境之Pycharm

    Python集成开发环境之PycharmPycharm 是 Python 的跨平台集成开发环境 隶属于 JetBrains 是一家捷克的软件开发公司 Python 的集成开发环境有很多 比如在安装 Anaconda 时自带的 Spyder 功能有限 调试不方便 Pycharm 是专业用于 Python 程序开发的 IDE 目前 Pycharm 有专业版与社区版两种 专业版收费 社区版免费 功能与专业版差别不大 只是专业版支持一些网页编程和远程调试功能 一般也用不到

    2026年3月27日
    2
  • Unix时间戳(Unix timestamp)转换工具 – 站长工具

    Unix时间戳(Unix timestamp)转换工具 – 站长工具站长之家站长论坛站长俱乐部站长问答网站建设资源站长交易赚钱站长工具ALEXA排名查询百度权重查询SEO概况查询友情链接查询GooglePR查询Whois信息查询域名备案查询站长素材字体下载高清壁纸简历模板高清图片矢量素材PPT模板PSD素材源码下载网站排行行业网站排名地区网站排名手机版工具旧版SEO工具包立即登录 注册新帐号海外服务器租用托管-台湾直销SEO快速排名前3、百度好搜搜狗新:外链…

    2022年5月17日
    220
  • 常见广域网技术

    常见广域网技术广域网封装技术广域网分装方式 HDLC PPP FR 其中 HDLC 和 FR 相继已经被淘汰 HDLCHDLC highleveldat 高级数据链路控制 简称 HDLC 是一种面相比特的链路层协议 广域网中会使用串行链路来提供远距离数据传输 HDLC 是思科研发 思科串行接口默认封装 HDLC 二层技术 注意 1 如果思科和华为串行接口对接需要将封装类型改为一致 CISCO 私有的 HDLC 和工业标准的 HDLC 不是一回事 假设一条链路两端设备使用不同的 HDLC 是不能

    2026年3月18日
    2
  • java运行时异常和非运行时异常区别_常用的运行时异常

    java运行时异常和非运行时异常区别_常用的运行时异常1.Java异常机制Java把异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。Java中的异常分为两大类:错误Error和异常Exception,Java异常体系结构如下图所示:图片来源:http://blog.csdn.net/wuwenxiang91322/article/details/103463372.Throwab

    2022年9月30日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号