pythonqgisqlistwidgetqlist

Unable to get length of QList despite docs telling me i can


I'm working on a small plugin for QGIS 3.12+ and regardless of what i try i'm unable to perform the simple task of retrieving the number of items in a QList that's returned by QListWidget.selectedItems()

I'm not well versed in python, but have read through the docs and tried each of the below with all of them failing.

What am i missing?

#Returns items as i can loop though them with: for item in selected:
selected = self.dlg.qListWidgetLayers.selectedItems()

#TypeError: count() takes exactly one argument (0 given)
#Docs claim there's a count that doesn't need the parameter...
intTotal = selected.count()

#AttributeError: 'list' object has no attribute 'size'
intTotal = selected.size()

#AttributeError: 'list' object has no attribute 'length'
intTotal = selected.length()

#AttributeError: 'list' object has no attribute 'len'
intTotal = selected.len() #<- This attempt is incorrect, should actually be len(selected)


Solution

  • Just wanted to say thanks for this question - I had the same type of problem, so I wanted to document it, as it was even trickier to track down than the original question; and I would have had a hard time tracking an answer down without this question and the comments.

    Overall, I'd say the answer is, as @Jongware hinted in comments, that a QList in the C++ API automatically gets converted to a plain list in the Python API - the problem being is that this is not clearly stated in the documentation.

    Here was my particular problem: from a widget resembling QComboBox, I tried to inspect the selectionChanged event of its QCompleter popup model by:

    completer.popup().selectionModel().selectionChanged.connect(self.on_selection_changed)
    # ...
    
    def on_selection_changed(self, selected, deselected):
        print(f"on_selection_changed {selected=}")
    

    In https://doc.qt.io/qt-5/qitemselectionmodel.html#selectionChanged, for the C++ API, it is noted the signature is void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); and indeed, in the printout I got

    on_selection_changed selected=<PyQt5.QtCore.QItemSelection object at 0x000001acc52dbdf0> 
    

    So far, so good. Now, in https://doc.qt.io/qtforpython-5/PySide2/QtCore/QItemSelection.html for the Python API, it is stated

    [...] A QItemSelection is basically a list of selection ranges, see QItemSelectionRange [...]

    ... which is taken verbatim from the C++ documentation at https://doc.qt.io/qt-5/qitemselection.html (which however additionally notes "Inherits: QList")

    Also, the Python documentation notes that there are methods:

    However, when I tried to print {selected.length()=} I got:

    AttributeError: 'QItemSelection' object has no attribute 'length'
    

    ... which is even worse of an error message than "'list' object has no attribute 'length'", because it does not even imply any conversion to a plain Python list object, but instead it implies that the original C++ object has no such method, which is clearly in conflict with the documentation.

    However, once I got the hint that a QList in C++ API is automatically converted to a list in Python API, I can now confirm that for me, in Python, len(selected) worked instead of selected.length(); and selected[0] worked instead of selected.at(0).

    My guess is that the PyQt5 documentation was automatically generated from the C++ documentation, but somehow did not take into account the automatic conversion of QList to list, and therefore automatically included the C++ methods in the PyQt5 documentation, even if they do not belong there really.