如何调试 QComboBox 发出多个 "activated" 信号

How to debug QComboBox emitting multiple "activated" signals

我找到了一个很好的资源 here,用于构建 QComboBox,它提供了经过过滤的建议列表。除了每次我 select 组合框中的建议选项时 "activated" 和 "currentIndexChanged" 信号都会发出三次之外,它运行良好。根据选项是通过鼠标 select 编辑还是使用箭头键和回车按钮编辑,行为会有所不同。

我的问题是,我该如何调试它?代码中没有必要捕捉和阻止前两个信号被发出。有没有办法覆盖 QComboBox "activated" 信号以尝试在行为中捕获它?还是我必须定义自己的信号并使用它来代替?

代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Qt, QSortFilterProxyModel
from PySide2.QtWidgets import QCompleter, QComboBox

class ExtendedComboBox(QComboBox):
    def __init__(self, parent=None):
        super(ExtendedComboBox, self).__init__(parent)

        self.setFocusPolicy(Qt.StrongFocus)
        self.setEditable(True)

        # add a filter model to filter matching items
        self.pFilterModel = QSortFilterProxyModel(self)
        self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.pFilterModel.setSourceModel(self.model())

        # add a completer, which uses the filter model
        self.completer = QtWidgets.QCompleter(self)
        self.completer.setModel(self.pFilterModel)

        # always show all (filtered) completions
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.setCompleter(self.completer)

        # connect signals
        self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
        self.completer.activated.connect(self.on_completer_activated)

    # on selection of an item from the completer, select the corresponding item from combobox 
    def on_completer_activated(self, text):
        if text:
            index = self.findText(text)
            self.setCurrentIndex(index)
            # self.activated.emit(self.itemText(index))


    # on model change, update the models of the filter and completer as well 
    def setModel(self, model):
        super(ExtendedComboBox, self).setModel(model)
        self.pFilterModel.setSourceModel(model)
        self.completer.setModel(self.pFilterModel)


    # on model column change, update the model column of the filter and completer as well
    def setModelColumn(self, column):
        self.completer.setCompletionColumn(column)
        self.pFilterModel.setFilterKeyColumn(column)
        super(ExtendedComboBox, self).setModelColumn(column)    

def change_option(text):
    print(text)

if __name__ == "__main__":
    import sys
    from PySide2.QtWidgets import QApplication
    from PySide2.QtCore import QStringListModel

    app = QApplication(sys.argv)

    string_list = ['hola muchachos', 'adios amigos', 'hello world', 'good bye']

    combo = ExtendedComboBox()

    # either fill the standard model of the combobox
    combo.addItems(string_list)
    combo.currentIndexChanged[str].connect(change_option)
    # or use another model
    #combo.setModel(QStringListModel(string_list))

    combo.resize(300, 40)
    combo.show()

    sys.exit(app.exec_())

您会注意到,如果您 运行 代码并开始在文本框中键入 "hello",然后单击建议的 "hello world",activated 信号 returns 正确的 "hello world"。如果您开始输入 "hello" 但这次使用箭头键向下滚动到 "hello world",它将发出三次。

我已经尝试了同一个想法的多个实现,但都得到了相同的结果。在用新模型替换模型后,我什至注意到未修改的 QComboBox 有类似的行为。

PySide2 5.6.0a1 Windows 10.0.18362 内部版本 18362

感谢观看!

我没有 PySide2,但在大多数情况下,我认为你所要做的就是用 PySide2 替换我的 PyQt5 参考——因为这就是我所做的让你的程序从 PySide2 切换到 PyQt5 ---连同一些重组和微调,这给了我以下功能代码:

from sys import exit as sysExit

from PyQt5.QtCore import Qt, QSortFilterProxyModel, QStringListModel, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QCompleter, QComboBox, QCompleter, QHBoxLayout

class ExtendedComboBox(QComboBox):
    def __init__(self):
        QComboBox.__init__(self)

        self.setFocusPolicy(Qt.StrongFocus)
        self.setEditable(True)

        # add a filter model to filter matching items
        self.pFilterModel = QSortFilterProxyModel(self)
        self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.pFilterModel.setSourceModel(self.model())

        # add a completer, which uses the filter model
        self.completer = QCompleter(self)
        self.completer.setModel(self.pFilterModel)

        # always show all (filtered) completions
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.setCompleter(self.completer)

        # connect signals
        self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
        self.completer.activated.connect(self.on_completer_activated)

    # on selection of an item from the completer, select the corresponding item from combobox 
    def on_completer_activated(self, text):
        if text:
            index = self.findText(text)
            self.setCurrentIndex(index)
            # self.activated.emit(self.itemText(index))


    # on model change, update the models of the filter and completer as well 
    def setModel(self, model):
        self.setModel(model)
        self.pFilterModel.setSourceModel(model)
        self.completer.setModel(self.pFilterModel)


    # on model column change, update the model column of the filter and completer as well
    def setModelColumn(self, column):
        self.completer.setCompletionColumn(column)
        self.pFilterModel.setFilterKeyColumn(column)
        self.setModelColumn(column)    

class MainApp(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        string_list = ['hola muchachos', 'adios amigos', 'hello world', 'good bye']

        self.combo = ExtendedComboBox()

        # either fill the standard model of the combobox
        self.combo.addItems(string_list)
        self.combo.currentIndexChanged[str].connect(self.change_option)
        # or use another model
        #combo.setModel(QStringListModel(string_list))

        self.resize(300, 100)
        self.combo.resize(300, 50)

        HBox = QHBoxLayout()
        HBox.addWidget(self.combo)

        self.setLayout(HBox)

    @pyqtSlot(str)
    def change_option(self, text):
        print(text)

if __name__ == "__main__":
    MainThred = QApplication([])

    MainGui = MainApp()
    MainGui.show()

    sysExit(MainThred.exec_())

我认为问题是你试图将 Signals/Slots 与非 QObject 函数一起使用(又名)你的 change_option 函数与从 QObject 继承的任何东西没有直接关联所以我是不确定它在做什么或没有做什么,但这只是一个猜测,因为我所做的一切都被放入了一个正常的 Qt 结构中并且它工作得很好

我使用的是 PySide2 5.6.0a1,因为这是 Anaconda 在 Python 2.7 环境中安装的版本。 @eyllanesc 指出这是一个早期和过时的版本,可能有问题。

当我在 Python 3.7 环境中使用 PySide2-5.13.1 尝试相同的代码时,一切都按预期工作。