I'm using
treeView.selectedIndexes()to get selected items from a QTreeView, but each item is returned multiple times. Why does Qt seem to multiply the results, and how can I fix it?
If you've been working with QTreeView and a model like QFileSystemModel, you may have noticed something puzzling: when you select a single row in the tree, selectedIndexes() gives you back several QModelIndex objects instead of just one. And if you select multiple rows, the list grows even faster than you'd expect.
This behavior makes perfect sense once you understand what's going on under the hood. Let's walk through it.
QTreeView Is a Two-Dimensional View
A QTreeView displays data from a model, and that model can have multiple columns. QFileSystemModel, for example, has four columns by default:
| Column | Content |
|---|---|
| 0 | Name |
| 1 | Size |
| 2 | Type |
| 3 | Date Modified |
When you select a row in the tree view, the selection covers every column in that row. So selectedIndexes() returns one QModelIndex per column, per selected row.
If your model has 4 columns and you select 2 rows, selectedIndexes() returns 8 index objects — not 2.
Each of those QModelIndex objects has a different column number, which is why they have different memory addresses and why a simple not in check doesn't filter them out. They genuinely are different indexes; they just happen to refer to the same row.
Filtering to a Single Column
The most straightforward way to get one result per selected row is to filter the indexes by column. Column 0 is usually the one you care about (it contains the item name or primary data):
indexes = self.treeView.selectedIndexes()
indexes = [index for index in indexes if index.column() == 0]
This gives you exactly one QModelIndex per selected row. You can then use each index to retrieve the data you need from the model — for example, a file path:
for index in indexes:
path = self.file_system_model.filePath(index)
print(path)
Hiding Columns You Don't Need
If you're using QFileSystemModel and only want to show file names (not size, type, or date), you can hide the extra columns entirely. Do this after setting the model on the tree view:
self.treeView.setModel(self.file_system_model)
self.treeView.hideColumn(1) # Size
self.treeView.hideColumn(2) # Type
self.treeView.hideColumn(3) # Date Modified
With only one visible column, selectedIndexes() returns one index per selected row. This solves the duplication problem and simplifies your view at the same time.
Note that hideColumn() doesn't remove the columns from the model — it just hides them in the view. The data is still there if you need it.
A Complete Working Example
Here's a full example that sets up a QTreeView with QFileSystemModel, hides the extra columns, enables multi-selection, and prints the selected file paths when you click a button:
import sys
from pathlib import Path
from PyQt6.QtWidgets import (
QApplication,
QMainWindow,
QTreeView,
QVBoxLayout,
QWidget,
QPushButton,
QAbstractItemView,
QTextEdit,
)
from PyQt6.QtCore import QDir
from PyQt6.QtWidgets import QFileSystemModel
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("QTreeView Selected Indexes")
self.resize(600, 500)
# Set up the file system model.
self.file_system_model = QFileSystemModel()
self.file_system_model.setRootPath(QDir.homePath())
# Set up the tree view.
self.tree_view = QTreeView()
self.tree_view.setModel(self.file_system_model)
self.tree_view.setRootIndex(
self.file_system_model.index(QDir.homePath())
)
# Hide the extra columns (Size, Type, Date Modified).
self.tree_view.hideColumn(1)
self.tree_view.hideColumn(2)
self.tree_view.hideColumn(3)
# Enable multi-selection.
self.tree_view.setSelectionMode(
QAbstractItemView.SelectionMode.ExtendedSelection
)
# A button to print selected items.
self.button = QPushButton("Print Selected Items")
self.button.clicked.connect(self.print_selected_items)
# A text area to display results.
self.output = QTextEdit()
self.output.setReadOnly(True)
# Layout.
layout = QVBoxLayout()
layout.addWidget(self.tree_view)
layout.addWidget(self.button)
layout.addWidget(self.output)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def print_selected_items(self):
indexes = self.tree_view.selectedIndexes()
# If you haven't hidden columns, filter to column 0 only:
# indexes = [i for i in indexes if i.column() == 0]
self.output.clear()
self.output.append(
f"selectedIndexes() returned {len(indexes)} index(es):\n"
)
for index in indexes:
path = self.file_system_model.filePath(index)
self.output.append(path)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Run this, select a few files or folders (hold Ctrl or Shift to multi-select), and click the button. You'll see exactly one path per selected item.
When You Can't Hide Columns
Sometimes you need all the columns visible — maybe you want users to see file sizes and dates. In that case, keep the columns visible and filter in your code instead:
def print_selected_items(self):
indexes = self.tree_view.selectedIndexes()
# Keep only column 0 to get one index per row.
indexes = [index for index in indexes if index.column() == 0]
for index in indexes:
path = self.file_system_model.filePath(index)
print(path)
This approach works regardless of how many columns are visible.
Summary
selectedIndexes() returns one QModelIndex for every column in every selected row. With a 4-column model like QFileSystemModel, that means 4 indexes per row. You can handle this by:
- Hiding columns you don't need with
hideColumn(), so the selection only covers one column. - Filtering by column in your code with
[i for i in indexes if i.column() == 0].
Both approaches give you a clean, deduplicated list of selected items. Choose whichever fits your application best.
If you're working with table views and models more broadly, you may also find our guide on the ModelView architecture in PyQt6 helpful for understanding how models, views, and indexes interact. For more on using QTreeView with custom tree models, take a look at our QTreeView tutorial. And if you're displaying tabular data with QTableView instead, our tutorial on QTableView with numpy and pandas covers similar model concepts in that context.
PyQt/PySide 1:1 Coaching with Martin Fitzpatrick
Save yourself time and frustration. Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.