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