首页 > 解决方案 > 如何将 PyQt5 QTableView 的verticalHeader 移动到右侧?

问题描述

我想重新排列我的 QTableView 以便verticalHeader 出现在右侧,如下所示:
在此处输入图像描述

我尝试使用布局小部件进行此操作,但下面的代码给了我这个...
在此处输入图像描述

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QModelIndex, Qt
import sys


class MyTableModel(QtCore.QAbstractTableModel):
    def __init__(self, data=[[]], parent=None):
        super().__init__(parent)
        self.data = data

    def headerData(self, section: int, orientation: Qt.Orientation, role: int):
        if role == QtCore.Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return "Column " + str(section)
            else:
                return "Row " + str(section)

    def columnCount(self, parent=None):
        return len(self.data[0])

    def rowCount(self, parent=None):
        return len(self.data)

    def data(self, index: QModelIndex, role: int):
        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            col = index.column()
            return str(self.data[row][col])


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    data = [[11, 12, 13, 14, 15],
            [21, 22, 23, 24, 25],
            [31, 32, 33, 34, 35]]

    model = MyTableModel(data)
    view = QtWidgets.QTableView()
    view.setModel(model)

    gridLayout = QtWidgets.QGridLayout()
    gridLayout.addWidget(view, 0, 0, 1, 1)
    gridLayout.addWidget(view.verticalHeader(), 0, 1, 1, 1)

    container = QtWidgets.QWidget()
    container.setLayout(gridLayout)
    container.show()

    sys.exit(app.exec_())

标签: pythonpyqtpyqt5

解决方案


一个基本(但不完美)的解决方案是将layoutDirection小部件设置为RightToLeft

view.setLayoutDirection(QtCore.Qt.RightToLeft)

如果您仍然想保持文本向左对齐,您可以返回相反的布局方向TextAlignmentRole(文本对齐相对于layoutDirection,所以如果是RightToLeft,返回左对齐会导致右对齐):

def data(self, index: QModelIndex, role: int):
    # ...
    if role == QtCore.Qt.TextAlignmentRole:
        return QtCore.Qt.AlignRight

正如评论中所指出的,这有一些缺点:整个水平布局系统将被反转,这可能会导致用户出现意外行为。

您当然不应该尝试通过将项目视图的标题添加到另一个小部件的布局来重新设置或移动它,因为它不会按预期工作。

滚动区域有一个setViewportMargins()功能,但对于项目视图来说总是被覆盖(因为它用于根据标题的内容设置边距)。

一个可能的(并且显然比上面更好)的解决方案是对表进行子类化并覆盖updateGeometries()函数,以便我们可以在setViewportMargins() 之后调用.

之后,您必须手动设置两个标题的几何形状,并确保私有角落小部件(用于选择表中所有项目的左上角“隐藏”按钮)正确定位。

class RightHeaderTable(QtWidgets.QTableView):
    def __init__(self):
        super().__init__()
        self._cornerButton = self.findChild(QtWidgets.QAbstractButton)
        self._geometryRecursionBlock = False

    def updateGeometries(self):
        super().updateGeometries()
        # avoid recursive calls (important for interactively resized columns)
        if self._geometryRecursionBlock:
            return
        self._geometryRecursionBlock = True

        margins = self.viewportMargins()
        left = margins.left()
        margins.setRight(left)
        margins.setLeft(0)
        self.setViewportMargins(margins)

        vHeader = self.verticalHeader()
        geo = vHeader.geometry()
        geo.moveRight(self.viewport().geometry().right() + left)
        vHeader.setGeometry(geo)

        hHeader = self.horizontalHeader()
        geo = hHeader.geometry()
        geo.moveLeft(self.viewport().geometry().left())
        hHeader.setGeometry(geo)

        self._cornerButton.move(geo.width(), 0)

        self._geometryRecursionBlock = False

请注意,这是一个非常基本的实现。您可能会注意到一些意想不到的行为。


推荐阅读