我们的想法是通过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。我记得并试过但到目前为止没有成功。似乎与数据帧的使用有关。欢迎提出所有建议。提前谢谢。
答案 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()