I found a great resource here for building a QComboBox that gives a filtered list of suggested. It works well except for the fact that the "activated" and "currentIndexChanged" signals get emitted three times every time I select a suggested option in the combobox. The behavior is different depending on if the option is selected by mouse or using the arrow keys and the enter button.
My question is, how do I debug this? There is no point in the code to catch and prevent the first two signals from getting emitted. Is there a way to override the QComboBox "activated" signal to try and catch it in the act? Or do I have to define my own signal and use that instead?
Here's the code:
#!/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_())
You'll notice if you run the code and start typing "hello" into the text box, then click on the suggested "hello world", the activated
signal returns the correct "hello world". While if you start typing "hello" but this time use the arrow keys to scroll down to "hello world", it will emit three times.
I've tried multiple implementations of this same idea all with the same result. I've even noticed similar behavior with unmodified QComboBox after swapping out the model with a new one.
PySide2 5.6.0a1 Windows 10.0.18362 Build 18362
Thanks for taking a look!