Understanding setCurrentIndex() and Getting File Paths from QTreeView Selections

Why setCurrentIndex() returns None and how to properly work with selected indexes
Heads up! You've already completed this tutorial.

I'm iterating over selected indexes from a QTreeView connected to a QFileSystemModel and passing each index to setCurrentIndex(). Then I try to use the return value of setCurrentIndex() to build a file path. But I get a TypeError saying NoneType is being provided. The indexes are definitely QModelIndex objects, so why does setCurrentIndex() seem to return None?

This is a common source of confusion when working with Qt's model/view classes. The answer is straightforward once you see it: setCurrentIndex() doesn't return anything. In Python terms, that means it returns None.

So when you write:

python
index = self.treeView.setCurrentIndex(item)

...the variable index will always be None, regardless of what you pass in. That None then gets passed to Path(), which causes the TypeError you're seeing.

Why setCurrentIndex() returns None

In Qt, methods that start with set are setters — they change the state of a widget but don't return a value. setCurrentIndex() tells the tree view "make this index the current one," and that's all it does. It has no return value (or in C++ terms, it returns void).

This is a consistent pattern across Qt. For example:

  • setWindowTitle() — sets the title, returns nothing
  • setText() — sets text on a label, returns nothing
  • setCurrentIndex() — sets the current index, returns nothing

The corresponding getters are the ones that return values:

  • windowTitle() — returns the current title
  • text() — returns the current text
  • currentIndex() — returns the current index

What you actually need

Looking at the original code, the goal is to get a file path from a QModelIndex so it can be used in a subprocess command. You don't need setCurrentIndex() at all for this. The QFileSystemModel can give you a file path directly from any QModelIndex using its filePath() method.

Here's the corrected approach. Instead of:

python
# Wrong: setCurrentIndex() returns None
index = self.treeView.setCurrentIndex(item)
index_path = Path(index)

You want:

python
# Correct: get the file path directly from the model
file_path = self.fileSystemModel.filePath(item)
index_path = Path(file_path)

The filePath() method on QFileSystemModel takes a QModelIndex and returns the corresponding file path as a string. That string can then be passed to Path() without any issues.

The fixed build_project method

Here's what the corrected build_project() method looks like:

python
def build_project(self, item):
    if item and item.isValid():
        file_path = self.fileSystemModel.filePath(item)
    else:
        file_path = self.fileSystemModel.filePath(self.treeView.currentIndex())

    index_path = Path(file_path)
    parent_dir = index_path.parents[0]

    out = subprocess.run(
        [
            "bash", "-c",
            f"source {self.default_dir}/cdk/opencpi-setup.sh -r "
            f"&& cd {parent_dir} "
            f"&& ocpidev build project {index_path.name}"
        ],
        stderr=subprocess.STDOUT,
        stdout=subprocess.PIPE,
    )
    self.console_output.on_update_text(out.stdout.decode())

Notice a couple of changes:

  1. We use self.fileSystemModel.filePath(item) to convert the QModelIndex into a file path string.
  2. We also check item.isValid() in addition to truthiness. A QModelIndex can exist but be invalid (for example, a default-constructed index), so this is a good safety check.

Processing multiple selections

The add_item() method that iterates over selected indexes also deserves a closer look. When you call selectedIndexes() on a QTreeView, it returns one index per column for each selected row. If your model has four columns, selecting one row gives you four indexes.

To avoid processing the same file multiple times, you can filter to only the first column:

python
def add_item(self):
    """Add Project Explorer selected assets to Working Directory."""
    indexes = self.project.treeView.selectedIndexes()
    for index in indexes:
        if index.column() == 0:  # Only process each row once
            self.project.build_project(index)

Alternatively, you can use the selection model to get just the selected rows:

python
def add_item(self):
    """Add Project Explorer selected assets to Working Directory."""
    selection_model = self.project.treeView.selectionModel()
    indexes = selection_model.selectedRows()  # One index per row, column 0
    for index in indexes:
        self.project.build_project(index)

A complete working example

Here's a self-contained example that demonstrates selecting multiple items from a QTreeView backed by a QFileSystemModel and retrieving their file paths. You can run this, select some files or folders, and click the button to see the paths printed. If you're new to PyQt6, you might want to start with creating your first window before working through this example.

python
import sys
from pathlib import Path

from PyQt6.QtWidgets import (
    QApplication,
    QAbstractItemView,
    QMainWindow,
    QPushButton,
    QTreeView,
    QTextEdit,
    QVBoxLayout,
    QWidget,
)
from PyQt6.QtGui import QFileSystemModel


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTreeView File Path Example")
        self.resize(800, 600)

        # Set up the file system model
        self.file_system_model = QFileSystemModel()
        self.file_system_model.setRootPath(str(Path.home()))

        # 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(str(Path.home()))
        )
        self.tree_view.setSelectionMode(
            QAbstractItemView.SelectionMode.ExtendedSelection
        )

        # A button to process selections
        self.process_button = QPushButton("Process Selected Items")
        self.process_button.clicked.connect(self.process_selections)

        # A text area to show output
        self.output = QTextEdit()
        self.output.setReadOnly(True)

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.tree_view)
        layout.addWidget(self.process_button)
        layout.addWidget(self.output)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def process_selections(self):
        """Get file paths from all selected rows."""
        self.output.clear()

        selection_model = self.tree_view.selectionModel()
        indexes = selection_model.selectedRows()

        if not indexes:
            self.output.append("No items selected.")
            return

        for index in indexes:
            # Get the file path directly from the model
            file_path = self.file_system_model.filePath(index)
            path = Path(file_path)

            self.output.append(f"Name: {path.name}")
            self.output.append(f"Full path: {path}")
            self.output.append(f"Parent: {path.parent}")
            self.output.append(f"Is file: {path.is_file()}")
            self.output.append("---")


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

Select one or more items in the tree (hold Ctrl or Shift to select multiples), then click the button. Each selected item's path information will appear in the text area below.

Summary

The root cause of the original error was assigning the return value of setCurrentIndex() to a variable and then trying to use it as a file path. Since setCurrentIndex() is a setter with no return value, it always gives back None in Python.

To get a file path from a QModelIndex when using QFileSystemModel, call filePath() on the model itself, passing in the index. This gives you a clean string path that you can convert to a Path object or use however you need.

This example uses a QVBoxLayout to arrange the widgets — for more on organizing your UI, see the PyQt6 layouts tutorial. If you're interested in learning more about how Qt's model/view system works, take a look at the Model View Architecture guide. And for a deeper dive into tree views specifically, see the QTreeView tutorial.

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

Packaging Python Applications with PyInstaller by Martin Fitzpatrick

This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

More info Get the book

Martin Fitzpatrick

Understanding setCurrentIndex() and Getting File Paths from QTreeView Selections 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. Martin founded PythonGUIs to provide easy to follow GUI programming tutorials to the Python community. He has written a number of popular Python books on the subject.