如何将 PyQt5 QTableView 的 verticalHeader 移到右侧?

How can I move a PyQt5 QTableView's verticalHeader to the right side?

我想重新排列我的 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_())

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

view.setLayoutDirection(QtCore.Qt.RightToLeft)

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

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

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

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

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

一个可能的(并且显然比上面的更好)解决方案是继承 table 并覆盖 updateGeometries() 函数,这样我们就可以调用 setViewportMargins() 之后.

之后,您必须手动设置两个 header 的几何形状,并确保 private 角落小部件(top-left“用于 select table 中所有项目的隐藏”按钮已正确定位。

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

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