import json import re from datetime import datetime from PyQt5.QtCore import Qt, QTimer, QAbstractTableModel, QModelIndex, QPoint, QSize from PyQt5.QtGui import QFont, QBrush, QColor from openpyxl import load_workbook class ExcelParser: @staticmethod def parseProcedure(filePath): wb = load_workbook(filename=filePath) if '测试脚本' in wb.sheetnames: sheet = wb['测试脚本'] else: sheet = wb.active specInfo = { "规程名称": sheet['B1'].value, "规程编号": sheet['D1'].value, "规程类型": sheet['F1'].value } testCaseInfo = { "测试用例": sheet['B2'].value, "用例编号": sheet['D2'].value, "工况描述": sheet['H2'].value } testSteps = [] currentStep = None stepCounter = 0 for rowIdx in range(5, sheet.max_row + 1): cellA = sheet[f'A{rowIdx}'].value cellB = sheet[f'B{rowIdx}'].value cellC = sheet[f'C{rowIdx}'].value if cellB is None and cellA is None: continue if cellA and re.match(r'STEP\d+[::]', str(cellA)): if currentStep: testSteps.append(currentStep) stepCounter += 1 stepName = str(cellA).replace(':', ':').strip() stepDesc = str(cellB) if cellB else "" currentStep = { "步骤ID": stepName, "步骤描述": stepDesc, "操作类型": cellC if cellC else "", "预期结果": sheet[f'D{rowIdx}'].value, "子步骤": [] } elif currentStep and cellA and str(cellA).isdigit(): subStep = { "序号": int(cellA), "操作": str(cellB) if cellB else "", "操作类型": cellC if cellC else "", "预期结果": sheet[f'D{rowIdx}'].value, "实际结果": sheet[f'E{rowIdx}'].value, "一致性": sheet[f'F{rowIdx}'].value if sheet[f'F{rowIdx}'].value == "是" else "否", "测试时间": sheet[f'H{rowIdx}'].value, "备注": sheet[f'I{rowIdx}'].value } currentStep["子步骤"].append(subStep) if currentStep: testSteps.append(currentStep) return { "文件路径": filePath, "规程信息": specInfo, "测试用例信息": testCaseInfo, "测试步骤": testSteps } class StepTableModel(QAbstractTableModel): columns = ['序号', '实验步骤', '执行时间', '是否与预期一致', '实际结果', '备注'] def __init__(self, testSteps): super().__init__() self.stepData = [] self.stepIndex = 0 for mainStep in testSteps: self.stepData.append({ 'id': self.stepIndex, 'isMain': True, 'stepId': mainStep['步骤ID'], 'description': mainStep['步骤描述'], 'executed': False, 'time': None, 'result': None, 'note': "" # 新增备注字段,主步骤默认为空 }) self.stepIndex += 1 for subStep in mainStep['子步骤']: self.stepData.append({ 'id': self.stepIndex, 'isMain': False, 'stepId': f"{mainStep['步骤ID']}{subStep['序号']}", 'description': subStep['操作'], 'executed': False, 'time': None, 'result': None, 'note': subStep['备注'] # 新增:从Excel解析的备注字段 }) self.stepIndex += 1 def rowCount(self, parent=QModelIndex()): return len(self.stepData) def columnCount(self, parent=QModelIndex()): return len(self.columns) def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None row = index.row() col = index.column() step = self.stepData[row] if role == Qt.DisplayRole: if col == 0: return step['stepId'] elif col == 1: return step['description'] elif col == 2: return step['time'].strftime("%Y-%m-%d %H:%M:%S") if step['time'] else '' elif col == 3: return {True: '是', False: '否', None: ''}[step['result']] elif col == 4: return {True: '成功', False: '失败', None: ''}[step['result']] elif col == 5: return step['note'] if step['note'] else '' elif role == Qt.BackgroundRole: if step['executed']: if step['result']: return QBrush(QColor(144, 238, 144)) else: return QBrush(QColor(255, 182, 193)) elif step['isMain']: return QBrush(QColor(220, 220, 220)) elif role == Qt.FontRole and step['isMain']: font = QFont() font.setBold(True) return font # 新增:支持自动换行 elif role == Qt.TextAlignmentRole: if col in [1, 5]: # 描述和备注列 return Qt.AlignLeft | Qt.AlignVCenter elif role == Qt.TextWordWrap: if col in [1, 5]: return True return None def headerData(self, section, orientation, role): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.columns[section] return None def getStepInfo(self, row): if 0 <= row < len(self.stepData): return self.stepData[row] return None def getFullStepInfo(self, row): """获取完整的步骤信息""" if 0 <= row < len(self.stepData): return self.stepData[row] return None def updateStepResult(self, row, result, time): if 0 <= row < len(self.stepData): self.stepData[row]['executed'] = True self.stepData[row]['time'] = time self.stepData[row]['result'] = result self.dataChanged.emit(self.index(row, 0), self.index(row, self.columnCount()-1), [Qt.DisplayRole, Qt.BackgroundRole]) return True return False def resetExecutionState(self): """只重置执行状态(颜色),不清除时间和结果""" for step in self.stepData: step['executed'] = False step['result'] = None self.dataChanged.emit(self.index(0, 0), self.index(self.rowCount()-1, self.columnCount()-1), [Qt.BackgroundRole]) def resetAll(self): """完全重置所有状态(包括时间和结果)""" for step in self.stepData: step.update({ 'executed': False, 'time': None, 'result': None }) self.dataChanged.emit(self.index(0, 0), self.index(self.rowCount()-1, self.columnCount()-1), [Qt.DisplayRole, Qt.BackgroundRole])