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.
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_())
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.
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
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.
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])))
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