Search code examples
pythonpyqtpyqt5qlistwidget

(Py)Qt5: How to update selection when current item has been set programatically


Usecase: I have a QListWidget. When the user selects any row, I want to reset the current item and selection to row 3:

from PyQt5.QtWidgets import QApplication, QListWidget

app = QApplication([])
l = QListWidget()
l.setSelectionMode(QListWidget.SingleSelection)
l.addItems(list('abcde'))

def slot(current, previous):
    # sm = l.selectionModel()
    l.blockSignals(True)
    l.setCurrentRow(3)
    l.blockSignals(False)
    # sm.select(l.currentIndex(), sm.Select)  # Does not work
    # sm.setCurrentIndex(l.currentIndex(), sm.Select)  # Does not work

l.currentItemChanged.connect(slot)
l.show()
app.exec()

The example above sets row three as current row but keeps the selected row to the one that the user clicked. I already tried various combinations of QItemModel.select() and QItemModel.setCurrentIndex() and similar stuff, but nothing worked. I also found no answers on Google or the Qt forums.


Solution

  • Using blockSignals in this case is a double-edged sword because I think you're using it to avoid an infinite loop. but this also does not generate that the model is updated, besides that it is unnecessary since setCurrentRow() only updates if it is different from its previous value avoiding that problem.

    The solution is to use QTimer.singleShot() to update the change:

    import sys
    from functools import partial
    from PyQt5.QtWidgets import QApplication, QListWidget
    from PyQt5.QtCore import QTimer
    
    
    app = QApplication(sys.argv)
    l = QListWidget()
    l.setSelectionMode(QListWidget.SingleSelection)
    l.addItems(list('abcde'))
    
    row = 3
    
    def onCurrentRowChanged():
        QTimer.singleShot(0, partial(l.setCurrentRow, row))
    
    l.currentRowChanged.connect(onCurrentRowChanged)
    l.show()
    sys.exit(app.exec_())
    

    Note: the logic does not change if the currentRowChanged signal is changed to currentItemChanged.