Accessing a TableView from a Second QML File with PySide2

How to connect a Python QAbstractTableModel to a TableView loaded from a separate QML file
Heads up! You've already completed this tutorial.

If you've split your QML interface across multiple files — which is a great habit for keeping things organized — you may have run into a frustrating problem: you can't seem to connect your Python model to a TableView that lives inside a QML file loaded by a Loader. The TableView is right there in your QML, you've written a perfectly good QAbstractTableModel in Python, but the two just won't talk to each other.

Let's walk through why this happens and, more importantly, how to fix it.

Understanding the Problem

Here's the typical setup. You have a main.qml that uses a Loader to bring in another QML file:

qml
// main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 800
    height: 600

    Loader {
        id: pagesOther
        anchors.fill: parent
        source: Qt.resolvedUrl("pages/other.qml")
    }
}

And then inside other.qml, you have a TableView:

qml
// pages/other.qml
import QtQuick 2.15

TableView {
    id: activityTable
    anchors.fill: parent
    columnSpacing: 1
    rowSpacing: 1
    clip: true

    model: ActivityTableModel {}

    delegate: Rectangle {
        implicitWidth: 100
        implicitHeight: 50
        Text {
            text: display
        }
    }
}

And on the Python side, you're loading everything like this:

python
if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = MainWindow()
    engine.rootContext().setContextProperty("backend", main)
    engine.load(os.path.join(os.path.dirname(__file__), "qml/main.qml"))

The problem? QML doesn't know what ActivityTableModel is. You've defined the model class in Python, but you haven't told QML how to find it. Writing ActivityTableModel {} in QML as if it were a built-in QML type won't work unless you've explicitly registered that Python type with the QML engine.

There are two clean approaches to solve this, and we'll cover both.

Approach 1: Use a Context Property (Simplest)

The most straightforward solution — and the one that works naturally across Loader boundaries — is to set your model as a context property on the engine. Context properties are available to all QML files loaded by that engine, including files pulled in by a Loader. This is key.

Define Your Model in Python

First, let's create a basic QAbstractTableModel subclass. If you haven't written one before, don't worry — the structure is quite formulaic. You just need to tell Qt how many rows and columns you have, and what data to return for each cell. For a more thorough introduction to the model/view architecture, see our PySide2 ModelView Architecture tutorial.

python
import sys
import os
from PySide2.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine


class ActivityTableModel(QAbstractTableModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        # Some sample data — a list of lists
        self._data = [
            ["Running", "30 min", "Morning"],
            ["Swimming", "45 min", "Afternoon"],
            ["Cycling", "60 min", "Evening"],
        ]
        self._headers = ["Activity", "Duration", "Time of Day"]

    def rowCount(self, parent=QModelIndex()):
        return len(self._data)

    def columnCount(self, parent=QModelIndex()):
        return len(self._data[0]) if self._data else 0

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]
        return None

This gives us a simple 3x3 table of activities. The data() method returns values for the display role, which is what our QML delegate uses when it references display.

Set the Model as a Context Property

Now, instead of trying to instantiate ActivityTableModel from within QML, we create it in Python and hand it to the QML engine:

python
if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # Create the model instance in Python
    activity_model = ActivityTableModel()

    # Make it available to ALL QML files as "activityModel"
    engine.rootContext().setContextProperty("activityModel", activity_model)

    engine.load(os.path.join(os.path.dirname(__file__), "qml/main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

The line setContextProperty("activityModel", activity_model) makes the Python object available everywhere in QML under the name activityModel.

Use the Context Property in QML

Now update other.qml to use the context property instead of trying to create the model as a QML type:

qml
// pages/other.qml
import QtQuick 2.15

TableView {
    id: activityTable
    anchors.fill: parent
    columnSpacing: 1
    rowSpacing: 1
    clip: true

    model: activityModel  // <-- use the context property name

    delegate: Rectangle {
        implicitWidth: 100
        implicitHeight: 50
        Text {
            text: display
        }
    }
}

Notice the change: instead of model: ActivityTableModel {} (which tries to construct a new QML type), we now have model: activityModel (which references the Python object we injected).

That's it. The Loader in main.qml doesn't need to change at all:

qml
// main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 800
    height: 600

    Loader {
        id: pagesOther
        anchors.fill: parent
        source: Qt.resolvedUrl("pages/other.qml")
    }
}

Because context properties are global to the engine's root context, the loaded QML file sees activityModel just fine.

Approach 2: Register the Python Type with QML

If you'd prefer to instantiate the model directly in QML — using the ActivityTableModel {} syntax from the original code — you can do that too. You just need to register the Python class as a QML type first.

Register the Type Before Loading QML

In your Python entry point, use qmlRegisterType to make your class available as a QML type:

python
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType

# ... (ActivityTableModel class definition as before) ...

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # Register the Python class as a QML type
    qmlRegisterType(ActivityTableModel, "MyModels", 1, 0, "ActivityTableModel")

    engine.load(os.path.join(os.path.dirname(__file__), "qml/main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec_())

The arguments to qmlRegisterType are:

Argument Meaning
ActivityTableModel The Python class to register
"MyModels" The QML import module name (you choose this)
1 Major version
0 Minor version
"ActivityTableModel" The name to use in QML

Import and Use the Type in QML

Now in other.qml, import your custom module and use the type directly:

qml
// pages/other.qml
import QtQuick 2.15
import MyModels 1.0  // <-- import your registered module

TableView {
    id: activityTable
    anchors.fill: parent
    columnSpacing: 1
    rowSpacing: 1
    clip: true

    model: ActivityTableModel {}  // <-- now QML knows what this is

    delegate: Rectangle {
        implicitWidth: 100
        implicitHeight: 50
        Text {
            text: display
        }
    }
}

This approach creates a new instance of ActivityTableModel inside QML. It's useful when you want QML to own the lifecycle of the model, or when you might use the same model type in multiple places with different data.

Which Approach Should You Use?

Both approaches work well, but they serve slightly different purposes:

Context Property Registered Type
Model created in Python QML
Best when Python needs to control or update the model QML manages its own data
Number of instances One shared instance As many as you create in QML
Loader-friendly Yes, automatically Yes, with the import

For most cases where your Python backend is feeding data into the table — which is the common scenario — Approach 1 (context properties) is simpler and gives you easy access to the model from both Python and QML.

If you're building reusable components and want QML to be more self-contained, Approach 2 (registered types) is the cleaner architectural choice.

A Common Pitfall: Trying to Find Objects by ID

You might be tempted to try to "find" the TableView by its id from Python and set its model that way. Something like searching the QML object tree for an item named activityTable. While this can work in simple cases using findChild(), it's fragile and breaks easily with Loader because the loaded content may not exist yet when you try to access it. The Loader creates its content asynchronously (or at least independently), so the child object might not be in the tree when your Python code runs.

The two approaches above avoid this problem entirely. You're not searching for objects — you're either injecting a model that QML picks up automatically, or you're giving QML the ability to create the model itself. Both are more robust and idiomatic.

Summary

When you split your QML across multiple files and use a Loader, components inside the loaded file can't see Python types unless you've explicitly made them available. The two reliable ways to do this are:

  1. Set a context property with engine.rootContext().setContextProperty() — this makes a Python object globally available to all QML loaded by that engine.
  2. Register a Python type with qmlRegisterType() — this lets QML create instances of your Python class directly, as long as you import the module.

Either way, your TableView will be able to use the model, regardless of which QML file it lives in or how deeply it's nested inside Loader components.

If you're looking to go further with QML and PySide2, check out our tutorials on building QtQuick/QML applications with Python and QML animations and transformations. For working with TableView models using numpy and pandas, see PySide TableView with numpy & pandas.

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

Bring Your PyQt/PySide Application to Market

Stuck in development hell? I'll help you get your project focused, finished and released. Benefit from years of practical experience releasing software with Python.

Find out More

Martin Fitzpatrick

Accessing a TableView from a Second QML File with PySide2 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.