将 pandas DataFrame 与 PyQt5 QTableView 同步

Posted

技术标签:

【中文标题】将 pandas DataFrame 与 PyQt5 QTableView 同步【英文标题】:Synchronize pandas DataFrame with PyQt5 QTableView 【发布时间】:2020-07-09 10:06:31 【问题描述】:

我有一个可编辑的 QTableView,它从 pandas DataFrame 读取值。

我正在寻找的是,当我更改一个单元格的值时,pandas DataFrame 会自动同步。目的是继续使用更新后的DataFrame 进行操作,例如更新DataFrame 的其他行的值并刷新QTableView

我在论坛上看到有类似的问题,但是由于MVC对我来说是新的并且我不是专业程序员,我发现解决方案很难理解并且我没有设法实现它们在我的代码中。

我在这里粘贴我目前的代码。

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】:

解决方案太简单了,我不明白。如果有人可以向我解释发生了什么。

我指出我在run.py中所做的更改:

run.py 已编辑

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)

首先,当应用程序执行时:

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

更改表格中的一些值后:

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

现在我必须使用 DataFrame 来使行的值保持一致,但这是另一个问题。

【讨论】:

以上是关于将 pandas DataFrame 与 PyQt5 QTableView 同步的主要内容,如果未能解决你的问题,请参考以下文章

2020-03-12 PYQT5界面使用QtableView展示Pandas.DataFrame

用PyQt5来即时显示pandas Dataframe的数据,附qdarkstyle黑夜主题样式(美美哒的黑夜主题)

将 Pandas Multiindexed DataFrame 与 Singleindexed Pandas DataFrame 合并

Pandas:如何将 MultiIndex DataFrame 与单个索引 DataFrame 连接,以及自定义排序

将 model.predict() 的结果与原始 pandas DataFrame 合并?

pandas将Series转成DataFrame