From afa66d5c9284914dc83381e60f1186810d9823e9 Mon Sep 17 00:00:00 2001 From: zcwBit Date: Tue, 5 Aug 2025 11:43:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=95=8C=E9=9D=A2=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Static/HistoryViewer.qss | 246 ++++------- Static/ProcedureEditor.qss | 579 ++++++++++++++++++++++++++ UI/ProcedureManager/HistoryViewer.py | 103 ++++- UI/ProcedureManager/ProcedureTable.py | 453 ++++++++++++++++++++ 4 files changed, 1208 insertions(+), 173 deletions(-) create mode 100644 Static/ProcedureEditor.qss create mode 100644 UI/ProcedureManager/ProcedureTable.py diff --git a/Static/HistoryViewer.qss b/Static/HistoryViewer.qss index d7b0858..ea79b3b 100644 --- a/Static/HistoryViewer.qss +++ b/Static/HistoryViewer.qss @@ -55,7 +55,8 @@ QTableView { border: 1px solid #E5E7EB; border-radius: 3px; gridline-color: #F3F4F6; - font-size: 10px; + font-size: 12px; + /* 调大表格内容字体 */ color: #374151; selection-background-color: #EBF8FF; selection-color: #1E40AF; @@ -67,9 +68,11 @@ QTableView { } QTableView::item { - padding: 1px 3px; + padding: 2px 4px; + /* 稍微增加内边距以适应更大字体 */ border: none; - min-height: 12px; + min-height: 14px; + /* 增加行高以适应更大字体 */ } QTableView::item:selected { @@ -97,25 +100,30 @@ QTableView::item:focus { /* ==================== 历史记录表格专用样式 ==================== */ QTableView#historyTable { - max-height: 100px; /* 极小的最大高度 */ - min-height: 50px; /* 极小的最小高度 */ - font-size: 9px; /* 极小的字体 */ + /* 完全移除高度限制,让分割器控制大小 */ + font-size: 11px; + /* 调大历史记录表格字体 */ } QTableView#historyTable::item { - padding: 0px 2px; /* 极度紧凑的内边距 */ - min-height: 10px; /* 极小的行高 */ + padding: 1px 3px; + /* 稍微增加内边距 */ + min-height: 12px; + /* 稍微增加行高 */ } /* ==================== 步骤详情表格专用样式 ==================== */ QTableView:not(#historyTable) { - min-height: 150px; /* 步骤详情表格最小高度 */ - font-size: 10px; + /* 移除最小高度限制,让分割器控制大小 */ + font-size: 12px; + /* 调大步骤详情表格字体 */ } QTableView:not(#historyTable)::item { - padding: 2px 4px; /* 适中的内边距 */ - min-height: 14px; /* 适中的行高 */ + padding: 2px 4px; + /* 适中的内边距 */ + min-height: 14px; + /* 适中的行高 */ } /* ==================== 表格头部样式 ==================== */ @@ -129,7 +137,8 @@ QTableView QHeaderView::section { background-color: #F8F9FA; color: #374151; font-weight: 600; - font-size: 9px; + font-size: 8px; + /* 调小表头字体 */ padding: 1px 3px; border: 1px solid #E5E7EB; border-left: none; @@ -139,8 +148,10 @@ QTableView QHeaderView::section { /* 历史记录表格头部 - 极度紧凑 */ QTableView#historyTable QHeaderView::section { - padding: 0px 2px; /* 极小的头部内边距 */ - font-size: 8px; /* 极小的字体 */ + padding: 0px 2px; + /* 极小的头部内边距 */ + font-size: 7px; + /* 更小的表头字体 */ font-weight: 600; min-height: 8px; } @@ -243,64 +254,81 @@ QTableView QScrollBar::sub-page:horizontal { /* ==================== 按钮样式 ==================== */ QPushButton { - font-size: 10px; - font-weight: 600; - padding: 2px 6px; - border-radius: 2px; - min-width: 50px; - min-height: 16px; - border: 1px solid; - margin: 0px 1px; + font-size: 13px; + font-weight: bold; + padding: 8px 20px; + border-radius: 8px; + min-width: 120px; + min-height: 36px; + border: 1px solid #D1D5DB; + margin: 4px 8px; + font-family: "Microsoft YaHei", sans-serif; + background-color: #F9FAFB; + color: #374151; } -/* 删除历史记录按钮 - 红色主题 */ -QPushButton[text="删除历史记录"] { - color: #DC2626; - background-color: #FEF2F2; - border-color: #FECACA; +/* 删除历史记录按钮 - 现代红色主题 */ +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[text="删除历史记录"]:hover { - background-color: #FEE2E2; - border-color: #FCA5A5; - color: #B91C1C; +QPushButton#deleteHistoryButton:hover { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FCA5A5, stop: 1 #EF4444); + border: 2px solid #F87171; } -QPushButton[text="删除历史记录"]:pressed { - background-color: #FECACA; - border-color: #F87171; - color: #991B1B; +QPushButton#deleteHistoryButton:pressed { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #DC2626, stop: 1 #B91C1C); + border: 2px solid #B91C1C; } -QPushButton[text="删除历史记录"]:disabled { - background-color: #F3F4F6; +QPushButton#deleteHistoryButton:disabled { + background: #F3F4F6; color: #9CA3AF; - border-color: #E5E7EB; + border: 1px solid #E5E7EB; } -/* 导出报告按钮 - 绿色主题 */ -QPushButton[text="导出报告"] { - color: #059669; - background-color: #ECFDF5; - border-color: #BBF7D0; +/* 导出报告按钮 - 现代绿色主题 */ +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[text="导出报告"]:hover { - background-color: #D1FAE5; - border-color: #86EFAC; - color: #047857; +QPushButton#exportReportButton:hover { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #6EE7B7, stop: 1 #10B981); + border: 2px solid #34D399; } -QPushButton[text="导出报告"]:pressed { - background-color: #A7F3D0; - border-color: #6EE7B7; - color: #065F46; +QPushButton#exportReportButton:pressed { + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #059669, stop: 1 #047857); + border: 2px solid #047857; } -QPushButton[text="导出报告"]:disabled { - background-color: #F3F4F6; +QPushButton#exportReportButton:disabled { + background: #F3F4F6; color: #9CA3AF; - border-color: #E5E7EB; + border: 1px solid #E5E7EB; } /* ==================== 分割器样式 ==================== */ @@ -433,33 +461,7 @@ QLineEdit#stepResultText[status="default"] { border-color: #E5E7EB; } -/* 对话框按钮 */ -QDialog QPushButton { - font-size: 10px; - font-weight: 600; - padding: 3px 10px; - border-radius: 2px; - min-width: 50px; - border: 1px solid; -} - -QDialogButtonBox QPushButton { - color: #1D4ED8; - background-color: #EBF8FF; - border-color: #BFDBFE; -} -QDialogButtonBox QPushButton:hover { - background-color: #DBEAFE; - border-color: #93C5FD; - color: #1E40AF; -} - -QDialogButtonBox QPushButton:pressed { - background-color: #BFDBFE; - border-color: #60A5FA; - color: #1E3A8A; -} /* ==================== 工具提示样式 ==================== */ QToolTip { @@ -548,9 +550,7 @@ QTableView::item[status="unknown"] { } /* ==================== 焦点样式 ==================== */ -QWidget:focus { - outline: none; -} + QTableView:focus { border-color: #3B82F6; @@ -562,84 +562,6 @@ QPushButton:focus { border-width: 1px; } -/* ==================== 极度紧凑布局优化 ==================== */ - -/* 移除所有不必要的边距和内边距 */ -QWidget { - margin: 0px; - padding: 0px; -} - -/* 搜索区域极度紧凑化 */ -QHBoxLayout { - margin: 1px; - spacing: 2px; -} - -/* 按钮区域极度紧凑化 */ -QHBoxLayout QPushButton { - margin: 0px 1px; -} -/* 分割器区域极度紧凑化 */ -QSplitter { - margin: 0px; - padding: 0px; - spacing: 0px; -} -/* 表格边距优化 */ -QTableView { - margin: 0px; - padding: 0px; -} - -/* 对话框内容极度紧凑化 */ -QDialog { - padding: 4px; -} -QDialog QVBoxLayout { - margin: 1px; - spacing: 2px; -} - -/* ==================== 超紧凑模式 ==================== */ -QWidget[size="ultra-compact"] { - font-size: 9px; -} - -QWidget[size="ultra-compact"] QPushButton { - padding: 1px 4px; - font-size: 9px; - min-height: 12px; - min-width: 40px; -} - -QWidget[size="ultra-compact"] QLineEdit { - padding: 0px 2px; - font-size: 9px; - min-height: 8px; -} - -QWidget[size="ultra-compact"] QTableView::item { - padding: 0px 1px; - min-height: 8px; -} - -QWidget[size="ultra-compact"] QTableView#historyTable::item { - padding: 0px 1px; - min-height: 6px; -} - -QWidget[size="ultra-compact"] QTableView#historyTable { - max-height: 60px; - min-height: 30px; - font-size: 8px; -} - -QWidget[size="ultra-compact"] QTableView#historyTable QHeaderView::section { - padding: 0px 1px; - font-size: 7px; - min-height: 6px; -} \ No newline at end of file diff --git a/Static/ProcedureEditor.qss b/Static/ProcedureEditor.qss new file mode 100644 index 0000000..de45316 --- /dev/null +++ b/Static/ProcedureEditor.qss @@ -0,0 +1,579 @@ +/* ==================== DCS2025 规程编辑器样式 ==================== */ + +/* ==================== 主容器样式 ==================== */ +QWidget { + background-color: #FFFFFF; + color: #374151; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; +} + +/* ==================== 标题样式 ==================== */ +QLabel#procedureEditorTitle { + font-size: 20px; + font-weight: 700; + color: #1F2937; + padding: 12px 16px; + background-color: #F8F9FA; + border: 1px solid #E5E7EB; + border-radius: 8px; + margin-bottom: 16px; +} + +/* ==================== 基本信息区域样式 ==================== */ +QWidget#basicInfoGroup { + background-color: #F8F9FA; + border: 1px solid #E5E7EB; + border-radius: 8px; + margin-bottom: 8px; +} + +QLabel#basicInfoTitle { + font-size: 14px; + font-weight: 600; + color: #374151; + margin-bottom: 4px; +} + +/* 字段标签样式 */ +QLabel[fieldLabel="true"] { + font-size: 13px; + font-weight: 500; + color: #4B5563; + text-align: right; +} + +/* 输入框样式 */ +QLineEdit#procedureNameEdit, +QLineEdit#procedureNumberEdit, +QLineEdit#procedureTypeEdit, +QLineEdit#procedureDescriptionEdit { + background-color: #FFFFFF; + border: 1px solid #D1D5DB; + border-radius: 4px; + padding: 6px 12px; + font-size: 13px; + color: #374151; + selection-background-color: #EBF8FF; + selection-color: #1E40AF; +} + +QLineEdit#procedureNameEdit:focus, +QLineEdit#procedureNumberEdit:focus, +QLineEdit#procedureTypeEdit:focus, +QLineEdit#procedureDescriptionEdit:focus { + border-color: #3B82F6; + outline: none; + background-color: #FEFEFE; +} + +QLineEdit#procedureNameEdit:hover, +QLineEdit#procedureNumberEdit:hover, +QLineEdit#procedureTypeEdit:hover, +QLineEdit#procedureDescriptionEdit:hover { + border-color: #9CA3AF; + background-color: #FEFEFE; +} + +/* ==================== 步骤区域样式 ==================== */ +QWidget#stepsGroup { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 8px; +} + +QLabel#stepsTitle { + font-size: 14px; + font-weight: 600; + color: #374151; +} + +/* ==================== 按钮样式 ==================== */ + +/* 添加步骤按钮 - 绿色主题 */ +QPushButton#addStepButton { + color: #047857; + font-size: 13px; + font-weight: 600; + padding: 6px 12px; + background-color: #ECFDF5; + border-radius: 4px; + border: 1px solid #BBF7D0; + min-width: 80px; +} + +QPushButton#addStepButton:hover { + background-color: #D1FAE5; + border-color: #86EFAC; + color: #065F46; +} + +QPushButton#addStepButton:pressed { + background-color: #A7F3D0; + border-color: #6EE7B7; + color: #064E3B; +} + +/* 删除步骤按钮 - 红色主题 */ +QPushButton#deleteStepButton { + color: #B91C1C; + font-size: 13px; + font-weight: 600; + padding: 6px 12px; + background-color: #FEF2F2; + border-radius: 4px; + border: 1px solid #FECACA; + min-width: 80px; +} + +QPushButton#deleteStepButton:hover { + background-color: #FEE2E2; + border-color: #FCA5A5; + color: #991B1B; +} + +QPushButton#deleteStepButton:pressed { + background-color: #FECACA; + border-color: #F87171; + color: #7F1D1D; +} + +/* 保存规程按钮 - 蓝色主题 */ +QPushButton#saveProcedureButton { + color: #1D4ED8; + font-size: 14px; + font-weight: 600; + padding: 8px 16px; + background-color: #EBF8FF; + border-radius: 6px; + border: 1px solid #BFDBFE; + min-width: 100px; +} + +QPushButton#saveProcedureButton:hover { + background-color: #DBEAFE; + border-color: #93C5FD; + color: #1E40AF; +} + +QPushButton#saveProcedureButton:pressed { + background-color: #BFDBFE; + border-color: #60A5FA; + color: #1E3A8A; +} + +/* 取消编辑按钮 - 灰色主题 */ +QPushButton#cancelEditButton { + color: #6B7280; + font-size: 14px; + font-weight: 600; + padding: 8px 16px; + background-color: #F9FAFB; + border-radius: 6px; + border: 1px solid #E5E7EB; + min-width: 100px; +} + +QPushButton#cancelEditButton:hover { + background-color: #F3F4F6; + border-color: #D1D5DB; + color: #4B5563; +} + +QPushButton#cancelEditButton:pressed { + background-color: #E5E7EB; + border-color: #9CA3AF; + color: #374151; +} + +/* ==================== 步骤表格样式 ==================== */ +QTableWidget#stepsTable { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 6px; + gridline-color: #F3F4F6; + font-size: 13px; + color: #374151; + selection-background-color: #EBF8FF; + selection-color: #1E40AF; + outline: none; + show-decoration-selected: 1; +} + +/* 表格项基础样式 */ +QTableWidget#stepsTable::item { + padding: 8px; + border: none; + min-height: 36px; +} + +/* 表格项选中状态 - 整行选中效果 */ +QTableWidget#stepsTable::item:selected { + background-color: #EBF8FF; + color: #1E40AF; + border: none; +} + +/* 表格项hover状态 - 整行hover效果 */ +QTableWidget#stepsTable::item:hover { + background-color: #F0F9FF; + color: #1F2937; +} + +/* 表格项选中且hover状态 */ +QTableWidget#stepsTable::item:selected:hover { + background-color: #DBEAFE; + color: #1565C0; +} + +/* 表格项焦点状态 */ +QTableWidget#stepsTable::item:focus { + background-color: #EBF8FF; + color: #1E40AF; + outline: none; + border: none; +} + +/* 表格项编辑状态 */ +QTableWidget#stepsTable::item:edit-focus { + background-color: #FEFEFE; + border: 2px solid #3B82F6; + border-radius: 2px; +} + +/* 交替行颜色 */ +QTableWidget#stepsTable::item:alternate { + background-color: #F9FAFB; +} + +QTableWidget#stepsTable::item:alternate:hover { + background-color: #F0F9FF; +} + +QTableWidget#stepsTable::item:alternate:selected { + background-color: #EBF8FF; +} + +/* ==================== 表格头部样式 ==================== */ +QTableWidget#stepsTable QHeaderView { + background-color: transparent; + border: none; + outline: none; +} + +QTableWidget#stepsTable QHeaderView::section { + background-color: #F8F9FA; + color: #374151; + font-weight: 600; + font-size: 13px; + padding: 10px 8px; + border: 1px solid #E5E7EB; + border-left: none; + text-align: center; +} + +QTableWidget#stepsTable QHeaderView::section:first { + border-left: 1px solid #E5E7EB; + border-top-left-radius: 6px; +} + +QTableWidget#stepsTable QHeaderView::section:last { + border-top-right-radius: 6px; +} + +QTableWidget#stepsTable QHeaderView::section:hover { + background-color: #F3F4F6; + color: #1F2937; +} + +QTableWidget#stepsTable QHeaderView::section:pressed { + background-color: #E5E7EB; + color: #111827; +} + +/* 垂直表头隐藏 */ +QTableWidget#stepsTable QHeaderView::section:vertical { + border: none; + background-color: transparent; +} + +/* ==================== 滚动条样式 ==================== */ +QTableWidget#stepsTable QScrollBar:vertical { + background-color: #F9FAFB; + width: 12px; + border-radius: 6px; + border: none; +} + +QTableWidget#stepsTable QScrollBar::handle:vertical { + background-color: #D1D5DB; + border-radius: 6px; + min-height: 20px; + margin: 2px; +} + +QTableWidget#stepsTable QScrollBar::handle:vertical:hover { + background-color: #9CA3AF; +} + +QTableWidget#stepsTable QScrollBar::handle:vertical:pressed { + background-color: #6B7280; +} + +QTableWidget#stepsTable QScrollBar::add-line:vertical, +QTableWidget#stepsTable QScrollBar::sub-line:vertical { + border: none; + background: none; + height: 0px; +} + +QTableWidget#stepsTable QScrollBar::add-page:vertical, +QTableWidget#stepsTable QScrollBar::sub-page:vertical { + background: none; +} + +QTableWidget#stepsTable QScrollBar:horizontal { + background-color: #F9FAFB; + height: 12px; + border-radius: 6px; + border: none; +} + +QTableWidget#stepsTable QScrollBar::handle:horizontal { + background-color: #D1D5DB; + border-radius: 6px; + min-width: 20px; + margin: 2px; +} + +QTableWidget#stepsTable QScrollBar::handle:horizontal:hover { + background-color: #9CA3AF; +} + +QTableWidget#stepsTable QScrollBar::handle:horizontal:pressed { + background-color: #6B7280; +} + +QTableWidget#stepsTable QScrollBar::add-line:horizontal, +QTableWidget#stepsTable QScrollBar::sub-line:horizontal { + border: none; + background: none; + width: 0px; +} + +QTableWidget#stepsTable QScrollBar::add-page:horizontal, +QTableWidget#stepsTable QScrollBar::sub-page:horizontal { + background: none; +} + +/* ==================== 对话框样式 ==================== */ + +/* 步骤编辑对话框 */ +QDialog { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 8px; +} + +QDialog QLabel { + font-size: 13px; + color: #374151; + font-weight: 500; +} + +QDialog QLineEdit { + background-color: #FFFFFF; + border: 1px solid #D1D5DB; + border-radius: 4px; + padding: 6px 12px; + font-size: 13px; + color: #374151; + selection-background-color: #EBF8FF; + selection-color: #1E40AF; +} + +QDialog QLineEdit:focus { + border-color: #3B82F6; + outline: none; + background-color: #FEFEFE; +} + +QDialog QTextEdit { + background-color: #FFFFFF; + border: 1px solid #D1D5DB; + border-radius: 4px; + padding: 6px 12px; + font-size: 13px; + color: #374151; + selection-background-color: #EBF8FF; + selection-color: #1E40AF; +} + +QDialog QTextEdit:focus { + border-color: #3B82F6; + outline: none; + background-color: #FEFEFE; +} + +QDialog QSpinBox { + background-color: #FFFFFF; + border: 1px solid #D1D5DB; + border-radius: 4px; + padding: 6px 12px; + font-size: 13px; + color: #374151; + min-width: 80px; +} + +QDialog QSpinBox:focus { + border-color: #3B82F6; + outline: none; +} + +/* 对话框按钮 */ +QDialog QPushButton { + font-size: 13px; + font-weight: 600; + padding: 8px 16px; + border-radius: 4px; + min-width: 80px; +} + +QDialog QPushButton[text="OK"] { + color: #1D4ED8; + background-color: #EBF8FF; + border: 1px solid #BFDBFE; +} + +QDialog QPushButton[text="OK"]:hover { + background-color: #DBEAFE; + border-color: #93C5FD; + color: #1E40AF; +} + +QDialog QPushButton[text="Cancel"] { + color: #6B7280; + background-color: #F9FAFB; + border: 1px solid #E5E7EB; +} + +QDialog QPushButton[text="Cancel"]:hover { + background-color: #F3F4F6; + border-color: #D1D5DB; + color: #4B5563; +} + +/* ==================== 右键菜单样式 ==================== */ +QMenu { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 6px; + padding: 4px 0; + font-size: 13px; + color: #374151; +} + +QMenu::item { + padding: 8px 16px; + margin: 0 4px; + border-radius: 4px; +} + +QMenu::item:selected { + background-color: #F0F9FF; + color: #1F2937; +} + +QMenu::item:pressed { + background-color: #EBF8FF; + color: #1E40AF; +} + +QMenu::separator { + height: 1px; + background-color: #E5E7EB; + margin: 4px 8px; +} + +/* ==================== 工具提示样式 ==================== */ +QToolTip { + background-color: #1F2937; + color: #FFFFFF; + border: none; + border-radius: 4px; + padding: 6px 8px; + font-size: 12px; + opacity: 230; +} + +/* ==================== 消息框样式 ==================== */ +QMessageBox { + background-color: #FFFFFF; + color: #374151; + font-size: 13px; +} + +QMessageBox QPushButton { + font-size: 13px; + font-weight: 600; + padding: 6px 16px; + border-radius: 4px; + min-width: 70px; +} + +QMessageBox QPushButton[text="OK"], +QMessageBox QPushButton[text="Yes"] { + color: #1D4ED8; + background-color: #EBF8FF; + border: 1px solid #BFDBFE; +} + +QMessageBox QPushButton[text="OK"]:hover, +QMessageBox QPushButton[text="Yes"]:hover { + background-color: #DBEAFE; + border-color: #93C5FD; +} + +QMessageBox QPushButton[text="Cancel"], +QMessageBox QPushButton[text="No"] { + color: #6B7280; + background-color: #F9FAFB; + border: 1px solid #E5E7EB; +} + +QMessageBox QPushButton[text="Cancel"]:hover, +QMessageBox QPushButton[text="No"]:hover { + background-color: #F3F4F6; + border-color: #D1D5DB; +} + +/* ==================== 响应式设计 ==================== */ + +/* 紧凑模式 */ +QWidget[size="compact"] { + font-size: 12px; +} + +QWidget[size="compact"] QPushButton { + padding: 4px 8px; + font-size: 12px; +} + +QWidget[size="compact"] QLineEdit { + padding: 4px 8px; + font-size: 12px; +} + +/* 大尺寸模式 */ +QWidget[size="large"] { + font-size: 14px; +} + +QWidget[size="large"] QPushButton { + padding: 10px 20px; + font-size: 14px; +} + +QWidget[size="large"] QLineEdit { + padding: 8px 16px; + font-size: 14px; +} \ No newline at end of file diff --git a/UI/ProcedureManager/HistoryViewer.py b/UI/ProcedureManager/HistoryViewer.py index 0386517..3bedd37 100644 --- a/UI/ProcedureManager/HistoryViewer.py +++ b/UI/ProcedureManager/HistoryViewer.py @@ -30,13 +30,70 @@ class HistoryViewerWidget(QWidget): qssFile.open(QFile.ReadOnly | QFile.Text) stream = QTextStream(qssFile) stream.setCodec("UTF-8") - self.setStyleSheet(stream.readAll()) + 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) def initUi(self): layout = QVBoxLayout() @@ -86,10 +143,10 @@ class HistoryViewerWidget(QWidget): splitter.setContentsMargins(0, 0, 0, 0) splitter.addWidget(self.table) splitter.addWidget(self.stepTable) - # 设置比例:上方历史记录表格占15%,下方步骤详情表格占85% - splitter.setSizes([120, 680]) - splitter.setStretchFactor(0, 0) # 历史记录表格不拉伸 - splitter.setStretchFactor(1, 1) # 步骤详情表格可拉伸 + # 设置比例:上方历史记录表格占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,占据主要空间 @@ -99,14 +156,14 @@ class HistoryViewerWidget(QWidget): buttonLayout.setSpacing(2) self.deleteButton = QPushButton("删除历史记录") - self.deleteButton.setIcon(qta.icon('fa5s.trash', color='#B91C1C')) - self.deleteButton.setMaximumHeight(24) # 限制按钮高度 + self.deleteButton.setObjectName("deleteHistoryButton") # 设置对象名称 + self.deleteButton.setIcon(qta.icon('fa5s.trash', color='#FFFFFF')) # 白色图标 self.deleteButton.clicked.connect(self.deleteSelectedHistory) buttonLayout.addWidget(self.deleteButton) self.exportButton = QPushButton("导出报告") - self.exportButton.setIcon(qta.icon('fa5s.file-export', color='#047857')) - self.exportButton.setMaximumHeight(24) # 限制按钮高度 + self.exportButton.setObjectName("exportReportButton") # 设置对象名称 + self.exportButton.setIcon(qta.icon('fa5s.file-export', color='#FFFFFF')) # 白色图标 self.exportButton.clicked.connect(self.exportReport) buttonLayout.addWidget(self.exportButton) @@ -115,6 +172,9 @@ class HistoryViewerWidget(QWidget): self.setLayout(layout) + # 在UI创建完成后应用按钮样式 + QTimer.singleShot(0, self.applyButtonStyles) + def setupTableDisplay(self, table): """设置表格显示属性 - 极度紧凑版""" table.setWordWrap(False) # 禁用自动换行以节省空间 @@ -136,9 +196,9 @@ class HistoryViewerWidget(QWidget): verticalHeader.setVisible(False) # 隐藏行号以节省空间 verticalHeader.setSectionResizeMode(QHeaderView.Fixed) if table.objectName() == "historyTable": - verticalHeader.setDefaultSectionSize(12) # 历史表格极小行高 + verticalHeader.setDefaultSectionSize(20) # 历史表格适中行高,适应2:3比例 else: - verticalHeader.setDefaultSectionSize(16) # 步骤表格稍大行高 + verticalHeader.setDefaultSectionSize(18) # 步骤表格稍大行高 if table == self.table: table.setSelectionMode(QTableView.ExtendedSelection) @@ -617,12 +677,33 @@ class HistoryViewerWidget(QWidget): """窗口显示事件""" super().showEvent(event) QTimer.singleShot(100, self.adjustTablesAfterShow) + QTimer.singleShot(500, self.forceSplitterRatio) # 延迟强制设置比例 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]) + + + def selectAllHistory(self): """全选历史记录""" self.table.selectAll() diff --git a/UI/ProcedureManager/ProcedureTable.py b/UI/ProcedureManager/ProcedureTable.py new file mode 100644 index 0000000..7a4c3fe --- /dev/null +++ b/UI/ProcedureManager/ProcedureTable.py @@ -0,0 +1,453 @@ + +from PyQt5 import QtCore +from PyQt5.QtWidgets import QHeaderView, QStyle, QStyleOptionButton, QTableView, QAbstractItemView, QDesktopWidget +from PyQt5.QtCore import (pyqtSignal, Qt, QRect) +from PyQt5.QtCore import Qt, QTimer, QAbstractTableModel, QModelIndex, QPoint, QSize +from PyQt5.QtGui import QFont, QBrush, QColor + +from PyQt5.QtWidgets import QTableView, QStyledItemDelegate +from utils import Globals + +class BackgroundDelegate(QStyledItemDelegate): + def __init__(self, *args): + super(BackgroundDelegate, self).__init__(*args) + self.setObjectName('item') + + def sizeHint(self, option, index): + """设置项目的尺寸提示,确保足够的高度""" + size = super().sizeHint(option, index) + size.setHeight(max(46, size.height())) # 最小高度60px + return size + + def paint(self, painter, option, index): + # 确保绘制区域有足够的高度 + if index.data(QtCore.Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + # 减少高度调整,保持更多的绘制区域 + option.rect.setHeight(max(46, height - 4)) # 最小保持56px高度 + option.rect.moveTop(top + 2) + painter.fillRect(option.rect, index.data(QtCore.Qt.BackgroundRole)) + super().paint(painter, option, index) + + + +class ProcedureTabel(QTableView): + def __init__(self, testSteps, parent=None): + super(ProcedureTabel, self).__init__(parent) + self.parent = parent + self.createTableSection(testSteps) + self.setupTableHeaders() + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) + self.loadStylesheet() + + def loadStylesheet(self): + """加载表格样式表""" + try: + from PyQt5.QtCore import QFile, QTextStream + qssPath = "Static/ProcedureTable.qss" + qssFile = QFile(qssPath) + if qssFile.exists(): + qssFile.open(QFile.ReadOnly | QFile.Text) + stream = QTextStream(qssFile) + stream.setCodec("UTF-8") + self.setStyleSheet(stream.readAll()) + qssFile.close() + print(f"✅ ProcedureTable成功加载样式表: {qssPath}") + else: + print(f"⚠️ ProcedureTable样式表文件不存在: {qssPath}") + except Exception as e: + print(f"❌ ProcedureTable加载样式表失败: {str(e)}") + + def createTableSection(self, testSteps): + """创建表格区域""" + self.tableModel = StepTableModel(testSteps) + self.setModel(self.tableModel) + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.parent.showContextMenu) + + # 设置表格编辑行为 + self.setEditTriggers(QTableView.DoubleClicked) + + # 设置选择行为为整行选中 + self.setSelectionBehavior(QTableView.SelectRows) + self.setSelectionMode(QAbstractItemView.SingleSelection) # 单行选择 + + # 设置表格显示优化 + self.setAlternatingRowColors(False) # 交替行颜色 + self.setShowGrid(True) # 显示网格线 + self.setGridStyle(Qt.SolidLine) # 实线网格 + self.setSortingEnabled(False) # 禁用排序避免干扰执行顺序 + + # 启用鼠标跟踪以支持hover效果 + self.setMouseTracking(True) + + # 设置最小行高 + self.verticalHeader().setMinimumSectionSize(30) + + self.setupTableHeaders() + + # 初始调整所有行高 + self.adjustAllRowHeights() + + + + + + def setupTableHeaders(self): + """设置表格头部""" + try: + header = self.horizontalHeader() + if header: + # 设置表格自动调整行高 + self.verticalHeader().setDefaultSectionSize(35) + self.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) + self.verticalHeader().hide() + + # 设置列宽模式 - 让表格填充满整个宽度 + header.setStretchLastSection(True) # 让最后一列自动拉伸填充剩余空间 + + # 设置各列的调整模式 + header.setSectionResizeMode(0, QHeaderView.Fixed) # 步骤ID - 固定宽度 + header.setSectionResizeMode(1, QHeaderView.Stretch) # 步骤描述 - 拉伸 + header.setSectionResizeMode(2, QHeaderView.Fixed) # 操作类型 - 固定宽度 + header.setSectionResizeMode(3, QHeaderView.Fixed) # 执行时间 - 固定宽度 + header.setSectionResizeMode(4, QHeaderView.Fixed) # 执行结果 - 固定宽度 + header.setSectionResizeMode(5, QHeaderView.Stretch) # 详细结果 - 拉伸 + header.setSectionResizeMode(6, QHeaderView.Stretch) # 备注 - 拉伸 + + # 设置固定列的宽度 + self.setColumnWidth(0, 80) # 步骤ID + self.setColumnWidth(2, 120) # 操作类型 + self.setColumnWidth(3, 160) # 执行时间 + self.setColumnWidth(4, 80) # 执行结果 + + # 设置拉伸列的最小宽度 + header.setMinimumSectionSize(100) # 设置所有列的最小宽度 + + # 启用文本换行 + self.setWordWrap(True) + + except Exception as e: + print(f"设置表格头部时出错: {e}") + + def adjustRowHeight(self, row): + """调整指定行的高度以适应内容""" + try: + # 获取该行的内容 + stepInfo = self.tableModel.getStepInfo(row) + if not stepInfo: + return + + # 计算需要的行高,确保所有值都不为None + description = stepInfo.get('description') or '' + result = stepInfo.get('result') or '' + note = stepInfo.get('note') or '' + + # 确保所有值都是字符串类型 + if not isinstance(description, str): + description = str(description) if description is not None else '' + if not isinstance(result, str): + result = str(result) if result is not None else '' + if not isinstance(note, str): + note = str(note) if note is not None else '' + + # 基于最长文本计算行高 + max_text_length = max(len(description), len(result), len(note)) + + # 基础行高 + base_height = 35 + + # 根据文本长度动态调整 + if max_text_length > 100: + height = base_height + (max_text_length // 50) * 15 + elif max_text_length > 50: + height = base_height + 15 + else: + height = base_height + + # 设置行高,最大不超过150像素 + height = min(height, 150) + self.setRowHeight(row, height) + + except Exception as e: + print(f"调整行高时出错: {e}") + + def adjustAllRowHeights(self): + """调整所有行的高度""" + try: + if not hasattr(self, 'tableModel') or self.tableModel is None: + return + + row_count = self.tableModel.rowCount() + if row_count <= 0: + return + + for row in range(row_count): + self.adjustRowHeight(row) + except Exception as e: + print(f"调整所有行高时出错: {e}") + + def mouseMoveEvent(self, event): + """鼠标移动事件,实现hover效果""" + super().mouseMoveEvent(event) + + # 获取鼠标位置对应的行 + index = self.indexAt(event.pos()) + if index.isValid(): + row = index.row() + if row != self.tableModel.hoverRow: + # 清除之前的hover行 + oldHoverRow = self.tableModel.hoverRow + self.tableModel.hoverRow = row + + # 更新显示 + if oldHoverRow >= 0: + self.update(self.tableModel.index(oldHoverRow, 0)) + for col in range(self.tableModel.columnCount()): + self.update(self.tableModel.index(oldHoverRow, col)) + + # 更新新的hover行 + self.update(self.tableModel.index(row, 0)) + for col in range(self.tableModel.columnCount()): + self.update(self.tableModel.index(row, col)) + else: + # 鼠标不在任何行上,清除hover效果 + if self.tableModel.hoverRow >= 0: + oldHoverRow = self.tableModel.hoverRow + self.tableModel.hoverRow = -1 + self.update(self.tableModel.index(oldHoverRow, 0)) + for col in range(self.tableModel.columnCount()): + self.update(self.tableModel.index(oldHoverRow, col)) + + def leaveEvent(self, event): + """鼠标离开事件,清除hover效果""" + super().leaveEvent(event) + if self.tableModel.hoverRow >= 0: + oldHoverRow = self.tableModel.hoverRow + self.tableModel.hoverRow = -1 + self.update(self.tableModel.index(oldHoverRow, 0)) + for col in range(self.tableModel.columnCount()): + self.update(self.tableModel.index(oldHoverRow, col)) + + +class StepTableModel(QAbstractTableModel): + columns = ['序号', '实验步骤','操作类型', '执行时间', '是否与预期一致', '实际结果', '备注'] + + def __init__(self, testSteps): + super().__init__() + self.stepData = [] + self.stepIndex = 0 + self.hoverRow = -1 # 添加hover行跟踪 + + for mainStep in testSteps: + self.stepData.append({ + 'id': self.stepIndex, + 'isMain': True, + 'stepId': mainStep['步骤ID'], + 'description': mainStep['步骤描述'], + 'executed': False, + 'stepType': mainStep['操作类型'], + 'time': None, + 'result': None, + 'note': mainStep.get('备注', '') # 修复:从主步骤的备注字段读取 + }) + self.stepIndex += 1 + + for subStep in mainStep['子步骤']: + self.stepData.append({ + 'id': self.stepIndex, + 'isMain': False, + 'stepId': f"{mainStep['步骤ID']}{subStep['序号']}", + 'description': subStep['操作'], + 'stepType': 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'] or '' + elif col == 1: + return step['description'] or '' + elif col == 2: + return step['stepType'] or '' + elif col == 3: + return step['time'].strftime("%Y-%m-%d %H:%M:%S") if step['time'] else '' + elif col == 4: + # print(step['result']) + if step['executed']: + if '失败' in step['result']: + return '✘' + else: + return '✓' + elif col == 5: + # print(step['result']) + return step['result'] or '' + elif col == 6: + return step['note'] if step['note'] else '' + + elif role == Qt.BackgroundRole: + # 优先级:执行结果 > hover效果 > 主步骤背景 + if step['executed']: + if '失败' in step['result']: + # 失败状态:红色背景 + if row == self.hoverRow: + return QBrush(QColor(255, 160, 170)) # hover时稍微深一点的红色 + else: + return QBrush(QColor(255, 182, 193)) # 浅红色 + else: + # 成功状态:绿色背景 + if row == self.hoverRow: + return QBrush(QColor(120, 220, 120)) # hover时稍微深一点的绿色 + else: + return QBrush(QColor(144, 238, 144)) # 浅绿色 + elif row == self.hoverRow: + # hover效果:浅蓝色背景 + if step['isMain']: + return QBrush(QColor(200, 200, 230)) # 主步骤hover时的颜色 + else: + return QBrush(QColor(230, 240, 255)) # 子步骤hover时的颜色 + elif step['isMain']: + # 主步骤默认背景:浅灰色 + return QBrush(QColor(220, 220, 220)) + + elif role == Qt.TextAlignmentRole: + if col in [0, 2, 3, 4]: # 步骤ID、操作类型、执行时间、执行结果居中对齐 + return Qt.AlignCenter + else: # 其他列左对齐并顶部对齐 + return Qt.AlignLeft | Qt.AlignTop + + 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 flags(self, index): + """返回单元格标志""" + flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable + return flags + + 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): + # print(result) + 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]) + + def getTestSteps(self): + """获取测试步骤数据,用于保存""" + testSteps = [] + currentMainStep = None + + for step in self.stepData: + if step['isMain']: + # 如果有前一个主步骤,先添加到结果中 + if currentMainStep: + testSteps.append(currentMainStep) + + # 创建新的主步骤 + currentMainStep = { + '步骤ID': step['stepId'], + '步骤描述': step['description'], + '操作类型': step['stepType'], + '预期结果': '', + '子步骤': [] + } + else: + # 子步骤 + if currentMainStep: + # 从步骤ID中提取序号 + stepId = step['stepId'] + if currentMainStep['步骤ID'] in stepId: + subStepId = stepId.replace(currentMainStep['步骤ID'], '') + else: + subStepId = stepId + + subStep = { + '序号': subStepId, + '操作': step['description'], + '操作类型': step['stepType'], + '预期结果': '', + '实际结果': step.get('result', ''), + '一致性': '是' if step.get('result') and '失败' not in step.get('result', '') else '否', + '测试时间': step.get('time', ''), + '备注': step.get('note', '') + } + currentMainStep['子步骤'].append(subStep) + + # 添加最后一个主步骤 + if currentMainStep: + testSteps.append(currentMainStep) + + return testSteps \ No newline at end of file