1. 前言
前段时间一直研究如何实现QComboBox的每个item都可以删除, 也就是在每个item的最右侧都有一个Icon, 点击这个Icon就可以删除当前item. 网上也有很多代码, 但都不太符合我的要求, 要么代码太多, 要么实现的不对, 还有让我花钱买的, 还有让我关注的, 所以一直都没有太大进展. 后面我转变思路, 用qml实现了类似的功能, 然后让ChatGPT根据qml帮我实现, 效果还不错.
2. 环境
qt6.5.3
win11专业版
3. qml代码
// pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Dialogs
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Controls.Basic
ApplicationWindow {
id: appWindow
title: "qb"
visible: true
width: 320
height: 900
maximumWidth: 320
maximumHeight: 900
minimumWidth: 320
minimumHeight: 900
Component.onCompleted: {
for (var i = 0; i < 5; i++) {
var obj = {"itemName":i}
appComboModel.append(obj)
}
}
ComboBox {
id: appCombo
width: 100
height: 30
font.pixelSize: 12
model: ListModel {
id: appComboModel
}
textRole: "itemName"
delegate: ItemDelegate {
width: appCombo.width
height: appCombo.height
highlighted: appCombo.highlightedIndex === index
// 输入框
Text {
text: itemName
width: parent.width
height: parent.height
font.pixelSize: 12
verticalAlignment: Text.AlignVCenter
rightPadding: parent.height // 为右侧图标留出空间
// 绘制右侧图标
Rectangle {
width: parent.height
height: parent.height
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
Image {
source: "qrc:/img/clear.png"
anchors.centerIn: parent
width: parent.width
height: parent.height
}
// 捕获右侧点击事件
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
appCombo.model.remove(index)
}
}
}
}
}
}
}
4. QComboBox代码
其实告诉ChatGPT实现ItemDelegate即可, 代码如下:
#ifndef COMBOCLEAR_H
#define COMBOCLEAR_H
#include <QComboBox>
#include <QPainter>
#include <QMouseEvent>
#include <QStyledItemDelegate>
#include <QIcon>
class ClearItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ClearItemDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent), clearIcon(":/img/clear.png") {}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
// 默认绘制文本
QStyledItemDelegate::paint(painter, option, index);
// 计算清除按钮的区域(高 DPI 兼容)
int iconSize = option.rect.height();
QRect iconRect(option.rect.right() - iconSize, option.rect.top(), iconSize, iconSize);
// 画背景
painter->fillRect(iconRect, QColor(220, 220, 220)); // 灰色背景
// 画边框
painter->setPen(QPen(Qt::black, 1)); // 黑色边框
painter->drawRect(iconRect);
// 画删除图标
clearIcon.paint(painter, iconRect);
}
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
{
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
int iconSize = option.rect.height();
QRect iconRect(option.rect.right() - iconSize, option.rect.top(), iconSize, iconSize);
if (iconRect.contains(mouseEvent->pos()))
{
// 触发删除信号
emit clearItem(index);
return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
signals:
void clearItem(const QModelIndex &index);
private:
QIcon clearIcon;
};
class ComboClear : public QComboBox
{
Q_OBJECT
public:
explicit ComboClear(QWidget *parent = nullptr) : QComboBox(parent)
{
ClearItemDelegate *delegate = new ClearItemDelegate(this);
setItemDelegate(delegate);
// 连接信号槽,删除点击的 item
connect(delegate, &ClearItemDelegate::clearItem, this, &ComboClear::onClearItem);
}
private slots:
void onClearItem(const QModelIndex &index)
{
if (!index.isValid())
return;
// 获取被删除项的文本
QString deletedText = model()->data(index, Qt::DisplayRole).toString();
// 删除 model 里的项
int row = index.row();
model()->removeRow(row);
// 选中上一个如果有的话
if (model()->rowCount() > 0)
{
setCurrentIndex(qMax(0, row - 1));
}
// 触发信号,通知外部
emit clearItemText(deletedText);
}
signals:
void clearItemText(const QString &text);
};
#endif // COMBOCLEAR_H
5. 结束
毕竟不是专业做Qt, 如有问题请指教, 不胜感激.