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)
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:
def length ()
https://doc.qt.io/qtforpython-5/PySide2/QtCore/QItemSelection.html#PySide2.QtCore.PySide2.QtCore.QItemSelection.lengthdef at (i)
https://doc.qt.io/qtforpython-5/PySide2/QtCore/QItemSelection.html#PySide2.QtCore.PySide2.QtCore.QItemSelection.atHowever, 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.