python - 在 PyQt5 的 TableView 中显示 pandas DataFrame,其中列设置为索引

标签 python pyqt pyqt5

我在Qt设计器中设计了一个UI,由1个按钮和1个QTableView组成。该按钮用于浏览 Excel,根据我的代码对其进行操作并将结果显示在 TableView 中。 。这是我的代码:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
import numpy as np
import pandas as pd
import glob
import os
import csv
from itertools import combinations
from PyQt5.QtWidgets import QDialog, QApplication


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


class Ui_Rulepriority(object):
    def setupUi(self, Rulepriority):
        Rulepriority.setObjectName("Rulepriority")
        Rulepriority.resize(820, 480)
        self.verticalLayout = QtWidgets.QVBoxLayout(Rulepriority)
        self.verticalLayout.setObjectName("verticalLayout")
        self.OpenCsv = QtWidgets.QPushButton(Rulepriority)
        self.OpenCsv.setObjectName("OpenCsv")
        self.verticalLayout.addWidget(self.OpenCsv)
        self.OpenCsv.clicked.connect(self.file_open)
        self.tableView = QtWidgets.QTableView(Rulepriority)
        self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.tableView.setObjectName("tableView")
        self.verticalLayout.addWidget(self.tableView)

        self.retranslateUi(Rulepriority)
        self.OpenCsv.clicked.connect(self.tableView.show)
        QtCore.QMetaObject.connectSlotsByName(Rulepriority)

    def retranslateUi(self, Rulepriority):
        _translate = QtCore.QCoreApplication.translate
        Rulepriority.setWindowTitle(_translate("Rulepriority", "Violation Solving Prioritization tool"))
        self.OpenCsv.setText(_translate("Rulepriority", "Browse excel and get solving probability"))
        Rulepriority.setWindowIcon(QtGui.QIcon('favicon.ico'))

    def file_open(self):
        path, _ = QtWidgets.QFileDialog.getOpenFileName(Rulepriority, 'Open csv', QtCore.QDir.rootPath(),
                                                        'Data_*.xlsx')

        df1 = pd.read_excel(path)
        df2 = df1.set_index(['Priority', 'RID to solve', 'Probability', 'RID per probability', 'Remarks']).sort_values(by=['Priority'],
                                                                                                       ascending=True)
        model = DataFrameModel(df2)
        self.tableView.setModel(model)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Rulepriority = QtWidgets.QDialog()
    ui = Ui_Rulepriority()
    ui.setupUi(Rulepriority)
    Rulepriority.show()
    sys.exit(app.exec_())

这里def file_open(self)用于浏览excel表格。用于在 QTableView 中显示 pandas DataFrame ,我正在使用class DataFrameModel(QtCore.QAbstractTableModel)我从这个答案借来的How to display a Pandas data frame with PyQt5/PySide2

这是我浏览的数据的样子:

enter image description here 我希望上面的数据看起来像这样:

enter image description here

为此,我使用此代码:

df2 = df1.set_index(['Priority', 'RID to solve', 'Probability', 'RID per probability', 'Remarks']).sort_values(by=['Priority'],
                                                                                                       ascending=True)

但是当我尝试在前端显示相同的结果时,我得到像这样的逗号分隔结果:

enter image description here

如果你们能帮我得到上面的显示格式,我将不胜感激。

最佳答案

解决了问题。我发现不寻常的索引导致了问题,所以,

而不是:

df2 = df1.set_index(['Priority', 'RID to solve', 'Probability', 'RID per probability', 'Remarks']).sort_values(by=['Priority'],
                                                                                                   ascending=True)

我用的是这个:

cols = ['Priority','RID to solve']
dm = df1[cols].apply(lambda x: x.duplicated())    
df1[cols]= df1[cols].mask(m, '')

这段代码的作用是,首先用缺失值替换重复值,然后在数据帧列上屏蔽它们以获得如下结果:

enter image description here

这是经过上述审核的完整代码:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
import numpy as np
import pandas as pd
import glob
import os
import csv
from itertools import combinations
from PyQt5.QtWidgets import QDialog, QApplication


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


class Ui_Rulepriority(object):
    def setupUi(self, Rulepriority):
        Rulepriority.setObjectName("Rulepriority")
        Rulepriority.resize(820, 480)
        self.verticalLayout = QtWidgets.QVBoxLayout(Rulepriority)
        self.verticalLayout.setObjectName("verticalLayout")
        self.OpenCsv = QtWidgets.QPushButton(Rulepriority)
        self.OpenCsv.setObjectName("OpenCsv")
        self.verticalLayout.addWidget(self.OpenCsv)
        self.OpenCsv.clicked.connect(self.file_open)
        self.tableView = QtWidgets.QTableView(Rulepriority)
        self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.tableView.setObjectName("tableView")
        self.verticalLayout.addWidget(self.tableView)
        QtCore.QFileSystemWatcher()
        self.retranslateUi(Rulepriority)
        self.OpenCsv.clicked.connect(self.tableView.show)
        QtCore.QMetaObject.connectSlotsByName(Rulepriority)

    def retranslateUi(self, Rulepriority):
        _translate = QtCore.QCoreApplication.translate
        Rulepriority.setWindowTitle(_translate("Rulepriority", "Violation Solving Prioritization tool"))
        self.OpenCsv.setText(_translate("Rulepriority", "Browse excel and get solving probability"))
        Rulepriority.setWindowIcon(QtGui.QIcon('favicon.ico'))

    def file_open(self):
        path, _ = QtWidgets.QFileDialog.getOpenFileName(Rulepriority, 'Open csv', QtCore.QDir.rootPath(),
                                                        'Data_*.xlsx')

        df1 = pd.read_excel(path)

        cols = ['Priority', 'RID to solve']
        dm = df1[cols].apply(lambda x: x.duplicated())
        df1[cols] = df1[cols].mask(dm, '')
        model = DataFrameModel(df1)
        self.tableView.setModel(model)

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Rulepriority = QtWidgets.QDialog()
    ui = Ui_Rulepriority()
    ui.setupUi(Rulepriority)
    Rulepriority.show()
    sys.exit(app.exec_())

干杯:)

关于python - 在 PyQt5 的 TableView 中显示 pandas DataFrame,其中列设置为索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58234343/

相关文章:

python - 如何在 python 中创建伸缩术语集?

python - 使用PyQt5,如何将点与线连接起来?

python - QCheckbox 检查所有其他 QCheckBoxes

python - 禁用从 GUI (PyQt) 打开 cmd

python-3.x - 每当从 PyQt5 迁移到 Pyside2 后调用 QFileDialog 时,QThread 关闭

Python Twisted 多客户端

python - Pandas - 以不等间隔绘制事件

python - Pyspark - 调用 pandas_udf 时出错,返回 Series.interpolate() 作为结果

python - 如何在pyqt MessageBox中使用变量

cmd - 'Pyuic4' 不被识别为内部外部命令