想法:
需要在页面的多个tablewidget中,每个表格控件的最后一列加上勾选框,勾选状态下,此行可编辑,取消勾选之后,该行置灰不可编辑;新增按钮可新增一行
运行结果:
代码
添加勾选框方法
def setup_checkbox(self, table_widget, row, column):
"""
对表格的单元格添加QCheckBox并连接信号
:param table_widget: QTableWidget对象
:param row: 行
:param column: 列
:return:
"""
widget = QWidget() # 方法内的局部变量,每次调用都是新的副本,所以才能单独让每个tablewidget的勾选框独立连接信号
widget.checkbox = QCheckBox() # 将checkbox放在widget中
widget.checkbox.setCheckState(Qt.Checked) # 默认全部勾选
playout = QHBoxLayout(widget)
playout.addWidget(widget.checkbox) # 为小部件添加checkbox属性
playout.setAlignment(Qt.AlignCenter) # 设置小控件水平居中
widget.setLayout(playout) # 在QWidget放置布局
table_widget.setCellWidget(row, column, widget)
# 连接勾选框的stateChanged信号,使用lambda来捕获row和table_widget,并只传递状态参数给回调函数
widget.checkbox.stateChanged.connect(lambda state: self.on_checkbox_state_changed(table_widget, row, state))
新增一行方法
def table_add_row(self, table_widget):
# 在表格下方新增一行
current_row = table_widget.rowCount() # 当前行数
current_col = table_widget.columnCount() # 当前列数
# 插入新行
table_widget.insertRow(current_row)
# 添加勾选框,并且为每个勾选框连接信号
self.setup_checkbox(table_widget, current_row, current_col-1)
for column in range(current_col-1):
# 默认新建的单元格内容是空,所以填充item和属性
item = QTableWidgetItem()
# 设置单元格属性居中
item.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(current_row, column, item)
# 自动调整行高
table_widget.resizeRowToContents(current_row)
self.set_cells_attribute(table_widget)
部分代码
从xml读取内容填充进不同的tablewidget中
class Config_FL_Window(QWidget, Ui_Freq_lineLoss):
# 初始化,配置频点和线损的页面
def __init__(self, app_path='./'):
"""
:param app_path: 程序运行目录
"""
super().__init__()
self.setupUi(self)
self._app_path = app_path # 配置文件的根目录
# 不同监听口在不同表格中显示
self.models_dict = {"TT": self.tableWidget_TT, "TR": self.tableWidget_TR, "TS": self.tableWidget_TS,
"FT": self.tableWidget_FT, "FR": self.tableWidget_FR}
# 不同表格下面的新增按钮
self.add_buttons = {"TT": self.add_TT, "TR": self.add_TR, "TS": self.add_TS, "FT": self.add_FT, "FR": self.add_FR}
# 配置表格的列对应配置文件的标签属性
self.file_with_table_title = {0: "1", 1: "2", 2: "3", 3: "4"}
self.changed_stat = False # 表格修改状态符
self.changed_msg = "" # 保存时的告警消息
# # 设置自定义的信号和槽
self.CreateSignalSlot()
def CreateSignalSlot(self):
""" 设置信号与槽"""
self.save_button.clicked.connect(self.save_config)
self.cancel_button.clicked.connect(self.cancel)
for model in self.models_dict: # 遍历监听口模式,打印在不同的模式模块中
# 检测QTableWidget内容是否发生变化
self.models_dict[model].itemChanged.connect(self.handle_item_changed)
for model, add_b in self.add_buttons.items():
# 把按键对应的模块tablewidget传递进去
add_b.clicked.connect(lambda checked, table=self.models_dict[model]: self.table_add_row(table))
def pop_message(self, m_type, message):
"""
弹窗提示
:param m_type: 弹窗的类型,[information,warning,question,critical,about]
:param message: 提示消息内容
:return:
"""
if m_type == 'information':
QMessageBox.information(self, '消息', message, QMessageBox.Ok)
elif m_type == 'warning':
QMessageBox.warning(self, '警告', message, QMessageBox.Ok)
elif m_type == 'question':
QMessageBox.question(self, '问题', message, QMessageBox.Ok | QMessageBox.No)
elif m_type == 'critical':
QMessageBox.critical(self, '错误', message, QMessageBox.Ok)
elif m_type == 'about':
QMessageBox.about(self, '关于', message)
else:
raise "未识别的提示框类型 " + m_type
def handle_item_changed(self, item):
# 槽函数,当单元格内容发生变化时调用
self.changed_stat = True
if item.tableWidget() == self.tableWidget_TT:
self.changed_msg = f"TT模式,在位置({item.row() + 1}, {item.column() + 1})发生变化,保存后退出 或 取消修改"
elif item.tableWidget() == self.tableWidget_TR:
self.changed_msg = f"TR单元格内容在位置({item.row() + 1}, {item.column() + 1})发生变化,保存后退出 或 取消修改"
elif item.tableWidget() == self.tableWidget_TS:
self.changed_msg = f"TS单元格内容在位置({item.row() + 1}, {item.column() + 1})发生变化,保存后退出 或 取消修改"
elif item.tableWidget() == self.tableWidget_FT:
self.changed_msg = f"FT单元格内容在位置({item.row() + 1}, {item.column() + 1})发生变化,保存后退出 或 取消修改"
elif item.tableWidget() == self.tableWidget_FR:
self.changed_msg = f"FR单元格内容在位置({item.row() + 1}, {item.column() + 1})发生变化,保存后退出 或 取消修改"
else:
print("未添加该表格的提示")
self.changed_msg = "存在表格修改,保存后退出 或 取消修改"
def on_checkbox_state_changed(self, table_widget, row: int, state):
"""
根据table_widget来区分是哪个QTableWidget触发了信号
:param table_widget: QTableWidget对象
:param row: 哪一行
:param state: 勾选的状态
:return:
"""
# print(f"TableWidget {table_widget}: Checkbox in row {row} changed state to {Qt.Checked if state == Qt.Checked else Qt.Unchecked}")
# 检查勾选框的状态
if state == Qt.Unchecked:
# 将整行置灰(示例中使用浅灰色)
for col in range(table_widget.columnCount()):
if table_widget.cellWidget(row, col) is not None:
# 如果单元格中有小部件(如QCheckBox),则可能不需要改变背景
continue
item = table_widget.item(row, col)
# 禁用编辑
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
if item is not None:
item.setBackground(Qt.lightGray)
else:
# 如果是勾选状态,取消置灰处理
for col in range(table_widget.columnCount() - 1):
item = table_widget.item(row, col)
# 恢复可编辑
item.setFlags(item.flags() | Qt.ItemIsEditable)
item.setBackground(Qt.white)
def init_object(self, config_file, title):
# 设置该窗口的标题,以及配置文件初始化信息
self.setWindowTitle(title)
self.confg_filename = config_file
self.config_file_path = os.path.join(self._app_path, 'config', config_file) # 拼接指定的配置文件路径
print(self._app_path)
xml_file = self.find_config_file(self._app_path, config_file) # 查找回来的xml文件路径
if xml_file is not None:
self.config_tree = ET.parse(xml_file) # 解析XML文件
self.config_root = self.config_tree.getroot() # 获取XML文档的根元素
self.filled_content() # 填充表格内容,之后才初始化下面的状态符
self.changed_stat = False # 表格修改状态符
self.changed_msg = "" # 保存时的提示信息
else:
self.close() # 关闭窗口
def find_config_file(self, root_dir, filename, base_filename="config.xml"):
"""
搜索配置文件路径,如果不存在则用默认配置文件
:param root_dir: 查找文件的根目录
:param filename: 要查找的文件名
:param base_filename: 默认配置文件的文件名
:return: 配置文件路径,如果不存在则返回None
"""
config_path = os.path.dirname(self.config_file_path) # 指定的配置文件的路径
print(config_path)
if not os.path.isdir(config_path):
# 不存在目录则创建
os.makedirs(config_path, exist_ok=True) # exist_ok=True表示如果目录已存在,则不会抛出异常
self.pop_message('critical', f"模板配置文件丢失!请放置配置文件到{config_path, base_filename}") # 错误,找不到配置文件
return None
else:
if not os.path.isfile(self.config_file_path):
print(f"找不到指定配置文件{self.config_file_path}")
self.pop_message('warning', f"指定配置文件未找到{self.config_file_path},读取模板配置") # 错误,找不到配置文件
basefile_path = os.path.join(config_path, base_filename)
if not os.path.isfile(basefile_path):
self.pop_message('critical',
f"模板配置文件丢失!请放置配置文件到{basefile_path}") # 错误,找不到配置文件
return None
else:
return basefile_path
else:
return self.config_file_path
def set_cells_attribute(self, table_wigdet):
"""
设置表格属性
:param table_wigdet: 表格对象
:return:
"""
# 设置表格的列标题,并设置列的模式为"stretch",在这种模式下列直接自适应显示,无法对列的宽度和高度进行设置
table_wigdet.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 解决如果保存ui时页面不在表格页,表头就隐藏
table_wigdet.horizontalHeader().setVisible(True)
table_wigdet.verticalHeader().setVisible(True)
# 设置自定义代理,限制单元格输入内容
table_wigdet.setItemDelegate(NumberRestrictedDelegate(table_wigdet))
def filled_content(self):
"""设置单元格居中显示,填充配置文件的内容到UI中"""
for model in self.models_dict: # 遍历监听口模式,打印在不同的模式模块中
model_values = self.config_root.find(f'.//{model}') # 取配置文件相对应的值
for row in range(model_values.__len__()):
# for row in range(models_dict[model].rowCount()):
for column in range(self.models_dict[model].columnCount()):
# 如果配置文件的数据多于UI表格中固定的行数,则要添加一行
if row > self.models_dict[model].rowCount() - 1:
self.models_dict[model].insertRow(row)
if column < self.file_with_table_title.__len__(): # 依据对应字典配置的长度来设置单元格值
item = QTableWidgetItem(model_values[row].get(self.file_with_table_title[column]))
# 设置单元格属性居中
item.setTextAlignment(Qt.AlignCenter)
self.models_dict[model].setItem(row, column, item)
else:
# 设置该列单元格为勾选框
# 添加勾选框,并且为每个勾选框连接信号
self.setup_checkbox(self.models_dict[model], row, column)
# 自动调整表格高度
self.models_dict[model].resizeRowToContents(row)
self.set_cells_attribute(self.models_dict[model])
def setup_checkbox(self, table_widget, row, column):
"""
对表格的单元格添加QCheckBox并连接信号
:param table_widget: QTableWidget对象
:param row: 行
:param column: 列
:return:
"""
widget = QWidget() # 方法内的局部变量,每次调用都是新的副本,所以才能单独让每个tablewidget的勾选框独立连接信号
widget.checkbox = QCheckBox() # 将checkbox放在widget中
widget.checkbox.setCheckState(Qt.Checked) # 默认全部勾选
playout = QHBoxLayout(widget)
playout.addWidget(widget.checkbox) # 为小部件添加checkbox属性
playout.setAlignment(Qt.AlignCenter) # 设置小控件水平居中
widget.setLayout(playout) # 在QWidget放置布局
table_widget.setCellWidget(row, column, widget)
# 连接勾选框的stateChanged信号,使用lambda来捕获row和table_widget,并只传递状态参数给回调函数
widget.checkbox.stateChanged.connect(lambda state: self.on_checkbox_state_changed(table_widget, row, state))
def table_add_row(self, table_widget):
# 在表格下方新增一行
current_row = table_widget.rowCount() # 当前行数
current_col = table_widget.columnCount() # 当前列数
# 插入新行
table_widget.insertRow(current_row)
# 添加勾选框,并且为每个勾选框连接信号
self.setup_checkbox(table_widget, current_row, current_col-1)
for column in range(current_col-1):
# 默认新建的单元格内容是空,所以填充item和属性
item = QTableWidgetItem()
# 设置单元格属性居中
item.setTextAlignment(Qt.AlignCenter)
table_widget.setItem(current_row, column, item)
# 自动调整行高
table_widget.resizeRowToContents(current_row)
self.set_cells_attribute(table_widget)
def validate_table_rows(self, text_list:list, title:str, row:int):
"""
校验内容填写完整,不缺少项
:param text_list: 一行数据,列表格式
:param title: 告警提示的模块名称
:param row: 提示第几行数据
:return: bool
"""
# 遍历这一行的所有数据
num = text_list.__len__() # 有多少数据
for i in range(num):
# 记录有值的单元格数
if text_list[i].strip() != "":
continue
else:
# 该行不满足条件,可以进行相应的处理(例如提示用户)
self.pop_message("warning", f"{title}模块,第{row}行存在未填写完整内容,如不需要则取消该行勾选状态")
return False
return True