PDA

View Full Version : QListView::selectedIndexes() comes back in the wrong order when in IconMode



chezifresh
2nd December 2011, 00:44
I'm using a QListView in icon mode but the selection is coming back in the wrong order. Is this a Qt bug or am I missing a setting on my QListView to account for this strange behavior?
Try running the code below in a python interpreter, shift select item 0 - item 40 and then print out the order of the selected items.

PyQt example:


from PyQt4 import QtGui

app = QtGui.QApplication([])
view = QtGui.QListWidget()
view.setViewMode(QtGui.QListView.IconMode)
view.setSelectionMode(QtGui.QListView.ExtendedSele ction)

for i in range(100):
view.addItem(QtGui.QListWidgetItem(str(i)))


view.show()
# select items 0-40
for item in view.selectedItems():
print item.text()



You'll get a result like this:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 16, 26, 17, 27, 18, 19, 28, 29, 20, 30, 21, 31, 22, 32, 23, 33, 34, 35, 36, 37, 38, 39, 40

If the QListView is set to ListMode then the items come back in the expected order. And in case 'the expected order' is too vague, I would expect them to come back in numeric order, the same as ListMode.

ChrisW67
2nd December 2011, 01:32
Where does the Qt documentation say anything about the order of indexes in the selection?

chezifresh
2nd December 2011, 01:42
I'd imagine its implicit. If you drag and drop something from one part of your view to another you'd expect that when you drop them, they would appear in the same order that you dragged them. Are you saying that any time you drag more than one thing in Qt the order in the drag object is non-deterministic? I doubt that was the intent.

If you drag multiple items in a list view or a tree view and drop them on the Qt example 'dropsite', they will appear in the QMimeData in the same order in which they are displayed.

ChrisW67
2nd December 2011, 02:27
Drag and drop is entirely within programmer control. The drag payload is constructed by the sender and can be structured or ordered any way the sender desires, and interpreted however the receiver likes. It is entirely determined by the programmer.

Selections are also deterministic: you always get all the indexes of selected items and if you query the same selection twice you get the same result. That they are not in the 'expected order' is not an indication of a random process.

A selection can be built up by adding single items, ranges of items, overlapping ranges, disjoint ranges, items under an arbitrary rectangular area in a view displaying the items a variable layout, selections made while the view is sorted in different orders, all merged into a compact representation of the selection. There is no single notion of the 'correct' or 'natural' order that makes sense for all: is it the order of insertion in the list, alphabetical or numerical order of the DisplayRole (there may be none), row or column major for a table, pre- or post-order for a tree, order that the user selected the fragments of the selection, order of the top-left corner of section fragments...

If you want the items in the selection sorted then you are free to sort them yourself. Alternatively, you could iterate the list in your 'expected order' and query if each index is in the selection rather than relying on the selection list being in some order.

chezifresh
2nd December 2011, 02:39
Thanks for the answer, I totally agree. I think I was a bit thrown by the fact that changing view modes in the QListWidget would change the selectedItems() order.

I fixed this entirely in 2 places. First I overloaded selectedItems:


def selectedItems(self):
indexes = self.selectionModel().selectedIndexes()
indexes.sort(cmp=lambda a, b : cmp(a.row(), b.row()))
items = [self.itemFromIndex(index) for index in indexes]
return items

def mimeData(self, items):
mimedata = QtCore.QMimeData()
ordered_items = []
for idx in range(self.count()):
item = self.item(idx)
if item in items:
ordered_items.append(item)
...


the mimeData implementation is not exactly optimized, but for some reason QListWidgetItems dont seem to have an index() method to get their model index. Otherwise I would also sort them by their rows