treeView.selectedIndexes() returns same item multiple times

Heads up! You've already completed this tutorial.

Cody_Jackson | 2021-03-11 18:42:16 UTC | #1

Trying to get the indexes from multi-selection of items in a treeView. But each item is returned multiple times. Screenshot from 2021-03-11 08-35-28|333x500

Looking at the raw returned items, each one has a different memory address, so it's not simply calling the same object multiple times, but creating multiple instances and returning each one. Screenshot from 2021-03-11 08-37-14|389x500

I've tried to use a for loop to try and eliminate all but one of the responses, but it doesn't work.

python
        def get_items(self):
             indexes = self.treeView.selectedIndexes()
             seen = []
             for item in indexes:
                if item not in seen:
                    self.console.on_update_text(str(item))
                    self.console.on_update_text("\n")
                    seen.append(item)

(Sorry. I can't seem to get the code tags to actually present the code correctly.)


Cody_Jackson | 2021-03-11 19:12:30 UTC | #2

I think I might have solved it.

The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]

python
    def get_items(self):
        indexes = self.treeView.selectedIndexes()
        items = []
        for index in indexes:
            item = self.fileSystemModel.filePath(index)
            items.append(item)

        seen = []
        for item in items:
            if item not in seen:
                seen.append(item)
                self.console.on_update_text(item)
                self.console.on_update_text("\n")

Using this results in the following: Screenshot from 2021-03-11 09-13-22|483x500

If someone has a better way of doing this, please let me know. Also, if someone can tell me why Qt seems to quadruple the results of selectedIndexes(), I would appreciate it.


martin | 2021-03-11 18:47:18 UTC | #3

Hey @Cody_Jackson based on this Qt forum post on a multi-column treeview .selectedIndexes() will return an index for each column.

I only see 2 column in your screenshot, but perhaps you have some hidden?


Cody_Jackson | 2021-03-11 18:56:08 UTC | #4

@martin I didn't even realize that. Yes, I have the default columns created by a File Explorer example I found. So I need to figure out how to set only one column, as I don't need the other columns.

Thank you. That really helps a lot.

FWIW, here is the File Explorer code: class ProjectExplorerTree(QTreeView): """Project Explorer box"""

python
def __init__(self, tree: QTreeView, console) -> None:
    super().__init__()
    self.treeView = tree
    self.default_dir = f"{str(Path.home())}/opencpi"  # May have to change this later
    self.console_output = console

    self.fileSystemModel = QFileSystemModel(self.treeView)
    self.fileSystemModel.setReadOnly(False)
    root: Optional[QModelIndex] = self.fileSystemModel.setRootPath(f"{self.default_dir}/projects")
    self.treeView.setModel(self.fileSystemModel)
    self.treeView.setRootIndex(root)
    self.treeView.setSelectionMode(self.treeView.ExtendedSelection)
    self.treeView.setRootIsDecorated(True)
    self.treeView.setAlternatingRowColors(True)
    self.treeView.setDragEnabled(True)  # Allow items to be dragged from this panel

Cody_Jackson | 2021-03-11 19:13:47 UTC | #5

Had to add self.treeView.hideColumn(int) to the init() method.

python
    self.treeView.setModel(self.fileSystemModel)
    self.treeView.hideColumn(1)
    self.treeView.hideColumn(2)
    self.treeView.hideColumn(3)
    self.treeView.setRootIndex(root)

martin | 2021-03-11 19:13:32 UTC | #6

Great! This has caught me out before, don't really think of the treeview as 2d model.

If you can't limit the selection to a single column on the view/model, you could filter out a single column in your method e.g.

python
indexes = [i for i in indexes if i.column() == 0]
# or
indexes = filter(lambda i: i.column() == 0, indexes)

Cody_Jackson | 2021-03-11 19:19:42 UTC | #7

I think what's most painful with Qt is that there are so many places to look due to inheritance, and there is some naming inconsistency between the classes, e.g. headers vs. columns.

For example, to add a column, you use setHeader(), or maybe setHeaderData(), but to remove columns, you use hideColumn(), but there's also setHeaderHidden().

For me, it's a lot of trial and error, plus hours of searching for tutorials.


Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

treeView.selectedIndexes() returns same item multiple times was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.