PyQt - Внедрение QAbstractTableModel для отображения в QTableView
Я хотел бы отобразить фрейм данных pandas в таблице PyQt. Я сделал некоторый прогресс в этом, но не смог правильно получить класс Table Model. Любая помощь с этим была бы высоко оценена.
** Обратите внимание на полный код примера здесь **
Я пытаюсь создать допустимый класс, основанный на QtCore.QAbstractTableModel. Следуя предыдущему вопросу о QItemDelegates, я пытаюсь создать модель таблицы из pandas DataFrame для вставки реальных данных. У меня есть пример рабочего кода здесь, но если я заменил свой TableModel на TableModel2 в классе Widget (ln 152), я не могу отобразить таблицу.
class TableModel2(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(TableModel2, self).__init__()
#QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.datatable = None
self.headerdata = None
self.dataFrame = None
self.model = QtGui.QStandardItemModel(self)
def update(self, dataIn):
print 'Updating Model'
self.datatable = dataIn
print 'Datatable : {0}'.format(self.datatable)
headers = dataIn.columns.values
header_items = [
str(field)
for field in headers
]
self.headerdata = header_items
print 'Headers'
print self.headerdata
for i in range(len(dataIn.index.values)):
for j in range(len(dataIn.columns.values)):
#self.datatable.setItem(i,j,QtGui.QTableWidgetItem(str(df.iget_value(i, j))))
self.model.setItem(i,j,QtGui.QStandardItem(str(dataIn.iget_value(i, j))))
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable.index)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable.columns.values)
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
elif role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
#return QtCore.QVariant(self.model.data(index))
return QtCore.QVariant(self.model.data(index))
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant()
return QtCore.QVariant(self.headerdata[col])
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
print "setData", index.row(), index.column(), value
def flags(self, index):
if (index.column() == 0):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
else:
return QtCore.Qt.ItemIsEnabled
Я пытаюсь создать модель, а затем добавить ее в представление, например:
class Widget(QtGui.QWidget):
"""
A simple test widget to contain and own the model and table.
"""
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
l=QtGui.QVBoxLayout(self)
cdf = self.get_data_frame()
self._tm=TableModel(self)
self._tm.update(cdf)
self._tv=TableView(self)
self._tv.setModel(self._tm)
for row in range(0, self._tm.rowCount()):
self._tv.openPersistentEditor(self._tm.index(row, 0))
l.addWidget(self._tv)
def get_data_frame(self):
df = pd.DataFrame({'Name':['a','b','c','d'],
'First':[2.3,5.4,3.1,7.7], 'Last':[23.4,11.2,65.3,88.8], 'Class':[1,1,2,1], 'Valid':[True, True, True, False]})
return df
Спасибо за внимание!
Примечание: Изменить 2
Я включил QStandardItemModel в TableModel2. Также удалена функция dataFrameToQtTable после комментария @mata. Это становится немного ближе, но все еще не работает.
Ответы
Ответ 1
Хорошо, я понял это с приведенным выше предложением и некоторой помощью из книги Rapid GUI от Summerfield. В модели QAbstractTableModel нет базовой модели. Только три функции должны быть переопределены, и данные могут храниться в любом пользовательском формате, если он возвращается в вызове данных.
Очень простая реализация может быть:
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(TableModel, self).__init__()
self.datatable = None
def update(self, dataIn):
print 'Updating Model'
self.datatable = dataIn
print 'Datatable : {0}'.format(self.datatable)
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable.index)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable.columns.values)
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
i = index.row()
j = index.column()
return '{0}'.format(self.datatable.iget_value(i, j))
else:
return QtCore.QVariant()
def flags(self, index):
return QtCore.Qt.ItemIsEnabled
Это позволяет просматривать любой совместимый фрейм данных в виде Qt.
Я обновил Gist над здесь
Это должно заставить вас идти быстро, если вам также нужно это сделать.
Ответ 2
Вероятно, это ваша проблема:
def rowCount(self, parent=QtCore.QModelIndex()):
if type(self.datatable) == pd.DataFrame:
...
def columnCount(self, parent=QtCore.QModelIndex()):
if (self.datatable) == pd.DataFrame:
...
Вы устанавливаете datatable
в QTableWidget
в dataFrameToQtTable
, поэтому он не может быть pd.DataFrame
, ваши методы всегда будут возвращать 0.
Без проверки типа вы сразу поймете проблему. Вы действительно хотите игнорировать все случаи, когда ваш тип не соответствует (лучше пусть он вызывает ошибку, если он не соответствует тому же интерфейсу, который вы ожидаете)? Typechecks в большинстве случаев не нужны.
Ответ 3
Pandas 0.13 в качестве экспериментальной особенности:
Поддержка PySide для qtpandas DataFrameModel
и DataFrameWidget
см. https://github.com/pydata/pandas/blob/master/doc/source/faq.rst
вы можете добавить эту функцию, используя
from pandas.sandbox.qtpandas import DataFrameModel, DataFrameWidget