如何重新排列QTableView的列顺序

5
下面的代码创建了一个由模型/代理框架驱动的单个 QTableView。
在 source-model 中声明的 self.headerNames 列表变量存储 Header 的列名。该列表中的名称数量由同一 source model 的 columnCount() 方法使用,以返回视图中的列数:
def columnCount(self, parent=QModelIndex()):
    return len(self.headerNames)
< p > Proxy模型的headerData()通过source model访问self.headerNames变量:

sourceModel=self.sourceModel()

role==Qt.DisplayRole 时,代理将检索并返回列的名称给 QTableView:

return QVariant( sourceModel.headerNames[column] )

在标题列右键单击时,已经实现了右键菜单。那部分功能正常。但由于我找不到其他人如何实现它的例子,所以我必须自己设计它的工作方式。如果您发现它可以改进,我会很感激。
接下来我想要实现的是能够任意重新排列列的能力。但我不确定从哪里开始。
注:请忽略在QTableView中显示的项目名称。我希望将代码尽可能简单,只关注标题/列主题。
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_())

你知道吗?通过setMovable函数,你可以开启列拖动功能,而不需要自己实现它... - sebastian
1
setMovable 在5.11版本中似乎已经过时。这里建议您改用setSectionsMovable - Jason
2个回答

10

如果您真的想自己重新排列列:

您可以通过horizontalHeadermoveSectionswapSections方法移动或交换部分。

headerView.moveSection(x, y)

将列 x 移动到其后面是列 y,同时保留原有的顺序。
headerView.swapSections(x,y)

显然,will会交换这两列的位置。


谢谢Sebastian!我很感激你的帮助! - alphanumeric

0

感谢在标题列右键单击示例中实现右键菜单。我正在寻找与您相同的功能。只有一件事,当重新选中时,应该显示该列,即您的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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接