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()