原理:使用三张表进行堆叠,在水平或者垂直滑动条的值发生变化时改变这三张表的堆叠顺序,使用stackUnder()进行堆叠顺序的改变
直接撸代码:
1 import typing 2 from PyQt5 import QtCore, QtGui 3 from PyQt5.QtWidgets import QTableView, QAbstractItemView, QHeaderView, QAbstractSlider 4 from PyQt5.QtCore import Qt, QModelIndex 5 6 7 class FreezeTableView(QTableView, QAbstractSlider): 8 9 def __init__(self, model): 10 super(FreezeTableView, self).__init__() 11 self.model = model 12 self.frozenTableView = QTableView(self) 13 self.horizontalView = QTableView(self) 14 self.up = True 15 16 def init(self): 17 self.setModel(self.model) 18 self.frozenTableInit() 19 self.horizontalViewInit() 20 21 self.horizontalHeader().sectionResized.connect(self.updateSectionWidth) 22 self.verticalHeader().sectionResized.connect(self.updateSectionHeight) 23 self.verticalScrollBar().valueChanged.connect(self.vConnectFV) 24 self.frozenTableView.verticalScrollBar().valueChanged.connect(self.fVConnectV) 25 self.horizontalScrollBar().valueChanged.connect(self.hConnectH) 26 27 def vConnectFV(self, a0: int): 28 self.viewport().stackUnder(self.frozenTableView) 29 self.frozenTableView.stackUnder(self.horizontalView) 30 self.frozenTableView.verticalScrollBar().setValue(a0) 31 32 def fVConnectV(self, a0: int): 33 self.viewport().stackUnder(self.frozenTableView) 34 self.frozenTableView.stackUnder(self.horizontalView) 35 self.verticalScrollBar().setValue(a0) 36 37 def hConnectH(self, a0: int): 38 self.viewport().stackUnder(self.horizontalView) 39 self.horizontalView.stackUnder(self.frozenTableView) 40 self.horizontalView.horizontalScrollBar().setValue(a0) 41 42 def frozenTableInit(self): 43 self.frozenTableView.setModel(self.model) 44 self.frozenTableView.verticalHeader().hide() 45 self.frozenTableView.setFocusPolicy(Qt.NoFocus) 46 self.frozenTableView.horizontalHeader().setFixedHeight(self.horizontalHeader().height()) 47 self.frozenTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) 48 49 self.viewport().stackUnder(self.frozenTableView) 50 self.frozenTableView.setStyleSheet('QTableView {' 51 'border: none;' 52 'background-color: #8EDE21;' 53 'selection-background-color: #999}') 54 self.frozenTableView.setSelectionModel(self.selectionModel()) 55 [self.frozenTableView.setColumnHidden(col, True) for col in range(1, self.model.columnCount())] 56 self.frozenTableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 57 self.frozenTableView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 58 self.frozenTableView.show() 59 60 self.updateFrozenTableGeometry() 61 self.frozenTableView.setVerticalScrollMode(self.ScrollPerPixel) 62 self.setVerticalScrollMode(self.ScrollPerPixel) 63 self.setHorizontalScrollMode(self.ScrollPerPixel) 64 65 def horizontalViewInit(self): 66 self.horizontalView.setModel(self.model) 67 self.horizontalView.horizontalHeader().hide() 68 self.horizontalView.setFocusPolicy(Qt.NoFocus) 69 self.horizontalView.verticalHeader().setFixedWidth(self.verticalHeader().width()) 70 self.horizontalView.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) 71 72 self.frozenTableView.stackUnder(self.horizontalView) 73 self.horizontalView.setStyleSheet('QTableView { border: none;' 74 'background-color: #8EDE21;' 75 'selection-background-color: #999}') 76 self.horizontalView.setSelectionModel(self.selectionModel()) 77 [self.horizontalView.setRowHidden(row, True) for row in range(1, self.model.rowCount())] 78 self.horizontalView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 79 self.horizontalView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 80 self.horizontalView.show() 81 82 self.updateFrozenTableGeometry() 83 self.horizontalView.setHorizontalScrollMode(self.ScrollPerPixel) 84 85 def updateFrozenTableGeometry(self): 86 self.frozenTableView.setGeometry(self.verticalHeader().width() + self.frameWidth(), 87 self.frameWidth(), self.columnWidth(0), 88 self.viewport().height() + self.horizontalHeader().height()) 89 self.horizontalView.setGeometry(self.frameWidth(), self.frameWidth() + self.horizontalHeader().height(), 90 self.viewport().width() + self.verticalHeader().width(), self.rowHeight(0)) 91 92 def updateSectionWidth(self, logicalIndex, oldSize, newSize): 93 self.horizontalView.setColumnWidth(logicalIndex, newSize) 94 if not logicalIndex: 95 self.frozenTableView.setColumnWidth(0, newSize) 96 self.updateFrozenTableGeometry() 97 98 def updateSectionHeight(self, logicalIndex, oldSize, newSize): 99 self.frozenTableView.setRowHeight(logicalIndex, newSize) 100 if not logicalIndex: 101 self.horizontalView.setRowHeight(0, newSize) 102 self.updateFrozenTableGeometry() 103 104 def resizeEvent(self, e: QtGui.QResizeEvent) -> None: 105 QTableView.resizeEvent(self, e) 106 self.updateFrozenTableGeometry() 107 108 def scrollTo(self, index: QtCore.QModelIndex, hint: QAbstractItemView.ScrollHint = ...) -> None: 109 if index.column() > 0 or index.row() > 0: 110 QTableView.scrollTo(self, index, hint) 111 112 def moveCursor(self, cursorAction: QAbstractItemView.CursorAction, 113 modifiers: typing.Union[QtCore.Qt.KeyboardModifiers, 114 QtCore.Qt.KeyboardModifier]) -> QtCore.QModelIndex: 115 current = QTableView.moveCursor(self, cursorAction, modifiers) 116 if cursorAction == QAbstractItemView.MoveLeft and current.column() > 0 117 and self.visualRect(current).topLeft().x() < self.frozenTableView.columnWidth(0): 118 newValue = self.verticalScrollBar().value() + self.visualRect(current).topLeft().x() 119 - self.frozenTableView.columnWidth(0) 120 self.horizontalScrollBar().setValue(newValue) 121 if cursorAction == QAbstractItemView.MoveUp and current.row() > 0 122 and self.visualRect(current).topLeft().y() < self.horizontalView.rowHeight(0): 123 newValue = self.horizontalScrollBar().value() + self.visualRect(current).topLeft().y() 124 - self.horizontalView.rowHeight(0) 125 self.verticalScrollBar().setValue(newValue) 126 127 return current
具体完整代码,可以给git clone https://github.com/llkid/freezeWidget.git,c++实现也是通过该原理实现,有兴趣的可以尝试写一下。