How can I enable editing on a QTableView in PyQt6?

Modifying your model to allow editing of your data source
Heads up! You've already completed this tutorial.

In the Model-Views course, we covered Displaying Tabular Data in Qt ModelViews. This takes a data source, such as a list of lists, a NumPy array, or a Pandas DataFrame, and displays it in a QTableView. But often, displaying is just the first step—you also want your users to be able to add and edit the table, updating the underlying data object.

Reader Vic T asked:

I have been trying for a few days to get edit mode to work with a QTableView using Pandas for the model via QAbstractTableModel. Having searched all over the internet although, I found suggestions to implement the flags() method, but it doesn't seem to work.

This is correct. You need to implement the flags() method on your model to inform Qt that your model supports editing. To do this, your method needs to return the Qt.ItemFlag.ItemIsEditable flag, which you or together (using the pipe | character) with the other flags.

In this guide, you'll learn the three steps required to make a QTableView editable in PyQt6: implementing flags(), setData(), and updating data(). We'll cover complete working examples using Python lists, Pandas DataFrames, and NumPy arrays.

Implement flags() to enable editing on QTableView

The first step to making your QTableView editable is to return the ItemIsEditable flag from your model's flags() method. Without this flag, double-clicking a cell in the table view won't open an editor widget:

python
    def flags(self, index):
        return (
            Qt.ItemFlag.ItemIsSelectable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsEditable
        )

However, to get the editing working, you also need to implement a setData() method. This method is the model's interface between Qt and your data object. It takes care of making the changes to the data.

Remember, Qt views don't know anything about your data beyond what you tell them via the model. Likewise, they also don't know how to update your list, NumPy array, or DataFrame objects with the new data that has been provided. You need to handle that yourself!

Implement setData() to write changes to the data source

Below are some example setData() methods for lists of lists, NumPy, and Pandas. The only difference between each implementation is how we index into the data object:

python
    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            self._data[index.row()][index.column()] = value
            return True
python
    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            self._data.iloc[index.row(),index.column()] = value
            return True
python
    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            self._data[index.row(), index.column()] = value
            return True

Notice that we first need to check whether the role is Qt.ItemDataRole.EditRole to determine if an edit is currently being made. After making the edit, we return True to confirm the data was set successfully.

Update data() to show the current value when editing

If you try the above on your model, you should be able to edit the values. However, you'll notice that when editing, it clears the current value of the cell — you have to start from an empty cell. To display the current value when editing, you need to modify the data() method to return the current value when the role is Qt.ItemDataRole.EditRole as well as when it is Qt.ItemDataRole.DisplayRole.

For example:

python
    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data[index.row()][index.column()]
                return str(value)
python
    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data.iloc[index.row(), index.column()]
                return str(value)
python
    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data[index.row(), index.column()]
                return str(value)

That's it! You should now have a properly editable QTableView in PyQt6.

Below are complete working examples for each data source type: Python lists, Pandas DataFrames, and NumPy arrays.

Editable QTableView with a Python list of lists

The following example uses a nested Python list of lists as a data source for an editable QTableView. This is the simplest approach and doesn't require any external libraries:

python
from PyQt6.QtCore import QAbstractTableModel, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView

class ListModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        # The following takes the first sub-list, and returns
        # the length (only works if all rows are an equal length)
        return len(self._data[0])

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data[index.row()][index.column()]
                return str(value)

    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            self._data[index.row()][index.column()] = value
            return True
        return False

    def flags(self, index):
        return (
            Qt.ItemFlag.ItemIsSelectable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsEditable
        )

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QTableView()

        data = [
            [1, 9, 2],
            [1, 0, -1],
            [3, 5, 2],
            [3, 3, 2],
            [5, 8, 9],
        ]

        self.model = ListModel(data)
        self.table.setModel(self.model)

        self.setCentralWidget(self.table)

app = QApplication([])
window = MainWindow()
window.show()
app.exec()

Editable QTableView with a Pandas DataFrame

The following example uses a Pandas DataFrame as the data source for an editable QTableView, adding column headings from the DataFrame column names:

python
import pandas as pd
from PyQt6.QtCore import QAbstractTableModel, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView

class PandasModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data.iloc[index.row(), index.column()]
                return str(value)

    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            self._data.iloc[index.row(), index.column()] = value
            return True
        return False

    def headerData(self, col, orientation, role):
        if (
            orientation == Qt.Orientation.Horizontal
            and role == Qt.ItemDataRole.DisplayRole
        ):
            return self._data.columns[col]

    def flags(self, index):
        return (
            Qt.ItemFlag.ItemIsSelectable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsEditable
        )

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QTableView()

        data = pd.DataFrame(
            [
                [1, 9, 2],
                [1, 0, -1],
                [3, 5, 2],
                [3, 3, 2],
                [5, 8, 9],
            ],
            columns=["A", "B", "C"],
        )

        self.model = PandasModel(data)
        self.table.setModel(self.model)

        self.setCentralWidget(self.table)

app = QApplication([])
window = MainWindow()
window.show()
app.exec()

Editable QTableView with a NumPy array

The following example uses a NumPy array as the data source for an editable QTableView. Because the array will only accept valid values (in this case, integers) when setting, we must first coerce the value to an integer before setting it on the array. If you enter something which isn't a valid integer (e.g. jdskfjdskjfndsf ), the int() call will throw a ValueError, which we catch. By returning False when this exception is thrown, we cancel the edit:

python
import numpy as np
from PyQt6.QtCore import QAbstractTableModel, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView

class NumPyModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
                value = self._data[index.row(), index.column()]
                return str(value)

    def setData(self, index, value, role):
        if role == Qt.ItemDataRole.EditRole:
            try:
                value = int(value)
            except ValueError:
                return False
            self._data[index.row(), index.column()] = value
            return True
        return False

    def flags(self, index):
        return (
            Qt.ItemFlag.ItemIsSelectable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsEditable
        )

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QTableView()

        data = np.array(
            [
                [1, 9, 2],
                [1, 0, -1],
                [3, 5, 2],
                [3, 3, 2],
                [5, 8, 9],
            ]
        )

        self.model = NumPyModel(data)
        self.table.setModel(self.model)

        self.setCentralWidget(self.table)

app = QApplication([])
window = MainWindow()
window.show()
app.exec()

Summary

To make a QTableView editable in PyQt6 using QAbstractTableModel, you need to implement three things:

  1. flags() — Return Qt.ItemFlag.ItemIsEditable to enable editing on cells.
  2. setData() — Handle writing the new value back to your data source (list, Pandas DataFrame, or NumPy array).
  3. data() with EditRole — Return the current cell value for EditRole so the editor is pre-populated.

These same principles apply whether you're using PyQt6, PyQt5, or PySide6 — only the enum paths differ slightly between frameworks.

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

PyQt/PySide Office Hours 1:1 with Martin Fitzpatrick

Save yourself time and frustration. Get one on one help with your projects. Bring issues, bugs and questions about usability to architecture and maintainability, and leave with solutions.

Book Now 60 mins ($195)

Martin Fitzpatrick

How can I enable editing on a QTableView in PyQt6? was written by Martin Fitzpatrick with contributions from Leo Well.

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.