冻结表格列PyQt

2 篇文章 0 订阅

QT有个官方的例子:Frozen Column Example,在Qt Creator例子查找即可

官方例子python版本:

Frozen Column Example - Qt for Python

不过官方python版应该是机器直接翻译的C++版的,代码都不正常

根据C++和python版,自己实现了python版的,完善部分功能

import sys

from PyQt5.QtWidgets import QApplication, QHeaderView, QTableView, QAbstractItemView, QScrollBar, QFrame
from PyQt5.QtCore import Qt
# from PyQt5.QtCore import Signal, Slot
# from PyQt5.QtCore import SIGNAL, SLOT
from PyQt5.QtGui import QStandardItemModel, QStandardItem


class FreezeTableWidget(QTableView):
    """
    冻结部分列类

    参照例程 frozencolumn 实现,
    https://doc.qt.io/qtforpython/overviews/qtwidgets-itemviews-frozencolumn-example.html?highlight=frozen
    中也包含部分修改后的python代码,似乎不完整
    """

    def __init__(self, parent=None):
        super(FreezeTableWidget, self).__init__()

        # 创建一个QTableView子控件
        self.frozenTableView = QTableView(self)
        self.frozenColumnCount = 1  # 冻结列数,冻结前几列

        # connect the headers and scrollbars of both tableviews together
        # self.connect(self.horizontalHeader(), SIGNAL('sectionResized(int,int,int)'), self, SLOT('updateSectionWidth(int,int,int)'))
        self.horizontalHeader().sectionResized.connect(self.updateSectionWidth)
        # self.connect(self.verticalHeader(), SIGNAL('sectionResized(int,int,int)'), self, SLOT('updateSectionHeight(int,int,int)'))
        self.verticalHeader().sectionResized.connect(self.updateSectionHeight)

        # self.connect(self.frozenTableView.horizontalHeader(), SIGNAL('sectionResized(int,int,int)'), self, SLOT('frozenTableHeaderSectionResized(int,int,int)'))
        self.frozenTableView.horizontalHeader().sectionResized.connect(self.frozenTableHeaderSectionResized)

        # self.connect(self.frozenTableView.verticalScrollBar(), SIGNAL('valueChanged(int)'), self.verticalScrollBar(), SLOT('setValue(int)'))
        self.frozenTableView.verticalScrollBar().valueChanged.connect(self.verticalScrollBar().setValue)
        # self.connect(self.verticalScrollBar(), SIGNAL('valueChanged(int)'), self.frozenTableView.verticalScrollBar(), SLOT('setValue(int)'))
        self.verticalScrollBar().valueChanged.connect(self.frozenTableView.verticalScrollBar().setValue)

    def initFrozenTableView(self):
        """
        初始化 frozenTableView 表格对象
        :return:
        """
        if self.model() is None:
            return
        self.frozenTableView.setModel(self.model())
        # self.frozenTableView.setFocusPolicy(Qt.NoFocus)
        self.frozenTableView.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        self.frozenTableView.verticalHeader().hide()
        # self.frozenTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) # 设置表头固定大小

        self.viewport().stackUnder(self.frozenTableView)

        # 设置无边框
        # self.frozenTableView.setFrameShape(QFrame.NoFrame)
        # self.frozenTableView.setFrameStyle(QFrame.NoFrame | QFrame.Plain)
        self.frozenTableView.setFrameStyle(QFrame.Shape.NoFrame | QFrame.Shadow.Plain)
        # self.frozenTableView.setStyleSheet("QTableView { border: none;background-color: #8EDE21;selection-background-color: #999}")  # 设置无边框和背景色
        # self.frozenTableView.setStyleSheet("QTableView { border: none;}")   # 设置无边框

        self.frozenTableView.setSelectionModel(self.selectionModel())  # 设置相同的选择模式

        for col in range(self.model().columnCount()):
            # frozenTableView隐藏不冻结列
            self.frozenTableView.setColumnHidden(col, True if col >= self.frozenColumnCount else False)
            # frozenTableView冻结列设置宽度等于主表格
            self.frozenTableView.setColumnWidth(col, self.columnWidth(col))

        # 不显示滚动条
        self.frozenTableView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.frozenTableView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.frozenTableView.show()

        self.updateFrozenTableGeometry()

        self.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
        self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
        self.frozenTableView.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
        self.frozenTableView.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)

    def setFrozenColumnCount(self, frozenColumnCount: int):
        """
        设置冻结列数
        :param frozenColumnCount:
        :return:
        """
        self.frozenColumnCount = frozenColumnCount
        self.initFrozenTableView()  # 重新初始化 frozenTableView

    # 重载父类函数
    def setModel(self, model):
        """
        设置数据模型
        :param model:
        :return:
        """
        super().setModel(model)
        self.initFrozenTableView()  # 重新初始化 frozenTableView
        # 连接信号,在列改变时重新设置 frozenTableView 的列显示隐藏
        model.columnsInserted.connect(self.columnsInserted)

    def setSortingEnabled(self, enable):
        """
        设置是否支持排序
        :param enable:
        :return:
        """
        super().setSortingEnabled(enable)
        self.frozenTableView.setSortingEnabled(enable)

    # @Slot()
    def updateSectionWidth(self, logicalIndex, oldSize, newSize):
        """
        更新冻结table的列宽
        :param logicalIndex:
        :param oldSize:
        :param newSize:
        :return:
        """
        if logicalIndex < self.frozenColumnCount:
            self.frozenTableView.setColumnWidth(logicalIndex, newSize)
            self.updateFrozenTableGeometry()

    # @Slot()
    def updateSectionHeight(self, logicalIndex, oldSize, newSize):
        """
        更新冻结table的行高
        :param logicalIndex:
        :param oldSize:
        :param newSize:
        :return:
        """
        self.frozenTableView.setRowHeight(logicalIndex, newSize)

    # @Slot()
    def frozenTableHeaderSectionResized(self, logicalIndex, oldSize, newSize):
        """
        冻结列表格大小改变,同时修改主表格相应列宽
        :param logicalIndex:
        :param oldSize:
        :param newSize:
        :return:
        """
        # print("frozenTableHeaderSectionResized({},{},{})".format(logicalIndex, oldSize, newSize))
        if logicalIndex < self.frozenColumnCount:
            self.setColumnWidth(logicalIndex, newSize)

    def resizeEvent(self, event):
        """
        重载父类函数
        :param event:
        :return:
        """
        super().resizeEvent(event)
        self.updateFrozenTableGeometry()

    def moveCursor(self, cursorAction, modifiers):
        """
        重载父类函数
        :param cursorAction:
        :param modifiers:
        :return:
        """
        # print("moveCursor({}, {})".format(cursorAction, modifiers))
        current = super().moveCursor(cursorAction, modifiers)
        # 游标左移时避免 frozenTableView 遮住当前选择的项
        if cursorAction == QAbstractItemView.CursorAction.MoveLeft \
                and current.column() >= self.frozenColumnCount \
                and self.visualRect(current).topLeft().x() < self.frozenTableView.width():
            newValue = self.horizontalScrollBar().value() + self.visualRect(
                current).topLeft().x() - self.frozenTableView.width()
            self.horizontalScrollBar().setValue(newValue)

        # 右移、上下移动游标时,避免 frozenTableView 遮住当前选择的项
        if ((cursorAction == QAbstractItemView.CursorAction.MoveRight
             or cursorAction == QAbstractItemView.CursorAction.MoveUp
             or cursorAction == QAbstractItemView.CursorAction.MoveDown
             or cursorAction == QAbstractItemView.CursorAction.MovePageUp
             or cursorAction == QAbstractItemView.CursorAction.MovePageDown
             or cursorAction == QAbstractItemView.CursorAction.MoveNext
             or cursorAction == QAbstractItemView.CursorAction.MovePrevious)
                and self.visualRect(current).topLeft().x() < self.frozenTableView.width()):
            newValue = self.horizontalScrollBar().value() + self.visualRect(
                current).topLeft().x() - self.frozenTableView.width()
            self.horizontalScrollBar().setValue(newValue)

        return current

    def scrollTo(self, index, hint):
        """
        重载父类函数
        :param index:
        :param hint:
        :return:
        """
        # print("scrollTo({}, {})".format(index, hint))
        if index.column() >= self.frozenColumnCount:
            super().scrollTo(index, hint)

    def updateFrozenTableGeometry(self):
        """
        更新冻结表格Geometry
        :return:
        """
        x = self.verticalHeader().width() + self.frameWidth()
        y = self.frameWidth()
        w = 0  # self.columnWidth(0)
        for col in range(0, self.frozenColumnCount):
            w += self.columnWidth(col)
        h = self.viewport().height() + self.horizontalHeader().height()
        # 设置新位置和宽高
        self.frozenTableView.setGeometry(x, y, w, h)

    # @Slot()
    def columnsInserted(self, parent, first, last):
        """
        在列改变时重新设置 frozenTableView 的列显示隐藏
        :param parent:
        :param first:
        :param last:
        :return:
        """
        # print("columnsInserted({},{},{})".format(parent, first, last))
        for col in range(self.model().columnCount()):
            # frozenTableView隐藏不冻结列
            self.frozenTableView.setColumnHidden(col, True if col >= self.frozenColumnCount else False)
            # frozenTableView冻结列设置宽度等于主表格
            self.frozenTableView.setColumnWidth(col, self.columnWidth(col))


if __name__ == '__main__':
    def test():
        # 测试例子
        app = QApplication(sys.argv)

        model = QStandardItemModel()
        horizontalHeaderLabel = []
        for col in range(20):
            horizontalHeaderLabel.append("列{}".format(col))
        model.setHorizontalHeaderLabels(horizontalHeaderLabel)
        for col in range(len(horizontalHeaderLabel)):
            for row in range(50):
                # if col != 4:
                model.setItem(row, col, QStandardItem('{},{}'.format(row, col)))
                # else:
                #    model.setItem(row, col, QStandardItem('{},{}XXXXXXXXXXXXXXXX'.format(row, col)))

        tableView = FreezeTableWidget()
        tableView.setWindowTitle("Frozen Column Example")
        tableView.resize(560, 680)
        tableView.setSortingEnabled(True)

        tableView.setFrozenColumnCount(2)
        tableView.setModel(model)

        tableView.show()

        sys.exit(app.exec())


    # 调用测试例子
    test()

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值