5

我正在尝试以用户可以从下拉列表中删除项目的方式调整 QComboBox 的用户界面(无需先选择它们)。

背景是我正在使用 QComboBox 来指示当前打开的数据文件。我还将它用作最近打开文件的缓存。我希望用户能够删除他不想再列出的条目。这可以通过点击删除键,或上下文菜单,或任何易于实现的方式来实现。我不想依赖先选择项目。在 Firefox 中可以找到类似的行为,其中可以删除条目字段的旧缓存建议。

我正在考虑对 QComboBox 使用的列表视图进行子类化,但是,我没有找到足够的文档来帮助我入门。

我将不胜感激任何提示和建议。我正在使用 PyQt,但 C++ 示例没有问题。

4

5 回答 5

6

我使用installEventFilter文档中的代码解决了这个问题。

//must be in a header, otherwise moc gets confused with missing vtable
class DeleteHighlightedItemWhenShiftDelPressedEventFilter : public QObject
{
     Q_OBJECT
protected:
    bool eventFilter(QObject *obj, QEvent *event);
};

bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress)
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key::Key_Delete && keyEvent->modifiers() == Qt::ShiftModifier)
        {
            auto combobox = dynamic_cast<QComboBox *>(obj);
            if (combobox){
                combobox->removeItem(combobox->currentIndex());
                return true;
            }
        }
    }
    // standard event processing
    return QObject::eventFilter(obj, event);
}

myQComboBox->installEventFilter(new DeleteHighlightedItemWhenShiftDelPressedEventFilter);
于 2014-11-17T16:16:50.220 回答
4
comboBox->removeItem(int index) // removes item at index
于 2013-07-23T21:00:47.927 回答
2

您可以使用自动化流程的专用类,因此最终可以节省时间。

例如,在名为Krusader的程序中使用了一个 名为KrHistoryComboBox的类(它继承自KHistoryComboBox类)。

虽然这一次,对于这个答案:以下代码是直接从 a 继承的版本QComboBox(尽管 aQComboBox不能做与 a 一样多的事情KHistoryComboBox),以及它的一个使用示例:

文件 main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

文件 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

文件 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "krhistorcombobox.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // Creates a new editable comboBox, and populates it with data
    KrHistorComboBox *combox;
    combox = new KrHistorComboBox(this);
    combox->setEditable(true);
    QStringList elementsToAdd = {"one", "two", "three", "four", "five", "six"};
    combox->insertItems(0, elementsToAdd);
}

MainWindow::~MainWindow()
{
    delete ui;
}

文件 krhistorcombobox.h

/*****************************************************************************
* Copyright (C) 2018-2019 Shie Erlich <krusader@users.sourceforge.net>      *
* Copyright (C) 2018-2019 Rafi Yanai <krusader@users.sourceforge.net>       *
* Copyright (C) 2018-2019 Krusader Krew [https://krusader.org]              *
*                                                                           *
* This file is part of Krusader [https://krusader.org].                     *
*                                                                           *
* Krusader is free software: you can redistribute it and/or modify          *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation, either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* Krusader is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License for more details.                              *
*                                                                           *
* You should have received a copy of the GNU General Public License         *
* along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
*****************************************************************************/

#ifndef KRHISTORCOMBOBOX_H
#define KRHISTORCOMBOBOX_H

// QtWidgets
#include <QComboBox>

/**
 * A specialized version of a QComboBox, e.g. it deletes the current
 *  item when the user presses Shift+Del
 */
class KrHistorComboBox : public QComboBox
{
    Q_OBJECT

public:
    explicit KrHistorComboBox(QWidget *parent = nullptr);
};

#endif // KRHISTORCOMBOBOX_H

文件 krhistorcombobox.cpp

/*****************************************************************************
* Copyright (C) 2018-2019 Shie Erlich <krusader@users.sourceforge.net>      *
* Copyright (C) 2018-2019 Rafi Yanai <krusader@users.sourceforge.net>       *
* Copyright (C) 2018-2019 Krusader Krew [https://krusader.org]              *
*                                                                           *
* This file is part of Krusader [https://krusader.org].                     *
*                                                                           *
* Krusader is free software: you can redistribute it and/or modify          *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation, either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* Krusader is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License for more details.                              *
*                                                                           *
* You should have received a copy of the GNU General Public License         *
* along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
*****************************************************************************/

#include "krhistorcombobox.h"

// QtCore
#include <QEvent>
// QtGui
#include <QKeyEvent>
// QtWidgets
#include <QAbstractItemView>

/**
 *  A KrHistorComboBox event filter that e.g. deletes the current item when Shift+Del is pressed
 *  There was more information in https://doc.qt.io/qt-5/qobject.html#installEventFilter,
 *  https://forum.qt.io/post/160618 and
 *  https://stackoverflow.com/questions/17820947/remove-items-from-qcombobox-from-ui/52459337#52459337
 */
class KHBoxEventFilter : public QObject
{
    Q_OBJECT

public:
    explicit KHBoxEventFilter(QObject *parent = nullptr) : QObject(parent) {}

protected:
    bool eventFilter(QObject *obj, QEvent *event) override;
};

bool KHBoxEventFilter::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        auto keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->modifiers() == Qt::ShiftModifier && keyEvent->key() == Qt::Key::Key_Delete) {
            auto comboBox = qobject_cast<QComboBox *>(obj);
            if (comboBox != nullptr) {
                // Delete the current item
                comboBox->removeItem(comboBox->currentIndex());
                return true;
            }
        }
    }
    // Perform the usual event processing
    return QObject::eventFilter(obj, event);
}

/**
 *  An event filter for the popup list of a KrHistorComboBox, e.g. it deletes the current
 *  item when the user presses Shift+Del
 */
class KHBoxListEventFilter : public QObject
{
    Q_OBJECT

public:
    explicit KHBoxListEventFilter(QObject *parent = nullptr) : QObject(parent) {}

protected:
    bool eventFilter(QObject *obj, QEvent *event) override;
};

bool KHBoxListEventFilter::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        auto keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->modifiers() == Qt::ShiftModifier && keyEvent->key() == Qt::Key::Key_Delete) {
            auto itemView = qobject_cast<QAbstractItemView *>(obj);
            if (itemView->model() != nullptr) {
                // Delete the current item from the popup list
                itemView->model()->removeRow(itemView->currentIndex().row());
                return true;
            }
        }
    }
    // Perform the usual event processing
    return QObject::eventFilter(obj, event);
}

#include "krhistorcombobox.moc" // required for class definitions with Q_OBJECT macro in implementation files

KrHistorComboBox::KrHistorComboBox(QWidget *parent): QComboBox(parent)
{
    installEventFilter(new KHBoxEventFilter(this));

    QAbstractItemView *itemView = view();
    if (itemView != nullptr)
        itemView->installEventFilter(new KHBoxListEventFilter(this));
}

文件 krexample.pro

#-------------------------------------------------
#
# Project created by QtCreator 2018-09-22T18:33:23
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = untitled
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
        main.cpp \
        krhistorcombobox.cpp \
        mainwindow.cpp

HEADERS += \
        krhistorcombobox.h \
        mainwindow.h

FORMS += \
        mainwindow.ui

文件 mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow" >
  <property name="geometry" >
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle" >
   <string>MainWindow</string>
  </property>
  <widget class="QMenuBar" name="menuBar" />
  <widget class="QToolBar" name="mainToolBar" />
  <widget class="QWidget" name="centralWidget" />
  <widget class="QStatusBar" name="statusBar" />
 </widget>
 <layoutDefault spacing="6" margin="11" />
 <pixmapfunction></pixmapfunction>
 <resources/>
 <connections/>
</ui>

这是正在执行的示例程序的屏幕截图,在按下 Shift+Del 之前(这将删除名为“two”的选项):

按 Shift+Del 之前的程序,这将删除名为的选项


注意:本答案中的一些源代码基于https://doc.qt.io/qt-5/qobject.html#installEventFilterhttps://forum.qt.io/post/160618和出色的工作https://stackoverflow.com/a/26976984中名为“nwp”的用户(尽管该答案不包括在看到弹出列表时删除弹出列表元素的代码,并且它具有“内存泄漏” (一个对象被构造但没有被销毁)因此,如果开发人员添加了一个析构函数,例如~DeleteHighlightedItemWhenShiftDelPressedEventFilter() { QTextStream(stdout) << "DESTRUCTED" << endl; },开发人员后来看到析构函数的代码从未执行过,因此存在内存泄漏;目前我没有堆栈溢出点以便在https://stackoverflow.com/a/26976984中添加评论)。

于 2018-09-22T17:51:28.257 回答
1

很抱歉这么晚了,但我想贡献一些我发现的其他方法,以防其他人像我一样在寻找它。这些方法已经用 Qt 5.6 进行了测试。我不能保证它们可以在其他版本中工作。

一种可能性是收听 QCombobox 的 view() 的“pressed()”信号。这样我们就可以使用鼠标右键从列表中删除项目。我很惊讶地看到 view() 始终可用,从不为 NULL,并且在显示时可以删除项目,因此以下操作非常有效:

class MyCombobox : public QComboBox
{
  Q_OBJECT
  public: MyCombobox(QWidget *pParent = NULL);
  protected slots: void itemMouseDown(const QModelIndex &pIndex);
};

MyCombobox::MyCombobox(QWidget *pParent)
{
  connect( QComboBox::view(), SIGNAL(pressed(const QModelIndex &)),
           this, SLOT(itemMouseDown(const QModelIndex &)) );
}

void MyCombobox::itemMouseDown(const QModelIndex &pIndex)
{
  if( QApplication::mouseButtons() == Qt::RightButton )
  {
    QComboBox::model()->removeRow(pIndex.row());
  }
}

第二种选择是安装事件过滤器,但也安装到视图中。这样我们就可以使用删除键或其他任何东西来删除项目。测试 NULL 指针和无效的行索引可能是个好主意,但为了清楚起见,我省略了这一点。

class MyCombobox : public QComboBox
{
  Q_OBJECT
  public: MyCombobox(QWidget *pParent = NULL);
  protected: bool eventFilter(QObject *pWatched, QEvent *pEvent);
};

MyCombobox::MyCombobox(QWidget *pParent)
{
  QComboBox::view()->installEventFilter(this);
}

bool MyCombobox::eventFilter(QObject *pWatched, QEvent *pEvent)
{
  if( pEvent->type() == QEvent::KeyPress )
  {
    QKeyEvent *tKeyEvent = static_cast<QKeyEvent*>(pEvent);
    if( tKeyEvent->key() == Qt::Key_Delete )
    {
      QComboBox::model()->removeRow(QComboBox::view()->currentIndex().row());
      return true;
    }
  }

  return QObject::eventFilter(pWatched, pEvent);
}

而已。

于 2019-07-02T10:22:52.493 回答
0

您可以通过以下方式删除 QComboBox 的活动选定值:

ui->comboBox->removeItem(ui->comboBox->currentIndex());
于 2021-01-14T20:24:29.800 回答