Синхронизировать pandas DataFrame с PyQt5 QTableView - PullRequest
0 голосов
/ 09 июля 2020

У меня есть редактируемый QTableView, который считывает значения из pandas DataFrame.

enter image description here

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.

введите описание изображения здесь

Я видел, что на форуме есть похожие вопросы, но поскольку 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 Ответ

0 голосов
/ 14 июля 2020

Решение настолько простое, что я его не понимаю. Если бы кто-нибудь мог просто объяснить мне, что происходит.

Я указываю на изменения, которые я сделал в 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)

Во-первых, при запуске приложения:

enter image description here

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

Теперь мне нужно работать с DataFrame, чтобы значения строк были согласованными, но это другой вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...