在tablemodel上排序后,tableview没有更新

时间:2017-05-23 21:14:54

标签: python pandas pyqt pyqt5 qabstracttablemodel

我们的想法是通过PyQt5 MV编程习惯显示数据帧,并对呈现的数据帧执行一些基本的排序和过滤操作。

显示部分都很好但是现在我被困在工具的排序部分。 Print语句向我展示了自己排序的数据帧,它是未更新的视图。所以现在代码:

import sys
import operator
tmp = [('23-02-1978', '19:03:13', 'eh', None, 'even more some data'),
       ('23-02-1978', '19:01:45',  'ss', 'some data ', 'even more some data'),
       ('23-02-1978', '19:02:55',  'he', 'some data ', 'even more some data')]    


tmp1 = [('23-02-1978', '19:02:33',  'eh', 'some data ', '666', 'even more some data'),
        ('23-02-1978', '19:03:22',  'ss', 'some data ', '777', 'even more some data'),
        ('23-02-1978', '19:01:45',  'he', 'some data ', '888', 'even more some data')]  


from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget, QAction,
                             QGroupBox, QCheckBox, QTableView, QTableWidgetItem, 
                             QTabWidget, QGridLayout,QLineEdit, QFormLayout, 
                             QVBoxLayout, QHBoxLayout, QLabel, QDialog, QHeaderView)

from PyQt5.QtGui import QIcon, QFont 
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QAbstractTableModel, QVariant, QModelIndex, QSortFilterProxyModel


from pandas import DataFrame


class DataFrameModel(QAbstractTableModel): 
    def __init__(self): 
        """ datain: a list of lists
            headerdata: a list of strings
        """
        super(DataFrameModel, self).__init__()
        self._df = DataFrame()

    def setDataFrame(self, df):
        self._df = df;

    def signalUpdate(self):
        ''' tell viewers to update their data (this is full update, not
        efficient)'''
        self.layoutChanged.emit()


    #------------- table display functions -----------------
    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return QVariant()

        if orientation == Qt.Horizontal:
            try:
                return self._df.columns.tolist()[section]
            except (IndexError, ):
                return QVariant()
        elif orientation == Qt.Vertical:
            try:
                # return self.df.index.tolist()
                return self._df.index.tolist()[section]
            except (IndexError, ):
                return QVariant()

    def data(self, index, role=Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return QVariant()

        if not index.isValid():
            return QVariant()

        return QVariant(str(self._df.ix[index.row(), index.column()]))

    def flags(self, index):
            flags = super(DataFrameModel, self).flags(index)
            return flags

    def setData(self, index, value, role):
        row = self._df.index[index.row()]
        col = self._df.columns[index.column()]
        if hasattr(value, 'toPyObject'):
            # PyQt4 gets a QVariant
            value = value.toPyObject()
        else:
            # PySide gets an unicode
            dtype = self._df[col].dtype
            if dtype != object:
                value = None if value == '' else dtype.type(value)
        self._df.set_value(row, col, value)
        return True

    def rowCount(self, parent=QModelIndex()): 
        return len(self._df.index)

    def columnCount(self, parent=QModelIndex()): 
        return len(self._df.columns)

    def sort(self, column, order=Qt.AscendingOrder):
        """Sort table by given column number.
        """
        print('sort clicked col {} order {}'.format(column, order))
        self.layoutAboutToBeChanged.emit()
        print(self._df.columns[column])
        self._df.sort_values('time', ascending=order == Qt.AscendingOrder, inplace=True)
        print(self._df)
        self.layoutChanged.emit()


class DataFrameWidget(QWidget):
    ''' a simple widget for using DataFrames in a gui '''
    def __init__(self, dataFrame, parent=None):
        super(DataFrameWidget, self).__init__(parent)

        self.dataModel = DataFrameModel()
        # Set DataFrame
        self.dataTable = QTableView()
#        self.proxy = QSortFilterProxyModel()
#        self.proxy.setSourceModel(self.dataModel)
        self.dataTable.setModel(self.dataModel)

        self.dataTable.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.setDataFrame(dataFrame)
        self.dataTable.setSortingEnabled(True)
        self.dataTable.sortByColumn(0,0)


        layout = QVBoxLayout()
        layout.addWidget(self.dataTable)
        self.setLayout(layout)

    def setDataFrame(self, dataFrame):
        self.dataModel.setDataFrame(dataFrame)
        self.dataModel.signalUpdate()


def testDf():
    ''' creates test dataframe '''
#    data = {'int': [1, 2, 3], 'float': [1.5, 2.5, 3.5],
#            'string': ['a', 'b', 'c'], 'nan': [np.nan, np.nan, np.nan]}

#    data = [(1, 1.5, 'a', np.nan),
#            (2, 2.5, 'b', np.nan),
#            (3, 3.5, 'c', np.nan)]



    return DataFrame(tmp, columns=['date', 'time', 'string', 'nan', 'bla'])

class Form(QDialog):
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        df = testDf()  # make up some data
        widget = DataFrameWidget(df)

        layout = QVBoxLayout()
        layout.addWidget(widget)
        self.setLayout(layout)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    form = Form()
    form.show()
    exit(app.exec_())

使用SortFilterProxy适用于此示例,但在较大的数据帧上速度非常慢。

对于非数据帧数据,上面的代码示例确实有效,读取已排序。仅使用元组列表创建模型/视图就可以了。

我发现的建议主要是两个方向:记住发信号或使用sortfilterproxy。我记得并试过但到目前为止没有成功。似乎与数据帧的使用有关。欢迎提出所有建议。提前谢谢。

1 个答案:

答案 0 :(得分:1)

在下一部分中,我展示了您的展示结果,在其中我们看到它被重新排序,但索引也被重新排序,这导致当它没有更新时更改。

sort clicked col 0 order 1
date
         date      time string         nan                  bla
0  23-02-1978  19:03:13     eh        None  even more some data
2  23-02-1978  19:02:55     he  some data   even more some data
1  23-02-1978  19:01:45     ss  some data   even more some data
sort clicked col 0 order 0
date
         date      time string         nan                  bla
1  23-02-1978  19:01:45     ss  some data   even more some data
2  23-02-1978  19:02:55     he  some data   even more some data
0  23-02-1978  19:03:13     eh        None  even more some data

要更新数据,您必须使用reset_index()重置索引。

def sort(self, column, order):
    """Sort table by given column number.
    """
    print('sort clicked col {} order {}'.format(column, order))
    self.layoutAboutToBeChanged.emit()
    print(self._df.columns[column])
    self._df.sort_values('time', ascending=order == Qt.AscendingOrder, inplace=True)
    self._df.reset_index(inplace=True, drop=True) # <-- this is the change
    print(self._df)
    self.layoutChanged.emit()