【问题标题】:How to display a Pandas data frame with PyQt5/PySide2如何使用 PyQt5/PySide2 显示 Pandas 数据框
【发布时间】:2017-11-20 01:11:55
【问题描述】:

self.tableView.set??????????(df) 下面的行有问题,它应该在 PyQt5 中显示数据框。我放 ???那里我缺少我需要的代码。

def btn_clk(self):
        path = self.lineEdit.text()
        df = pd.read_csv(path)
        self.tableView.set??????????(df)

其余的代码都可以工作,因为如果我在上面的代码中使用print(df),数据框就会在IPython控制台中打印出来。因此,Pandas 会读取 CSV 文件并将其打印出来。

但是,我尝试了很多方法让它在 PyQt5 中显示,但没有任何效果。我对 PyQt 不是很熟悉,刚开始玩它就卡在这里了。

这是我的代码:

from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(662, 512)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setObjectName("lineEdit")
        self.verticalLayout.addWidget(self.lineEdit)
        self.tableView = QtWidgets.QTableView(self.centralwidget)
        self.tableView.setObjectName("tableView")
        self.verticalLayout.addWidget(self.tableView)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.verticalLayout.addWidget(self.pushButton)
        self.horizontalLayout.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 662, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))


        self.pushButton.clicked.connect(self.btn_clk)

        MainWindow.show()

    def btn_clk(self):
        path = self.lineEdit.text()
        df = pd.read_csv(path)
        self.tableView.set????????????(df)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pandas pyqt pyqt5 qtableview


    【解决方案1】:

    修复排序...

    from natsort import natsorted, index_natsorted, order_by_index
    def sort(self, column, order):
        if order == 0:
            self._dataframe = self._dataframe.reindex(index=order_by_index(self._dataframe.index, index_natsorted(self._dataframe[column])))
        else:
            self._dataframe = self._dataframe.reindex(index=order_by_index(self._dataframe.index, reversed(index_natsorted(self._dataframe[column]))))
    
        self._dataframe.reset_index(inplace=True, drop=True)
        self.setDataFrame(self._dataframe)
        
    

    【讨论】:

      【解决方案2】:

      就像@DanielR 所说,标记的答案不再有效,但this tutorial 似乎是。

      pandas.版本='1.0.1', PYQT_VERSION_STR = 5.11.3

      
      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()])
              return None
      
          def headerData(self, col, orientation, role):
              if orientation == Qt.Horizontal and role == Qt.DisplayRole:
                  return self._data.columns[col]
              return None
      

      【讨论】:

        【解决方案3】:

        QTableView 的情况下,数据必须通过模型提供,因为它实现了MVC (Model-View-Controller) 范例,在 pandas 的情况下没有默认值模型,但我们可以创建一个自定义,如下所示:

        class PandasModel(QtCore.QAbstractTableModel): 
            def __init__(self, df = pd.DataFrame(), parent=None): 
                QtCore.QAbstractTableModel.__init__(self, parent=parent)
                self._df = df.copy()
        
            def toDataFrame(self):
                return self._df.copy()
        
            def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
                if role != QtCore.Qt.DisplayRole:
                    return QtCore.QVariant()
        
                if orientation == QtCore.Qt.Horizontal:
                    try:
                        return self._df.columns.tolist()[section]
                    except (IndexError, ):
                        return QtCore.QVariant()
                elif orientation == QtCore.Qt.Vertical:
                    try:
                        # return self.df.index.tolist()
                        return self._df.index.tolist()[section]
                    except (IndexError, ):
                        return QtCore.QVariant()
        
            def data(self, index, role=QtCore.Qt.DisplayRole):
                if role != QtCore.Qt.DisplayRole:
                    return QtCore.QVariant()
        
                if not index.isValid():
                    return QtCore.QVariant()
        
                return QtCore.QVariant(str(self._df.ix[index.row(), index.column()]))
        
            def setData(self, index, value, role):
                row = self._df.index[index.row()]
                col = self._df.columns[index.column()]
                if hasattr(value, 'toPyObject'):
                    # PyQt4 gets a QVariant
                    value = value.toPyObject()
                else:
                    # PySide gets an unicode
                    dtype = self._df[col].dtype
                    if dtype != object:
                        value = None if value == '' else dtype.type(value)
                self._df.set_value(row, col, value)
                return True
        
            def rowCount(self, parent=QtCore.QModelIndex()): 
                return len(self._df.index)
        
            def columnCount(self, parent=QtCore.QModelIndex()): 
                return len(self._df.columns)
        
            def sort(self, column, order):
                colname = self._df.columns.tolist()[column]
                self.layoutAboutToBeChanged.emit()
                self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
                self._df.reset_index(inplace=True, drop=True)
                self.layoutChanged.emit()
        

        然后使用它:

        def btn_clk(self):
            path = self.lineEdit.text()
            df = pd.read_csv(path)
            model = PandasModel(df)
            self.tableView.setModel(model)
        

        完整代码为here

        2019 年 3 月 7 日更新:

        一些 Pandas 方法已被弃用,因此我实现了一个新版本(也可以在 QML 中使用,如 answer 所示):

        class DataFrameModel(QtCore.QAbstractTableModel):
            DtypeRole = QtCore.Qt.UserRole + 1000
            ValueRole = QtCore.Qt.UserRole + 1001
        
            def __init__(self, df=pd.DataFrame(), parent=None):
                super(DataFrameModel, self).__init__(parent)
                self._dataframe = df
        
            def setDataFrame(self, dataframe):
                self.beginResetModel()
                self._dataframe = dataframe.copy()
                self.endResetModel()
        
            def dataFrame(self):
                return self._dataframe
        
            dataFrame = QtCore.pyqtProperty(pd.DataFrame, fget=dataFrame, fset=setDataFrame)
        
            @QtCore.pyqtSlot(int, QtCore.Qt.Orientation, result=str)
            def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = QtCore.Qt.DisplayRole):
                if role == QtCore.Qt.DisplayRole:
                    if orientation == QtCore.Qt.Horizontal:
                        return self._dataframe.columns[section]
                    else:
                        return str(self._dataframe.index[section])
                return QtCore.QVariant()
        
            def rowCount(self, parent=QtCore.QModelIndex()):
                if parent.isValid():
                    return 0
                return len(self._dataframe.index)
        
            def columnCount(self, parent=QtCore.QModelIndex()):
                if parent.isValid():
                    return 0
                return self._dataframe.columns.size
        
            def data(self, index, role=QtCore.Qt.DisplayRole):
                if not index.isValid() or not (0 <= index.row() < self.rowCount() \
                    and 0 <= index.column() < self.columnCount()):
                    return QtCore.QVariant()
                row = self._dataframe.index[index.row()]
                col = self._dataframe.columns[index.column()]
                dt = self._dataframe[col].dtype
        
                val = self._dataframe.iloc[row][col]
                if role == QtCore.Qt.DisplayRole:
                    return str(val)
                elif role == DataFrameModel.ValueRole:
                    return val
                if role == DataFrameModel.DtypeRole:
                    return dt
                return QtCore.QVariant()
        
            def roleNames(self):
                roles = {
                    QtCore.Qt.DisplayRole: b'display',
                    DataFrameModel.DtypeRole: b'dtype',
                    DataFrameModel.ValueRole: b'value'
                }
                return roles
        

        【讨论】:

        • 非常感谢您这样做。我应该使用from PandasModel import PandasModelPandasModel 导入到我的原始代码中吗?我试图把它放在一起,但它还没有工作。当我尝试导入 PandasModel 时,我收到 no module named PandasModel 错误。我确定我错过了一些东西。你能指出我正确的方向吗?
        • 通过添加您应该如何使用它的代码来更新我的答案
        • 哇!这真是太棒了!非常感谢。这是一个很大的帮助。效果很好。
        • 很好的答案,但是 PySide2 不使用 QVariant,关于如何更改代码以使用 PySide2 的任何建议?
        • 上面的课程对我不起作用。它无法顺利加载数以千计的行。我发现了另一个这样的 PandasModel 示例,它具有超过 100 万行的魅力:learndataanalysis.org/… 在 youtube 频道上:youtube.com/watch?v=hJEQEECZSH0&t=556s
        猜你喜欢
        • 2016-09-25
        • 1970-01-01
        • 2020-07-24
        • 2020-06-22
        • 2021-12-19
        • 1970-01-01
        • 2017-03-06
        • 1970-01-01
        相关资源
        最近更新 更多