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:
QTextEdit
using QWidgetMapper
. Right now it displays all data in one row.
Current display:
Desired output in QTextEdit:TextEditModel
when I monitor data changes in TableModel
the 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.
self.df
dataframe in TextEditModel
in way that it always will be updated as soon as the dataframe in TableModel
changes? Right now I'm making a copy of dataframe in modify_data_frame
method, 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())
Just in case if someone will come across the same problem. I solved this in this way:
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 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 = TableModel(df)
self.table1View.setModel(model)
model.dataChanged.connect(lambda: self.display_data(self.modify_data_frame(model.df)))
model.layoutChanged.connect(lambda: self.display_data(self.modify_data_frame(model.df)))
self.display_data(self.modify_data_frame(model.df))
def modify_data_frame(self, df):
self.df = df.copy()
self.df["number"] = self.df["number"].astype(int) + 1
self.df["number"] = self.df["number"].astype(str)
return self.df
def display_data(self, df):
self.textEdit.clear()
self.textEdit.append(df.to_string())
app = QApplication(sys.argv)
mywindow = MainWidget()
mywindow.show()
sys.exit(app.exec())