冻结多行多列(模拟表头)的 QTableWidget 实现的完美 python 版本
最编程
2024-03-26 20:25:58
...
从网上搜索类似功能的代码好难,不是晦涩难懂就是答非所问,要不就是要积分充值什么的,即使充值和给积分,答案也不好,有的还是C++的部分代码。在网上搜索到某位兄弟从原C++版本更改后的python版,实现了能冻结部分列的功能,于是仔细研究下,先改为冻结部分行功能,再将冻结行和冻结列综合起来,完美实现了同时冻结行和列模拟表头。我这里使用QTableWidget实现,比QTableView更方便使用。具体设置,setFrozenRowColumnCount(frozenRowCount=0,frozenColumnCount=0):1,默认不冻结行和列;2,可以冻结行或者冻结列;3,同时冻结多行和多列。增加3个QTableWidget,1个冻结列,模拟VerticalHeaderView,1个冻结行,模拟HorizontalHeaderView,另一个放在左上角,作为冻结行和冻结列的交叉部分。除冻结完美实行外,setItem()、item()、setSpan()等操作和函数如同使用一个QTableWidget。全部代码如下,在Windows10/7+python3.8+PyQt5下运行正常:
##完美实现冻结多行多列(模拟表头)
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class FreezeTableWidget(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.frozenColumnTableWidget = QTableWidget(self)#冻结列
self.frozenColumnTableWidget.hide()#先隐藏
self.frozenColumnCount=0
self.frozenRowTableWidget = QTableWidget(self)#冻结行
self.frozenRowTableWidget.hide()#先隐藏
self.frozenRowCount=0
self.frozenLeftTopTableWidget = QTableWidget(self)#左上角
self.frozenLeftTopTableWidget.hide()#先隐藏
def setFrozenRowColumnCount(self,frozenRowCount=0,frozenColumnCount=0):
if frozenRowCount>self.rowCount():#初始化冻结行
frozenRowCount=self.rowCount()
self.frozenRowCount = frozenRowCount if frozenRowCount>0 else 0
if self.frozenRowCount>0:
self.initFrozenRowTableWidget()
if frozenColumnCount>self.columnCount():#初始化冻结列
frozenColumnCount=self.columnCount()
self.frozenColumnCount = frozenColumnCount if frozenColumnCount>0 else 0
if self.frozenColumnCount>0:
self.initFrozenColumnTableWidget()
if self.frozenRowCount>0 and self.frozenColumnCount>0:
self.initFrozenLeftTopTableWidget()#初始化左上角
def frozenLeftTopTableWidgetObject(self):
return self.frozenLeftTopTableWidget if self.frozenRowCount>0 and self.frozenColumnCount>0 else None
def frozenColumnTableWidgetObject(self):
return self.frozenColumnTableWidget if self.frozenColumnCount>0 else None
def frozenRowTableWidgetObject(self):
return self.frozenRowTableWidget if self.frozenRowCount>0 else None
def initFrozenLeftTopTableWidget(self):#左上角公共表头
self.frozenLeftTopTableWidget.setRowCount(self.frozenRowCount)
self.frozenLeftTopTableWidget.setColumnCount(self.frozenColumnCount)
for row in range(self.frozenRowCount):
for col in range(self.frozenColumnCount):
if self.item(row,col)!=None:#复制item #不好复制cellwidget
item=QTableWidgetItem()
item.setText(self.item(row,col).text())
item.setFlags(self.item(row,col).flags())
item.setBackground(self.item(row,col).background())
item.setTextAlignment(self.item(row,col).textAlignment())
self.frozenLeftTopTableWidget.setItem(row,col,item)
self.frozenLeftTopTableWidget.setColumnWidth(col,self.columnWidth(col))
self.frozenLeftTopTableWidget.setRowHeight(row,self.rowHeight(row))
self.frozenLeftTopTableWidget.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.frozenLeftTopTableWidget.verticalHeader().hide()#都不要表头
self.frozenLeftTopTableWidget.horizontalHeader().hide()
self.horizontalHeader().hide()
self.verticalHeader().hide()
self.frozenLeftTopTableWidget.raise_()
# 设置无边框
self.frozenLeftTopTableWidget.setFrameStyle(QFrame.Shape.NoFrame | QFrame.Shadow.Plain)
# 不显示滚动条
self.frozenLeftTopTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenLeftTopTableWidget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenLeftTopTableWidget.show()
self.updateFrozenLeftTopTableGeometry()
def initFrozenColumnTableWidget(self):# 初始化 frozenColumnTableWidget
self.frozenColumnTableWidget.setColumnCount(self.frozenColumnCount)#只复制冻结列数据
self.frozenColumnTableWidget.setRowCount(self.rowCount())
for col in range(self.frozenColumnCount):#设置列宽
for row in range(self.rowCount()):
if self.item(row,col)!=None:#复制item #不好复制cellwidget
item=QTableWidgetItem()
item.setText(self.item(row,col).text())
item.setFlags(self.item(row,col).flags())
item.setBackground(self.item(row,col).background())
item.setTextAlignment(self.item(row,col).textAlignment())
self.frozenColumnTableWidget.setItem(row,col,item)
self.frozenColumnTableWidget.setRowHeight(row, self.rowHeight(row))
self.frozenColumnTableWidget.setColumnWidth(col, self.columnWidth(col))
self.frozenColumnTableWidget.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.frozenColumnTableWidget.verticalHeader().hide()#都不要表头
self.frozenColumnTableWidget.horizontalHeader().hide()
self.horizontalHeader().hide()
self.verticalHeader().hide()
self.viewport().stackUnder(self.frozenColumnTableWidget)
# 设置无边框
self.frozenColumnTableWidget.setFrameStyle(QFrame.Shape.NoFrame | QFrame.Shadow.Plain)
# 不显示滚动条
self.frozenColumnTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenColumnTableWidget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenColumnTableWidget.show()
self.updateFrozenColumnTableGeometry()
self.horizontalHeader().sectionResized.connect(self.updateHorizontalSectionWidth)
self.verticalHeader().sectionResized.connect(self.updateVerticalSectionHeight)
self.frozenColumnTableWidget.horizontalHeader().sectionResized.connect(self.frozenTableHorizontalHeaderSectionResized)
self.frozenColumnTableWidget.verticalScrollBar().valueChanged.connect(self.verticalScrollBar().setValue)##垂直滚动条
self.verticalScrollBar().valueChanged.connect(self.frozenColumnTableWidget.verticalScrollBar().setValue)
self.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.frozenColumnTableWidget.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.frozenColumnTableWidget.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
def initFrozenRowTableWidget(self):# 初始化 frozenRowTableWidget
self.frozenRowTableWidget.setRowCount(self.frozenRowCount)#只复制冻结列数据
self.frozenRowTableWidget.setColumnCount(self.columnCount())
for row in range(self.frozenRowCount):#设置行高
for col in range(self.columnCount()):
if self.item(row,col)!=None:#复制item #不好复制cellwidget
item=QTableWidgetItem()
item.setText(self.item(row,col).text())
item.setFlags(self.item(row,col).flags())
item.setBackground(self.item(row,col).background())
item.setTextAlignment(self.item(row,col).textAlignment())
self.frozenRowTableWidget.setItem(row,col,item)
self.frozenRowTableWidget.setColumnWidth(col, self.columnWidth(col) )
self.frozenRowTableWidget.setRowHeight(row, self.rowHeight(row) )
self.frozenRowTableWidget.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.frozenRowTableWidget.verticalHeader().hide()#都不要表头
self.frozenRowTableWidget.horizontalHeader().hide()
self.horizontalHeader().hide()
self.verticalHeader().hide()
self.viewport().stackUnder(self.frozenRowTableWidget)
# 设置无边框
self.frozenRowTableWidget.setFrameStyle(QFrame.Shape.NoFrame | QFrame.Shadow.Plain)
# 不显示滚动条
self.frozenRowTableWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenRowTableWidget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.frozenRowTableWidget.show()
self.updateFrozenRowTableGeometry()
self.horizontalHeader().sectionResized.connect(self.updateHorizontalSectionWidth)
self.verticalHeader().sectionResized.connect(self.updateVerticalSectionHeight)
self.frozenRowTableWidget.verticalHeader().sectionResized.connect(self.frozenTableVerticalHeaderSectionResized)##
self.frozenRowTableWidget.horizontalScrollBar().valueChanged.connect(self.horizontalScrollBar().setValue)##水平滚动条
self.horizontalScrollBar().valueChanged.connect(self.frozenRowTableWidget.horizontalScrollBar().setValue)
self.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.frozenRowTableWidget.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.frozenRowTableWidget.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
def updateHorizontalSectionWidth(self, logicalIndex, oldSize, newSize):
if self.frozenColumnCount>0:
if logicalIndex < self.frozenColumnCount:
self.frozenColumnTableWidget.setColumnWidth(logicalIndex, newSize)
self.updateFrozenColumnTableGeometry()
if self.frozenRowCount>0:
self.frozenRowTableWidget.setColumnWidth(logicalIndex, newSize)
def updateVerticalSectionHeight(self, logicalIndex, oldSize, newSize):
if self.frozenRowCount>0:
if logicalIndex < self.frozenRowCount:
self.frozenRowTableWidget.setRowHeight(logicalIndex, newSize)
self.updateFrozenRowTableGeometry()
if self.frozenColumnCount>0:
self.frozenColumnTableWidget.setRowHeight(logicalIndex, newSize)
def frozenTableHorizontalHeaderSectionResized(self, logicalIndex, oldSize, newSize):
if self.frozenColumnCount>0:
if logicalIndex < self.frozenColumnCount:
self.setColumnWidth(logicalIndex, newSize)
def frozenTableVerticalHeaderSectionResized(self, logicalIndex, oldSize, newSize):
if self.frozenRowCount>0:
if logicalIndex < self.frozenRowCount:
self.setRowHeight(logicalIndex, newSize)
def resizeEvent(self, event):
super().resizeEvent(event)
if self.frozenColumnCount>0:
self.updateFrozenColumnTableGeometry()
if self.frozenRowCount>0:
self.updateFrozenRowTableGeometry()
if self.frozenColumnCount>0 and self.frozenRowCount>0:
self.updateFrozenLeftTopTableGeometry()
def updateFrozenColumnTableGeometry(self):
if self.frozenColumnCount>0:
x = self.frameWidth()
y = self.frameWidth()
w = 0
for col in range(self.frozenColumnCount):
w += self.columnWidth(col)
h = self.viewport().height()
# 设置新位置和宽高
self.frozenColumnTableWidget.setGeometry(x, y, w, h)
def updateFrozenRowTableGeometry(self):
if self.frozenRowCount>0:
x = self.frameWidth()
y = self.frameWidth()
w = self.viewport().width()
h = 0
for row in range(self.frozenRowCount):
h += self.rowHeight(row)
# 设置新位置和宽高
self.frozenRowTableWidget.setGeometry(x, y, w, h)
def updateFrozenLeftTopTableGeometry(self):
x = self.frameWidth()
y = self.frameWidth()
w = 0
for col in range(self.frozenColumnCount):
w += self.columnWidth(col)
h = 0
for row in range(self.frozenRowCount):
h += self.rowHeight(row)
# 设置新位置和宽高
self.frozenLeftTopTableWidget.setGeometry(x, y, w, h)
def setItem(self,row,col,item):
if self.frozenRowCount>0 and row<self.frozenRowCount and \
self.frozenColumnCount>0 and col<self.frozenColumnCount:
self.frozenLeftTopTableWidget.setItem(row,col,item)
elif self.frozenRowCount>0 and row<self.frozenRowCount:
self.frozenRowTableWidget.setItem(row,col,item)
elif self.frozenColumnCount>0 and col<self.frozenColumnCount:
self.frozenColumnTableWidget.setItem(row,col,item)
else:
super().setItem(row,col,item)
def item(self,row,col):
if self.frozenRowCount>0 and row<self.frozenRowCount and \
self.frozenColumnCount>0 and col<self.frozenColumnCount:
return self.frozenLeftTopTableWidget.item(row,col)
elif self.frozenRowCount>0 and row<self.frozenRowCount:
return self.frozenRowTableWidget.item(row,col)
elif self.frozenColumnCount>0 and col<self.frozenColumnCount:
return self.frozenColumnTableWidget.item(row,col)
else:
return super().item(row,col)
def setCellWidget(self,row,col,cellwidget):
super().setCellWidget(row,col,cellwidget)
if self.frozenColumnCount>0:
self.frozenColumnTableWidget.setRowHeight(row,self.rowHeight(row))
self.frozenColumnTableWidget.setColumnWidth(col,self.columnWidth(col))
if self.frozenRowCount>0:
self.frozenRowTableWidget.setRowHeight(row,self.rowHeight(row))
self.frozenRowTableWidget.setColumnWidth(col,self.columnWidth(col))
def setSpan(self,row,col,rows,cols):
if self.frozenColumnCount>0 and col+cols<=self.frozenColumnCount+1 and \
self.frozenRowCount>0 and row+rows<=self.frozenRowCount+1:
self.frozenLeftTopTableWidget.setSpan(row,col,rows,cols)
elif self.frozenColumnCount>0 and col+cols<=self.frozenColumnCount+1:
self.frozenColumnTableWidget.setSpan(row,col,rows,cols)
elif self.frozenRowCount>0 and row+rows<=self.frozenRowCount+1:
self.frozenRowTableWidget.setSpan(row,col,rows,cols)
elif row>=self.frozenRowCount and col>=self.frozenColumnCount and \
row+rows<self.rowCount() and col+cols<self.columnCount():
super().setSpan(row,col,rows,cols)
def setRowHeight(self,row,height):
super().setRowHeight(row,height)
if self.frozenColumnCount>0 :
self.frozenColumnTableWidget.setRowHeight(row,height)
if self.frozenRowCount>0 and row<self.frozenRowCount:
self.frozenRowTableWidget.setRowHeight(row,height)
if self.frozenColumnCount>0 :
self.frozenLeftTopTableWidget.setRowHeight(row,height)
def setColumnWidth(self,col,width):
super().setColumnWidth(col,width)
if self.frozenRowCount>0:
self.frozenRowTableWidget.setColumnWidth(col,width)
if self.frozenColumnCount>0 and col<self.frozenColumnCount:
self.frozenColumnTableWidget.setColumnWidth(col,width)
if self.frozenRowCount>0:
self.frozenLeftTopTableWidget.setColumnWidth(col,width)
def setRowCount(self,count):
lastRowCount=self.rowCount()
if count!=lastRowCount:
super().setRowCount(count)
self.frozenColumnTableWidget.setRowCount(count)
def setColumnCount(self,count):
lastColumnCount=self.columnCount()
if count!=lastColumnCount:
super().setColumnCount(count)
self.frozenRowTableWidget.setColumnCount(count)
if __name__ == '__main__':
# 测试例子
app = QApplication(sys.argv)
tableWidget = FreezeTableWidget()
tableWidget.setRowCount(30)
tableWidget.setColumnCount(10)
tableWidget.setFrozenRowColumnCount(3,3)#冻结 行数 列数
tableWidget.setWindowTitle("QTableWidget冻结多行多列当做表头完美示例")
tableWidget.resize(800,700)
tableWidget.setSortingEnabled(True)
tableWidget.verticalHeader().hide()
for row in range(tableWidget.rowCount()):
for col in range(tableWidget.columnCount()):
if row <3 or col<3:
item=QTableWidgetItem("冻结(%d,%d)" % (row,col) )
item.setBackground(QColor("#eeeeee"))
else:
item=QTableWidgetItem("内容%d,%d" % (row,col) )
item.setBackground(QColor("#ffffff"))
item.setTextAlignment(Qt.AlignCenter)
tableWidget.setItem(row,col,item)
tableWidget.show()
tableWidget.setRowCount(50)
tableWidget.setColumnCount(20)
tableWidget.setSpan(0,0,3,3) ;tableWidget.item(0,0).setText("AA")
tableWidget.setSpan(4,1,3,2) ;tableWidget.item(4,1).setText("BB")
tableWidget.setSpan(1,5,2,3) ;tableWidget.item(1,5).setText("CC")
tableWidget.setSpan(3,3,5,5) ;tableWidget.item(3,3).setText("DD")
sys.exit(app.exec())
运行效果如下:
有兴趣的朋友可以和我交流 V:cxz7558 Q:190561115