实现方式:自定义一个CheckBox样式的QHeaderView,并将它设置为TableWidget的表头, 绑定clicked函数;将TableWidget每一行的第一列设为CheckBox,绑定clicked函数;自定义中间对齐及是否可编辑的Delegate;
TableWidget运行代码:
# coding=utf-8
# python3
"""
@FileName:结合TableWidget的自定义全选表头.py
@Author: XiaoFang
@CreateFileTime: 2024/6/27 18:53
@Description: pyqt
"""
from PyQt5.QtWidgets import (QApplication, QTableWidget, QTableWidgetItem, QWidget, QVBoxLayout, QStyle, QLabel,
QStyledItemDelegate, QLineEdit)
from PyQt5.QtCore import Qt
from ButtonWidget import ButtonWidget
from ButtonHeader import ButtonHeader
class CenterAlignedDelegate(QStyledItemDelegate):
_edit_column = [4]
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.displayAlignment = Qt.AlignCenter
def createEditor(self, parent, option, index):
if index.column() in self._edit_column: # tableWidget部分可编辑 只允许第四列可编辑
editor = QLineEdit(parent)
editor.setAlignment(Qt.AlignCenter)
return editor
return None # 返回 None 来禁止编辑
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.resize(500, 300)
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(10) # 设置布局间距
self.table_widget = QTableWidget(4, 5)
layout.addWidget(self.table_widget)
self.show_label = QLabel(self)
layout.addWidget(self.show_label)
self.check_header = ButtonHeader(_style=QStyle.CE_CheckBox, isOn=False) # 实例化自定义表头
self.table_widget.setHorizontalHeader(self.check_header) # 设置表头段
self.check_header.select_all_clicked.connect(self.select_all_items) # 行表头复选框单击信号与槽
delegate = CenterAlignedDelegate() # 自定义中间对齐及是否可编辑
self.table_widget.setItemDelegate(delegate)
self.table_widget.setHorizontalHeaderLabels(['', '姓名', '性别', '体重', '年龄']) # 设置表头内容
self.table_widget.horizontalHeader().setVisible(True) # 显示水平表头
self.table_widget.verticalHeader().setVisible(False) # 隐藏垂直表头
self.table_widget.setColumnWidth(0, 35)
self.table_widget.setColumnWidth(1, 60)
self.table_widget.setColumnWidth(2, 60)
self.table_widget.setColumnWidth(3, 60)
datas = [
['张三', '男', 60, 20],
['李四', '男', 70, 30],
['王五', '女', 80, 40],
['赵六', '女', 90, 50],]
for idx, item_infor in enumerate(datas):
print(item_infor)
_widget = ButtonWidget(_type='checkbox') # 实例化复选框控件
button = _widget.get_button()
self.check_header.all_header_combobox.append(button)
button.clicked.connect(lambda: self.select_items(0))
self.table_widget.setCellWidget(idx, 0, _widget)
self.table_widget.setItem(idx, 1, QTableWidgetItem(item_infor[0]))
self.table_widget.setItem(idx, 2, QTableWidgetItem(item_infor[1]))
self.table_widget.setItem(idx, 3, QTableWidgetItem(str(item_infor[2])))
self.table_widget.setItem(idx, 4, QTableWidgetItem(str(item_infor[3])))
def select_all_items(self, state):
self.check_header.change_state(state)
self.select_items(1)
def select_items(self, select_all_flag, *args):
names = []
for index, item in enumerate(self.check_header.all_header_combobox):
if item.checkState() == Qt.Checked:
names.append(self.table_widget.item(index, 1).text())
if not select_all_flag:
if all(item.checkState() == Qt.Checked for item in self.check_header.all_header_combobox):
self.check_header.all_change_state(True)
if all(item.checkState() == Qt.Unchecked for item in self.check_header.all_header_combobox):
self.check_header.all_change_state(False)
self.show_label.setText(','.join(names))
if __name__ == '__main__':
app = QApplication([])
window = MyWidget()
window.show()
app.exec_()
自定义QHeaderView控件,ButtonHeader代码:
# coding=utf-8
# python3
"""
@FileName:ButtonHeader.py
@Author: XiaoFang
@CreateFileTime: 2024/6/27 18:27
@Description: 自定义表头QHeaderView
"""
from PyQt5.QtWidgets import QHeaderView, QStyleOptionButton, QStyle
from PyQt5.QtCore import pyqtSignal, QRect, Qt
class ButtonHeader(QHeaderView):
"""自定义表头类
QStyle.CE_CheckBox: CheckBox
QStyle.CE_RadioButton: RadioButton
...
self.check_header = ButtonHeader(_style=QStyle.CE_CheckBox, isOn=False) # 实例化自定义表头
self.EventsTableWidget.setHorizontalHeader(self.check_header) # 设置表头段
self.check_header.select_all_clicked.connect(self.select_all_items) # 行表头复选框单击信号与槽
self.EventsTableWidget.setHorizontalHeaderLabels(['', '1', '2'])
"""
# 自定义 复选框全选信号
select_all_clicked = pyqtSignal(bool)
# 这4个变量控制列头复选框的样式,位置以及大小
_x_offset = 7
_y_offset = 0
_width = 20
_height = 20
def __init__(self, _style=QStyle.CE_CheckBox, orientation=Qt.Horizontal, parent=None, isOn=False):
super(ButtonHeader, self).__init__(orientation, parent)
self.all_header_combobox = []
self.isOn = isOn
self._style = _style
def paintSection(self, painter, rect, logicalIndex):
painter.save()
super(ButtonHeader, self).paintSection(painter, rect, logicalIndex)
painter.restore()
self._y_offset = int((rect.height() - self._width) / 2.)
if logicalIndex == 0:
option = QStyleOptionButton()
option.rect = QRect(rect.x() + self._x_offset, rect.y() + self._y_offset, self._width, self._height)
option.state = QStyle.State_Enabled | QStyle.State_Active
if self.isOn:
option.state |= QStyle.State_On
else:
option.state |= QStyle.State_Off
self.style().drawControl(self._style, option, painter)
def mousePressEvent(self, event):
index = self.logicalIndexAt(event.pos())
if 0 == index:
x = self.sectionPosition(index)
if x + self._x_offset < event.pos().x() < x + self._x_offset + self._width and self._y_offset < event.pos().y() < self._y_offset + self._height:
if self.isOn:
self.isOn = False
else:
self.isOn = True
self.select_all_clicked.emit(self.isOn) # 当用户点击了行表头复选框,发射 自定义信号 select_all_clicked()
self.updateSection(0)
super(ButtonHeader, self).mousePressEvent(event)
# 自定义信号 select_all_clicked 的槽方法
def change_state(self, isOn):
if isOn:
# 将所有的复选框都设为勾选状态
for i in self.all_header_combobox:
i.setCheckState(Qt.Checked)
else:
for i in self.all_header_combobox:
i.setCheckState(Qt.Unchecked)
# 改变行表头复选框状态(不会触发勾选事件)
def all_change_state(self, isOn):
self.isOn = isOn
self.updateSection(0)
自定义widget控件,ButtonWidgetd代码:
# coding=utf-8
# python3
"""
@FileName:自定义按钮widget类.py
@Author: XiaoFang
@CreateFileTime: 2024/6/27 18:10
@Description: pyqt==5.15.7
"""
from PyQt5.QtWidgets import QWidget, QCheckBox, QHBoxLayout, QRadioButton, QPushButton
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QPixmap, QIcon
class ButtonWidget(QWidget):
def __init__(self, parent=None, _type='pushbutton', size=(20, 20), icon_picture=None, icon_size=(18, 18)):
super(ButtonWidget, self).__init__(parent)
self.setAutoFillBackground(True)
if _type == 'checkbox':
self._button = QCheckBox()
elif _type == 'radiobutton':
self._button = QRadioButton()
elif _type == 'pushbutton':
self._button = QPushButton()
self._button.setMinimumSize(*size)
self._button.setMaximumSize(*size)
self._button.setChecked(False)
button_layout = QHBoxLayout()
button_layout.addWidget(self._button, alignment=Qt.AlignCenter)
button_layout.setContentsMargins(0, 0, 0, 0)
button_layout.setSpacing(0)
button_layout.setAlignment(Qt.AlignCenter)
self.setLayout(button_layout)
if icon_picture is not None:
self._button.setIconSize(QSize(*icon_size))
self._button.setStyleSheet("QPushButton {border:0px; background-color:none;}\n"
"QPushButton:hover {background-color:rgb(200,100,100);}")
icon = QIcon()
icon.addPixmap(QPixmap(icon_picture), QIcon.Normal, QIcon.Off)
self._button.setIcon(icon)
def set_size(self, size):
self._button.setMinimumSize(*size)
self._button.setMaximumSize(*size)
def get_button(self):
return self._button
运行效果: