2017-05-23 30 views
1

這個想法是通過PyQt5 MV編程習慣用法來顯示一個數據幀,並對提供的數據幀執行一些基本的排序和過濾操作。pyqt5 tableview在tablemodel上排序後沒有更新

顯示部分一切正常,但現在我被困在工具的排序部分。打印語句向我展示了它自己排序的數據框,它是未更新的視圖。所以,現在的代碼:

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工作了這個例子,但在更大的dataframes很慢。

上面的代碼expample確實工作,讀取排序,爲非數據框數據。用元組列表創建模型/視圖很好。

我發現的建議主要有兩個方向:記住要表示視圖或使用sortfilterproxy。我記得並嘗試過,但至今沒有成功。似乎與使用數據幀有關。歡迎提供所有建議。提前致謝。

回答

0

在下一部分中,我將展示您的印象的結果,其中我們看到它被重新排序,但索引也被重新排序,並且這導致在未更新時發生更改。

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() 
+0

重置索引,該死的看着錯誤的方向。感謝您的快速回復,我一回家就會改變這一點。 –

+0

如果我的答案可以幫助您將其標記爲正確。 :P – eyllanesc