|
|
|
@ -174,6 +174,18 @@ class StepExecutor(QWidget):
|
|
|
|
|
self.actualElapsedTime = 0 # 实际已用时间
|
|
|
|
|
self.lastStepStartTime = None # 上一步开始时间
|
|
|
|
|
|
|
|
|
|
# 等待操作相关状态
|
|
|
|
|
self.isWaiting = False # 是否正在等待
|
|
|
|
|
self.waitingStepId = None # 正在等待的步骤ID
|
|
|
|
|
self.waitStartTime = None # 等待开始时间
|
|
|
|
|
self.waitDuration = 0 # 等待持续时间
|
|
|
|
|
|
|
|
|
|
# 设置操作超时相关状态
|
|
|
|
|
self.isSettingTimeout = False # 是否正在设置操作超时等待
|
|
|
|
|
self.settingStepId = None # 正在超时等待的设置步骤ID
|
|
|
|
|
self.settingResult = None # 设置操作的结果
|
|
|
|
|
self.settingStartTime = None # 设置超时开始时间
|
|
|
|
|
|
|
|
|
|
def initTimers(self):
|
|
|
|
|
"""初始化定时器"""
|
|
|
|
|
self.timer = QTimer()
|
|
|
|
@ -182,6 +194,16 @@ class StepExecutor(QWidget):
|
|
|
|
|
self.countdownTimer = QTimer()
|
|
|
|
|
self.countdownTimer.timeout.connect(self.updateCountdown)
|
|
|
|
|
|
|
|
|
|
# 添加等待操作专用定时器
|
|
|
|
|
self.waitTimer = QTimer()
|
|
|
|
|
self.waitTimer.setSingleShot(True) # 单次触发
|
|
|
|
|
self.waitTimer.timeout.connect(self.onWaitCompleted)
|
|
|
|
|
|
|
|
|
|
# 添加设置操作超时专用定时器
|
|
|
|
|
self.setTimer = QTimer()
|
|
|
|
|
self.setTimer.setSingleShot(True) # 单次触发
|
|
|
|
|
self.setTimer.timeout.connect(self.onSetTimeoutCompleted)
|
|
|
|
|
|
|
|
|
|
def initUi(self, testSteps):
|
|
|
|
|
"""初始化用户界面"""
|
|
|
|
|
layout = QVBoxLayout()
|
|
|
|
@ -608,6 +630,27 @@ class StepExecutor(QWidget):
|
|
|
|
|
"""停止自动执行"""
|
|
|
|
|
self.isRunning = False
|
|
|
|
|
self.timer.stop()
|
|
|
|
|
|
|
|
|
|
# 停止等待定时器(如果正在等待)
|
|
|
|
|
if self.waitTimer.isActive():
|
|
|
|
|
self.waitTimer.stop()
|
|
|
|
|
if self.isWaiting:
|
|
|
|
|
print(f"停止执行,中断等待操作 (步骤 {self.waitingStepId})")
|
|
|
|
|
self.isWaiting = False
|
|
|
|
|
self.waitingStepId = None
|
|
|
|
|
self.waitStartTime = None
|
|
|
|
|
self.waitDuration = 0
|
|
|
|
|
|
|
|
|
|
# 停止设置操作超时定时器(如果正在超时等待)
|
|
|
|
|
if self.setTimer.isActive():
|
|
|
|
|
self.setTimer.stop()
|
|
|
|
|
if self.isSettingTimeout:
|
|
|
|
|
print(f"停止执行,中断设置超时等待 (步骤 {self.settingStepId})")
|
|
|
|
|
self.isSettingTimeout = False
|
|
|
|
|
self.settingStepId = None
|
|
|
|
|
self.settingResult = None
|
|
|
|
|
self.settingStartTime = None
|
|
|
|
|
|
|
|
|
|
self.updateButtonStates(True, False, True, True, True)
|
|
|
|
|
|
|
|
|
|
self.saveCurrentResults()
|
|
|
|
@ -650,7 +693,7 @@ class StepExecutor(QWidget):
|
|
|
|
|
"""处理轮次完成"""
|
|
|
|
|
self.saveCurrentResults()
|
|
|
|
|
|
|
|
|
|
if self.remainingCycles > 1:
|
|
|
|
|
if self.remainingCycles > 1 or self.infiniteCycles:
|
|
|
|
|
self.prepareNextCycle()
|
|
|
|
|
else:
|
|
|
|
|
self.finishExecution()
|
|
|
|
@ -758,6 +801,22 @@ class StepExecutor(QWidget):
|
|
|
|
|
self.actualElapsedTime += stepExecutionTime
|
|
|
|
|
print(f"步骤执行时间: {stepExecutionTime:.1f}秒, 累计已用时间: {self.actualElapsedTime:.1f}秒")
|
|
|
|
|
|
|
|
|
|
# 特殊处理等待操作
|
|
|
|
|
if result == 'WAITING':
|
|
|
|
|
print(f"步骤 {stepInfo['stepId']} 进入等待状态,暂时不记录结果")
|
|
|
|
|
# 更新表格显示为等待中状态
|
|
|
|
|
self.tableModel.updateStepResult(row, '等待中...', datetime.now())
|
|
|
|
|
self.tableView.adjustRowHeight(row)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
# 特殊处理设置操作超时等待
|
|
|
|
|
if result == 'SETTING_TIMEOUT':
|
|
|
|
|
print(f"步骤 {stepInfo['stepId']} 进入设置超时等待状态,暂时不记录结果")
|
|
|
|
|
# 更新表格显示为设置超时等待中状态
|
|
|
|
|
self.tableModel.updateStepResult(row, '成功设置触发条件,等待响应时间结果返回中...', datetime.now())
|
|
|
|
|
self.tableView.adjustRowHeight(row)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
if result is None:
|
|
|
|
|
print(f"警告:步骤 {stepInfo['stepId']} 返回了None结果,设置为False")
|
|
|
|
|
result = '执行失败,未检测到关键字'
|
|
|
|
@ -834,47 +893,104 @@ class StepExecutor(QWidget):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def performSetOperation(self, stepId, description, keyword, stepType = None):
|
|
|
|
|
"""执行设置操作"""
|
|
|
|
|
"""执行设置操作 - 支持非阻塞超时等待"""
|
|
|
|
|
# print(f"执行设置操作 (步骤 {stepId}): {description}")
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
# 简单匹配等号前变量名和等号后数值
|
|
|
|
|
variablePattern = r'([^\s=]+)=([0-9]+(?:\.[0-9]+)?)'
|
|
|
|
|
# 优化的正则表达式:支持负值、小数、科学计数法
|
|
|
|
|
# 匹配模式:变量名=数值(支持正负数、小数、科学计数法)
|
|
|
|
|
variablePattern = r'([^\s=]+)\s*=\s*([-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)'
|
|
|
|
|
matches = re.findall(variablePattern, description)
|
|
|
|
|
|
|
|
|
|
if matches:
|
|
|
|
|
# print(f"检测到 {len(matches)} 个需要设置的变量:")
|
|
|
|
|
result = ''
|
|
|
|
|
hasTimeoutOperation = False
|
|
|
|
|
|
|
|
|
|
for varName, varValue in matches:
|
|
|
|
|
varName = varName.strip()
|
|
|
|
|
# print(f" 变量: {varName} = {varValue}")
|
|
|
|
|
try:
|
|
|
|
|
# 转换为浮点数,支持各种数值格式
|
|
|
|
|
numericValue = float(varValue)
|
|
|
|
|
# print(f" 变量: {varName} = {numericValue}")
|
|
|
|
|
|
|
|
|
|
# 处理超时设置
|
|
|
|
|
if stepType and 'Time' in stepType:
|
|
|
|
|
timeMatch = re.search(r'Time\s*=\s*(\d+)\s*ms', stepType, re.IGNORECASE)
|
|
|
|
|
if timeMatch:
|
|
|
|
|
timeoutMS = int(timeMatch.group(1))
|
|
|
|
|
else:
|
|
|
|
|
timeoutMS = 2000
|
|
|
|
|
# print(timeoutMS)
|
|
|
|
|
success = self.protocolManager.writeVariableValue(varName, float(varValue), trigger = True, timeoutMS = timeoutMS)
|
|
|
|
|
time.sleep(timeoutMS/1000 + 2)
|
|
|
|
|
|
|
|
|
|
print(f"使用非阻塞超时设置: {timeoutMS}ms + 2秒")
|
|
|
|
|
success = self.protocolManager.writeVariableValue(
|
|
|
|
|
varName, numericValue, trigger=True, timeoutMS=timeoutMS
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 记录当前结果(不包含超时等待的最终结果)
|
|
|
|
|
if success:
|
|
|
|
|
result += f"{varName} = {numericValue} (设置中...)\n"
|
|
|
|
|
else:
|
|
|
|
|
result += f"{varName} 设置失败\n"
|
|
|
|
|
|
|
|
|
|
# 如果有超时设置,需要非阻塞等待
|
|
|
|
|
if success:
|
|
|
|
|
hasTimeoutOperation = True
|
|
|
|
|
# 设置超时等待状态
|
|
|
|
|
self.isSettingTimeout = True
|
|
|
|
|
self.settingStepId = stepId
|
|
|
|
|
self.settingResult = result
|
|
|
|
|
self.settingStartTime = datetime.now()
|
|
|
|
|
|
|
|
|
|
# 启动超时定时器 (timeoutMS + 2000ms)
|
|
|
|
|
totalWaitTime = timeoutMS + 1000
|
|
|
|
|
self.setTimer.start(totalWaitTime)
|
|
|
|
|
|
|
|
|
|
# 暂停主执行定时器
|
|
|
|
|
if self.timer.isActive():
|
|
|
|
|
self.timer.stop()
|
|
|
|
|
|
|
|
|
|
# 更新状态显示
|
|
|
|
|
self.updateStatusDisplay(f"等待时间响应结果返回中... ({totalWaitTime/1000:.1f}秒)", "orange")
|
|
|
|
|
|
|
|
|
|
# 返回特殊标记,表示正在超时等待中
|
|
|
|
|
return 'SETTING_TIMEOUT'
|
|
|
|
|
else:
|
|
|
|
|
success = self.protocolManager.writeVariableValue(varName, float(varValue))
|
|
|
|
|
success = self.protocolManager.writeVariableValue(varName, numericValue)
|
|
|
|
|
|
|
|
|
|
# 记录结果
|
|
|
|
|
if success:
|
|
|
|
|
result += f"{varName} = {varValue}\n"
|
|
|
|
|
result += f"{varName} = {numericValue}\n"
|
|
|
|
|
else:
|
|
|
|
|
result += f"{varName} 设置失败\n"
|
|
|
|
|
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
# 数值转换失败
|
|
|
|
|
result += f"{varName} 数值格式错误: {varValue}\n"
|
|
|
|
|
print(f"数值转换错误: {varValue} -> {str(e)}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 其他异常
|
|
|
|
|
result += f"{varName} 执行异常: {str(e)}\n"
|
|
|
|
|
print(f"执行异常: {str(e)}")
|
|
|
|
|
|
|
|
|
|
# 如果没有超时操作,直接返回结果
|
|
|
|
|
if not hasTimeoutOperation:
|
|
|
|
|
return result
|
|
|
|
|
else:
|
|
|
|
|
result += f"{varName}强制失败\n"
|
|
|
|
|
# 如果有超时操作,已经在上面返回了 'SETTING_TIMEOUT'
|
|
|
|
|
return result
|
|
|
|
|
else:
|
|
|
|
|
return '"强制失败,未检测到关键字"'
|
|
|
|
|
return '强制失败,未检测到有效的变量赋值格式'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def performCheckOperation(self, stepId, description, keyword, stepType = None):
|
|
|
|
|
"""执行检查操作"""
|
|
|
|
|
print(f"执行检查操作 (步骤 {stepId}): {description}")
|
|
|
|
|
|
|
|
|
|
# 支持多种比较符号的正则表达式
|
|
|
|
|
# 支持多种比较符号的正则表达式,支持负值、小数、科学计数法
|
|
|
|
|
# 匹配格式:变量名 比较符号 数值
|
|
|
|
|
# 支持的比较符号:=, ==, >, <, >=, <=, !=, <>
|
|
|
|
|
variablePattern = r'([^\s=><!]+)\s*(=|==|>|<|>=|<=|!=|<>)\s*([0-9]+(?:\.[0-9]+)?)'
|
|
|
|
|
variablePattern = r'([^\s=><!]+)\s*(=|==|>|<|>=|<=|!=|<>)\s*([-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)'
|
|
|
|
|
matches = re.findall(variablePattern, description)
|
|
|
|
|
|
|
|
|
|
if matches:
|
|
|
|
@ -957,20 +1073,169 @@ class StepExecutor(QWidget):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def performWaitOperation(self, stepId, description, keyword, stepType):
|
|
|
|
|
"""执行等待操作,等待时间取自描述字段中的数字(秒)"""
|
|
|
|
|
"""执行等待操作,等待时间取自描述字段中的数字(秒)- 非阻塞版本"""
|
|
|
|
|
print(f"执行等待操作 (步骤 {stepId}): {description}")
|
|
|
|
|
import re, time
|
|
|
|
|
# 只提取描述中的数字(支持小数),不再匹配"等待"字样
|
|
|
|
|
match = re.search(r'([0-9]+(?:\.[0-9]+)?)', description)
|
|
|
|
|
import re
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
# 支持负值的数字匹配(虽然等待时间通常不会是负数,但保持一致性)
|
|
|
|
|
match = re.search(r'([-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)', description)
|
|
|
|
|
if match:
|
|
|
|
|
seconds = float(match.group(1))
|
|
|
|
|
print(f" 等待 {seconds} 秒...")
|
|
|
|
|
time.sleep(seconds)
|
|
|
|
|
return '执行成功'
|
|
|
|
|
if seconds <= 0:
|
|
|
|
|
print(f" 等待时间无效: {seconds} 秒,必须大于0")
|
|
|
|
|
return '等待时间无效,必须大于0秒'
|
|
|
|
|
|
|
|
|
|
print(f" 开始非阻塞等待 {seconds} 秒...")
|
|
|
|
|
|
|
|
|
|
# 设置等待状态
|
|
|
|
|
self.isWaiting = True
|
|
|
|
|
self.waitingStepId = stepId
|
|
|
|
|
self.waitStartTime = datetime.now()
|
|
|
|
|
self.waitDuration = seconds
|
|
|
|
|
|
|
|
|
|
# 启动等待定时器(转换为毫秒)
|
|
|
|
|
self.waitTimer.start(int(seconds * 1000))
|
|
|
|
|
|
|
|
|
|
# 暂停主执行定时器,避免继续执行下一步
|
|
|
|
|
if self.timer.isActive():
|
|
|
|
|
self.timer.stop()
|
|
|
|
|
|
|
|
|
|
# 更新状态显示
|
|
|
|
|
self.updateStatusDisplay(f"等待中... ({seconds}秒)", "orange")
|
|
|
|
|
|
|
|
|
|
# 返回特殊标记,表示正在等待中
|
|
|
|
|
return 'WAITING'
|
|
|
|
|
else:
|
|
|
|
|
print(" 未检测到等待时间")
|
|
|
|
|
return '未检测到等待时间'
|
|
|
|
|
|
|
|
|
|
def onWaitCompleted(self):
|
|
|
|
|
"""等待操作完成的回调函数"""
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
if not self.isWaiting:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 计算实际等待时间
|
|
|
|
|
if self.waitStartTime:
|
|
|
|
|
actualWaitTime = (datetime.now() - self.waitStartTime).total_seconds()
|
|
|
|
|
print(f" 等待完成,实际等待时间: {actualWaitTime:.2f} 秒")
|
|
|
|
|
|
|
|
|
|
# 重置等待状态
|
|
|
|
|
self.isWaiting = False
|
|
|
|
|
waitingStepId = self.waitingStepId
|
|
|
|
|
self.waitingStepId = None
|
|
|
|
|
self.waitStartTime = None
|
|
|
|
|
self.waitDuration = 0
|
|
|
|
|
|
|
|
|
|
# 更新步骤结果为成功
|
|
|
|
|
if waitingStepId:
|
|
|
|
|
# 找到对应的步骤行并更新结果
|
|
|
|
|
for row in range(self.tableModel.rowCount()):
|
|
|
|
|
stepInfo = self.tableModel.getStepInfo(row)
|
|
|
|
|
if stepInfo and stepInfo.get('stepId') == waitingStepId:
|
|
|
|
|
# 更新步骤结果
|
|
|
|
|
self.tableModel.updateStepResult(row, '等待完成', datetime.now())
|
|
|
|
|
|
|
|
|
|
# 更新步骤结果记录
|
|
|
|
|
executionTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
stepResult = {
|
|
|
|
|
'step_id': str(waitingStepId),
|
|
|
|
|
'step_description': stepInfo.get('description', ''),
|
|
|
|
|
'execution_time': executionTime,
|
|
|
|
|
'result': '等待完成'
|
|
|
|
|
}
|
|
|
|
|
self.stepResults.append(stepResult)
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
|
|
|
|
if hasattr(self, 'currentExecutionId'):
|
|
|
|
|
self.dbManager.updateStepResults(self.currentExecutionId, self.stepResults)
|
|
|
|
|
|
|
|
|
|
# 更新表格行高
|
|
|
|
|
self.tableView.adjustRowHeight(row)
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# 恢复执行流程
|
|
|
|
|
if self.isRunning:
|
|
|
|
|
# 更新状态显示
|
|
|
|
|
cycleNum = self.getCurrentCycleNumber()
|
|
|
|
|
self.updateStatusDisplay(f"执行中 - 第{cycleNum}轮", "green")
|
|
|
|
|
|
|
|
|
|
# 重新启动主执行定时器
|
|
|
|
|
stepInterval = int(self.stepIntervalSpin.value() * 1000)
|
|
|
|
|
self.timer.start(stepInterval)
|
|
|
|
|
else:
|
|
|
|
|
# 如果不是自动执行模式,更新状态为就绪
|
|
|
|
|
self.updateStatusDisplay("就绪", "blue")
|
|
|
|
|
|
|
|
|
|
def onSetTimeoutCompleted(self):
|
|
|
|
|
"""设置操作超时完成的回调函数"""
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
if not self.isSettingTimeout:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 计算实际超时等待时间
|
|
|
|
|
if self.settingStartTime:
|
|
|
|
|
actualWaitTime = (datetime.now() - self.settingStartTime).total_seconds()
|
|
|
|
|
print(f" 设置超时等待完成,实际等待时间: {actualWaitTime:.2f} 秒")
|
|
|
|
|
|
|
|
|
|
# 获取保存的结果并更新为最终结果
|
|
|
|
|
finalResult = self.settingResult
|
|
|
|
|
if finalResult:
|
|
|
|
|
# 将 "设置中..." 替换为 "设置完成"
|
|
|
|
|
finalResult = finalResult.replace("(设置中...)", "(触发成功)")
|
|
|
|
|
else:
|
|
|
|
|
finalResult = "设置超时等待完成"
|
|
|
|
|
|
|
|
|
|
# 重置设置超时状态
|
|
|
|
|
self.isSettingTimeout = False
|
|
|
|
|
settingStepId = self.settingStepId
|
|
|
|
|
self.settingStepId = None
|
|
|
|
|
self.settingResult = None
|
|
|
|
|
self.settingStartTime = None
|
|
|
|
|
|
|
|
|
|
# 更新步骤结果
|
|
|
|
|
if settingStepId:
|
|
|
|
|
# 找到对应的步骤行并更新结果
|
|
|
|
|
for row in range(self.tableModel.rowCount()):
|
|
|
|
|
stepInfo = self.tableModel.getStepInfo(row)
|
|
|
|
|
if stepInfo and stepInfo.get('stepId') == settingStepId:
|
|
|
|
|
# 更新步骤结果
|
|
|
|
|
self.tableModel.updateStepResult(row, finalResult, datetime.now())
|
|
|
|
|
|
|
|
|
|
# 更新步骤结果记录
|
|
|
|
|
executionTime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
stepResult = {
|
|
|
|
|
'step_id': str(settingStepId),
|
|
|
|
|
'step_description': stepInfo.get('description', ''),
|
|
|
|
|
'execution_time': executionTime,
|
|
|
|
|
'result': finalResult
|
|
|
|
|
}
|
|
|
|
|
self.stepResults.append(stepResult)
|
|
|
|
|
|
|
|
|
|
# 保存到数据库
|
|
|
|
|
if hasattr(self, 'currentExecutionId'):
|
|
|
|
|
self.dbManager.updateStepResults(self.currentExecutionId, self.stepResults)
|
|
|
|
|
|
|
|
|
|
# 更新表格行高
|
|
|
|
|
self.tableView.adjustRowHeight(row)
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# 恢复执行流程
|
|
|
|
|
if self.isRunning:
|
|
|
|
|
# 更新状态显示
|
|
|
|
|
cycleNum = self.getCurrentCycleNumber()
|
|
|
|
|
self.updateStatusDisplay(f"执行中 - 第{cycleNum}轮", "green")
|
|
|
|
|
|
|
|
|
|
# 重新启动主执行定时器
|
|
|
|
|
stepInterval = int(self.stepIntervalSpin.value() * 1000)
|
|
|
|
|
self.timer.start(stepInterval)
|
|
|
|
|
else:
|
|
|
|
|
# 如果不是自动执行模式,更新状态为就绪
|
|
|
|
|
self.updateStatusDisplay("就绪", "blue")
|
|
|
|
|
|
|
|
|
|
def performDeltaTOperation(self, stepId, description, keyword, stepType):
|
|
|
|
|
"""执行接收操作"""
|
|
|
|
|
result = self.protocolManager.recvDeltaT()
|
|
|
|
@ -989,6 +1254,22 @@ class StepExecutor(QWidget):
|
|
|
|
|
if not fromAuto:
|
|
|
|
|
self.stopAutoExecute()
|
|
|
|
|
|
|
|
|
|
# 重置等待状态
|
|
|
|
|
if self.waitTimer.isActive():
|
|
|
|
|
self.waitTimer.stop()
|
|
|
|
|
self.isWaiting = False
|
|
|
|
|
self.waitingStepId = None
|
|
|
|
|
self.waitStartTime = None
|
|
|
|
|
self.waitDuration = 0
|
|
|
|
|
|
|
|
|
|
# 重置设置操作超时状态
|
|
|
|
|
if self.setTimer.isActive():
|
|
|
|
|
self.setTimer.stop()
|
|
|
|
|
self.isSettingTimeout = False
|
|
|
|
|
self.settingStepId = None
|
|
|
|
|
self.settingResult = None
|
|
|
|
|
self.settingStartTime = None
|
|
|
|
|
|
|
|
|
|
self.cycleSpin.setValue(1)
|
|
|
|
|
self.infiniteCheckbox.setChecked(False)
|
|
|
|
|
self.isRunning = False
|
|
|
|
@ -1004,7 +1285,7 @@ class StepExecutor(QWidget):
|
|
|
|
|
self.resetCountdown()
|
|
|
|
|
|
|
|
|
|
# 重置后重新调整行高
|
|
|
|
|
self.adjustAllRowHeights()
|
|
|
|
|
# self.adjustAllRowHeights()
|
|
|
|
|
|
|
|
|
|
def onExportReportClicked(self):
|
|
|
|
|
"""导出报告点击事件"""
|
|
|
|
|