欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

冻结多行多列(模拟表头)的 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