如何re-arrange QTableView的列顺序
How to re-arrange QTableView's Columns Order
下面的代码创建了一个由 Model/Proxy 框架驱动的 QTableView。
在 source-model
中声明的 self.headerNames
list-variable 存储 Header 的列的名称。此列表中的名称数由相同的 source model
的 columnCount()
方法用于 return 视图中的列数:
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
Proxy
模型的 headerData()
通过 source model
访问此 self.headerNames
变量:
sourceModel=self.sourceModel()
在 if role==Qt.DisplayRole
上,代理检索并return将列的名称发送到 QTableView:
return QVariant( sourceModel.headerNames[column] )
在 header-column right-click 上实现了一个 right-click 菜单。
那部分工作正常。但是由于我找不到任何关于其他人如何做的例子,我不得不自己设计它的工作方式。如果您发现它可以改进,我将不胜感激。
接下来我要实现的是 re-arrange 以任意顺序排列列的能力。但我不确定从哪里开始。
P.s。请忽略 QTableView 中显示的项目的名称。
我想让代码尽可能简单,只关注 Header/Column 主题。
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Item_A_001','Item_A_002','Item_B_001','Item_B_002']
self.headerNames=['Column 0','Column 1','Column 2','Column 3','Column 4','Column 5','Column 6','Column 7']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
def data(self, index, role):
if not index.isValid(): return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
row=index.row()
if row<len(self.items):
return QVariant(self.items[row])
else:
return QVariant()
class Proxy(QSortFilterProxyModel):
def __init__(self):
super(Proxy, self).__init__()
def filterAcceptsRow(self, row, parent):
return True
def headerData(self, column, orientation, role=Qt.DisplayRole):
sourceModel=self.sourceModel()
if role==Qt.TextAlignmentRole:
if orientation==Qt.Horizontal:
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
if role==Qt.DisplayRole:
if orientation==Qt.Horizontal:
return QVariant( sourceModel.headerNames[column] )
else:
return QVariant()
else:
return QVariant()
return QVariant(int(column+1))
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tableModel=Model(self)
proxyModel=Proxy()
proxyModel.setSourceModel(tableModel)
self.tableview=QTableView(self)
self.tableview.setModel(proxyModel)
self.tableview.horizontalHeader().setStretchLastSection(True)
self.tableview.setSelectionMode(QAbstractItemView.MultiSelection)
header=self.tableview.horizontalHeader()
header.setContextMenuPolicy(Qt.CustomContextMenu)
header.connect(header, SIGNAL("customContextMenuRequested(QPoint)" ), self.headerRightClicked)
self.resHeaderMenu=QMenu(self)
for column in range(proxyModel.columnCount()):
columnName=proxyModel.headerData(column, Qt.Horizontal).toPyObject()
actn=QAction('%s'%columnName, self.resHeaderMenu, checkable=True)
actn.setChecked(True)
actn.triggered.connect(self.resHeaderMenuTriggered)
self.resHeaderMenu.addAction(actn)
layout = QVBoxLayout(self)
layout.addWidget(self.tableview)
self.setLayout(layout)
def headerRightClicked(self, QPos):
parentPosition=self.tableview.mapToGlobal(QPoint(0, 0))
menuPosition=parentPosition + QPos
self.resHeaderMenu.move(menuPosition)
self.resHeaderMenu.show()
def resHeaderMenuTriggered(self, arg):
print 'resHeaderMenuTriggered', arg
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.tableview.setColumnHidden(i, True)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
如果您真的想自己重新排列列:
您可以通过 horizontalHeader
的 moveSection and swapSections 方法移动或交换部分。
headerView.moveSection(x, y)
将移动第 x
列,使其成为 y
列之后,而
headerView.swapSections(x,y)
将 - 显然 - 交换两列的位置。
感谢您在 header-column right-click 示例中实现的 right-click 菜单。正确寻找与您相同的功能。只有一件事,当刻度为 re-checked 时,该列应该显示,即你的 resHeaderMenuTriggered()
应该写成:
def resHeaderMenuTriggered(self, arg):
print('resHeaderMenuTriggered', arg)
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.proxyView.setColumnHidden(i, True)
else:
self.proxyView.setColumnHidden(i, False)
下面的代码创建了一个由 Model/Proxy 框架驱动的 QTableView。
在 source-model
中声明的 self.headerNames
list-variable 存储 Header 的列的名称。此列表中的名称数由相同的 source model
的 columnCount()
方法用于 return 视图中的列数:
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
Proxy
模型的 headerData()
通过 source model
访问此 self.headerNames
变量:
sourceModel=self.sourceModel()
在 if role==Qt.DisplayRole
上,代理检索并return将列的名称发送到 QTableView:
return QVariant( sourceModel.headerNames[column] )
在 header-column right-click 上实现了一个 right-click 菜单。 那部分工作正常。但是由于我找不到任何关于其他人如何做的例子,我不得不自己设计它的工作方式。如果您发现它可以改进,我将不胜感激。
接下来我要实现的是 re-arrange 以任意顺序排列列的能力。但我不确定从哪里开始。
P.s。请忽略 QTableView 中显示的项目的名称。 我想让代码尽可能简单,只关注 Header/Column 主题。
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class Model(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items = ['Item_A_001','Item_A_002','Item_B_001','Item_B_002']
self.headerNames=['Column 0','Column 1','Column 2','Column 3','Column 4','Column 5','Column 6','Column 7']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return len(self.headerNames)
def data(self, index, role):
if not index.isValid(): return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
row=index.row()
if row<len(self.items):
return QVariant(self.items[row])
else:
return QVariant()
class Proxy(QSortFilterProxyModel):
def __init__(self):
super(Proxy, self).__init__()
def filterAcceptsRow(self, row, parent):
return True
def headerData(self, column, orientation, role=Qt.DisplayRole):
sourceModel=self.sourceModel()
if role==Qt.TextAlignmentRole:
if orientation==Qt.Horizontal:
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
if role==Qt.DisplayRole:
if orientation==Qt.Horizontal:
return QVariant( sourceModel.headerNames[column] )
else:
return QVariant()
else:
return QVariant()
return QVariant(int(column+1))
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tableModel=Model(self)
proxyModel=Proxy()
proxyModel.setSourceModel(tableModel)
self.tableview=QTableView(self)
self.tableview.setModel(proxyModel)
self.tableview.horizontalHeader().setStretchLastSection(True)
self.tableview.setSelectionMode(QAbstractItemView.MultiSelection)
header=self.tableview.horizontalHeader()
header.setContextMenuPolicy(Qt.CustomContextMenu)
header.connect(header, SIGNAL("customContextMenuRequested(QPoint)" ), self.headerRightClicked)
self.resHeaderMenu=QMenu(self)
for column in range(proxyModel.columnCount()):
columnName=proxyModel.headerData(column, Qt.Horizontal).toPyObject()
actn=QAction('%s'%columnName, self.resHeaderMenu, checkable=True)
actn.setChecked(True)
actn.triggered.connect(self.resHeaderMenuTriggered)
self.resHeaderMenu.addAction(actn)
layout = QVBoxLayout(self)
layout.addWidget(self.tableview)
self.setLayout(layout)
def headerRightClicked(self, QPos):
parentPosition=self.tableview.mapToGlobal(QPoint(0, 0))
menuPosition=parentPosition + QPos
self.resHeaderMenu.move(menuPosition)
self.resHeaderMenu.show()
def resHeaderMenuTriggered(self, arg):
print 'resHeaderMenuTriggered', arg
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.tableview.setColumnHidden(i, True)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
如果您真的想自己重新排列列:
您可以通过 horizontalHeader
的 moveSection and swapSections 方法移动或交换部分。
headerView.moveSection(x, y)
将移动第 x
列,使其成为 y
列之后,而
headerView.swapSections(x,y)
将 - 显然 - 交换两列的位置。
感谢您在 header-column right-click 示例中实现的 right-click 菜单。正确寻找与您相同的功能。只有一件事,当刻度为 re-checked 时,该列应该显示,即你的 resHeaderMenuTriggered()
应该写成:
def resHeaderMenuTriggered(self, arg):
print('resHeaderMenuTriggered', arg)
for i, actn in enumerate(self.resHeaderMenu.actions()):
if not actn.isChecked():
self.proxyView.setColumnHidden(i, True)
else:
self.proxyView.setColumnHidden(i, False)