Villa wrote
I need advice on how to make a
QTableView
editable. I created a UI containing a QTableView and set it to be editable with double click. Now I am able to edit a number but as soon as I press Enter the number returns to the original value without updating the dataframe.
In the model views course we covered displaying tabular data in Qt5 ModelViews. We implemented the data()
method to return our values to the view for display. Qt doesn't understand your data structure so can't display it without your help, and the same goes for writing the edited data back to the data structure.
Because of this, making a QTableView
editable is handled on the model. To support editing you now need to implement a setData()
method. This works like the data()
method, but should accept an index & value and use these to update the dataframe data.
You also need to implement the flags()
method to add the Qt.ItemIsEditable
flag, making elements editable.
If you only want certain cells to be editable, you can decide when to apply the editable flag based on the index in the flags()
method.
A full working example for PyQt5 is below, using a Pandas dataframe:
Purchasing Power Parity
Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
import pandas as pd
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def flags(self, index):
if not index.isValid():
return Qt.ItemIsEnabled
return super().flags(index) | Qt.ItemIsEditable # add editable flag.
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
def setData(self, index, value, role):
if role == Qt.EditRole:
# Set the value into the frame.
self._data.iloc[index.row(), index.column()] = value
return True
return False
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
data = pd.DataFrame([
[1, 9, 2],
[1, 0, -1],
[3, 5, 2],
[3, 3, 2],
[5, 8, 9],
], columns = ['A', 'B', 'C'], index=['Row 1', 'Row 2', 'Row 3', 'Row 4', 'Row 5'])
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
app=QtWidgets.QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()
The setData
method should return True
if the value was successfully updated, or False
if not. If False
is returned the value displayed will be reverted to the previous value.
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.