import sys
import json
import os
import copy
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QPushButton, QListWidget, QLabel,
QDialog, QLineEdit, QTextEdit, QComboBox,
QMessageBox, QTabWidget, QToolBar, QAction,
QTreeWidget, QTreeWidgetItem, QSplitter,
QStatusBar, QToolButton, QStackedWidget,
QDateEdit, QListWidgetItem, QCheckBox,
QFormLayout, QScrollArea, QSizePolicy)
from PyQt5.QtCore import Qt, QSize, QDate
from PyQt5.QtGui import QIcon, QFont, QColor, QPalette, QBrush, QLinearGradient
class WorkflowManager(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("工作流程管理器")
self.resize(1000, 650)
self.workflows = []
self.current_workflow = None
self.current_stage_index = 0
self.load_workflows()
# 设置主窗口样式
self.setStyleSheet("""
QMainWindow {
background-color: #f8f9fa;
font-family: 'Segoe UI', 'Arial', sans-serif;
}
QLabel {
font-size: 12px;
}
QPushButton {
padding: 6px 12px;
border-radius: 4px;
font-weight: 500;
}
QListWidget {
background-color: white;
border: 1px solid #e3e6f0;
border-radius: 4px;
}
""")
# 创建主界面
self.create_main_ui()
# 加载默认工作流程
if self.workflows:
self.current_workflow = self.workflows[0]
self.update_workflow_display()
def filter_artifacts(self):
"""过滤成果物"""
if not self.current_workflow or self.current_stage_index < 0:
return
search_text = self.artifacts_search.text().lower()
current_stage = self.current_workflow["stages"][self.current_stage_index]
for i in range(self.artifacts_list.count()):
item = self.artifacts_list.item(i)
widget = self.artifacts_list.itemWidget(item)
# 获取复选框文本
checkbox = widget.findChild(QCheckBox)
if checkbox:
artifact_name = checkbox.text().lower()
# 根据搜索文本显示/隐藏
item.setHidden(search_text not in artifact_name and search_text != "")
def toggle_select_all(self):
"""切换全选/全不选状态"""
if not self.current_workflow or self.current_stage_index < 0:
return
current_stage = self.current_workflow["stages"][self.current_stage_index]
artifacts = current_stage.get("artifacts", [])
if not artifacts:
return
# 检查当前是否全部已选
all_selected = all(a["completed"] for a in artifacts)
new_state = not all_selected
# 更新所有成果物状态
for i in range(self.artifacts_list.count()):
item = self.artifacts_list.item(i)
widget = self.artifacts_list.itemWidget(item)
checkbox = widget.findChild(QCheckBox)
if checkbox:
checkbox.setChecked(new_state)
# 更新数据
for artifact in artifacts:
if artifact["name"] == checkbox.text():
artifact["completed"] = new_state
# 更新按钮文本
self.select_all_btn.setText("全不选" if new_state else "全选")
self.update_stage_status()
def update_stage_status(self):
"""更新阶段状态 - 添加进度条显示"""
# ... [原有代码] ...
# 添加进度条显示
if total_artifacts > 0:
progress_width = int((completed_artifacts / total_artifacts) * 100)
self.progress_bar.setStyleSheet(f"""
QWidget {{
background-color: #eaecf4;
border-radius: 4px;
}}
QWidget::before {{
content: "";
display: block;
background-color: {status_color};
width: {progress_width}%;
height: 100%;
border-radius: 4px;
}}
""")
def create_main_ui(self):
"""创建主界面 - 修复了中央部件设置问题"""
# 创建中央部件
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setContentsMargins(15, 15, 15, 15)
# 创建工具栏
toolbar = QToolBar("工具栏")
toolbar.setIconSize(QSize(24, 24))
self.addToolBar(Qt.TopToolBarArea, toolbar)
# 添加工具栏动作
new_workflow_action = QAction(QIcon(), "新建工作流程", self)
new_workflow_action.triggered.connect(self.new_workflow)
toolbar.addAction(new_workflow_action)
edit_workflow_action = QAction(QIcon(), "编辑工作流程", self)
edit_workflow_action.triggered.connect(self.edit_workflow)
toolbar.addAction(edit_workflow_action)
delete_workflow_action = QAction(QIcon(), "删除工作流程", self)
delete_workflow_action.triggered.connect(self.delete_workflow)
toolbar.addAction(delete_workflow_action)
toolbar.addSeparator()
stats_action = QAction(QIcon(), "统计信息", self)
stats_action.triggered.connect(self.show_stats)
toolbar.addAction(stats_action)
# 创建主分割器
main_splitter = QSplitter(Qt.Horizontal)
main_layout.addWidget(main_splitter, 1)
# 左侧面板(工作流程列表)
left_panel = QWidget()
left_layout = QVBoxLayout(left_panel)
left_layout.setContentsMargins(0, 0, 0, 0)
workflows_label = QLabel("工作流程列表")
workflows_label.setStyleSheet("font-size: 14px; font-weight: 600; color: #4e73df; margin-bottom: 8px;")
left_layout.addWidget(workflows_label)
self.workflows_tree = QTreeWidget()
self.workflows_tree.setHeaderHidden(True)
self.workflows_tree.setStyleSheet("""
QTreeWidget {
border: 1px solid #e3e6f0;
border-radius: 4px;
background-color: white;
}
""")
self.workflows_tree.itemClicked.connect(self.select_workflow)
left_layout.addWidget(self.workflows_tree)
# 更新工作流程树
self.update_workflows_tree()
main_splitter.addWidget(left_panel)
# 右侧面板(工作流程详情)
right_panel = QWidget()
right_layout = QVBoxLayout(right_panel)
right_layout.setContentsMargins(0, 0, 0, 0)
# 工作流程标题
self.flow_title = QLabel("无选中的工作流程")
self.flow_title.setStyleSheet("font-size: 16px; font-weight: 600; color: #4e73df; margin-bottom: 15px;")
right_layout.addWidget(self.flow_title)
# 流程阶段显示区域
self.flow_stages = QHBoxLayout()
self.flow_stages.setSpacing(15)
self.flow_stages.setContentsMargins(0, 0, 0, 0)
right_layout.addLayout(self.flow_stages)
# 成果物区域 - 优化后的代码
artifacts_container = QWidget()
artifacts_container.setStyleSheet("background-color: white; border-radius: 6px; padding: 15px;")
artifacts_layout = QVBoxLayout(artifacts_container)
# 添加搜索框
search_container = QHBoxLayout()
search_container.setContentsMargins(0, 0, 0, 10)
search_label = QLabel("搜索成果物:")
search_label.setStyleSheet("font-size: 12px;")
search_container.addWidget(search_label)
self.artifacts_search = QLineEdit()
self.artifacts_search.setPlaceholderText("输入关键词过滤...")
self.artifacts_search.setStyleSheet("padding: 5px; border: 1px solid #d1d3e2; border-radius: 4px;")
self.artifacts_search.textChanged.connect(self.filter_artifacts)
search_container.addWidget(self.artifacts_search)
artifacts_layout.addLayout(search_container)
# 成果物标题
artifacts_title_layout = QHBoxLayout()
self.artifacts_label = QLabel("成果物检查")
self.artifacts_label.setStyleSheet("font-size: 14px; font-weight: 600; color: #4e73df;")
artifacts_title_layout.addWidget(self.artifacts_label)
# 添加全选/全不选按钮
self.select_all_btn = QPushButton("全选")
self.select_all_btn.setStyleSheet("""
background-color: #4e73df;
color: white;
padding: 3px 8px;
border-radius: 3px;
font-size: 11px;
""")
self.select_all_btn.clicked.connect(self.toggle_select_all)
artifacts_title_layout.addWidget(self.select_all_btn)
artifacts_layout.addLayout(artifacts_title_layout)
# 使用滚动区域包裹成果物列表
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setStyleSheet("border: none;")
scroll_area.setMinimumHeight(200) # 设置最小高度
scroll_content = QWidget()
scroll_layout = QVBoxLayout(scroll_content)
scroll_layout.setContentsMargins(0, 0, 0, 0)
self.artifacts_list = QListWidget()
self.artifacts_list.setStyleSheet("""
QListWidget {
border: none;
background-color: white;
}
QListWidget::item {
border-bottom: 1px solid #eaecf4;
}
QListWidget::item:selected {
background-color: transparent;
}
""")
self.artifacts_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll_layout.addWidget(self.artifacts_list)
scroll_area.setWidget(scroll_content)
artifacts_layout.addWidget(scroll_area, 1) # 设置弹性系数为1
# 状态信息 - 优化显示
status_container = QWidget()
status_container.setStyleSheet("background-color: #f8f9fa; border-radius: 4px; padding: 8px;")
status_layout = QVBoxLayout(status_container)
self.status_info = QLabel("")
self.status_info.setAlignment(Qt.AlignCenter)
self.status_info.setStyleSheet("font-weight: 500;")
status_layout.addWidget(self.status_info)
# 添加进度条
self.progress_bar = QWidget()
self.progress_bar.setFixedHeight(8)
self.progress_bar.setStyleSheet("background-color: #eaecf4; border-radius: 4px;")
status_layout.addWidget(self.progress_bar)
artifacts_layout.addWidget(status_container)
# 按钮区域
btn_layout = QHBoxLayout()
btn_layout.setSpacing(10)
self.prev_stage_btn = QPushButton("上一步")
self.prev_stage_btn.setStyleSheet("background-color: #6c757d; color: white; padding: 8px;")
self.prev_stage_btn.clicked.connect(self.prev_stage)
btn_layout.addWidget(self.prev_stage_btn)
self.next_stage_btn = QPushButton("下一步")
self.next_stage_btn.setStyleSheet("background-color: #4e73df; color: white; padding: 8px;")
self.next_stage_btn.clicked.connect(self.next_stage)
btn_layout.addWidget(self.next_stage_btn)
btn_layout.addStretch()
right_layout.addLayout(btn_layout)
main_splitter.addWidget(right_panel)
main_splitter.setSizes([200, 600])
# 状态栏
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
self.status_bar.showMessage("就绪", 5000)
# 更新按钮状态
self.update_buttons_state()
def update_workflows_tree(self):
"""更新工作流程树"""
self.workflows_tree.clear()
for workflow in self.workflows:
item = QTreeWidgetItem(self.workflows_tree)
item.setText(0, workflow["name"])
item.setData(0, Qt.UserRole, workflow)
item.setToolTip(0, workflow["description"])
if self.workflows:
self.workflows_tree.setCurrentItem(self.workflows_tree.topLevelItem(0))
def load_workflows(self):
"""加载工作流程并处理兼容性"""
try:
if os.path.exists("workflows.json"):
with open("workflows.json", "r") as f:
self.workflows = json.load(f)
# 兼容旧数据结构
for workflow in self.workflows:
for stage in workflow["stages"]:
if stage.get("artifacts") and isinstance(stage["artifacts"][0], str):
stage["artifacts"] = [{"name": name, "completed": False}
for name in stage["artifacts"]]
else:
# 创建默认工作流程
default_workflow = {
"name": "软件开发变更流程",
"description": "标准的软件开发变更管理流程",
"stages": [
{
"name": "变更点分析",
"description": "分析客户提供的变更需求",
"color": "#4e73df",
"artifacts": [{"name": "需求分析文档", "completed": False}],
"completed": False
},
{
"name": "设计",
"description": "设计系统架构和接口",
"color": "#1cc88a",
"artifacts": [
{"name": "系统设计文档", "completed": False},
{"name": "接口规范", "completed": False}
],
"completed": False
},
{
"name": "实现",
"description": "编写代码实现功能",
"color": "#36b9cc",
"artifacts": [{"name": "源代码", "completed": False}],
"completed": False
},
{
"name": "测试",
"description": "执行单元测试和集成测试",
"color": "#f6c23e",
"artifacts": [
{"name": "测试计划", "completed": False},
{"name": "测试报告", "completed": False}
],
"completed": False
},
{
"name": "部署",
"description": "部署应用到生产环境",
"color": "#e74a3b",
"artifacts": [{"name": "部署文档", "completed": False}],
"completed": False
}
],
"tasks": []
}
self.workflows = [default_workflow]
self.save_workflows()
except Exception as e:
QMessageBox.critical(self, "加载错误", f"加载工作流程时出错: {str(e)}")
self.workflows = []
def save_workflows(self):
"""保存工作流程"""
try:
with open("workflows.json", "w") as f:
json.dump(self.workflows, f, indent=4)
self.status_bar.showMessage("工作流程已保存", 2000)
except Exception as e:
QMessageBox.critical(self, "保存错误", f"保存工作流程时出错: {str(e)}")
def update_workflow_display(self):
"""更新工作流程显示"""
if not self.current_workflow:
# 清空显示
self.flow_title.setText("无选中的工作流程")
self.clear_flow_stages()
self.artifacts_label.setText("成果物检查")
self.artifacts_list.clear()
self.status_info.setText("")
self.update_buttons_state()
return
# 更新标题
self.flow_title.setText(f"{self.current_workflow['name']} - {self.current_workflow['description']}")
# 清空现有阶段显示
self.clear_flow_stages()
# 添加阶段
for i, stage in enumerate(self.current_workflow["stages"]):
stage_widget = self.create_stage_widget(stage, i)
self.flow_stages.addWidget(stage_widget)
# 更新成果物显示
self.update_artifacts_display()
# 更新按钮状态
self.update_buttons_state()
def clear_flow_stages(self):
"""清空流程阶段显示"""
while self.flow_stages.count():
item = self.flow_stages.takeAt(0)
if item.widget():
item.widget().deleteLater()
def create_stage_widget(self, stage, index):
"""创建单个阶段显示组件"""
widget = QWidget()
widget.setMinimumWidth(120)
widget.setMaximumWidth(180)
layout = QVBoxLayout(widget)
layout.setContentsMargins(10, 10, 10, 10)
# 设置背景色
bg_color = stage["color"]
if index == self.current_stage_index:
# 当前阶段使用更深的颜色
bg_color = self.darken_color(bg_color, 20)
widget.setStyleSheet(f"""
background-color: {bg_color};
border-radius: 8px;
color: white;
font-weight: 500;
""")
# 阶段名称
name_label = QLabel(stage["name"])
name_label.setStyleSheet("font-size: 14px; font-weight: 600;")
name_label.setAlignment(Qt.AlignCenter)
layout.addWidget(name_label)
# 阶段描述
desc_label = QLabel(stage["description"])
desc_label.setStyleSheet("font-size: 11px;")
desc_label.setWordWrap(True)
desc_label.setAlignment(Qt.AlignCenter)
layout.addWidget(desc_label)
# 阶段状态
status = "已完成" if stage["completed"] else "进行中" if index == self.current_stage_index else "未开始"
status_label = QLabel(status)
status_label.setStyleSheet("font-size: 12px; font-weight: 500; margin-top: 8px;")
status_label.setAlignment(Qt.AlignCenter)
layout.addWidget(status_label)
# 点击事件
widget.mousePressEvent = lambda event, idx=index: self.select_stage(idx)
return widget
def darken_color(self, hex_color, percent):
"""使颜色变暗指定百分比"""
# 移除#号
hex_color = hex_color.lstrip('#')
# 转换为RGB
r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
# 计算变暗值
r = max(0, int(r * (1 - percent / 100)))
g = max(0, int(g * (1 - percent / 100)))
b = max(0, int(b * (1 - percent / 100)))
# 返回新颜色
return f"#{r:02x}{g:02x}{b:02x}"
def select_workflow(self, item):
"""选择工作流程"""
if not item:
return
self.current_workflow = item.data(0, Qt.UserRole)
self.current_stage_index = 0
self.update_workflow_display()
self.update_artifacts_display()
def select_stage(self, index):
"""选择阶段"""
if not self.current_workflow or index < 0 or index >= len(self.current_workflow["stages"]):
return
self.current_stage_index = index
self.update_workflow_display()
self.update_artifacts_display()
self.status_bar.showMessage(f"已选择阶段: {self.current_workflow['stages'][index]['name']}", 2000)
def prev_stage(self):
"""返回上一阶段"""
if not self.current_workflow or self.current_stage_index <= 0:
return
self.current_stage_index -= 1
self.update_workflow_display()
self.update_artifacts_display()
self.status_bar.showMessage(f"已返回阶段: {self.current_workflow['stages'][self.current_stage_index]['name']}", 2000)
def next_stage(self):
"""进入下一阶段"""
if not self.current_workflow or self.current_stage_index >= len(self.current_workflow["stages"]) - 1:
return
# 检查当前阶段是否完成
current_stage = self.current_workflow["stages"][self.current_stage_index]
artifacts = current_stage.get("artifacts", [])
total_artifacts = len(artifacts)
completed_artifacts = sum(1 for a in artifacts if a["completed"])
if total_artifacts > 0 and completed_artifacts < total_artifacts:
QMessageBox.warning(self, "无法继续", "请先完成当前阶段的所有成果物!")
return
self.current_stage_index += 1
self.update_workflow_display()
self.update_artifacts_display()
self.status_bar.showMessage(f"已进入阶段: {self.current_workflow['stages'][self.current_stage_index]['name']}", 2000)
def update_buttons_state(self):
"""更新按钮状态"""
if not self.current_workflow:
self.prev_stage_btn.setEnabled(False)
self.next_stage_btn.setEnabled(False)
return
# 上一阶段按钮
self.prev_stage_btn.setEnabled(self.current_stage_index > 0)
# 下一阶段按钮
self.next_stage_btn.setEnabled(self.current_stage_index < len(self.current_workflow["stages"]) - 1)
def new_workflow(self):
"""新建工作流程"""
dialog = WorkflowDialog(self)
if dialog.exec_() == QDialog.Accepted:
new_workflow = dialog.get_workflow()
if new_workflow:
self.workflows.append(new_workflow)
self.current_workflow = new_workflow
self.current_stage_index = 0
self.save_workflows()
self.update_workflows_tree()
self.update_workflow_display()
self.update_artifacts_display()
def edit_workflow(self):
"""编辑工作流程"""
if not self.current_workflow:
QMessageBox.warning(self, "警告", "请先选择一个工作流程!")
return
dialog = WorkflowDialog(self, self.current_workflow)
if dialog.exec_() == QDialog.Accepted:
edited_workflow = dialog.get_workflow()
if edited_workflow:
index = self.workflows.index(self.current_workflow)
self.workflows[index] = edited_workflow
self.current_workflow = edited_workflow
self.save_workflows()
self.update_workflows_tree()
self.update_workflow_display()
self.update_artifacts_display()
def delete_workflow(self):
"""删除当前工作流程"""
if not self.current_workflow:
QMessageBox.warning(self, "警告", "请先选择一个工作流程!")
return
reply = QMessageBox.question(
self, '确认删除',
f"确定要删除工作流程【{self.current_workflow['name']}】吗?\n此操作不可恢复!",
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
try:
# 从工作流程列表中移除当前工作流程
self.workflows.remove(self.current_workflow)
# 更新当前选中的工作流程
if self.workflows:
self.current_workflow = self.workflows[0]
self.current_stage_index = 0
else:
self.current_workflow = None
self.current_stage_index = -1
# 保存更改并更新界面
self.save_workflows()
self.update_workflows_tree()
self.update_workflow_display()
self.update_artifacts_display()
self.status_bar.showMessage(f"工作流程已删除", 2000)
except Exception as e:
QMessageBox.critical(self, "删除错误", f"删除工作流程时出错: {str(e)}")
def update_artifacts_display(self):
"""更新成果物显示并恢复状态"""
self.artifacts_list.clear()
if not self.current_workflow or self.current_stage_index < 0:
self.artifacts_label.setText("成果物检查")
self.status_info.setText("")
return
try:
current_stage = self.current_workflow["stages"][self.current_stage_index]
self.artifacts_label.setText(f"成果物检查 - {current_stage['name']}")
for artifact in current_stage.get("artifacts", []):
item = QListWidgetItem()
widget = QWidget()
layout = QHBoxLayout(widget)
layout.setContentsMargins(10, 5, 10, 5)
checkbox = QCheckBox(artifact["name"])
checkbox.setChecked(artifact["completed"])
checkbox.setStyleSheet("font-size: 19px;")
checkbox.stateChanged.connect(lambda state, a=artifact: self.update_artifact_status(a, state))
layout.addWidget(checkbox)
item.setSizeHint(widget.sizeHint())
self.artifacts_list.addItem(item)
self.artifacts_list.setItemWidget(item, widget)
# 更新状态信息
self.update_stage_status()
except Exception as e:
print(f"更新成果物显示时出错: {e}")
def update_artifact_status(self, artifact, state):
"""更新成果物状态"""
if not self.current_workflow:
return
try:
artifact["completed"] = (state == Qt.Checked)
self.update_stage_status()
except Exception as e:
print(f"更新成果物状态时出错: {e}")
def update_stage_status(self):
"""更新阶段状态"""
if not self.current_workflow or self.current_stage_index < 0:
return
try:
current_stage = self.current_workflow["stages"][self.current_stage_index]
artifacts = current_stage.get("artifacts", [])
total_artifacts = len(artifacts)
completed_artifacts = sum(1 for a in artifacts if a["completed"])
# 更新状态文本
if total_artifacts == 0:
status_text = "当前阶段没有成果物"
status_color = "#6c757d"
elif completed_artifacts == 0:
status_text = "状态: 待开始 (0% 完成)"
status_color = "#e74a3b"
current_stage["completed"] = False
elif completed_artifacts < total_artifacts:
percent = (completed_artifacts / total_artifacts) * 100
status_text = f"状态: 进行中 ({percent:.0f}% 完成)"
status_color = "#f6c23e"
current_stage["completed"] = False
else:
status_text = "状态: 已完成 (100% 完成)"
status_color = "#1cc88a"
current_stage["completed"] = True
self.status_info.setText(status_text)
self.status_info.setStyleSheet(f"""
font-size: 12px;
font-weight: 500;
color: white;
background-color: {status_color};
padding: 6px;
border-radius: 4px;
margin-top: 8px;
""")
self.update_buttons_state()
except Exception as e:
print(f"更新阶段状态时出错: {e}")
def show_stats(self):
"""显示统计信息"""
if not self.current_workflow:
return
# 计算统计数据
total_stages = len(self.current_workflow["stages"])
completed_stages = sum(1 for stage in self.current_workflow["stages"] if stage["completed"])
progress = (completed_stages / total_stages) * 100 if total_stages > 0 else 0
# 创建统计对话框
stats_dialog = QDialog(self)
stats_dialog.setWindowTitle("流程统计")
stats_dialog.resize(400, 300)
layout = QVBoxLayout(stats_dialog)
# 标题
title = QLabel(f"{self.current_workflow['name']} - 统计信息")
title.setStyleSheet("font-size: 16px; font-weight: 600; color: #4e73df;")
layout.addWidget(title)
# 进度条
progress_bar = QWidget()
progress_bar.setFixedHeight(30)
progress_bar.setStyleSheet(f"""
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop:0 #1cc88a, stop:{progress/100} #1cc88a,
stop:{progress/100} #e74a3b, stop:1 #e74a3b);
border-radius: 4px;
""")
layout.addWidget(progress_bar)
# 进度文本
progress_text = QLabel(f"整体进度: {progress:.1f}% ({completed_stages}/{total_stages} 阶段完成)")
progress_text.setAlignment(Qt.AlignCenter)
layout.addWidget(progress_text)
# 阶段详情
stages_label = QLabel("阶段完成情况:")
stages_label.setStyleSheet("font-weight: 600; margin-top: 15px;")
layout.addWidget(stages_label)
stages_list = QListWidget()
for stage in self.current_workflow["stages"]:
status = "✓ 已完成" if stage["completed"] else "✗ 未完成"
item = QListWidgetItem(f"{stage['name']} - {status}")
item.setForeground(QColor("#1cc88a" if stage["completed"] else "#e74a3b"))
stages_list.addItem(item)
layout.addWidget(stages_list)
stats_dialog.exec_()
class WorkflowDialog(QDialog):
"""工作流程编辑对话框"""
def __init__(self, parent=None, workflow=None):
super().__init__(parent)
self.setWindowTitle("编辑工作流程" if workflow else "新建工作流程")
self.resize(600, 450)
# 深拷贝工作流程数据,避免直接修改原始数据
self.workflow = copy.deepcopy(workflow) if workflow else {
"name": "",
"description": "",
"stages": [],
"tasks": []
}
self.setup_ui()
def setup_ui(self):
"""设置对话框UI"""
layout = QVBoxLayout(self)
layout.setContentsMargins(15, 15, 15, 15)
# 标题
title_text = "编辑工作流程" if self.workflow.get("name") else "新建工作流程"
title = QLabel(title_text)
title.setStyleSheet("font-size: 16px; font-weight: 600; color: #4e73df; margin-bottom: 15px;")
layout.addWidget(title)
# 基本信息
form_layout = QFormLayout()
form_layout.setLabelAlignment(Qt.AlignRight)
form_layout.setVerticalSpacing(10)
self.name_edit = QLineEdit(self.workflow["name"])
self.name_edit.setPlaceholderText("输入工作流程名称")
form_layout.addRow("流程名称:", self.name_edit)
self.desc_edit = QTextEdit(self.workflow["description"])
self.desc_edit.setPlaceholderText("输入工作流程描述")
self.desc_edit.setFixedHeight(60)
form_layout.addRow("流程描述:", self.desc_edit)
layout.addLayout(form_layout)
# 阶段管理
stages_label = QLabel("阶段管理")
stages_label.setStyleSheet("font-size: 14px; font-weight: 600; color: #4e73df; margin-top: 15px; margin-bottom: 8px;")
layout.addWidget(stages_label)
# 阶段列表和编辑区
stages_container = QHBoxLayout()
stages_container.setSpacing(15)
# 阶段列表
stages_list_container = QVBoxLayout()
self.stages_list = QListWidget()
self.stages_list.setFixedHeight(150)
self.update_stages_list()
self.stages_list.itemSelectionChanged.connect(self.on_stage_selection_changed)
stages_list_container.addWidget(self.stages_list)
stages_container.addLayout(stages_list_container, 1)
# 阶段编辑区
stage_edit_container = QVBoxLayout()
self.stage_edit = QWidget()
self.stage_edit.setStyleSheet("background-color: #f8f9fa; border-radius: 6px; padding: 10px;")
stage_edit_layout = QFormLayout(self.stage_edit)
stage_edit_layout.setVerticalSpacing(8)
self.stage_name_edit = QLineEdit()
self.stage_name_edit.setPlaceholderText("输入阶段名称")
stage_edit_layout.addRow("阶段名称:", self.stage_name_edit)
self.stage_desc_edit = QTextEdit()
self.stage_desc_edit.setPlaceholderText("输入阶段描述")
self.stage_desc_edit.setFixedHeight(50)
stage_edit_layout.addRow("阶段描述:", self.stage_desc_edit)
self.stage_color_edit = QComboBox()
self.stage_color_edit.addItems(["#4e73df", "#1cc88a", "#36b9cc", "#f6c23e", "#e74a3b", "#6f42c1", "#fd7e14", "#20c997"])
stage_edit_layout.addRow("阶段颜色:", self.stage_color_edit)
# 成果物编辑
artifacts_label = QLabel("成果物 (每行一个):")
stage_edit_layout.addRow(artifacts_label)
self.artifacts_edit = QTextEdit()
self.artifacts_edit.setPlaceholderText("输入成果物名称,每行一个")
self.artifacts_edit.setFixedHeight(80)
stage_edit_layout.addRow(self.artifacts_edit)
stage_edit_container.addWidget(self.stage_edit)
# 阶段操作按钮
stage_btn_layout = QHBoxLayout()
stage_btn_layout.setSpacing(8)
self.add_stage_btn = QPushButton("添加")
self.add_stage_btn.setStyleSheet("background-color: #1cc88a; color: white; padding: 5px;")
self.add_stage_btn.clicked.connect(self.add_stage)
stage_btn_layout.addWidget(self.add_stage_btn)
self.update_stage_btn = QPushButton("更新")
self.update_stage_btn.setStyleSheet("background-color: #4e73df; color: white; padding: 5px;")
self.update_stage_btn.clicked.connect(self.update_current_stage)
stage_btn_layout.addWidget(self.update_stage_btn)
self.remove_stage_btn = QPushButton("删除")
self.remove_stage_btn.setStyleSheet("background-color: #e74a3b; color: white; padding: 5px;")
self.remove_stage_btn.clicked.connect(self.remove_stage)
stage_btn_layout.addWidget(self.remove_stage_btn)
stage_edit_container.addLayout(stage_btn_layout)
stages_container.addLayout(stage_edit_container, 2)
layout.addLayout(stages_container)
# 对话框按钮
btn_layout = QHBoxLayout()
btn_layout.setAlignment(Qt.AlignRight)
btn_layout.setSpacing(10)
save_btn = QPushButton("保存")
save_btn.setStyleSheet("background-color: #4e73df; color: white; min-width: 80px; padding: 6px;")
save_btn.clicked.connect(self.on_save)
btn_layout.addWidget(save_btn)
cancel_btn = QPushButton("取消")
cancel_btn.setStyleSheet("background-color: #6c757d; color: white; min-width: 80px; padding: 6px;")
cancel_btn.clicked.connect(self.reject)
btn_layout.addWidget(cancel_btn)
layout.addLayout(btn_layout)
# 初始化选择
if self.stages_list.count() > 0:
self.stages_list.setCurrentRow(0)
def update_stages_list(self):
"""更新阶段列表显示"""
self.stages_list.clear()
for stage in self.workflow["stages"]:
item = QListWidgetItem(stage["name"])
item.setData(Qt.UserRole, stage)
self.stages_list.addItem(item)
def on_stage_selection_changed(self):
"""当阶段选择变化时更新编辑区域"""
current_item = self.stages_list.currentItem()
if current_item:
self.select_stage(current_item)
else:
self.clear_stage_edit()
def select_stage(self, item):
"""加载选中阶段的数据到编辑区域"""
stage = item.data(Qt.UserRole)
self.stage_name_edit.setText(stage["name"])
self.stage_desc_edit.setText(stage["description"])
# 设置颜色
color_index = self.stage_color_edit.findText(stage["color"])
if color_index >= 0:
self.stage_color_edit.setCurrentIndex(color_index)
# 加载成果物
artifact_names = [a["name"] for a in stage.get("artifacts", [])]
self.artifacts_edit.setText("\n".join(artifact_names))
def add_stage(self):
"""添加新阶段"""
name = self.stage_name_edit.text().strip()
if not name:
QMessageBox.warning(self, "警告", "请输入阶段名称!")
return
# 从文本创建成果物对象
artifacts = [
{"name": a.strip(), "completed": False}
for a in self.artifacts_edit.toPlainText().split("\n")
if a.strip()
]
new_stage = {
"name": name,
"description": self.stage_desc_edit.toPlainText().strip(),
"color": self.stage_color_edit.currentText(),
"artifacts": artifacts,
"completed": False
}
self.workflow["stages"].append(new_stage)
self.update_stages_list()
# 选中新添加的阶段
self.stages_list.setCurrentRow(len(self.workflow["stages"]) - 1)
QMessageBox.information(self, "成功", f"阶段 '{name}' 已添加!", QMessageBox.Ok)
def update_current_stage(self):
"""更新当前选中的阶段"""
current_item = self.stages_list.currentItem()
if not current_item:
QMessageBox.warning(self, "警告", "请先选择一个阶段!")
return
name = self.stage_name_edit.text().strip()
if not name:
QMessageBox.warning(self, "警告", "请输入阶段名称!")
return
stage = current_item.data(Qt.UserRole)
stage["name"] = name
stage["description"] = self.stage_desc_edit.toPlainText().strip()
stage["color"] = self.stage_color_edit.currentText()
# 更新成果物列表
artifact_names = [a.strip() for a in self.artifacts_edit.toPlainText().split("\n") if a.strip()]
# 创建新的成果物列表,保留现有成果物的状态
new_artifacts = []
existing_artifacts = {a["name"]: a for a in stage.get("artifacts", [])}
for name in artifact_names:
# 如果成果物已存在,保留其状态
if name in existing_artifacts:
new_artifacts.append(existing_artifacts[name])
else:
# 新增成果物,默认状态为未完成
new_artifacts.append({"name": name, "completed": False})
stage["artifacts"] = new_artifacts
# 更新阶段列表并刷新显示
current_row = self.stages_list.currentRow()
self.update_stages_list()
self.stages_list.setCurrentRow(current_row)
# 重新加载当前阶段的信息到编辑区域
self.select_stage(self.stages_list.currentItem())
QMessageBox.information(self, "成功", f"阶段 '{name}' 已更新!", QMessageBox.Ok)
def remove_stage(self):
"""删除当前阶段"""
current_item = self.stages_list.currentItem()
if not current_item:
QMessageBox.warning(self, "警告", "请先选择一个阶段!")
return
stage_name = current_item.text()
# 检查是否是该工作流程的唯一阶段
if len(self.workflow["stages"]) <= 1:
QMessageBox.warning(self, "警告", "工作流程必须至少包含一个阶段!")
return
reply = QMessageBox.question(
self, '确认删除',
f"确定要删除阶段【{stage_name}】吗? 所有关联任务也将被删除!",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
stage = current_item.data(Qt.UserRole)
# 删除关联任务
self.workflow["tasks"] = [task for task in self.workflow["tasks"] if task.get("stage") != stage["name"]]
# 删除阶段
self.workflow["stages"].remove(stage)
# 更新列表
self.update_stages_list()
# 清空编辑区域
self.clear_stage_edit()
# 自动选择下一个阶段(如果有)
if self.stages_list.count() > 0:
self.stages_list.setCurrentRow(0)
else:
self.clear_stage_edit()
QMessageBox.information(self, "成功", f"阶段 '{stage_name}' 已删除!", QMessageBox.Ok)
def clear_stage_edit(self):
"""清空阶段编辑区域"""
self.stage_name_edit.clear()
self.stage_desc_edit.clear()
self.stage_color_edit.setCurrentIndex(0)
self.artifacts_edit.clear()
def get_workflow(self):
"""获取编辑后的工作流程"""
# 确保在关闭对话框前更新当前选中的阶段
if self.stages_list.currentItem():
self.update_current_stage()
return {
"name": self.name_edit.text().strip(),
"description": self.desc_edit.toPlainText().strip(),
"stages": self.workflow["stages"],
"tasks": self.workflow["tasks"]
}
def on_save(self):
"""保存工作流程前的验证和处理"""
# 检查工作流程名称
if not self.name_edit.text().strip():
QMessageBox.warning(self, "警告", "工作流程名称不能为空!")
return
# 检查阶段数量
if not self.workflow["stages"]:
QMessageBox.warning(self, "警告", "工作流程必须包含至少一个阶段!")
return
# 检查阶段名称
for stage in self.workflow["stages"]:
if not stage.get("name", "").strip():
QMessageBox.warning(self, "警告", "阶段名称不能为空!")
return
# 确保在关闭对话框前更新当前选中的阶段
if self.stages_list.currentItem():
self.update_current_stage()
# 所有检查通过,接受对话框
self.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
# 设置应用程序样式
app.setStyle("Fusion")
palette = QPalette()
palette.setColor(QPalette.Window, QColor(248, 249, 250))
palette.setColor(QPalette.WindowText, QColor(73, 80, 87))
palette.setColor(QPalette.Base, QColor(255, 255, 255))
palette.setColor(QPalette.AlternateBase, QColor(248, 249, 250))
palette.setColor(QPalette.ToolTipBase, QColor(255, 255, 255))
palette.setColor(QPalette.ToolTipText, QColor(73, 80, 87))
palette.setColor(QPalette.Text, QColor(73, 80, 87))
palette.setColor(QPalette.Button, QColor(248, 249, 250))
palette.setColor(QPalette.ButtonText, QColor(73, 80, 87))
palette.setColor(QPalette.BrightText, QColor(255, 255, 255))
palette.setColor(QPalette.Highlight, QColor(78, 115, 223))
palette.setColor(QPalette.HighlightedText, QColor(255, 255, 255))
app.setPalette(palette)
# 设置应用字体
app_font = QFont("Segoe UI", 9)
app.setFont(app_font)
manager = WorkflowManager()
manager.show()
sys.exit(app.exec_())
这里按照你的要求新增之后有问题,无法运行了
最新发布