|
|
|
@ -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"<b>步骤ID:</b> {stepId}"))
|
|
|
|
|
infoGroupLayout.addWidget(QLabel(f"<b>实际/预期对比:</b> {operationType}"))
|
|
|
|
|
infoGroupLayout.addWidget(QLabel(f"<b>执行时间:</b> {execTime}"))
|
|
|
|
|
infoGroupLayout.addWidget(QLabel(f"<b>执行状态:</b> {execStatus}"))
|
|
|
|
|
infoGroup.setLayout(infoGroupLayout)
|
|
|
|
|
|
|
|
|
|
layout.addWidget(QLabel("<b>基本信息</b>"))
|
|
|
|
|
layout.addWidget(infoGroup)
|
|
|
|
|
|
|
|
|
|
# 步骤描述区域
|
|
|
|
|
layout.addWidget(QLabel("<b>步骤描述</b>"))
|
|
|
|
|
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("<b>执行结果详情</b>"))
|
|
|
|
|
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):
|
|
|
|
|
"""删除选中的历史记录"""
|
|
|
|
|