Search code examples
pythonqtpyqtsignals-slotsqcombobox

QComboBox not emitting signal when it's editable and using a model


I am trying to catch the highlighted signal from a QComboBox, but it is not being emitted consistently. So far I've found out that:

  1. If QComboBox is not editable, the highlighted signal is emitted correctly.
  2. If QComboBox is editable and uses the default convenience model (ie no call to setModel is made), the highlighted signal is emitted.

    BUT:

  3. If QComboBox is editable and it uses a model other than the default convenience model, the highlighted signal is not emitted. I've tried this using a standard QStringList model and a custom model.

Is there a reason for this behavior? Am I missing something?

A code example follows, where combos 1-3 illustrate the 3 cases outlined above, and combo3 is not emitting the highlighted signal while combos 1 and 2 are.

Thanks!

import sys
from PyQt4 import QtCore
from PyQt4 import QtGui

class TestWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        data_items = ['item1', 'item2', 'item3', 'item4']
        combo1 = QtGui.QComboBox(self)
        combo1.setEditable(False)
        combo1.setModel(QtGui.QStringListModel(data_items, self))
        combo1.highlighted.connect(lambda i: messages.append('combo1 emits highlighted signal, item={}'.format(i)))
        combo2 = QtGui.QComboBox(self)
        combo2.setEditable(True)
        combo2.addItems(data_items)
        combo2.highlighted.connect(lambda i: messages.append('combo2 emits highlighted signal, item={}'.format(i)))
        combo3 = QtGui.QComboBox(self)
        combo3.setEditable(True)
        combo3.setModel(QtGui.QStringListModel(data_items, self))
        combo3.highlighted.connect(lambda i: messages.append('combo3 emits highlighted signal, item={}'.format(i)))
        messages = QtGui.QTextEdit(self)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(combo1)
        layout.addWidget(combo2)
        layout.addWidget(combo3)
        layout.addWidget(messages)
        self.setLayout(layout)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = TestWindow()
    window.show()
    app.exec_()

EDIT: It seems that the highlighted signal is emitted properly if I call setEditable(True) after calling setModel. Ie, swapping order or lines 2 and 3 of the 'combo3' calls like this:

        combo3 = QtGui.QComboBox(self)
        combo3.setModel(QtGui.QStringListModel(data_items, self))
        combo3.setEditable(True)
        combo3.highlighted.connect(lambda i: messages.append('combo3 emits highlighted signal, item={}'.format(i)))

This is a workaround for my immediate concerns, but I still find this quite puzzling so any insights on what's going on would be appreciated...


Solution

  • Whenever you set a new model, Qt often has to disconnect and reconnect several signals and possibly also delete and re-create some subcontrols. So in general, it is always wise to set the new model first, before making any other changes. It isn't just highlighting in a combo-box that can be affected in this way - any widget that uses the model/view framework (e.g. list-widgets, tree-widgets, table-widgets, etc) can behave the same way.

    It's hard to predict exactly which elements of a widget could be affected by re-setting the model - you'd probably have to study the source-code to be sure. So to avoid subtle bugs down the line, just get into the habit of setting the model before anything else.