From 03c38c6d8d17a161642f4ee3fa16793acedc36e2 Mon Sep 17 00:00:00 2001 From: zcwBit Date: Sat, 19 Jul 2025 00:22:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=A7=E8=A1=8C=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UI/ProcedureManager/HistoryViewer.py | 355 ++++++++++++++++++++++++-- UI/ProcedureManager/KeywordManager.py | 11 +- UI/ProcedureManager/StepExecutor.py | 6 +- 3 files changed, 345 insertions(+), 27 deletions(-) diff --git a/UI/ProcedureManager/HistoryViewer.py b/UI/ProcedureManager/HistoryViewer.py index e98c456..0c15abb 100644 --- a/UI/ProcedureManager/HistoryViewer.py +++ b/UI/ProcedureManager/HistoryViewer.py @@ -48,8 +48,9 @@ class HistoryViewerWidget(QWidget): self.stepTable = QTableView() self.stepTable.setEditTriggers(QTableView.NoEditTriggers) self.stepModel = QStandardItemModel() - self.stepModel.setHorizontalHeaderLabels(["步骤ID", "步骤描述", "执行时间", "执行结果"]) + self.stepModel.setHorizontalHeaderLabels(["步骤ID", "步骤描述", "执行时间", "执行状态", "详细结果", "实际/预期对比"]) self.stepTable.setModel(self.stepModel) + self.stepTable.doubleClicked.connect(self.showStepDetailDialog) self.setupTableDisplay(self.stepTable) # 分割窗口 @@ -188,42 +189,354 @@ class HistoryViewerWidget(QWidget): timeItem.setTextAlignment(Qt.AlignCenter) rowItems.append(timeItem) - # 执行结果 - resultText = '成功' if step.get('result', False) else '失败' - resultItem = QStandardItem(resultText) - resultItem.setTextAlignment(Qt.AlignCenter) - if step.get('result', False): - resultItem.setBackground(QBrush(QColor(200, 255, 200))) + # 解析执行结果 + resultInfo = self.parseStepResult(step.get('result', '')) + + # 执行状态 + statusItem = QStandardItem(resultInfo['statusText']) + statusItem.setTextAlignment(Qt.AlignCenter) + statusItem.setBackground(QBrush(resultInfo['statusColor'])) + if resultInfo['status'] == 'success': + statusItem.setText("✓ 成功") + elif resultInfo['status'] == 'failed': + statusItem.setText("✗ 失败") + elif resultInfo['status'] == 'partial': + statusItem.setText("⚠ 部分成功") else: - resultItem.setBackground(QBrush(QColor(255, 200, 200))) - rowItems.append(resultItem) + statusItem.setText("? 未知") + rowItems.append(statusItem) + + # 详细结果 + detailItem = QStandardItem(resultInfo['details']) + detailItem.setTextAlignment(Qt.AlignTop | Qt.AlignLeft) + detailItem.setToolTip(resultInfo['details']) + rowItems.append(detailItem) + + # 实际/预期对比 + comparisonItem = QStandardItem(resultInfo['comparison']) + comparisonItem.setTextAlignment(Qt.AlignCenter) + comparisonItem.setBackground(QBrush(resultInfo['typeColor'])) + comparisonItem.setToolTip(resultInfo['comparisonDetail']) + rowItems.append(comparisonItem) self.stepModel.appendRow(rowItems) self.adjustTableColumns(self.stepTable, self.stepModel) + def parseStepResult(self, result): + """解析步骤执行结果""" + if not result or result == 'None': + return { + 'status': 'failed', + 'statusText': '失败', + 'statusColor': QColor(255, 200, 200), + 'details': '无执行结果', + 'comparison': '无对比', + 'comparisonDetail': '步骤未执行或执行失败', + 'typeColor': QColor(220, 220, 220) + } + + result_str = str(result) + + # 设置操作结果解析 + if "=" in result_str: + # 提取设置的变量和值 + import re + matches = re.findall(r'([^\s=]+)\s*=\s*([0-9]+(?:\.[0-9]+)?)', result_str) + failedMatches = re.findall(r'([^\s=]+)强制失败', result_str) + + if matches or failedMatches: + successCount = len(matches) + failCount = len(failedMatches) + totalCount = successCount + failCount + + if failCount > 0: + status = 'partial' if successCount > 0 else 'failed' + statusColor = QColor(255, 255, 200) if status == 'partial' else QColor(255, 200, 200) + statusText = '部分成功' if status == 'partial' else '失败' + comparison = f"设置 {successCount}/{totalCount} 成功" + comparisonDetail = f"成功设置: {', '.join([f'{var}={val}' for var, val in matches])}" + if failedMatches: + comparisonDetail += f"; 失败变量: {', '.join(failedMatches)}" + else: + status = 'success' + statusColor = QColor(200, 255, 200) + statusText = '成功' + comparison = f"设置 {successCount} 个变量" + comparisonDetail = f"成功设置: {', '.join([f'{var}={val}' for var, val in matches])}" + + return { + 'status': status, + 'statusText': statusText, + 'statusColor': statusColor, + 'details': result_str, + 'comparison': comparison, + 'comparisonDetail': comparisonDetail, + 'typeColor': QColor(173, 216, 230) + } + elif "成功" in result_str or "✓" in result_str: + # 包含等号但没有匹配到具体变量,但有成功标识 + return { + 'status': 'success', + 'statusText': '成功', + 'statusColor': QColor(200, 255, 200), + 'details': result_str, + 'comparison': "设置成功", + 'comparisonDetail': "变量设置操作执行成功", + 'typeColor': QColor(173, 216, 230) + } + + # 检查操作结果解析 + if any(op in result_str for op in ['>', '<', '>=', '<=', '==', '!=']): + # 分析检查结果 + successCount = result_str.count('✓') + failCount = result_str.count('✗') + totalChecks = successCount + failCount + + if failCount > 0: + status = 'partial' if successCount > 0 else 'failed' + statusColor = QColor(255, 255, 200) if status == 'partial' else QColor(255, 200, 200) + statusText = '部分成功' if status == 'partial' else '失败' + comparison = f"{successCount}/{totalChecks} 通过" + comparisonDetail = f"检查结果: {successCount}个成功, {failCount}个失败" + else: + status = 'success' + statusColor = QColor(200, 255, 200) + statusText = '成功' + comparison = f"{totalChecks}/{totalChecks} 通过" + comparisonDetail = f"所有 {totalChecks} 个检查项均通过" + + return { + 'status': status, + 'statusText': statusText, + 'statusColor': statusColor, + 'details': result_str, + 'comparison': comparison, + 'comparisonDetail': comparisonDetail, + 'typeColor': QColor(255, 218, 185) + } + + # 等待操作结果解析 + if "等待" in result_str or "wait" in result_str.lower(): + # 提取等待时间 + import re + timeMatch = re.search(r'([0-9]+(?:\.[0-9]+)?)', result_str) + if timeMatch: + waitTime = timeMatch.group(1) + comparison = f"等待 {waitTime}s" + comparisonDetail = f"按预期等待了 {waitTime} 秒" + else: + comparison = "等待完成" + comparisonDetail = "等待操作按预期完成" + + return { + 'status': 'success', + 'statusText': '成功', + 'statusColor': QColor(200, 255, 200), + 'details': result_str, + 'comparison': comparison, + 'comparisonDetail': comparisonDetail, + 'typeColor': QColor(221, 160, 221) + } + + # 接收操作结果解析 + if "t1=" in result_str or "deltaT" in result_str: + # 计算接收到的数据点数量 + import re + dataPoints = re.findall(r't\d+=[\d.-]+', result_str) + comparison = f"接收 {len(dataPoints)} 个数据" + comparisonDetail = f"成功接收数据: {result_str}" + + return { + 'status': 'success', + 'statusText': '成功', + 'statusColor': QColor(200, 255, 200), + 'details': result_str, + 'comparison': comparison, + 'comparisonDetail': comparisonDetail, + 'typeColor': QColor(144, 238, 144) + } + + # 错误情况 + if "失败" in result_str or "error" in result_str.lower() or "错误" in result_str: + return { + 'status': 'failed', + 'statusText': '失败', + 'statusColor': QColor(255, 200, 200), + 'details': result_str, + 'comparison': '执行失败', + 'comparisonDetail': f"执行过程中出现错误: {result_str}", + 'typeColor': QColor(255, 182, 193) + } + + # 成功情况 + if "成功" in result_str or "执行成功" in result_str: + return { + 'status': 'success', + 'statusText': '成功', + 'statusColor': QColor(200, 255, 200), + 'details': result_str, + 'comparison': '执行成功', + 'comparisonDetail': '操作按预期成功执行', + 'typeColor': QColor(220, 220, 220) + } + + # 默认情况 + return { + 'status': 'unknown', + 'statusText': '未知', + 'statusColor': QColor(240, 240, 240), + 'details': result_str, + 'comparison': '结果未知', + 'comparisonDetail': f'无法解析的执行结果: {result_str}', + 'typeColor': QColor(220, 220, 220) + } + def adjustTableColumns(self, table, model): """调整表格列宽""" header = table.horizontalHeader() if not header: return - for col in range(model.columnCount()): - if col == 0: # ID列 - header.setSectionResizeMode(col, QHeaderView.ResizeToContents) - elif col == 1: # 描述列 - header.setSectionResizeMode(col, QHeaderView.Stretch) - elif col == 2: # 类型列 - header.setSectionResizeMode(col, QHeaderView.ResizeToContents) - elif col == 3: # 时间列 - header.setSectionResizeMode(col, QHeaderView.ResizeToContents) - elif col == 4: # 报告路径或结果列 - if model == self.model: + if model == self.model: # 历史记录表格 + for col in range(model.columnCount()): + if col == 0: # ID列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 1: # 规程全名列 header.setSectionResizeMode(col, QHeaderView.Stretch) - else: + elif col == 2: # 类型列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 3: # 执行时间列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 4: # 报告路径列 + header.setSectionResizeMode(col, QHeaderView.Stretch) + else: # 步骤详情表格 + for col in range(model.columnCount()): + if col == 0: # 步骤ID列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 1: # 步骤描述列 + header.setSectionResizeMode(col, QHeaderView.Stretch) + elif col == 2: # 执行时间列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 3: # 执行状态列 + header.setSectionResizeMode(col, QHeaderView.ResizeToContents) + elif col == 4: # 详细结果列 + header.setSectionResizeMode(col, QHeaderView.Stretch) + elif col == 5: # 操作类型列 header.setSectionResizeMode(col, QHeaderView.ResizeToContents) table.resizeRowsToContents() + + def showStepDetailDialog(self, index): + """显示步骤详情对话框""" + if not index.isValid(): + return + + row = index.row() + stepId = self.stepModel.item(row, 0).text() + stepDesc = self.stepModel.item(row, 1).text() + execTime = self.stepModel.item(row, 2).text() + execStatus = self.stepModel.item(row, 3).text() + detailResult = self.stepModel.item(row, 4).text() + operationType = self.stepModel.item(row, 5).text() + + # 创建详情对话框 + dialog = QDialog(self) + dialog.setWindowTitle(f"步骤详情 - {stepId}") + dialog.setMinimumSize(600, 400) + dialog.setModal(True) + + layout = QVBoxLayout() + + # 基本信息区域 + infoLayout = QVBoxLayout() + infoGroup = QWidget() + infoGroup.setStyleSheet(""" + QWidget { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 5px; + padding: 10px; + margin: 5px; + } + """) + + infoGroupLayout = QVBoxLayout() + infoGroupLayout.addWidget(QLabel(f"步骤ID: {stepId}")) + infoGroupLayout.addWidget(QLabel(f"实际/预期对比: {operationType}")) + infoGroupLayout.addWidget(QLabel(f"执行时间: {execTime}")) + infoGroupLayout.addWidget(QLabel(f"执行状态: {execStatus}")) + infoGroup.setLayout(infoGroupLayout) + + layout.addWidget(QLabel("基本信息")) + layout.addWidget(infoGroup) + + # 步骤描述区域 + layout.addWidget(QLabel("步骤描述")) + descTextEdit = QLineEdit() + descTextEdit.setText(stepDesc) + descTextEdit.setReadOnly(True) + descTextEdit.setStyleSheet(""" + QLineEdit { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 3px; + padding: 8px; + font-family: 'Consolas', monospace; + } + """) + layout.addWidget(descTextEdit) + + # 详细结果区域 + layout.addWidget(QLabel("执行结果详情")) + resultTextEdit = QLineEdit() + resultTextEdit.setText(detailResult) + resultTextEdit.setReadOnly(True) + + # 根据执行状态设置结果显示样式 + if "成功" in execStatus: + resultTextEdit.setStyleSheet(""" + QLineEdit { + background-color: #d4edda; + border: 1px solid #c3e6cb; + border-radius: 3px; + padding: 8px; + color: #155724; + font-family: 'Consolas', monospace; + } + """) + elif "失败" in execStatus: + resultTextEdit.setStyleSheet(""" + QLineEdit { + background-color: #f8d7da; + border: 1px solid #f5c6cb; + border-radius: 3px; + padding: 8px; + color: #721c24; + font-family: 'Consolas', monospace; + } + """) + else: + resultTextEdit.setStyleSheet(""" + QLineEdit { + background-color: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 3px; + padding: 8px; + color: #856404; + font-family: 'Consolas', monospace; + } + """) + + layout.addWidget(resultTextEdit) + + # 按钮区域 + buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) + buttonBox.accepted.connect(dialog.accept) + layout.addWidget(buttonBox) + + dialog.setLayout(layout) + dialog.exec_() def deleteSelectedHistory(self): """删除选中的历史记录""" diff --git a/UI/ProcedureManager/KeywordManager.py b/UI/ProcedureManager/KeywordManager.py index 2c931a2..ae9a3cb 100644 --- a/UI/ProcedureManager/KeywordManager.py +++ b/UI/ProcedureManager/KeywordManager.py @@ -24,6 +24,9 @@ class KeywordTableModel(QAbstractTableModel): """加载数据""" self.beginResetModel() self.keywordData = self.dbManager.getStepKeywords() + # 按ID排序确保顺序正确 + if self.keywordData: + self.keywordData.sort(key=lambda x: x[0]) # 按第一列(ID)排序 self.endResetModel() def rowCount(self, parent=QModelIndex()): @@ -46,7 +49,7 @@ class KeywordTableModel(QAbstractTableModel): if role == QtCore.Qt.DisplayRole: if col == 0: - return str(keywordInfo[0]) # ID + return str(row + 1) # 显示行号作为ID elif col == 1: return keywordInfo[1] # 关键词 elif col == 2: @@ -96,7 +99,7 @@ class KeywordEditDialog(QDialog): self.isEdit = keywordInfo is not None self.setWindowTitle("编辑关键词" if self.isEdit else "添加关键词") - self.setFixedSize(400, 200) + self.setFixedSize(500, 300) self.setupUi() if self.isEdit: @@ -335,4 +338,6 @@ class KeywordManagerWidget(QWidget): def getOperationTypes(self): """获取所有操作类型""" - return self.dbManager.getOperationTypes() \ No newline at end of file + return self.dbManager.getOperationTypes() + + \ No newline at end of file diff --git a/UI/ProcedureManager/StepExecutor.py b/UI/ProcedureManager/StepExecutor.py index d7cb20f..a59c102 100644 --- a/UI/ProcedureManager/StepExecutor.py +++ b/UI/ProcedureManager/StepExecutor.py @@ -529,7 +529,7 @@ class StepExecutor(QWidget): # 计算最终执行统计 totalSteps = len([step for step in self.tableModel.stepData]) executedSteps = len([step for step in self.tableModel.stepData if step.get('executed', False)]) - successSteps = len([step for step in self.tableModel.stepData if step.get('executed', False) and step.get('result', False)]) + successSteps = len([step for step in self.tableModel.stepData if step.get('executed', False)]) failedSteps = executedSteps - successSteps successRate = (successSteps/executedSteps*100) if executedSteps > 0 else 0 @@ -1099,7 +1099,7 @@ class StepExecutor(QWidget): for i in range(self.currentIndex, len(self.tableModel.stepData)): step = self.tableModel.stepData[i] if not step['isMain']: - stepType = step.get('stepType', '') + stepType = str(step.get('stepType', '')) if 'Time' in stepType: timeMatch = re.search(r'Time\s*=\s*(\d+)\s*ms', stepType, re.IGNORECASE) if timeMatch: @@ -1218,7 +1218,7 @@ class StepExecutor(QWidget): for step in self.tableModel.stepData: if not step['isMain']: # 根据步骤类型预估额外时间 - stepType = step.get('stepType', '') + stepType = str(step.get('stepType', '')) if 'Time' in stepType: # 设置操作带超时时间 timeMatch = re.search(r'Time\s*=\s*(\d+)\s*ms', stepType, re.IGNORECASE)