我有一个 pandas 数据框 (
df
),它使用 QTableView
显示在 TableModel
中。我还有 QTextEditor
,它应该使用 df
中使用的 TableModel
数据框进行一些数学运算,并且应该在 QTextEdit
中显示数据集名称(字符串变量)和修改后的数据框下方。为此,我创建了QTextEditModel
。用户可以更改 QTableView
中的值,并且 QTextEdit
视图应相应更新。用户无法在QTextEdit
中输入数据。由于QTextEditModel
应该监视来自dataChanged/layoutChanged
的TableModel
信号,我在TableModel
中初始化了QTextEditModel
,对我来说,这看起来是安排此类监视的最简单方法(也许是错误的)。我设法让它部分工作。
我有几个问题:
TextEditModel
中,当我监视代码中TableModel
中的以下行的数据变化时:self.table1ViewModel.dataChanged.connect(lambda: self.setData(self.index_value, self.table1ViewModel.df.copy()))
我将信号连接到
setData
,我需要提供QModelIndex
的值,但我不明白如何在这里获得QModelIndex
值。在初始化模型期间,我通过将 QModelIndex
保存在 index_value
变量中做了一些作弊。
self.df
中的 TextEditModel
数据框,以便一旦 TableModel
中的数据框发生变化,它总是会更新?现在我正在用 modify_data_frame
方法制作数据帧的副本,这看起来不是正确的方法。完整代码:
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())
以防万一有人遇到同样的问题。我用这种方法解决了这个问题:
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())