设计这样的类型名称为英文,定义名称为中文,标注标号的josn格式存储,文件名为parts_list同时在以下页面中添加"新增配件明细按钮”,打开页面,实现对josn格式的文件增删改查的业务,在以下代码中实现那#配件包配置编辑对话框
class ComponentPackageDialog(QDialog):
"""配件包配置编辑对话框"""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("配件包配置")
self.setMinimumSize(800, 600)
# 关键修复1:在设置背景前应用Fusion样式
self.setStyle(QStyleFactory.create("Fusion"))
self.setStyleSheet(UiMainTestStyle.UiMainTestStyle.set_new_form_backcolor_style())
# 关键修复3:确保布局有背景设置
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(255, 255, 255))
self.setPalette(palette)
# 加载配置
self.config_file = "component_packages.json"
self.packages = self.load_config()
self.init_ui()
def init_ui(self):
main_layout = QVBoxLayout()
# 关键修复4:为布局添加边距避免控件贴边
main_layout.setContentsMargins(15, 15, 15, 15)
main_layout.setSpacing(10)
# 顶部标题
#title_label = QLabel("配件包配置管理系统")
#title_label.setAlignment(Qt.AlignCenter)
#title_label.setStyleSheet("font-size: 18px; font-weight: bold; margin: 10px;")
#main_layout.addWidget(title_label)
# === 新增:查询区域 ===
search_layout = QHBoxLayout()
# 目标类型下拉选择框
search_layout.addWidget(QLabel("目标明细查询:"))
self.search_box = QComboBox()
self.search_box.setEditable(True) # 允许输入
self.search_box.setMinimumWidth(200)
self.search_box.setStyleSheet("background-color: white;")
self.populate_search_options() # 填充下拉选项
# 查询按钮
self.search_btn = QPushButton("查询")
self.search_btn.clicked.connect(self.search_packages)
search_layout.addWidget(self.search_box)
search_layout.addWidget(self.search_btn)
# 重置按钮
self.reset_btn = QPushButton("显示全部")
self.reset_btn.clicked.connect(self.reset_search)
search_layout.addWidget(self.reset_btn)
search_layout.addStretch(1) # 添加弹性空间使控件靠左
main_layout.addLayout(search_layout)
# 按钮区域
btn_layout = QHBoxLayout()
self.add_btn = QPushButton("添加配件包")
self.add_btn.clicked.connect(self.add_package)
btn_layout.addWidget(self.add_btn)
self.edit_btn = QPushButton("编辑选中项")
self.edit_btn.clicked.connect(self.edit_package)
btn_layout.addWidget(self.edit_btn)
self.delete_btn = QPushButton("删除选中项")
self.delete_btn.clicked.connect(self.delete_package)
btn_layout.addWidget(self.delete_btn)
# 新增状态切换按钮
self.toggle_status_btn = QPushButton("启用/禁用")
self.toggle_status_btn.clicked.connect(self.toggle_package_status)
self.toggle_status_btn.setEnabled(True) # 初始禁用
btn_layout.addWidget(self.toggle_status_btn)
button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.save_config)
button_box.rejected.connect(self.reject)
btn_layout.addWidget(button_box)
# 关键修复6:添加弹性空间使按钮居中
btn_layout.addStretch(1)
main_layout.addLayout(btn_layout)
# 底部按钮
# button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
# button_box.accepted.connect(self.save_config)
# button_box.rejected.connect(self.reject)
# main_layout.addWidget(button_box)
# 表格展示区域
self.table = QTableWidget()
self.table.setColumnCount(6) # 新增第五列用于明细
self.table.setHorizontalHeaderLabels([
"配件包ID",
"名称",
"显示名称",
"目标种类",
"目标明细",
"状态" # 新增状态列
])
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents) # ID列自适应
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeToContents) # 名称列拉伸
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents) # 显示名称列拉伸
self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeToContents) # 数量列自适应
self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch) # 明细列拉伸
self.table.horizontalHeader().setSectionResizeMode(5, QHeaderView.ResizeToContents) # 状态列自适应
#self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 关键修复5:设置表格透明背景
self.table.setStyleSheet("background-color: rgba(255, 255, 255, 0.7);")
self.populate_table()
main_layout.addWidget(self.table)
self.setLayout(main_layout)
# 连接信号:当选择发生变化时更新按钮状态
self.table.selectionModel().selectionChanged.connect(self.update_button_status_based_on_selection)
# 关键修复7:确保背景设置生效
self.update()
def update_button_status_based_on_selection(self):
"""根据选中行的状态更新按钮文本"""
if self.table.selectionModel().selectedRows():
row = self.table.selectionModel().selectedRows()[0].row()
package = self.packages[row]
current_status = package.get("enabled", True)
# 如果当前状态是启用,则按钮显示"禁用",反之亦然
self.toggle_status_btn.setText("禁用" if current_status else "启用")
# 同时更新按钮的颜色提示
if current_status:
self.toggle_status_btn.setStyleSheet("background-color: #ffcccc;") # 浅红色表示禁用操作
else:
self.toggle_status_btn.setStyleSheet("background-color: #ccffcc;") # 浅绿色表示启用操作
#else:
# 没有选中行时禁用按钮
#self.toggle_status_btn.setEnabled(False)
#self.toggle_status_btn.setText("启用/禁用")
#self.toggle_status_btn.setStyleSheet("") # 清除样式
def populate_search_options(self):
"""填充目标明细查询下拉框的选项(只保留正文名称)"""
self.search_box.clear()
self.search_box.addItem("") # 空选项
# 收集所有独特的目标正文名称
unique_names = set()
for package in self.packages:
for target in package.get("targets", []):
# 获取正文名称(优先使用def_name,其次display_name,最后使用type)
name = target.get("def_name") or target.get("display_name") or target.get("type", "")
if name: # 确保名称有效
unique_names.add(name)
# 按字母顺序添加选项
for name in sorted(unique_names):
self.search_box.addItem(name)
def format_target_description(self, target):
"""格式化目标类型为查询选项文本"""
english_type = target.get("type", "unknown")
chinese_name = target.get("def_name") or target.get("display_name") or target.get("type")
count = target.get("count", 1)
return f"{chinese_name}({english_type}):{count}个"
def search_packages(self):
"""根据输入的目标中文名称查询符合条件的配件包"""
search_text = self.search_box.currentText().strip()
# 如果没有输入文本,显示全部
if not search_text:
self.reset_search()
return
# 查询符合条件的配件包
matching_packages = []
for package in self.packages:
# 检查当前配件包的所有目标
for target in package.get("targets", []):
# 检查中文名称(def_name, display_name, type)
def_name = target.get("def_name", "").lower()
display_name = target.get("display_name", "").lower()
type_name = target.get("type", "").lower()
# 检查是否匹配
if (search_text.lower() in def_name or
search_text.lower() in display_name or
search_text.lower() in type_name):
matching_packages.append(package)
break # 找到一个匹配的目标即可,跳出内层循环
# 更新表格显示查询结果
self.display_packages(matching_packages)
# 显示查询结果统计
QMessageBox.information(
self,
"查询结果",
f"找到 {len(matching_packages)} 个包含目标 '{search_text}' 的配件包"
)
def display_packages(self, packages):
"""显示指定的配件包列表"""
sorted_packages = sorted(
packages,
key=lambda p: (not p.get('enabled', False), p.get('display_name', '').lower()),
reverse=False
)
self.table.setRowCount(len(sorted_packages))
for row, package in enumerate(sorted_packages):
# 配件包ID
id_item = QTableWidgetItem(str(row + 1))
id_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 0, id_item)
# 名称
self.table.setItem(row, 1, QTableWidgetItem(package.get("name", "")))
# 显示名称
self.table.setItem(row, 2, QTableWidgetItem(package.get("display_name", "")))
# 目标类型数量
targets_count = len(package.get("targets", []))
count_item = QTableWidgetItem(str(targets_count))
count_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 3, count_item)
# 目标类型明细 - 新增列
details = self.format_target_details(package.get("targets", []))
detail_item = QTableWidgetItem(details)
detail_item.setToolTip(details) # 添加悬停提示
self.table.setItem(row, 4, detail_item)
status = package.get("enabled", False) # 默认为启用状态
status_item = QTableWidgetItem("启用" if status else "禁用")
status_item.setTextAlignment(Qt.AlignCenter)
# 根据状态设置文本颜色
if status:
status_item.setForeground(QColor(0, 128, 0)) # 启用状态绿色
else:
status_item.setForeground(QColor(255, 0, 0)) # 禁用状态红色
self.table.setItem(row, 5, status_item)
self.update_button_status_based_on_selection()
def reset_search(self):
"""重置查询,显示所有配件包"""
self.search_box.setCurrentIndex(0) # 清空搜索框
self.populate_table() # 显示所有配件包
def populate_table(self):
"""填充表格数据"""
# 按状态排序:先启用(True)后禁用(False)
sorted_packages = sorted(
self.packages,
key=lambda p: (not p.get('enabled', False), p.get('display_name', '').lower()),
reverse=False
)
self.table.setRowCount(len(sorted_packages))
# 设置最小行高以容纳明细文本
self.table.verticalHeader().setDefaultSectionSize(80)
for row, package in enumerate(sorted_packages):
# 配件包ID
id_item = QTableWidgetItem(str(row + 1))
id_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 0, id_item)
# 名称
self.table.setItem(row, 1, QTableWidgetItem(package.get("name", "")))
# 显示名称
self.table.setItem(row, 2, QTableWidgetItem(package.get("display_name", "")))
# 目标类型数量
targets_count = len(package.get("targets", []))
count_item = QTableWidgetItem(str(targets_count))
count_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 3, count_item)
# 目标类型明细 - 新增列
details = self.format_target_details(package.get("targets", []))
detail_item = QTableWidgetItem(details)
detail_item.setToolTip(details) # 添加悬停提示
self.table.setItem(row, 4, detail_item)
status = package.get("enabled", False) # 默认为启用状态
status_item = QTableWidgetItem("启用" if status else "禁用")
status_item.setTextAlignment(Qt.AlignCenter)
# 根据状态设置文本颜色
if status:
status_item.setForeground(QColor(0, 128, 0)) # 启用状态绿色
else:
status_item.setForeground(QColor(255, 0, 0)) # 禁用状态红色
self.table.setItem(row, 5, status_item)
self.update_button_status_based_on_selection()
def format_target_details(self, targets):
"""格式化目标类型明细为多行文本"""
details = []
# for target in targets:
# type_name = target.get("type", "未知类型")
# count = target.get("count", 1)
# details.append(f"{type_name}: {count}个")
# 获取英文类型标识符
for target in targets:
english_type = target.get("type", "unknown")
# 获取中文描述(优先使用def_name,不存在则使用display_name)
chinese_name = target.get("def_name") or target.get("display_name")
# 组合中英文显示格式
display_type = f"{chinese_name}({english_type})"
# 获取数量
count = target.get("count", 1)
# 格式化条目
details.append(f"{display_type}: {count}个")
return "\n".join(details) if details else "无目标类型"
def load_config(self):
"""加载配置文件"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
packages = json.load(f)
# 为旧数据添加默认状态字段
for package in packages:
if "enabled" not in package:
package["enabled"] = False
return packages
except:
return []
return []
def save_config(self):
"""保存配置到文件"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.packages, f, ensure_ascii=False, indent=2)
QMessageBox.information(self, "成功", "配置已成功保存!")
self.accept()
except Exception as e:
QMessageBox.critical(self, "错误", f"保存配置失败: {str(e)}")
def get_selected_package(self):
"""获取选中的配件包"""
"""获取选中的配件包 - 更新按钮状态"""
selected_rows = self.table.selectionModel().selectedRows()
if not selected_rows:
# 没有选中行时禁用操作按钮
self.edit_btn.setEnabled(False)
self.delete_btn.setEnabled(False)
self.toggle_status_btn.setEnabled(False)
return None
# 有选中行时启用按钮
self.edit_btn.setEnabled(True)
self.delete_btn.setEnabled(True)
self.toggle_status_btn.setEnabled(True)
# 获取选中行索引
row = selected_rows[0].row()
# 更新状态按钮文本
current_status = self.packages[row].get("enabled", True)
self.toggle_status_btn.setText("禁用" if current_status else "启用")
return row
def add_package(self):
"""添加新配件包"""
dialog = PackageEditDialog(self)
if dialog.exec_() == QDialog.Accepted:
new_package = dialog.get_data()
new_package["enabled"] = False # 新增配件包默认禁用
self.packages.append(new_package)
self.populate_table()
def edit_package(self):
"""编辑选中的配件包"""
"""编辑选中的配件包 - 保留状态字段"""
# row = self.get_selected_package()
# if row is None:
# return
selected_rows = self.table.selectionModel().selectedRows()
# 1. 检查是否有选中行
if not selected_rows:
# 提供明确的用户反馈
QMessageBox.warning(
self,
"未选中配件包",
"请先选中一个配件包进行编辑!"
)
return
# 2. 获取选中行索引
row = selected_rows[0].row()
# 3. 验证行索引有效性
if row < 0 or row >= len(self.packages):
QMessageBox.critical(
self,
"无效选择",
f"选中的行索引无效: {row} (总配件包数: {len(self.packages)})"
)
return
# 保留当前状态
current_status = self.packages[row].get("enabled", True)
dialog = PackageEditDialog(self, self.packages[row])
if dialog.exec_() == QDialog.Accepted:
edited_data = dialog.get_data()
edited_data["enabled"] = current_status # 编辑后保持原状态
self.packages[row] = edited_data
self.populate_table()
def delete_package(self):
"""删除选中的配件包"""
selected_rows = self.table.selectionModel().selectedRows()
# 1. 检查是否有选中行
if not selected_rows:
# 提供明确的用户反馈
QMessageBox.warning(
self,
"未选中配件包",
"请先选中一个配件包进行删除!"
)
return
# 2. 获取选中行索引
row = selected_rows[0].row()
# 3. 验证行索引有效性
if row < 0 or row >= len(self.packages):
QMessageBox.critical(
self,
"无效选择",
f"选中的行索引无效: {row} (总配件包数: {len(self.packages)})"
)
return
reply = QMessageBox.question(
self, "确认删除",
f"确定要删除 '{self.packages[row].get('name', '')}' 配件包吗?",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
del self.packages[row]
self.populate_table()
def toggle_package_status(self):
"""启用/禁用选中的配件包"""
row = self.get_selected_package()
if row is None:
return
# 获取当前状态
package = self.packages[row]
current_status = self.packages[row].get("enabled", True)
# 切换状态
new_status = not current_status
package["enabled"] = new_status
# 更新表格显示
self.update_package_row(row, package)
# 更新按钮文本
self.update_button_status(row)
# 显示状态变更通知
package_name = package.get("name", "未知配件包")
status_text = "启用" if new_status else "禁用"
QMessageBox.information(
self, "状态更新",
f"配件包 '{package_name}' 已{status_text}"
)
self.update_button_status_based_on_selection()
def update_package_row(self, row, package):
"""更新指定行的状态显示"""
# 获取状态项
status_item = self.table.item(row, 5)
if not status_item:
status_item = QTableWidgetItem()
status_item.setTextAlignment(Qt.AlignCenter)
self.table.setItem(row, 5, status_item)
# 更新状态文本和颜色
new_status = package.get("enabled", True)
status_item.setText("启用" if new_status else "禁用")
if new_status:
status_item.setForeground(QColor(0, 128, 0)) # 启用状态绿色
else:
status_item.setForeground(QColor(255, 0, 0)) # 禁用状态红色
def update_button_status(self, row):
"""根据当前状态更新按钮文本"""
current_status = self.packages[row].get("enabled", True)
self.toggle_status_btn.setText("禁用" if current_status else "启用")
#单个配件包编辑对话框
class PackageEditDialog(QDialog):
"""单个配件包编辑对话框"""
def __init__(self, parent=None, data=None):
super().__init__(parent)
self.setWindowTitle("编辑配件包" if data else "添加配件包")
self.setMinimumSize(600, 400)
# 关键修复1:在设置背景前应用Fusion样式
self.setStyle(QStyleFactory.create("Fusion"))
# 关键修复2:使用样式表设置全局背景
self.setStyleSheet(UiMainTestStyle.UiMainTestStyle.set_new_form_backcolor_style())
# 设置白色背景
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(255, 255, 255)) # 白色
self.setPalette(palette)
# 初始化数据时添加默认的def_name字段
if data:
self.data = data
# 确保每个target都有def_name字段
for target in self.data.get("targets", []):
if "def_name" not in target:
target["def_name"] = target.get("type", "") + "_def"
else:
self.data = {
"name": "",
"display_name": "",
"targets": []
}
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
# 基本信息
form_layout = QVBoxLayout()
# 名称
name_layout = QHBoxLayout()
name_layout.addWidget(QLabel("名称:"))
self.name_edit = QLineEdit(self.data["name"])
self.name_edit.setStyleSheet(UiMainTestStyle.UiMainTestStyle.set_label_style())
name_layout.addWidget(self.name_edit)
form_layout.addLayout(name_layout)
# 显示名称
display_layout = QHBoxLayout()
display_layout.addWidget(QLabel("显示名称:"))
self.display_edit = QLineEdit(self.data["display_name"])
self.display_edit.setStyleSheet(UiMainTestStyle.UiMainTestStyle.set_label_style())
display_layout.addWidget(self.display_edit)
form_layout.addLayout(display_layout)
layout.addLayout(form_layout)
# 目标类型列表
layout.addWidget(QLabel("目标类型配置:"))
self.targets_table = QTableWidget()
self.targets_table.setColumnCount(3)
self.targets_table.setHorizontalHeaderLabels(["类型名称","定义名称", "所需数量"])
self.targets_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 关键修复5:设置表格透明背景
self.targets_table.setStyleSheet("background-color: rgba(255, 255, 255, 0.7);")
# 添加数据验证
self.targets_table.itemChanged.connect(self.validate_row)
self.populate_targets_table()
layout.addWidget(self.targets_table)
# 目标操作按钮
target_btn_layout = QHBoxLayout()
add_target_btn = QPushButton("添加类型")
add_target_btn.clicked.connect(self.add_target)
target_btn_layout.addWidget(add_target_btn)
remove_target_btn = QPushButton("删除选中")
remove_target_btn.clicked.connect(self.remove_target)
target_btn_layout.addWidget(remove_target_btn)
layout.addLayout(target_btn_layout)
# 底部按钮
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.setLayout(layout)
def validate_row(self, item):
"""验证单个行的数据"""
row = item.row()
type_item = self.targets_table.item(row, 0)
def_item = self.targets_table.item(row, 1)
count_item = self.targets_table.item(row, 2)
# 验证数据类型
if not type_item or not type_item.text().strip():
self.highlight_error(row, "类型名称不能为空")
return False
# 验证定义名称
if not def_item or not def_item.text().strip():
self.highlight_error(row, "定义名称不能为空")
return False
# 验证数量
if count_item:
try:
count = int(count_item.text())
if count <= 0:
self.highlight_error(row, "数量必须大于0")
return False
except ValueError:
self.highlight_error(row, "数量必须是整数")
return False
# 检查类型名称和定义名称是否相同
if type_item.text().strip() == def_item.text().strip():
self.highlight_error(row, "类型名称和定义名称不能相同")
return False
# 清除错误状态
self.clear_error(row)
return True
def validate_all_rows(self):
"""验证所有行的数据"""
all_valid = True
for row in range(self.targets_table.rowCount()):
if not self.validate_row(self.targets_table.item(row, 0)):
all_valid = False
return all_valid
def highlight_error(self, row, message):
"""高亮显示错误行"""
for col in range(3):
item = self.targets_table.item(row, col)
if item:
item.setBackground(QColor(255, 200, 200)) # 浅红色背景
item.setToolTip(message)
def clear_error(self, row):
"""清除错误标记"""
for col in range(3):
item = self.targets_table.item(row, col)
if item:
item.setBackground(QColor(255, 255, 255)) # 白色背景
item.setToolTip("")
def populate_targets_table(self):
"""填充目标类型表格(修复定义名称初始化)"""
self.targets_table.setRowCount(len(self.data["targets"]))
for row, target in enumerate(self.data["targets"]):
# 类型名称
self.targets_table.setItem(row, 0, QTableWidgetItem(target.get("type", "")))
# 定义名称
self.targets_table.setItem(row, 1, QTableWidgetItem(target.get("def_name", "")))
# 所需数量
count = target.get("count", 1)
count_item = QTableWidgetItem(str(count))
self.targets_table.setItem(row, 2, count_item)
def add_target(self):
"""添加新目标类型"""
"""添加新目标类型"""
row_count = self.targets_table.rowCount()
self.targets_table.insertRow(row_count)
# 默认设置不同的名称
base_name = f"新类型_{row_count + 1}"
self.targets_table.setItem(row_count, 0, QTableWidgetItem(base_name))
self.targets_table.setItem(row_count, 1, QTableWidgetItem(f"{base_name}_def")) # 确保默认名称不同
self.targets_table.setItem(row_count, 2, QTableWidgetItem("1"))
# 自动进入编辑模式
self.targets_table.editItem(self.targets_table.item(row_count, 0))
def remove_target(self):
"""删除选中目标类型"""
selected_rows = self.targets_table.selectionModel().selectedRows()
if not selected_rows:
return
for row in sorted(selected_rows, reverse=True):
self.targets_table.removeRow(row.row())
def get_data(self):
"""获取编辑后的数据(确保总是返回有效数据)"""
# 基本信息
self.data["name"] = self.name_edit.text().strip()
self.data["display_name"] = self.display_edit.text().strip()
# 目标类型
self.data["targets"] = []
if not self.targets_table:
return self.data
for row in range(self.targets_table.rowCount()):
type_item = self.targets_table.item(row, 0)
def_item = self.targets_table.item(row, 1)
count_item = self.targets_table.item(row, 2)
# 跳过无效行
if not type_item or not def_item or not count_item:
continue
target_type = type_item.text().strip()
def_name = def_item.text().strip()
# 处理数量
try:
count = int(count_item.text())
except ValueError:
count = 1
if target_type: # 忽略空类型
self.data["targets"].append({
"type": target_type,
"def_name": def_name,
"count": count
})
return self.data