QTableWidget实现冻结多行多列(模拟表头)完美python版

从网上搜索类似功能的代码好难,不是晦涩难懂就是答非所问,要不就是要积分充值什么的,即使充值和给积分,答案也不好,有的还是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

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用以下代码删除QTableWidget中选中的多行: ```python # 获取选中的行 selected_rows = [index.row() for index in self.tableWidget.selectedIndexes()] # 去重 selected_rows = list(set(selected_rows)) # 从后往前删除 selected_rows.sort(reverse=True) for row in selected_rows: self.tableWidget.removeRow(row) ``` 其中,self.tableWidget是QTableWidget对象。首先,获取选中的行,然后去重,最后从后往前删除每一行。 ### 回答2: QTableWidget 是 Qt 库中的一个用于展示表格数据的控件。有时候我们需要在 QTableWidget 中删除选中的多行,这里介绍一种实现方法。 首先,我们需要获取当前选中的行号,然后逐行删除。 ```python # 获取当前选中的行号 selectedRows = self.tableWidget.selectedRanges() # 循环遍历选中的每一行 for range in selectedRows: for i in range.topRow(), range.bottomRow()+1: self.tableWidget.removeRow(i) ``` 以上代码中,`selectedRanges()` 方法会返回一个 QTableWidgetSelectionRange 的列表,表示当前选中的所有行、列范围。循环遍历这个列表,然后遍历每个范围的所有行,调用 `removeRow()` 方法逐行删除。 完整代码实现如下: ```python from PyQt5 import QtWidgets, QtGui, QtCore class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) self.initUI() def initUI(self): self.tableWidget = QtWidgets.QTableWidget(4, 4) self.tableWidget.setHorizontalHeaderLabels(['列1', '列2', '列3', '列4']) self.tableWidget.setVerticalHeaderLabels(['行1', '行2', '行3', '行4']) self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) btn = QtWidgets.QPushButton('删除选中行') btn.clicked.connect(self.deleteSelectedRows) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.tableWidget) layout.addWidget(btn) self.setLayout(layout) def deleteSelectedRows(self): # 获取当前选中的行号 selectedRows = self.tableWidget.selectedRanges() # 循环遍历选中的每一行 for range in selectedRows: for i in range.topRow(), range.bottomRow()+1: self.tableWidget.removeRow(i) if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_()) ``` 以上代码实现了一个有 4 行 4 列的 QTableWidget,选中多行后点击按钮可删除选中行。 ### 回答3: QTableWidget是Qt中常用的表格控件,可以实现数据的表格展示和编辑。在QTableWidget中,如果需要删除选中的多行,可以按照以下步骤进行实现: 1. 获取当前选中的行数 ```python selected_rows = [] for item in table.selectedItems(): row = item.row() if row not in selected_rows: selected_rows.append(row) ``` 2. 根据选中的行数,逐一删除对应的行数据 ```python selected_rows.sort(reverse=True) # 倒序删除 for row in selected_rows: table.removeRow(row) ``` 完整代码示例: ```python def remove_rows(table): # 获取选中行数 selected_rows = [] for item in table.selectedItems(): row = item.row() if row not in selected_rows: selected_rows.append(row) # 倒序删除对应行 selected_rows.sort(reverse=True) for row in selected_rows: table.removeRow(row) ``` 调用remove_row函数即可删除选中的多行

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值