问题
I have an editable QTableView
which reads the values from a pandas
DataFrame
.
What I'm looking for is that when I change the value of one cell, the pandas
DataFrame
synchronizes automatically. The aim is to continue operating with the updated DataFrame
, for example, to update the values of the other rows of the DataFrame
and refresh the QTableView
.
I've seen that there are similar questions in the forum, but since MVC
is new to me and I'm not a professional programmer, I find it hard to understand the solutions and I haven't managed to implement them in my code.
I paste here the code I have so far.
run.py
import pandas as pd
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView
from cnt_init_values import CntInitialValues
from tablemodel_editable import PandasModel
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
# Packages.
# ---------
self.pd = pd
# Instancies of class.
# --------------------
self.cnt_init_val = CntInitialValues(self)
# Objects of the aplication.
# --------------------------
self.df_table = pd.DataFrame()
# PyQt5 objects.
# --------------
self.tableview = QTableView()
# Add initial values to QTableView and show them.
# -----------------------------------------------
self.df_table = self.cnt_init_val.dataframe_initial()
self.cnt_init_val.initial_values(self.df_table)
# PyQt5 outputs.
# ==============
def table_output(self, df):
print(self.df_table)
model = PandasModel(df)
self.tableview.setModel(model)
self.tableview.resize(350, 250)
self.tableview.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
myapp = MainWindow()
sys.exit(app.exec_())
cnt_init_values.py
from initialvalues import InitialValues
class CntInitialValues:
"""Controller"""
def __init__(self, window):
self.w = window
def dataframe_initial(self):
d_initial_values = InitialValues.d_INITIAL_VALUES
df_initial = self.w.pd.DataFrame(d_initial_values)
return df_initial
def initial_values(self, df):
self.w.table_output(df)
initialvalues.py
class InitialValues:
vals = [float(i) for i in range(6)]
d_INITIAL_VALUES = {
'x': vals,
'x²': [val**2 for val in vals],
'x³': [val**3 for val in vals]
}
tablemodel_editable.py
from PyQt5.QtCore import QAbstractTableModel, Qt
class PandasModel(QAbstractTableModel):
def __init__(self, data):
QAbstractTableModel.__init__(self)
self._data = data
def rowCount(self, parent=None):
return self._data.shape[0]
def columnCount(self, parnet=None):
return self._data.shape[1]
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
column_count = self.columnCount()
for column in range(0, column_count):
if (index.column() == column and role == Qt.TextAlignmentRole):
return Qt.AlignHCenter | Qt.AlignVCenter
return None
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[col]
return None
def setData(self, index, value, role):
if not index.isValid():
return False
if role != Qt.EditRole:
return False
row = index.row()
if row < 0 or row >= len(self._data.values):
return False
column = index.column()
if column < 0 or column >= self._data.columns.size:
return False
self._data.iloc[row][column] = value
self.dataChanged.emit(index, index)
return True
def flags(self, index):
flags = super(self.__class__,self).flags(index)
flags |= Qt.ItemIsEditable
flags |= Qt.ItemIsSelectable
flags |= Qt.ItemIsEnabled
flags |= Qt.ItemIsDragEnabled
flags |= Qt.ItemIsDropEnabled
return flags
回答1:
The solution is so simple that I don't understand it. If someone could just explain to me what's going on.
I point out the changes I've made in run.py
:
run.py edited
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
uic.loadUi('table.ui', self) # --- Loading from Qt Designer. ---
# Packages.
# ---------
self.pd = pd
# Instancies of class.
# --------------------
self.cnt_init_val = CntInitialValues(self)
# Attributes of the instance.
# ---------------------------
self.df_table = pd.DataFrame()
self.model = '' # --- Instance variable for model. ---
# PyQt5 objects.
# --------------
self.tableview = self.tableView
self.btn_print_df = self.pushButtonPrintDataFrame # --- pushButton to print DataFrame. ---
# Add initial values to QTableView and show them.
# -----------------------------------------------
self.df_table = self.cnt_init_val.dataframe_initial()
self.cnt_init_val.initial_values(self.df_table)
# Events.
# -------
self.btn_print_df.clicked.connect(self.dataframe_output) # --- pushButton prints DataFrame. ---
# PyQt5 outputs.
# ==============
def table_output(self, df):
print('\nInitial DataFrame:')
print(self.df_table)
self.model = PandasModel(df)
self.tableview.setModel(self.model)
self.tableview.show()
# Terminal outputs. # --- DaraFrame output. ---
# =================
def dataframe_output(self):
print('\nUpdated DataFrame:')
print(self.df_table)
Firstly, when the app is executed:
Initial DataFrame:
x x² x³
0 0.0 0.0 0.0
1 1.0 1.0 1.0
2 2.0 4.0 8.0
3 3.0 9.0 27.0
4 4.0 16.0 64.0
5 5.0 25.0 125.0
After changing some values in the table:
Updated DataFrame:
x x² x³
0 999.0 0.0 0.0
1 1.0 1.0 1.0
2 2.0 4.0 8.0
3 3.0 9.0 27.0
4 4.0 16.0 64.0
5 5.0 25.0 1111.0
Now I have to work with the DataFrame to make the values of the rows consistent, but that's another question.
来源:https://stackoverflow.com/questions/62812459/synchronize-pandas-dataframe-with-pyqt5-qtableview