假设我有以下数据框:
df = {'Banana': {0: 1, 1: 2, 2: 5}, 'Apple': {0: 3, 1: 4, 2: 3}, 'Elderberry': {0: 5, 1: 4, 2: 1},
'Clementine': {0: 4, 1: 7, 2: 0}, 'Fig': {0: 1, 1: 9, 2: 3}}
Banana Apple Elderberry Clementine Fig
0 1 3 5 4 1
1 2 4 4 7 9
2 5 3 1 0 3
传递给QAbstractTableModel
并显示为QTableView
:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import Qt
import pandas as pd
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, model):
super().__init__()
self.model = model
self.table = QtWidgets.QTableView()
self.table.setModel(self.model)
self.setCentralWidget(self.table)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
def data(self, index, role):
row = index.row()
column = index.column()
column_name = self._data.columns[column]
value = self._data.iloc[row, column]
if role == Qt.DisplayRole:
print(str(value))
return str(value)
app = QtWidgets.QApplication(sys.argv)
df = {'Banana': {0: 1, 1: 2, 2: 5}, 'Apple': {0: 3, 1: 4, 2: 3}, 'Elderberry': {0: 5, 1: 4, 2: 1}, 'Clementine': {0: 4, 1: 7, 2: 0}, 'Fig': {0: 1, 1: 9, 2: 3}}
data_model = TableModel(df)
window1 = MainWindow(data_model)
window1.resize(800, 400)
window1.show()
sys.exit(app.exec_())
我现在想使用QListView
创建一个单独的窗口,以显示df
列名的列表,允许我指定要在上述QTableView
中出现的列。 Like this:
如果取消选中QListView
中的一列,我希望将该列从QTableView
中删除。同样,如果我检查一列,则应将该列添加到表视图中。列表视图的目的实质上是允许用户指定哪些列应出现在表中。
最佳做法是什么?我想我可以创建一个QListWidget
并使用信号来更新QTableView
。但是我有点不愿意这样做,因为我想它可能会变得非常混乱。
因此,最佳做法是使用QListView
,以便两个小部件都自动引用相同的数据模型吗?
您必须使用代理模型来构建列表视图的模型(转置原始模型,将标题映射到单个行或列,并添加复选框),另一个用于过滤:
talk to each other like this
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data, parent=None):
super(TableModel, self).__init__(parent)
self._data = data
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return str(self._data.columns[section])
if orientation == QtCore.Qt.Vertical:
return str(self._data.index[section])
def data(self, index, role):
row = index.row()
column = index.column()
value = self._data.iloc[row, column]
if role == QtCore.Qt.DisplayRole:
return str(value)
class CustomProxy(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self.show_columns = set()
def show_column(self, c):
self.show_columns.add(c)
self.invalidateFilter()
def hide_column(self, c):
if c not in self.show_columns:
return
self.show_columns.remove(c)
self.invalidateFilter()
def filterAcceptsColumn(self, source_column, source_parent):
return source_column in self.show_columns
class HeaderProxyModel(QtCore.QIdentityProxyModel):
checked = QtCore.pyqtSignal(int, bool)
def __init__(self, parent=None):
super().__init__(parent)
self.checks = {}
def columnCount(self, index=QtCore.QModelIndex()):
return 1
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
return self.headerData(index.row(), QtCore.Qt.Vertical, role)
elif role == QtCore.Qt.CheckStateRole and index.column() == 0:
return self.checks.get(
QtCore.QPersistentModelIndex(index), QtCore.Qt.Unchecked
)
def setData(self, index, value, role=QtCore.Qt.EditRole):
if not index.isValid():
return False
if role == QtCore.Qt.CheckStateRole:
self.checks[QtCore.QPersistentModelIndex(index)] = value
self.checked.emit(index.row(), bool(value))
return True
return False
def flags(self, index):
fl = super().flags(index)
if index.column() == 0:
fl |= QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable
return fl
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, model):
super().__init__()
self.model = model
self.listview = QtWidgets.QListView()
tranpose = QtCore.QTransposeProxyModel()
tranpose.setSourceModel(self.model)
header_model = HeaderProxyModel()
header_model.setSourceModel(tranpose)
self.listview.setModel(header_model)
self.tableview = QtWidgets.QTableView()
self.filter_proxy = CustomProxy()
self.filter_proxy.setSourceModel(self.model)
self.tableview.setModel(self.filter_proxy)
header_model.checked.connect(self.on_checked)
central_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(central_widget)
hlay.addWidget(self.listview)
hlay.addWidget(self.tableview, stretch=1)
self.setCentralWidget(central_widget)
def on_checked(self, r, state):
if state:
self.filter_proxy.show_column(r)
else:
self.filter_proxy.hide_column(r)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
df = pd.DataFrame(
{
"Banana": {0: 1, 1: 2, 2: 5},
"Apple": {0: 3, 1: 4, 2: 3},
"Elderberry": {0: 5, 1: 4, 2: 1},
"Clementine": {0: 4, 1: 7, 2: 0},
"Fig": {0: 1, 1: 9, 2: 3},
}
)
data_model = TableModel(df)
window1 = MainWindow(data_model)
window1.resize(800, 400)
window1.show()
sys.exit(app.exec_())