I have a pandas dataframe (df) that is displayed in QTableView using TableModel. I also have QTextEditor, that should do some math operations with the df dataframe used in TableModel and should display data set name (string variable) and below the modified dataframe in a QTextEdit. For this I have created QTextEditModel. Users can change values in QTableView and QTextEdit view should be updated correspondingly. Users can not input data in QTextEdit. As QTextEditModel should monitor the dataChanged/layoutChanged signals from TableModel, I initialised the TableModel in QTextEditModel as to me it looks the easiest way to arrange such monitoring (maybe it is wrong). I managed to get it working partially.
I have several questions:
- I don't know how to display the pandas dataframe row by row in
QTextEditusingQWidgetMapper. Right now it displays all data in one row. Current display:
Desired output in QTextEdit:
- In
TextEditModelwhen I monitor data changes inTableModelthe following line in the code:
self.table1ViewModel.dataChanged.connect(lambda: self.setData(self.index_value, self.table1ViewModel.df.copy()))
I connected signal to setData and I need to provide value of QModelIndex, but I don't understand how can I get QModelIndex value here. I did some cheating with saving QModelIndex in index_value variable during the initialising the Models.
- How can I initialise the
self.dfdataframe inTextEditModelin way that it always will be updated as soon as the dataframe inTableModelchanges? Right now I'm making a copy of dataframe inmodify_data_framemethod, which doesn't look correct way to do so.
Full code:
from PySide6.QtCore import QAbstractTableModel, Qt
from PySide6.QtWidgets import QWidget, QTableView, QTextEdit, QHBoxLayout, QApplication, QDataWidgetMapper
import pandas as pd
import sys
class TableModel(QAbstractTableModel):
def __init__(self, df):
super(TableModel, self).__init__()
self.df = df
def data(self, index, role):
if index.isValid():
row = index.row()
col = index.column()
if role == Qt.DisplayRole:
value = self.df.iloc[row, col]
return value
elif role == Qt.EditRole:
value = self.df.iloc[row, col]
return value
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
row = index.row()
col = index.column()
self.df.iloc[row, col]=value
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def rowCount(self, index):
return self.df.shape[0]
def columnCount(self, index):
return self.df.shape[1]
class TextEditModel(QAbstractTableModel):
def __init__(self, df):
super(TextEditModel, self).__init__()
self.table1ViewModel = TableModel(df)
self.df = self.modify_data_frame(self.table1ViewModel.df)
self.index_value = None
self.table1ViewModel.dataChanged.connect(lambda: self.setData(self.index_value, self.modify_data_frame(self.table1ViewModel.df)))
# self.table1ViewModel.layoutChanged.connect(lambda: self.setData(self.index_value, self.table1ViewModel.df.copy()))
def data(self, index, role):
if index.isValid():
row = index.row()
col = index.column()
if role == Qt.EditRole:
self.index_value = index
name = "data set"
value = name + '\n' + self.df.to_string()
return value
def setData(self, index, df, role=Qt.EditRole):
if role == Qt.EditRole:
print("!")
print(type(df))
name = "data set"
# df["number"] = df["number"].astype(int) + 1
# self.df = df.copy()
# df["number"] = df["number"].astype(str)
df = name + '\n' + df.to_string()
print(df)
print(type(df))
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return Qt.ItemIsEnabled
def rowCount(self, index):
return self.df.shape[0]
def columnCount(self, index):
return self.df.shape[1]
def modify_data_frame(self, df):
print("!!!")
self.df = df.copy()
print(self.df)
self.df["number"] = self.df["number"].astype(int) + 1
self.df["number"] = self.df["number"].astype(str)
return self.df
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
df = pd.DataFrame({"name":["a", "b", "c"], "number": ["1", "2", "3"]})
self.table1View = QTableView()
self.textEdit = QTextEdit()
layout = QHBoxLayout(self)
layout.addWidget(self.table1View)
layout.addWidget(self.textEdit)
model = TextEditModel(df)
# self.textEdit.setModel(model)
self.table1View.setModel(model.table1ViewModel)
self.mapper = QDataWidgetMapper(self)
self.mapper.setModel(model)
self.mapper.addMapping(self.textEdit, 0)
self.mapper.toFirst()
app = QApplication(sys.argv)
mywindow = MainWidget()
mywindow.show()
sys.exit(app.exec())

TextEditModel? If you're only using it to "verbosely display" the dataframe, then it's quite useless. Also, QDataWidgetMapper is intended to display a row (or column if the orientation is vertical), since you actually need the whole model, then it's not what you need.