You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

718 lines
30 KiB
Python

4 months ago
import sys
import os
4 months ago
from PyQt5.QtCore import Qt, QTimer, QModelIndex, QSize, pyqtSignal, QFile, QTextStream
from PyQt5.QtGui import QBrush, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (QTableView, QPushButton, QVBoxLayout, QWidget, QHBoxLayout,
QLabel, QMenu, QFileDialog, QDialog, QLineEdit,
QDialogButtonBox, QMessageBox, QHeaderView, QSplitter)
4 months ago
import qtawesome as qta
from datetime import datetime
from utils.DBModels.ProcedureModel import DatabaseManager
4 months ago
from UI.ProcedureManager.StepExecutor import StepExecutor
4 months ago
4 months ago
class HistoryViewerWidget(QWidget):
4 months ago
def __init__(self, dbManager, parent=None):
super().__init__(parent)
self.dbManager = dbManager
self.initUi()
self.loadStylesheet() # 加载样式表
4 months ago
self.loadHistory()
def loadStylesheet(self):
"""加载历史查看器样式表"""
try:
qssPath = "Static/HistoryViewer.qss"
qssFile = QFile(qssPath)
if qssFile.exists():
qssFile.open(QFile.ReadOnly | QFile.Text)
stream = QTextStream(qssFile)
stream.setCodec("UTF-8")
qss_content = stream.readAll()
qssFile.close()
# 应用样式表到当前widget
self.setStyleSheet(qss_content)
# 确保按钮样式生效 - 直接设置按钮样式
self.applyButtonStyles()
print(f"✅ HistoryViewer成功加载样式表: {qssPath}")
else:
print(f"⚠️ HistoryViewer样式表文件不存在: {qssPath}")
except Exception as e:
print(f"❌ HistoryViewer加载样式表失败: {str(e)}")
def applyButtonStyles(self):
"""直接应用按钮样式"""
# 删除按钮样式
delete_style = """
QPushButton#deleteHistoryButton {
color: white;
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #F87171, stop: 1 #DC2626);
border: 2px solid #DC2626;
font-weight: bold;
border-radius: 8px;
font-size: 14px;
padding: 10px 20px;
min-height: 40px;
min-width: 120px;
}
QPushButton#deleteHistoryButton:hover {
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FCA5A5, stop: 1 #EF4444);
border: 2px solid #F87171;
}
"""
# 导出按钮样式
export_style = """
QPushButton#exportReportButton {
color: white;
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #34D399, stop: 1 #059669);
border: 2px solid #10B981;
font-weight: bold;
border-radius: 8px;
font-size: 14px;
padding: 10px 20px;
min-height: 40px;
min-width: 120px;
}
QPushButton#exportReportButton:hover {
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #6EE7B7, stop: 1 #10B981);
border: 2px solid #34D399;
}
"""
# 应用样式到按钮
if hasattr(self, 'deleteButton'):
self.deleteButton.setStyleSheet(delete_style)
if hasattr(self, 'exportButton'):
self.exportButton.setStyleSheet(export_style)
4 months ago
def initUi(self):
layout = QVBoxLayout()
layout.setContentsMargins(1, 1, 1, 1) # 极小边距
layout.setSpacing(1) # 极小间距
4 months ago
# 搜索栏 - 极度紧凑布局,固定高度
4 months ago
searchLayout = QHBoxLayout()
searchLayout.setContentsMargins(0, 0, 0, 0)
searchLayout.setSpacing(2)
4 months ago
self.searchEdit = QLineEdit()
self.searchEdit.setPlaceholderText("搜索规程...")
self.searchEdit.setMaximumHeight(20) # 限制搜索框高度
4 months ago
self.searchEdit.textChanged.connect(self.loadHistory)
searchLabel = QLabel("搜索:")
searchLabel.setMaximumHeight(20) # 限制标签高度
searchLayout.addWidget(searchLabel)
4 months ago
searchLayout.addWidget(self.searchEdit)
layout.addLayout(searchLayout, 0) # 拉伸因子为0不占用额外空间
4 months ago
# 历史记录表格 - 设置对象名称用于样式识别
4 months ago
self.table = QTableView()
self.table.setObjectName("historyTable") # 重要:设置对象名称
4 months ago
self.table.setEditTriggers(QTableView.NoEditTriggers)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(["ID", "规程全名", "类型", "执行时间", "报告路径"])
self.table.setModel(self.model)
self.table.doubleClicked.connect(self.openReportOrDetails)
self.table.setSelectionBehavior(QTableView.SelectRows)
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
self.table.customContextMenuRequested.connect(self.showHistoryContextMenu)
self.table.verticalHeader().hide()
4 months ago
self.setupTableDisplay(self.table)
4 months ago
# 步骤详情表格
self.stepTable = QTableView()
4 months ago
self.stepTable.setEditTriggers(QTableView.NoEditTriggers)
4 months ago
self.stepModel = QStandardItemModel()
self.stepModel.setHorizontalHeaderLabels(["步骤ID", "步骤描述", "执行时间", "执行状态", "详细结果", "实际/预期对比"])
4 months ago
self.stepTable.setModel(self.stepModel)
self.stepTable.doubleClicked.connect(self.showStepDetailDialog)
self.stepTable.verticalHeader().hide()
4 months ago
self.setupTableDisplay(self.stepTable)
4 months ago
# 使用分割器 - 表格占据绝大部分空间
4 months ago
splitter = QSplitter(Qt.Vertical)
splitter.setContentsMargins(0, 0, 0, 0)
4 months ago
splitter.addWidget(self.table)
splitter.addWidget(self.stepTable)
# 设置比例上方历史记录表格占40%下方步骤详情表格占60% (2:3比例)
splitter.setSizes([2, 3]) # 直接使用比例值
splitter.setStretchFactor(0, 2) # 历史记录表格拉伸因子为2
splitter.setStretchFactor(1, 3) # 步骤详情表格拉伸因子为3
splitter.setChildrenCollapsible(False) # 防止子窗口被完全折叠
layout.addWidget(splitter, 1) # 拉伸因子为1占据主要空间
# 操作按钮 - 极度紧凑布局,固定高度
4 months ago
buttonLayout = QHBoxLayout()
buttonLayout.setContentsMargins(0, 0, 0, 0)
buttonLayout.setSpacing(2)
4 months ago
4 months ago
self.deleteButton = QPushButton("删除历史记录")
self.deleteButton.setObjectName("deleteHistoryButton") # 设置对象名称
self.deleteButton.setIcon(qta.icon('fa5s.trash', color='#FFFFFF')) # 白色图标
4 months ago
self.deleteButton.clicked.connect(self.deleteSelectedHistory)
4 months ago
buttonLayout.addWidget(self.deleteButton)
4 months ago
self.exportButton = QPushButton("导出报告")
self.exportButton.setObjectName("exportReportButton") # 设置对象名称
self.exportButton.setIcon(qta.icon('fa5s.file-export', color='#FFFFFF')) # 白色图标
4 months ago
self.exportButton.clicked.connect(self.exportReport)
4 months ago
buttonLayout.addWidget(self.exportButton)
4 months ago
4 months ago
buttonLayout.addStretch()
layout.addLayout(buttonLayout, 0) # 拉伸因子为0不占用额外空间
4 months ago
self.setLayout(layout)
# 在UI创建完成后应用按钮样式
QTimer.singleShot(0, self.applyButtonStyles)
4 months ago
def setupTableDisplay(self, table):
"""设置表格显示属性 - 极度紧凑版"""
table.setWordWrap(False) # 禁用自动换行以节省空间
table.setAlternatingRowColors(True) # 启用交替行颜色
4 months ago
table.setSortingEnabled(True)
# 水平表头设置
4 months ago
header = table.horizontalHeader()
if header:
header.setStretchLastSection(True)
header.setSectionResizeMode(QHeaderView.Interactive)
header.setDefaultAlignment(Qt.AlignLeft)
header.setMinimumSectionSize(50) # 设置最小列宽
header.setDefaultSectionSize(80) # 设置默认列宽
4 months ago
# 垂直表头设置 - 极度紧凑
4 months ago
verticalHeader = table.verticalHeader()
if verticalHeader:
verticalHeader.setVisible(False) # 隐藏行号以节省空间
verticalHeader.setSectionResizeMode(QHeaderView.Fixed)
if table.objectName() == "historyTable":
verticalHeader.setDefaultSectionSize(20) # 历史表格适中行高适应2:3比例
else:
verticalHeader.setDefaultSectionSize(18) # 步骤表格稍大行高
4 months ago
if table == self.table:
table.setSelectionMode(QTableView.ExtendedSelection)
else:
table.setSelectionMode(QTableView.SingleSelection)
table.setSelectionBehavior(QTableView.SelectRows)
4 months ago
def loadHistory(self):
4 months ago
"""加载历史记录"""
4 months ago
self.model.removeRows(0, self.model.rowCount())
filterText = self.searchEdit.text().strip()
history = self.dbManager.getExecutionHistory(filterText)
for record in history:
4 months ago
rowItems = []
for i, value in enumerate(record):
item = QStandardItem(str(value) if value is not None else "")
item.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
if i in [1, 4]: # 规程全名和报告路径列
item.setToolTip(str(value) if value is not None else "")
rowItems.append(item)
self.model.appendRow(rowItems)
self.adjustTableColumns(self.table, self.model)
4 months ago
def openReportOrDetails(self, index):
4 months ago
"""打开报告或详情"""
executionId = self.model.item(index.row(), 0).text()
stepResults = self.dbManager.getStepResults(executionId)
self.showStepDetails(stepResults)
self.saveOperationHistory(executionId, "查看步骤详情", "")
4 months ago
def saveOperationHistory(self, executionId, operationType, operationDetail):
4 months ago
"""保存操作历史"""
4 months ago
self.dbManager.saveOperationHistory(
executionId,
operationType,
operationDetail,
datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
def showStepDetails(self, stepResults):
"""显示步骤详情"""
self.stepModel.removeRows(0, self.stepModel.rowCount())
if not stepResults:
return
for step in stepResults:
4 months ago
rowItems = []
# 步骤ID
stepIdItem = QStandardItem(step.get('step_id', ''))
stepIdItem.setTextAlignment(Qt.AlignCenter)
rowItems.append(stepIdItem)
# 步骤描述
descItem = QStandardItem(step.get('step_description', ''))
descItem.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
descItem.setToolTip(step.get('step_description', ''))
rowItems.append(descItem)
# 执行时间
timeItem = QStandardItem(step.get('execution_time', ''))
timeItem.setTextAlignment(Qt.AlignCenter)
rowItems.append(timeItem)
# 解析执行结果
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("⚠ 部分成功")
4 months ago
else:
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)
4 months ago
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)
}
4 months ago
def adjustTableColumns(self, table, model):
"""调整表格列宽"""
header = table.horizontalHeader()
if not header:
return
if model == self.model: # 历史记录表格
for col in range(model.columnCount()):
if col == 0: # ID列
header.setSectionResizeMode(col, QHeaderView.ResizeToContents)
elif col == 1: # 规程全名列
4 months ago
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)
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: # 操作类型列
4 months ago
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.setObjectName("stepDetailInfoGroup")
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.setObjectName("stepDescriptionText")
layout.addWidget(descTextEdit)
# 详细结果区域
layout.addWidget(QLabel("<b>执行结果详情</b>"))
resultTextEdit = QLineEdit()
resultTextEdit.setText(detailResult)
resultTextEdit.setReadOnly(True)
# 根据执行状态设置结果显示样式
if "成功" in execStatus:
resultTextEdit.setObjectName("stepResultText")
resultTextEdit.setProperty("status", "success")
elif "失败" in execStatus:
resultTextEdit.setObjectName("stepResultText")
resultTextEdit.setProperty("status", "error")
else:
resultTextEdit.setObjectName("stepResultText")
resultTextEdit.setProperty("status", "default")
layout.addWidget(resultTextEdit)
# 按钮区域
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
buttonBox.accepted.connect(dialog.accept)
layout.addWidget(buttonBox)
dialog.setLayout(layout)
dialog.exec_()
4 months ago
def deleteSelectedHistory(self):
"""删除选中的历史记录"""
4 months ago
selectedIndexes = self.table.selectionModel().selectedRows()
if not selectedIndexes:
4 months ago
QMessageBox.warning(self, "未选择", "请先选择要删除的历史记录")
return
4 months ago
executionIds = []
for index in selectedIndexes:
executionId = self.model.item(index.row(), 0).text()
executionIds.append(executionId)
4 months ago
reply = QMessageBox.question(
self,
"确认删除",
4 months ago
f"确定要删除选中的 {len(executionIds)} 条历史记录吗?\n此操作不可恢复!",
4 months ago
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
4 months ago
success = self.dbManager.deleteExecutionHistory(executionIds)
4 months ago
if success:
QMessageBox.information(self, "删除成功", "已成功删除选中的历史记录")
4 months ago
self.loadHistory()
4 months ago
else:
QMessageBox.warning(self, "删除失败", "删除历史记录时发生错误")
def exportReport(self):
4 months ago
"""导出报告"""
selectedIndexes = self.table.selectionModel().selectedRows()
if not selectedIndexes:
4 months ago
QMessageBox.warning(self, "未选择", "请先选择要导出的历史记录")
return
4 months ago
if len(selectedIndexes) > 1:
4 months ago
QMessageBox.warning(self, "选择过多", "一次只能导出一个历史记录的报告")
return
4 months ago
index = selectedIndexes[0]
executionId = self.model.item(index.row(), 0).text()
4 months ago
4 months ago
executionData = self.dbManager.getExecutionDetails(executionId)
if not executionData:
4 months ago
QMessageBox.warning(self, "数据错误", "无法获取执行详情数据")
return
try:
4 months ago
executor = StepExecutor(executionData['procedure_content'],
executionData['procedure_id'],
4 months ago
self.dbManager)
4 months ago
executor.stepResults = executionData['step_results']
4 months ago
for step in executor.tableModel.stepData:
4 months ago
stepResult = next((s for s in executionData['step_results']
4 months ago
if s['step_id'] == step['stepId']), None)
4 months ago
if stepResult:
4 months ago
step['executed'] = True
4 months ago
step['result'] = stepResult['result']
step['time'] = datetime.strptime(stepResult['execution_time'], "%Y-%m-%d %H:%M:%S")
resultPath = executor.generateReport()
if resultPath:
return
else:
QMessageBox.information(self, "导出取消", "用户取消了报告导出")
4 months ago
except Exception as e:
QMessageBox.critical(self, "导出错误", f"生成报告时出错:\n{str(e)}")
4 months ago
import traceback
print(f"导出报告详细错误: {traceback.format_exc()}")
4 months ago
def showHistoryContextMenu(self, pos):
4 months ago
"""显示右键菜单"""
4 months ago
index = self.table.indexAt(pos)
4 months ago
menu = QMenu()
4 months ago
if index.isValid():
4 months ago
deleteAction = menu.addAction(qta.icon('fa5s.trash', color='red'), "删除历史记录")
4 months ago
deleteAction.triggered.connect(self.deleteSelectedHistory)
4 months ago
menu.addSeparator()
exportAction = menu.addAction(qta.icon('fa5s.file-export', color='green'), "导出报告")
exportAction.triggered.connect(lambda: self.exportReportFromContextMenu(index))
menu.addSeparator()
selectAllAction = menu.addAction("全选")
selectAllAction.triggered.connect(self.selectAllHistory)
deselectAllAction = menu.addAction("取消全选")
deselectAllAction.triggered.connect(self.deselectAllHistory)
if index.isValid():
menu.exec_(self.table.viewport().mapToGlobal(pos))
else:
menu.exec_(self.table.mapToGlobal(pos))
def showEvent(self, event):
"""窗口显示事件"""
super().showEvent(event)
QTimer.singleShot(100, self.adjustTablesAfterShow)
QTimer.singleShot(500, self.forceSplitterRatio) # 延迟强制设置比例
4 months ago
def adjustTablesAfterShow(self):
"""窗口显示后调整表格"""
self.adjustTableColumns(self.table, self.model)
self.adjustTableColumns(self.stepTable, self.stepModel)
def forceSplitterRatio(self):
"""强制设置分割器比例为2:3"""
splitter = self.findChild(QSplitter)
if splitter:
total_height = splitter.height()
if total_height > 0:
# 计算2:3比例的实际像素值
history_height = int(total_height * 0.4) # 40%
step_height = int(total_height * 0.6) # 60%
# 设置表格的固定高度以确保比例
self.table.setFixedHeight(history_height)
self.stepTable.setMinimumHeight(step_height)
# 设置分割器尺寸
splitter.setSizes([history_height, step_height])
4 months ago
def selectAllHistory(self):
"""全选历史记录"""
self.table.selectAll()
def deselectAllHistory(self):
"""取消全选历史记录"""
self.table.clearSelection()
def exportReportFromContextMenu(self, index):
"""从右键菜单导出报告"""
self.table.selectRow(index.row())
self.exportReport()