Во-первых. Класс PandasModel правильный и не доставляет хлопот. То, что я сделал в main.py, было неправильно. Я отображаю TableView в QTabWidget и хочу иметь кнопку экспорта внизу. Поэтому мое уродливое решение состоит в том, чтобы поместить «слой» пустых QLabelWidgets на вкладку, а затем на нее поместить TableView. Вот мой полный пример с некоторыми данными.
import sys
from PyQt5 import QtCore
from PyQt5.QtCore import QAbstractTableModel, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QFileDialog,
QGridLayout, QPushButton, QTableView, QLabel, QHeaderView
import numpy as np
import pandas as pd
from functools import partial
def createDF():
df = pd.DataFrame(np.random.randint(0,11,size=(50, 5)), columns=list(['A','B','C',
'D','Tree']))
print(df)
return df
trees = { 1: 'Arborvitae (Thuja occidentalis)',
2: 'Black Ash (Fraxinus nigra)',
3: 'White Ash (Fraxinus americana)',
4: 'Bigtooth Aspen (Populus grandidentata)',
5: 'Quaking Aspen (Populus tremuloides)',
6: 'Basswood (Tilia americana)',
7: 'American Beech (Fagus grandifolia)',
8: 'Black Birch (Betula lenta)',
9: 'Gray Birch (Betula populifolia)',
10: 'Paper Birch (Betula papyrifera)'}
class PandasModel(QAbstractTableModel):
def __init__(self, data, parent=None):
"""
:param data: a pandas dataframe
:param parent:
"""
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
def headerData(self, rowcol, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self._data.columns[rowcol]
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return self._data.index[rowcol]
def flags(self, index):
flags = super(self.__class__, self).flags(index)
#flags |= QtCore.Qt.ItemIsEditable
flags |= QtCore.Qt.ItemIsSelectable
flags |= QtCore.Qt.ItemIsEnabled
flags |= QtCore.Qt.ItemIsDragEnabled
flags |= QtCore.Qt.ItemIsDropEnabled
return flags
def sort(self, Ncol, order):
"""Sort table by given column number.
"""
try:
self.layoutAboutToBeChanged.emit()
self._data = self._data.sort_values(self._data.columns[Ncol],
ascending=not order)
self.layoutChanged.emit()
except Exception as e:
print(e)
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.resize(400, 600)
self.tabs = QTabWidget()
self.tabs.layout = QGridLayout()
self.tabs.setLayout(self.tabs.layout)
self.grid = QGridLayout()
self.grid.addWidget(self.tabs)
self.setLayout(self.grid)
self.setCentralWidget(self.tabs)
self.df = createDF()
self.displayDF()
def displayDF(self):
self.result = QTabWidget()
self.result.layout = QGridLayout()
self.result.setLayout(self.result.layout)
self.tabs.addTab(self.result, 'Dataframe')
#empty space to put the export button in the bottom of the TableView
positions = [(i,j) for i in range(20) for j in range(10)]
space = ' '
i = 0
for position, leer in zip(positions, space):
emptyLabels = QLabel(leer)
self.result.layout.addWidget(emptyLabels, *position)
#export button
self.buttonExport = QPushButton("Export", self)
self.buttonExport.clicked.connect(partial(self.writeToCSV, self.df))
self.result.layout.addWidget(self.buttonExport, 21, 0)
# QTableView
self.view_minmax = QTableView(self.result)
self.modelminmax = PandasModel(self.df)
self.view_minmax.setModel(self.modelminmax)
self.view_minmax.resize(360,500)
self.view_minmax.clicked.connect(self.onClickedRow)
self.view_minmax.sortByColumn(4, Qt.DescendingOrder)
self.view_minmax.show()
def onClickedRow(self, index=None):
print("Click !")
print(index.data())
def writeToCSV(self, df):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, _ = QFileDialog.getSaveFileName(self,"Export results to a\
csv","","CSV (*.csv);;Text Files (*.txt)", options=options)
if fileName:
print(fileName)
df.to_csv (fileName, index = False, header=True)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle('Fusion')
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
Так что, когда я создаю #empty space QLabelWidget и кнопку экспорта после секции #QTableView, как это, происходит странное поведение с полосой прокрутки, потому что сигналы мыши отправьте в QLabelWidgets вместо QTabelView
# QTableView
self.view_minmax = QTableView(self.result)
self.modelminmax = PandasModel(self.df)
self.view_minmax.setModel(self.modelminmax)
self.view_minmax.resize(360,500)
self.view_minmax.clicked.connect(self.onClickedRow)
self.view_minmax.sortByColumn(4, Qt.DescendingOrder)
self.view_minmax.show()
#empty space to put the export button in the bottom of the TableView
positions = [(i,j) for i in range(20) for j in range(10)]
space = ' '
i = 0
for position, leer in zip(positions, space):
emptyLabels = QLabel(leer)
self.result.layout.addWidget(emptyLabels, *position)
#export button
self.buttonExport = QPushButton("Export", self)
self.buttonExport.clicked.connect(partial(self.writeToCSV, self.df))
self.result.layout.addWidget(self.buttonExport, 21, 0)
Возможно, это кому-нибудь поможет.