web-dev-qa-db-fra.com

Le moyen le plus rapide de remplir QTableView à partir de Pandas data frame

Je suis très nouveau sur PyQt et j'ai du mal à remplir un contrôle QTableView.

Mon code est le suivant:

def data_frame_to_ui(self, data_frame):
        """
        Displays a pandas data frame into the GUI
        """
        list_model = QtGui.QStandardItemModel()
        i = 0
        for val in data_frame.columns:
            # for the list model
            if i > 0:
                item = QtGui.QStandardItem(val)
                #item.setCheckable(True)
                item.setEditable(False)
                list_model.appendRow(item)
            i += 1
        self.ui.profilesListView.setModel(list_model)

        # for the table model
        table_model = QtGui.QStandardItemModel()

        # set table headers
        table_model.setColumnCount(data_frame.columns.size)
        table_model.setHorizontalHeaderLabels(data_frame.columns.tolist())
        self.ui.profileTableView.horizontalHeader().setStretchLastSection(True)

        # fill table model data
        for row_idx in range(10): #len(data_frame.values)
            row = list()
            for col_idx in range(data_frame.columns.size):
                val = QtGui.QStandardItem(str(data_frame.values[row_idx][col_idx]))
                row.append(val)
            table_model.appendRow(row)

        # set table model to table object
        self.ui.profileTableView.setModel(table_model)

En fait, dans le code, je réussis à remplir un QListView, mais les valeurs que j'ai définies pour QTableView ne sont pas affichées, vous pouvez également voir que j'ai tronqué les lignes à 10 car il faut une éternité pour afficher les centaines de lignes du bloc de données.

Alors, quel est le moyen le plus rapide de remplir le modèle de table à partir d'une trame de données pandas?

Merci d'avance.

15
Santi Peñate-Vera

Personnellement, je créerais simplement ma propre classe de modèle pour en faciliter la gestion.

Par exemple:

import sys
from PyQt4 import QtCore, QtGui
Qt = QtCore.Qt

class PandasModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.DisplayRole:
                return QtCore.QVariant(str(
                    self._data.values[index.row()][index.column()]))
        return QtCore.QVariant()


if __name__ == '__main__':
    application = QtGui.QApplication(sys.argv)
    view = QtGui.QTableView()
    model = PandasModel(your_pandas_data)
    view.setModel(model)

    view.show()
    sys.exit(application.exec_())
16
Wolph

Cela marche:

class PandasModel(QtCore.QAbstractTableModel):
    """
    Class to populate a table view with a pandas dataframe
    """
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.values[index.row()][index.column()])
        return None

    def headerData(self, col, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[col]
        return None

L'utiliser comme ceci:

model = PandasModel(your_pandas_data_frame)
your_tableview.setModel(model)

J'ai lu ici pour éviter QVariant() de PyQT 4.6 sur.

16
Santi Peñate-Vera

J'ai trouvé toutes les réponses proposées douloureusement lentes pour les DataFrames avec plus de 1000 lignes. Ce qui fonctionne pour moi à une vitesse fulgurante:

class PandasModel(QtCore.QAbstractTableModel):
    """
    Class to populate a table view with a pandas dataframe
    """
    def __init__(self, data, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1]

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, col, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[col]
        return None
4
m0nhawk

Il y a en fait du code dans pandas supportant l'intégration avec Qt.

Au moment de la rédaction de cette réponse, la dernière version pandas est 0.18.1 et vous pourriez faire:

from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget

Ce code semble être couplé à PySide, mais il devrait être relativement trivial de le faire fonctionner avec PyQt. En outre, ce code est obsolète et l'avertissement indique que le module sera supprimé à l'avenir.

Heureusement, ils ont extrait cela dans un projet séparé dans GitHub appelé pandas-qt:

https://github.com/datalyze-solutions/pandas-qt

J'essaierais de l'utiliser avant d'essayer de déployer mon propre modèle et de visualiser l'implémentation.

2
Gabriel Reis

Un moyen simple et plus rapide d'écrire une trame de données dans QtableWidget

# Takes a df and writes it to a qtable provided. df headers become qtable headers
@staticmethod
def write_df_to_qtable(df,table):
    headers = list(df)
    table.setRowCount(df.shape[0])
    table.setColumnCount(df.shape[1])
    table.setHorizontalHeaderLabels(headers)        

    # getting data from df is computationally costly so convert it to array first
    df_array = df.values
    for row in range(df.shape[0]):
        for col in range(df.shape[1]):
            table.setItem(row, col, QtGui.QTableWidgetItem(str(df_array[row,col])))
1
user11766756

Outre l'utilisation de QtCore.QAbstractTableModel, on peut également hériter de QtGui.QStandardItemModel. Je trouve que cette façon est plus facile de prendre en charge l'événement handleChanged émis par QTableView.

from PyQt5 import QtCore, QtGui

class PandasModel(QtGui.QStandardItemModel):
    def __init__(self, data, parent=None):
        QtGui.QStandardItemModel.__init__(self, parent)
        self._data = data
        for row in data.values.tolist():
            data_row = [ QtGui.QStandardItem("{0:.6f}".format(x)) for x in row ]
            self.appendRow(data_row)
        return

    def rowCount(self, parent=None):
        return len(self._data.values)

    def columnCount(self, parent=None):
        return self._data.columns.size

    def headerData(self, x, orientation, role):
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
            return self._data.columns[x]
        if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
            return self._data.index[x]
        return None
1
Frederick Li