diff --git a/Static/Main.qss b/Static/Main.qss index c195519..87a6dc0 100644 --- a/Static/Main.qss +++ b/Static/Main.qss @@ -1,1170 +1,1318 @@ -QWidget#centralwidget{ - - background-color: #F5F5F5; - - border-radius: 8px; +/* ==================== DCS2025 工业控制系统样式表 ==================== */ +/* 重构版本 - 消除重复,统一样式 */ +/* ==================== 全局变量和基础样式 ==================== */ +* { + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; } -QWidget#titlewidget{ - - background-color: #FFFFFF; - +/* ==================== 主容器样式 ==================== */ +QWidget#centralwidget { + background-color: #F5F5F5; + border-radius: 8px; } -QWidget#leftWidget{ - - background-color: #FFFFFF; - +QWidget#titlewidget { + background-color: #FFFFFF; } -QWidget#topWidget{ - - background-color: #2277EF; - +QWidget#topWidget { + background-color: #2277EF; } -QWidget#userWidget, QWidget#varWidget, QWidget#projectWidget, QWidget#trendWidget, QWidget#LoginWidget{ - - background-color: #FFFFFF; - +QWidget#userWidget, QWidget#varWidget, QWidget#projectWidget, QWidget#trendWidget, QWidget#LoginWidget { + background-color: #FFFFFF; + border-radius: 8px; + border: 1px solid #E5E7EB; } - -QWidget#userBtnWidget, QWidget#projectBtnWidget{ - - background-color: #FFFFFF; - - margin-top: 7px; - - margin-right: 7px; - +QWidget#userBtnWidget, QWidget#projectBtnWidget { + background-color: #FFFFFF; + margin-top: 7px; + margin-right: 7px; } -QWidget#userCheckWidget{ - - background-color: #FFFFFF; - - margin-top: 7px; - +QWidget#userCheckWidget { + background-color: #FFFFFF; + margin-top: 7px; } -QWidget#registerWidget{ -background : #181922; - +QWidget#registerWidget { + background: #181922; } -/*QStackedWidget#rightWidget{ - - background-color: #000000; - -}*/ - -QTabBar#varManageTabBar::tab{ - - font-family: ".SFNSDisplay-Medium"; +/* ==================== 左侧导航栏样式 ==================== */ +QWidget#leftWidget { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #FFFFFF, + stop:0.2 #FEFEFE, + stop:0.5 #FAFBFC, + stop:0.8 #F6F8FA, + stop:1 #F1F3F5); + border-right: 1px solid #E1E5E9; + min-width: 260px; + max-width: 260px; +} - font-size: 18px; +/* 导航按钮基础样式 */ +QPushButton#createProject, QPushButton#openProject, QPushButton#trendMag, +QPushButton#varMag, QPushButton#userMag, QPushButton#protocolMag, +QPushButton#controlMag, QPushButton#procedureMag { + border: none; + font-size: 16px; + text-align: left; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; + color: #6B7280; + background-color: transparent; + padding: 16px 20px; + margin: 4px 16px; + border-radius: 12px; + min-height: 52px; + icon-size: 22px 22px; +} + +/* 导航按钮悬停效果 */ +QPushButton#createProject:hover, QPushButton#openProject:hover, QPushButton#trendMag:hover, +QPushButton#varMag:hover, QPushButton#userMag:hover, QPushButton#protocolMag:hover, +QPushButton#controlMag:hover, QPushButton#procedureMag:hover { + color: #2277EF; + background: qlineargradient(x1:0, y1:0, x2:1, y2:0, + stop:0 rgba(34, 119, 239, 15), + stop:1 rgba(34, 119, 239, 5)); + border-radius: 12px; +} + +/* 导航按钮选中状态 */ +QPushButton#createProject:checked, QPushButton#openProject:checked, QPushButton#trendMag:checked, +QPushButton#varMag:checked, QPushButton#userMag:checked, QPushButton#protocolMag:checked, +QPushButton#controlMag:checked, QPushButton#procedureMag:checked { + background: qlineargradient(x1:0, y1:0, x2:1, y2:0, + stop:0 #2277EF, + stop:0.3 #3B82F6, + stop:0.7 #60A5FA, + stop:1 rgba(96, 165, 250, 200)); + color: #FFFFFF; + font-weight: bold; + border-radius: 12px; + border: none; +} - width: 160px; +/* ==================== 表格统一样式 ==================== */ +/* 表格容器基础样式 */ +QTableView { + border: none; + outline: none; + gridline-color: transparent; + background-color: #FFFFFF; + /* selection-background-color: #E3F2FD; */ + selection-color: #1976D2; + font-size: 15px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + font-weight: 400; + color: #374151; +} - height: 40px; +/* 表格项统一样式 */ +QTableView::item { + border: none; + outline: none; + min-height: 48px; + /* 不设置background-color,让模型的背景颜色生效 */ +} +/* 表格项悬停效果 */ +QTableView::item:hover { + /* background-color: #F8F9FA; */ + color: #2277EF; } +/* 表格项选中效果 */ +QTableView::item:selected { + /* background-color: #E3F2FD; */ + color: #1976D2; + font-weight: 500; +} +/* ==================== 专用表格样式 ==================== */ +/* 用户和工程表格特殊样式 */ +QTableView#userView, QTableView#projectView { + background-color: #FFFFFF; + padding: 10px; +} -QTabBar#varManageTabBar::tab:hover{ - - background-color: #3787f7; +QTableView#userView::item, QTableView#projectView::item { + /* background-color: #FFFFFF; */ + margin: 4px 2px; + border-radius: 6px; + min-height: 56px !important; + height: 56px; + padding: 8px 14px; + font-size: 15px; + line-height: 24px; +} - color: #000; +/* 变量表格样式 */ +QTableView#varView { + background-color: #FFFFFF; + font-size: 16px; +} - border: 2px solid #ccc; +QTableView#varView::item { + margin: 0px; + border-radius: 0px; + min-height: 46px; + padding: 8px 12px; + /* 不设置background-color,让模型的背景颜色生效 */ +} - /* font-weight: bold; */ +/* ==================== 表头统一样式 ==================== */ +/* 表头基础样式 */ +QHeaderView { + border: none; + outline: none; + background-color: transparent; +} - padding-bottom: 7px; +QHeaderView::section { + border: none; + outline: none; + text-align: center; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; + font-weight: 600; +} +/* 用户和工程表头样式 */ +QHeaderView#userHeader::section, QHeaderView#projectHeader::section { + color: #1F2937; + font-size: 16px; + height: 26px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #FFFFFF, + stop:1 #F8F9FA); + padding: 16px 12px; + border-bottom: 2px solid #E5E7EB; +} +/* 变量表头样式 */ +QHeaderView::section { + color: #FFFFFF; + font-size: 16px; + height: 26px; + background-color: #4A5568; + padding: 12px 8px; } -QTabBar#varManageTabBar::tab:selected{ - - background-color: #3787f7; +QHeaderView#paramHeader::section { + color: #FFFFFF; + font-size: 15px; + height: 26px; + background-color: #4A5568; + padding: 10px 8px; +} - color: #000; +/* ==================== 按钮统一样式 ==================== */ +/* 基础按钮样式 */ +QPushButton { + border: none; + outline: none; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; +} - border: 2px solid #ccc; +/* 设置按钮样式 */ +QPushButton#setButton { + min-height: 36px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2277EF, + stop:1 #1976D2); + border-radius: 6px; + color: #FFFFFF; + font-size: 14px; + font-weight: 600; + padding: 8px 16px; +} - font-weight: bold; +QPushButton#setButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, + stop:1 #2277EF); +} +QPushButton#setButton:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #1976D2, + stop:1 #1565C0); +} +/* 功能按钮样式 */ +QPushButton#forceBtn, QPushButton#exportBtn, QPushButton#importBtn, +QPushButton#createBtn, QPushButton#delBtn, QPushButton#messageBtn, +QPushButton#startProtocolBtn, QPushButton#clearBtn { + color: #2277EF; + font-size: 14px; + font-weight: 500; + padding: 6px 12px; + background-color: transparent; + border-radius: 4px; +} +QPushButton#forceBtn:hover, QPushButton#exportBtn:hover, QPushButton#importBtn:hover, +QPushButton#createBtn:hover, QPushButton#delBtn:hover, QPushButton#messageBtn:hover, +QPushButton#startProtocolBtn:hover, QPushButton#clearBtn:hover { + background-color: #E3F2FD; + font-weight: 600; } +/* 登录按钮样式 */ +QPushButton#loginButton, QPushButton#exitButton { + width: 80px; + height: 36px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2277EF, + stop:1 #1976D2); + border-radius: 6px; + color: #FFFFFF; + font-size: 14px; + font-weight: 600; +} +QPushButton#loginButton:hover, QPushButton#exitButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, + stop:1 #2277EF); +} -QPushButton{ +/* ==================== 输入框统一样式 ==================== */ +/* 基础输入框样式 */ +QLineEdit { + border: 1px solid #D1D5DB; + border-radius: 6px; + padding: 8px 12px; + font-size: 14px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + background-color: #FFFFFF; + color: #374151; +} - border: none; +QLineEdit:focus { + border-color: #2277EF; + background-color: #F8F9FF; +} +/* 设置输入框样式 */ +QLineEdit#setEdit { + min-width: 180px; + min-height: 32px; + font-size: 14px; } -QPushButton#closeBtn, QPushButton#minBtn, QPushButton#maxBtn{ - - background-color: #2277EF; +/* 用户输入框样式 */ +QLineEdit#userEdit, QLineEdit#pwdEdit { + width: 240px; + height: 36px; + background-color: #F9FAFB; + border: 1px solid #E5E7EB; + font-size: 14px; +} - border: none; +/* 搜索输入框样式 */ +QLineEdit#searchEdit { + background-color: #4b8cf2; + border: none; + padding: 8px 15px; + color: #FFFFFF; + font-size: 14px; +} +QLineEdit#searchEdit::placeholder { + color: rgba(255, 255, 255, 0.7); } +/* ==================== 标签统一样式 ==================== */ +/* 标题标签样式 */ +QLabel#titleLabel { + color: #FFFFFF; + font-size: 24px; + font-family: "PingFangSC-Semibold", "Microsoft YaHei", sans-serif; + font-weight: bold; +} -QPushButton#createProject, QPushButton#openProject, QPushButton#trendMag, QPushButton#varMag, QPushButton#userMag, QPushButton#protocolMag, QPushButton#controlMag{ +/* 设置标签样式 */ +QLabel#setlabel, QLabel#mesLabel { + background-color: #F3F4F6; + border: none; + min-width: 180px; + min-height: 32px; + border-radius: 6px; + padding: 8px 12px; + color: #374151; + font-size: 14px; + font-weight: 500; +} - border: none; +/* TCP/RTU标签样式 */ +QLabel#tcpLable, QLabel#rtuLable { + background-color: #F3F4F6; + border: none; + width: 200px; + height: 40px; + border-radius: 6px; + padding: 8px 12px; + color: #374151; + font-size: 16px; + font-weight: 500; +} - font-size: 26px; +/* ==================== 下拉框统一样式 ==================== */ +QComboBox { + /* border: 1px solid #D1D5DB; */ + border-radius: 6px; + padding: 6px 12px; + font-size: 14px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + background-color: #FFFFFF; + color: #374151; + min-height: 32px; +} - text-align: center; +QComboBox:focus { + border-color: #2277EF; +} - font-family: "Roboto"; +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 20px; + border: none; +} - color: #929eaa; +QComboBox::down-arrow { + image: url(./Static/down.png); + width: 12px; + height: 12px; +} +QComboBox QAbstractItemView { + border: 1px solid #E5E7EB; + border-radius: 6px; + background-color: #FFFFFF; + selection-background-color: #E3F2FD; + selection-color: #1976D2; + font-size: 14px; + outline: none; } +QComboBox QAbstractItemView::item { + padding: 8px 12px; + min-height: 32px; +} -QPushButton#createProject:hover, QPushButton#openProject:hover, QPushButton#trendMag:hover, QPushButton#varMag:hover, QPushButton#userMag:hover, QPushButton#protocolMag:hover, QPushButton#controlMag:hover{ +QComboBox QAbstractItemView::item:hover { + background-color: #F3F4F6; +} - color: #2277EF; +/* ==================== 滚动条统一样式 ==================== */ +QScrollBar:vertical { + background: #F3F4F6; + width: 12px; + border-radius: 6px; + margin: 0px; +} - background-color: #F7F7F7; +QScrollBar::handle:vertical { + background: #9CA3AF; + border-radius: 6px; + min-height: 20px; +} - border-right:4px solid #2277EF; +QScrollBar::handle:vertical:hover { + background: #6B7280; +} +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0px; } +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: transparent; +} -QPushButton#createProject:pressed, QPushButton#openProject:pressed, QPushButton#trendMag:pressed, QPushButton#varMag:pressed, QPushButton#userMag:pressed, QPushButton#protocolMag:pressed, QPushButton#controlMag:pressed{ +/* ==================== 复选框样式 ==================== */ +QCheckBox#userBox::indicator { + width: 20px; + height: 20px; +} - - background-color: #F5F5F6; +QCheckBox#userBox::indicator:checked { + image: url(./Static/checkoff.png); +} - color: #2277EF; +QCheckBox#userBox::indicator:unchecked { + image: url(./Static/checkon.png); +} - border-right:4px solid #2277EF; +/* ==================== 文本编辑框样式 ==================== */ +QTextEdit#mesEdit { + border: 1px solid #D1D5DB; + border-radius: 8px; + font-size: 14px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + background-color: #FFFFFF; + color: #374151; + padding: 8px; +} +QTextEdit#mesEdit:focus { + border-color: #2277EF; } -QPushButton#createProject:checked, QPushButton#openProject:checked, QPushButton#trendMag:checked, QPushButton#varMag:checked, QPushButton#userMag:checked, QPushButton#protocolMag:checked, QPushButton#controlMag:checked{ +/* ==================== 列表视图样式 ==================== */ +QListView#trendListView { + font-size: 14px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + border: 1px solid #E5E7EB; + border-radius: 6px; + background-color: #FFFFFF; +} - background-color: #F5F5F6; +QListView#trendListView::item { + padding: 8px 12px; + margin: 2px; + border-radius: 4px; +} - color: #2277EF; +QListView#trendListView::item:hover { + background-color: #F3F4F6; +} - border-right:4px solid #2277EF; +QListView#trendListView::item:selected { + background-color: #E3F2FD; + color: #1976D2; +} +/* ==================== 消息框样式 ==================== */ +QMessageBox { + background-color: #FFFFFF; + border-radius: 12px; + border: 1px solid #E5E7EB; + min-width: 400px; + min-height: 180px; + font-size: 14px; + padding: 20px; } -QPushButton#importBtn { +QMessageBox QLabel { + color: #374151; + font-size: 16px; + font-weight: 600; + padding: 12px 0; +} - color: #0076ff; +QMessageBox QPushButton { + min-width: 80px; + min-height: 36px; + font-size: 14px; + border-radius: 6px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #2277EF, + stop:1 #1976D2); + color: #FFFFFF; + font-weight: 600; + margin: 0 6px; +} - font-size: 22px; +QMessageBox QPushButton:hover { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #3B82F6, + stop:1 #2277EF); +} - margin-bottom: 6px +/* ==================== Tab标签页样式 ==================== */ +QTabBar#varManageTabBar::tab { + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; + font-size: 16px; + width: 160px; + height: 40px; + padding: 8px 16px; + margin: 2px; + border-radius: 6px; + background-color: #F3F4F6; + color: #6B7280; +} +QTabBar#varManageTabBar::tab:hover { + background-color: #E3F2FD; + color: #2277EF; } +QTabBar#varManageTabBar::tab:selected { + background-color: #2277EF; + color: #FFFFFF; + font-weight: 600; +} -QPushButton#forceBtn, QPushButton#exportBtn, QPushButton#importBtn, QPushButton#createBtn, QPushButton#delBtn, QPushButton#messageBtn, QPushButton#startProtocolBtn, QPushButton#clearBtn{ +/* ==================== 趋势界面样式 ==================== */ +QWidget#trendMainWidget { + background-color: #F5F5F5; + border-radius: 8px; +} - color: #0076ff; +QGroupBox { + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; + font-weight: 600; + border: 1px solid #E5E7EB; + border-radius: 8px; + margin-top: 12px; + padding-top: 8px; + background-color: #FFFFFF; +} - font-size: 22px; +QGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 8px; + background-color: #FFFFFF; + color: #374151; + font-size: 16px; +} +/* ==================== 注册界面样式 ==================== */ +QPushButton#registerPushButton { + width: 100px; + height: 32px; + border-radius: 6px; + color: #FFFFFF; + font-size: 14px; + font-weight: 600; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #10B981, + stop:1 #059669); } -QPushButton#forceBtn:hover, QPushButton#exportBtn:hover, QPushButton#importBtn:hover, -QPushButton#createBtn:hover, QPushButton#delBtn:hover, QPushButton#messageBtn:hover, -QPushButton#startProtocolBtn:hover, QPushButton#clearBtn:hover { +QPushButton#cancel_btn { + width: 100px; + height: 32px; + border-radius: 6px; + color: #FFFFFF; + font-size: 14px; + font-weight: 600; + background: #6B7280; +} - font: bold; +QLabel#registerlabel { + color: #6B7280; + font-size: 13px; + font-family: "PingFangSC-Regular", "Microsoft YaHei", sans-serif; + text-align: center; +} - border: none; +QLabel#registerlabel_1, QLabel#registerlabel_2 { + color: #FFFFFF; + font-size: 15px; + font-family: "PingFangSC-Medium", "Microsoft YaHei", sans-serif; + font-weight: 600; +} - margin-bottom: -2px +QLineEdit#registerlineEdit { + width: 360px; + height: 38px; + border-radius: 8px; + background: #21232f; + color: #FFFFFF; + font-size: 16px; + padding: 8px 12px; + border: 1px solid #4A5568; } -QPushButton#loginButton, QPushButton#exitButton{ +QLineEdit#registerlineEdit:focus { + border-color: #2277EF; +} +/* = +=================== 变量表下拉框样式 ==================== */ +/* 基础下拉框样式 */ - width: 54.75px; - height: 40px; - background-color: #5b8cff; +/* 移除偶数行样式 - 不需要奇偶行颜色区别 */ - border-radius: 5px; +/* 移除强制变量样式 - 所有下拉框使用统一样式 */ - color: #FFFFFF; +/* 下拉箭头样式 - 使用Static/down.png */ - font-family: ".SFNSDisplay-Medium"; - font-size: 13.26px; - font-weight: 520; +/* 全局下拉列表样式 - 强制修复黑色背景问题 */ +QComboBox QAbstractItemView { + background-color: white !important; + color: black !important; + border: 1px solid #E5E7EB; + border-radius: 4px; + selection-background-color: #E3F2FD !important; + selection-color: #1976D2 !important; + outline: none; + font-size: 14px; + show-decoration-selected: 1; } -QPushButton#loginButton:hover, QPushButton#exitButton:hover{ - - font: bold; - +QComboBox QAbstractItemView::item { + background-color: white !important; + color: black !important; + padding: 8px 12px; + min-height: 28px; border: none; - - margin-bottom: -2px } -QPushButton#mesButton{ - - width: 80px; - - height: 40px; - - background-color: #bfbfbf; - - border-radius: 5px; +QComboBox QAbstractItemView::item:hover { + background-color: #F3F4F6 !important; + color: black !important; +} - color: #000000; +QComboBox QAbstractItemView::item:selected { + background-color: #E3F2FD !important; + color: #1976D2 !important; +} - font-family: ".SFNSDisplay-Regular"; - font-size: 16px; +/* ==================== 历史趋势界面样式 ==================== */ - font-weight: 520; +/* 主趋势窗口 */ +QWidget#trendMainWidget { + background-color: #F8F9FA; + border: 1px solid #E5E7EB; + border-radius: 8px; +} +/* 变量列表组 */ +QGroupBox#trendVariableListGroup { + font-size: 14px; + font-weight: bold; + color: #374151; + border: 2px solid #E5E7EB; + border-radius: 8px; + margin-top: 12px; + padding-top: 8px; + background-color: #FFFFFF; } -QPushButton#mesButton:hover -{ - color:white; - background-color:rgb(44 , 137 , 255); - border: 1px solid rgb(11 , 137 , 234); +QGroupBox#trendVariableListGroup::title { + subcontrol-origin: margin; + left: 12px; + padding: 0 8px 0 8px; + color: #2277EF; + font-weight: bold; } -QPushButton#mesButton:pressed -{ - color:white; - background-color:rgb(14 , 135 , 228); - border: 1px solid rgb(12 , 138 , 235); - padding-left:3px; - padding-top:3px; +/* 搜索输入框 */ +QLineEdit#trendSearchInput { + background-color: #FFFFFF; + border: 2px solid #E5E7EB; + border-radius: 6px; + padding: 8px 12px; + font-size: 14px; + color: #374151; } +QLineEdit#trendSearchInput:focus { + border-color: #2277EF; + background-color: #F8F9FA; +} -QPushButton#setButton{ - - - min-height: 33px; - - background-color: #2277EF; - - border-radius: 5px; - - color: #FFFFFF; - - font-family: ".SFNSDisplay-Medium"; - - font-size: 20px; - - font-weight: 520; -} - -QPushButton#registerPushButton{ - width: 100px; - - height: 30px; - - border-radius: 6px; - - color: #ffffff; - - font-family: "PingFangSC-Medium"; - - font-size: 12px; - - font-weight: 400; - - line-height: 16px; - - background: qlineargradient(x1:0, y1:1, x2:0, y2:1,stop:0 #3EC169,stop:1 #1BBB70); -} - -QPushButton#cancel_btn{ - width: 100px; - - height: 30px; +QLineEdit#trendSearchInput:hover { + border-color: #9CA3AF; +} +/* 变量列表 */ +QListWidget#trendVarListWidget { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; border-radius: 6px; - - color: #ffffff; - - font-family: "PingFangSC-Medium"; - - font-size: 12px; - - font-weight: 400; - - line-height: 16px; - - background: #5c5858; -} - -QLabel#titleLabel{ - - color: #FFFFFF; - - font-size: 30px; - - font-family: "PingFangSC-Regular"; - - text-align: left; - -} - -QLabel#setlabel, QLabel#mesLabel{ - background-color: #f2f2f2; - - border: none; - - min-width: 180px; - - min-height: 30px; - - border-radius: 5px; - - padding-left : 3px; - - color: #282828; - - font-family: "ProximaNova-Regular"; - - font-size: 20px; - - font-weight: 500; - -} - -QLabel#tcpLable, QLabel#rtuLable{ - - background-color: #f2f2f2; - - border: none; - -/* width: 239px; - - height: 40px; -*/ - - width: 200px; - - height: 40px; - - border-radius: 5px; - - padding-left : 3px; - - color: #282828; - - font-family: "ProximaNova-Regular"; - - font-size: 25px; - - font-weight: 500; - -} - -QLabel#registerlabel{ - width: 308px; - - height: 16px; - - color: #5b5d6a; - - font-family: "PingFangSC-Regular"; - + selection-background-color: #E3F2FD; + selection-color: #1976D2; + outline: none; font-size: 13px; - - font-weight: 400; - - line-height: 16px; - - text-align: center; -} - -QLabel#registerlabel_2, QLabel#registerlabel_1{ - width: 36px; - - height: 19px; - - color: #ffffff; - - font-family: "PingFangSC-Medium"; - - font-size: 15px; - - font-weight: 400; - - line-height: 19px; - -} - - -QLineEdit#searchEdit{ - - background-color: #4b8cf2; - - border : none; - - padding-left : 15px; - - padding-top : 5px; - - padding-bottom : 5px; - - color: #ffffff; - - font-family: Lato; - -} - -QLineEdit#userEdit, QLineEdit#pwdEdit{ - - background-color: #f5f8f9; - - border: none; - - width: 239px; - - height: 40px; - - border-radius: 5px; - - padding-left : 10px; - - color: #889399; - - font-family: ".SFNSDisplay-Regular"; - - font-size: 13.26px; - - font-weight: 520; -} -QLineEdit#setEdit{ - - background-color: #ffffff; - - border: 1px solid #dddddd; - - min-width: 180px; - - min-height: 29px; - - border-radius: 5px; - - padding-left : 10px; - - color: #889399; - - font-family: ".SFNSDisplay-Regular"; - - font-size: 20px; - - font-weight: 520; -} - -QLineEdit#registerlineEdit{ - width: 360px; - - height: 38px; - - border-radius: 8px; - - background: #21232f; - - color: #5b5d6a; - - font-family: "PingFangSC-Regular"; - - font-size: 18px; - - font-weight: 400; - - line-height: 13px; -} - -QTableView#varView{ - - /*alternate-background-color:#cccccc;*/ - - background-color: #ffffff; - - selection-background-color: #AAAAAA; - - selection-color: #1A1A1A; - - border: none; - - font: 20px "PingFangSC-Regular"; - - color: #1A1A1A; - -} - - - - -QTableView#varView::item{ - - /*border : 0px;*/ - - /*margin-bottom: 4px;*/ - - /*margin-top: 4px;*/ - - /*background: #ffffff;*/ - - font-family: 20px "PingFangSC-Regular"; - -} -QTreeView{ - font-size: 15px; - -} - -QTreeView::item{ - height: 25px; - font-family: "PingFangSC-Regular"; - -} - -QTableView#userView, QTableView#projectView{ - - border: 0px; - - background-color: #F5F5F5; - - font: 20px "PingFangSC-Regular"; - - padding-left: 10px; - - padding-right: 7px; - -} - - -QTableView#userView::item,QTableView#projectView::item{ - - border : none; - - margin-bottom: 3px; - - margin-top: 3px; - - font-family: "PingFangSC-Regular"; - -} - -QTableView#userView::item{ - - background-color: #FFFFFF; - -} - -QHeaderView::section { - font-size: 10px; -} - -QHeaderView::section{ - color: #ffffff; - font-size: 23px; - font-family: "PingFangSC-Regular"; - font-weight: 400; - text-align:center; - height:35px; - background-color: #666666; - border-color: #D8D8D8; - /*border: none;*/ - /*outline:none*/ -} - -QHeaderView#paramHeader::section{ - - color: #ffffff; - - font-size: 18px; - - font-family: "PingFangSC-Regular"; - - font-weight: 400; - - text-align:center; - - height:35px; - - background-color: #666666; - - border-color: #D8D8D8; - - /*border: none;*/ - - /*outline:none*/ - -} - -QHeaderView{ - - border-color: #D8D8D8; - + padding: 4px; } -QHeaderView#userHeader::section, QHeaderView#projectHeader::section{ - - color: #112E53; - - font-size: 23px; - - font-family: "PingFangSC-Regular"; - - font-weight: 400; - - text-align:center; - - height:42px; - - background-color: #F5F5F5; - - border-color: #D8D8D8; - - border: none; - - outline:none; - - padding-top: 7px; - +QListWidget#trendVarListWidget::item { + padding: 8px 12px; + border-radius: 4px; + margin: 1px; + color: #374151; } -QHeaderView#userHeader, QHeaderView#projectHeader{ - - border-color: #ffffff; - +QListWidget#trendVarListWidget::item:hover { + background-color: #F3F4F6; + color: #1F2937; } -QScrollBar:vertical{ - - margin-left:5px; - - margin-top:35px; - - width: 10px; - - background:#FFFFFF; - +QListWidget#trendVarListWidget::item:selected { + background-color: #E3F2FD; + color: #1976D2; + font-weight: bold; } -QScrollBar::handle:vertical{ - - background:#5c5858; - -} - -QScrollBar::handle:vertical:hover{ - - background:#959595; - -} - -QScrollBar::add-line:vertical{ - - background:#FFFFFF; - -} - -QScrollBar::sub-line:vertical{ - - background:#FFFFFF; - +/* 时间范围组 */ +QGroupBox#trendTimeGroupBox { + font-size: 13px; + font-weight: bold; + color: #374151; + border: 2px solid #E5E7EB; + border-radius: 6px; + margin-top: 10px; + padding-top: 6px; + background-color: #FFFFFF; } -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical{ - - background:#FFFFFF; - +QGroupBox#trendTimeGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 6px 0 6px; + color: #059669; + font-weight: bold; } -QCheckBox#userBox::indicator{ - - width: 30px; height: 30px; - +/* 快速时间范围下拉框 */ +QComboBox#trendQuickRangeCombo { + background-color: #FFFFFF; + border: 2px solid #E5E7EB; + border-radius: 6px; + padding: 6px 8px; + font-size: 13px; + min-height: 24px; + color: #374151; } - -QCheckBox#userBox::indicator:checked{ - - image: url(./Static/checkoff.png); +QComboBox#trendQuickRangeCombo:hover { + border-color: #059669; + background-color: #F0FDF4; } - -QCheckBox#userBox::indicator:enabled:unchecked{ - - image: url(./Static/checkon.png); +QComboBox#trendQuickRangeCombo::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 20px; + border-left-width: 1px; + border-left-color: #E5E7EB; + border-left-style: solid; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + background-color: #F8F9FA; } -QComboBox#setBox{ - - background-color: #ffffff; - - border: 1px solid #dddddd; - - min-width: 180px; - - min-height: 29px; - - border-radius: 5px; - - padding-left : 10px; - - color: #889399; - - font-family: "SFNSDisplay-Regular"; - - font-size: 20px; - - font-weight: 520; - +QComboBox#trendQuickRangeCombo::down-arrow { + image: url(Static/down.png); + width: 12px; + height: 8px; } -QComboBox#ProTypeBox{ - border: 0px; - - background-color: #FFFFFF; - - font: 20px "PingFangSC-Regular"; - - padding-left: 7px; - - margin-top: 6.5px; - - padding-right: 7px; - - width: 400px; - - height: 43px; - +QComboBox#trendQuickRangeCombo QAbstractItemView { + background-color: #FFFFFF; + color: #374151; + border: 1px solid #E5E7EB; + border-radius: 4px; + selection-background-color: #F0FDF4; + selection-color: #059669; + outline: none; + font-size: 13px; } -QComboBox#timeBox{ - - border: 0px; - - font: 22px "PingFangSC-Regular"; - - width: 40px; - - height: 20px; - +/* 时间编辑器 */ +QDateTimeEdit#trendStartTimeEdit, QDateTimeEdit#trendEndTimeEdit { + background-color: #FFFFFF; + border: 2px solid #E5E7EB; + border-radius: 6px; + padding: 6px 8px; + font-size: 13px; + color: #374151; + min-height: 24px; } -QComboBox#TcRtdTypeBox,QComboBox#ModbusTypeBox { - - border: 0px; - - padding-right: 7px; - - width: 400px; - - height: 40px; - - font: 15px "PingFangSC-Regular"; - +QDateTimeEdit#trendStartTimeEdit:focus, QDateTimeEdit#trendEndTimeEdit:focus { + border-color: #059669; + background-color: #F0FDF4; } -QComboBox#setBox::drop-down, QComboBox#ProTypeBox::drop-down,QComboBox#TcRtdTypeBox::drop-down, QComboBox#ModbusTypeBox::drop-down{ - - subcontrol-origin: padding; - - subcontrol-position: top right; - - width: 15px; - - border:none; +QDateTimeEdit#trendStartTimeEdit:hover, QDateTimeEdit#trendEndTimeEdit:hover { + border-color: #9CA3AF; } -QComboBox#timeBox::drop-down{ - - subcontrol-origin: padding; - - subcontrol-position: top right; - - width: 25px; - - border:none; +/* 操作按钮组 */ +QGroupBox#trendButtonGroupBox { + font-size: 13px; + font-weight: bold; + color: #374151; + border: 2px solid #E5E7EB; + border-radius: 6px; + margin-top: 10px; + padding-top: 6px; + background-color: #FFFFFF; } -QComboBox#setBox::drop-down, QComboBox#ProTypeBox::drop-down,QComboBox#TcRtdTypeBox::drop-down, QComboBox#ModbusTypeBox::drop-down, QComboBox#timeBox::drop-down{ - - image: url(./Static/down.png); - +QGroupBox#trendButtonGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 6px 0 6px; + color: #059669; + font-weight: bold; } -QComboBox#setBox QAbstractItemView{ - +/* 趋势操作按钮 */ +QToolButton#trendQueryBtn { + background-color: #2277EF; + color: #FFFFFF; border: none; + border-radius: 6px; + padding: 12px 16px; + font-size: 13px; + font-weight: bold; + min-height: 40px; + min-width: 120px; +} - font-size: 20px; - - outline: 0px; - - font-family: ".SFNSDisplay-Regular"; - - selection-background-color: white; +QToolButton#trendQueryBtn:hover { + background-color: #1D4ED8; + border: 2px solid #1E40AF; +} - font-weight: 520; +QToolButton#trendQueryBtn:pressed { + background-color: #1E40AF; + border: 2px solid #1D4ED8; +} +QToolButton#trendRefreshBtn { + background-color: #059669; + color: #FFFFFF; + border: none; + border-radius: 6px; + padding: 12px 16px; + font-size: 13px; + font-weight: bold; + min-height: 40px; + min-width: 120px; } -QComboBox#ProTypeBox QAbstractItemView::item:hover{ - background-color:#7cceff; - color: #889399; +QToolButton#trendRefreshBtn:hover { + background-color: #047857; + border: 2px solid #065F46; } -QComboBox#ProTypeBox QAbstractItemView{ - text-align: center; +QToolButton#trendRefreshBtn:pressed { + background-color: #065F46; + border: 2px solid #047857; +} +QToolButton#trendAddBtn { + background-color: #7C3AED; + color: #FFFFFF; + border: none; + border-radius: 6px; + padding: 12px 16px; + font-size: 13px; + font-weight: bold; + min-height: 40px; + min-width: 120px; } -QComboBox#setBox QAbstractItemView::item{ +QToolButton#trendAddBtn:hover { + background-color: #6D28D9; + border: 2px solid #5B21B6; +} - background-color:white; +QToolButton#trendAddBtn:pressed { + background-color: #5B21B6; + border: 2px solid #6D28D9; +} - height: 49px; +QToolButton#trendClearBtn { + background-color: #DC2626; + color: #FFFFFF; + border: none; + border-radius: 6px; + padding: 12px 16px; + font-size: 13px; + font-weight: bold; + min-height: 40px; + min-width: 120px; +} - font-size: 20px; +QToolButton#trendClearBtn:hover { + background-color: #B91C1C; + border: 2px solid #991B1B; +} - padding-left : 10px; +QToolButton#trendClearBtn:pressed { + background-color: #991B1B; + border: 2px solid #B91C1C; +} - color: #889399; +/* 趋势图查看器组 */ +QWidget#trendViewerGroup { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 8px; +} - font-family: ".SFNSDisplay-Regular"; +/* 图表组 */ +QGroupBox#trendChartGroup { + font-size: 16px; + font-weight: bold; + color: #1F2937; + border: 2px solid #E5E7EB; + border-radius: 8px; + margin-top: 12px; + padding-top: 8px; + background-color: #FFFFFF; +} - font-weight: 520; +QGroupBox#trendChartGroup::title { + subcontrol-origin: margin; + left: 12px; + padding: 0 8px 0 8px; + color: #2277EF; + font-weight: bold; +} +/* 信息状态组 */ +QGroupBox#trendInfoGroup { + font-size: 13px; + font-weight: bold; + color: #374151; + border: 2px solid #E5E7EB; + border-radius: 6px; + margin-top: 8px; + padding-top: 6px; + background-color: #F8F9FA; } -QComboBox#setBox QAbstractItemView::item:hover{ +QGroupBox#trendInfoGroup::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 6px 0 6px; + color: #6B7280; + font-weight: bold; +} - background-color:#7cceff; +/* 状态标签 */ +QLabel#trendVarNameLabel { + color: #1F2937; + font-size: 13px; + font-weight: bold; + padding: 4px 8px; + background-color: #E3F2FD; + border-radius: 4px; +} - color:white; +QLabel#trendDataCountLabel { + color: #059669; + font-size: 13px; + font-weight: bold; + padding: 4px 8px; + background-color: #D1FAE5; + border-radius: 4px; +} - font-size: 28px; +QLabel#trendTimeRangeLabel { + color: #7C3AED; + font-size: 13px; + font-weight: bold; + padding: 4px 8px; + background-color: #F3E8FF; + border-radius: 4px; +} - padding-left : 10px; +QLabel#trendStatusLabel { + color: #DC2626; + font-size: 13px; + font-weight: bold; + padding: 4px 8px; + background-color: #FEF2F2; + border-radius: 4px; +} - color: #889399; +/* 信息气泡 */ +QLabel#trendInfoBubble { + background-color: rgba(0, 0, 0, 200); + color: #FFFFFF; + border: none; + border-radius: 6px; + padding: 8px 12px; + font-size: 12px; + font-weight: bold; +} - font-family: ".SFNSDisplay-Regular"; +/* ==================== 实时趋势界面样式 ==================== */ - font-weight: 520; +/* 实时趋势主窗口 */ +ActualTrend { + background-color: #F8F9FA; + border: 1px solid #E5E7EB; + border-radius: 8px; +} +/* 实时趋势工具栏 */ +ActualTrend QToolBar { + background-color: #FFFFFF; + border: 1px solid #E5E7EB; + border-radius: 6px; + spacing: 4px; + padding: 4px; } -QComboBox#timeBox QAbstractItemView::item:hover{ +ActualTrend QToolBar::handle { + background-color: #9CA3AF; + width: 8px; + border-radius: 4px; + margin: 4px; +} - background-color:#7cceff; - color: #889399; +ActualTrend QToolBar::separator { + background-color: #E5E7EB; + width: 1px; + margin: 4px; +} +/* 实时趋势画布容器 */ +ActualTrend QWidget { + background-color: #FFFFFF; + border-radius: 6px; } +/* ==================== 趋势界面通用样式 ==================== */ -QTextEdit#mesEdit{ - border: 2px solid #dddddd; - border-radius: 8px; - font-family: ".SFNSDisplay-Regular"; - font-size: 20px; +/* 所有趋势相关的滚动条 */ +QWidget[objectName*="trend"] QScrollBar:vertical { + background-color: #F3F4F6; + width: 12px; + border-radius: 6px; + margin: 0; } -QListView#trendListView{ - - font: 22px "PingFangSC-Regular"; +QWidget[objectName*="trend"] QScrollBar::handle:vertical { + background-color: #9CA3AF; + border-radius: 6px; + min-height: 20px; + margin: 2px; +} +QWidget[objectName*="trend"] QScrollBar::handle:vertical:hover { + background-color: #6B7280; } -QListView#trendListView::item{ +QWidget[objectName*="trend"] QScrollBar::add-line:vertical, +QWidget[objectName*="trend"] QScrollBar::sub-line:vertical { + height: 0px; +} - margin-bottom: 10px; +/* 趋势界面的分割器 */ +QWidget[objectName*="trend"] QSplitter::handle { + background-color: #E5E7EB; + border-radius: 2px; +} +QWidget[objectName*="trend"] QSplitter::handle:hover { + background-color: #9CA3AF; } -/* 历史趋势界面样式 */ -QWidget#trendMainWidget { - background-color: #F5F5F5; - border-radius: 8px; +/* 趋势界面的复选框 */ +QWidget[objectName*="trend"] QCheckBox { + color: #374151; + font-size: 13px; + spacing: 8px; } -QGroupBox#trendVariableListGroup { - font: bold 16px "PingFangSC-Medium"; - color: #2277EF; - border: 2px solid #E0E0E0; - border-radius: 8px; - margin-top: 12px; - padding-top: 8px; +QWidget[objectName*="trend"] QCheckBox::indicator { + width: 16px; + height: 16px; + border-radius: 3px; + border: 2px solid #9CA3AF; background-color: #FFFFFF; } -QGroupBox#trendVariableListGroup::title { - subcontrol-origin: margin; - left: 10px; - padding: 0 8px 0 8px; - background-color: #FFFFFF; +QWidget[objectName*="trend"] QCheckBox::indicator:hover { + border-color: #2277EF; } -QGroupBox#trendTimeGroupBox, QGroupBox#trendButtonGroupBox, QGroupBox#trendInfoGroup { - font: bold 14px "PingFangSC-Medium"; - color: #555555; - border: 1px solid #D0D0D0; - border-radius: 6px; - margin-top: 10px; - padding-top: 6px; - background-color: #FAFAFA; +QWidget[objectName*="trend"] QCheckBox::indicator:checked { + background-color: #2277EF; + border-color: #2277EF; + image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iOSIgdmlld0JveD0iMCAwIDEyIDkiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xIDQuNUw0LjUgOEwxMSAxIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L3N2Zz4K); } -QGroupBox#trendTimeGroupBox::title, QGroupBox#trendButtonGroupBox::title { - subcontrol-origin: margin; - left: 8px; - padding: 0 6px 0 6px; - background-color: #FAFAFA; +/* 趋势界面的标签通用样式 */ +QWidget[objectName*="trend"] QLabel { + color: #374151; + font-size: 13px; } -QLineEdit#trendSearchInput { - border: 2px solid #E0E0E0; - border-radius: 6px; - padding: 8px 12px; - font: 14px "PingFangSC-Regular"; +/* 趋势界面的输入框通用样式 */ +QWidget[objectName*="trend"] QLineEdit { background-color: #FFFFFF; - color: #333333; + border: 1px solid #D1D5DB; + border-radius: 4px; + padding: 6px 8px; + font-size: 13px; + color: #374151; } -QLineEdit#trendSearchInput:focus { +QWidget[objectName*="trend"] QLineEdit:focus { border-color: #2277EF; - background-color: #F8F9FF; -} - -QListWidget#trendVarListWidget { - border: 1px solid #E0E0E0; - border-radius: 6px; - background-color: #FFFFFF; - font: 14px "PingFangSC-Regular"; - selection-background-color: #E3F2FD; - selection-color: #1976D2; + outline: none; } -QListWidget#trendVarListWidget::item { - padding: 8px 12px; - border-bottom: 1px solid #F0F0F0; +/* 趋势界面的按钮通用悬停效果 */ +QWidget[objectName*="trend"] QPushButton:hover, +QWidget[objectName*="trend"] QToolButton:hover { + border: 2px solid #9CA3AF; } -QListWidget#trendVarListWidget::item:hover { - background-color: #F5F5F5; +QWidget[objectName*="trend"] QPushButton:pressed, +QWidget[objectName*="trend"] QToolButton:pressed { + border: 2px solid #6B7280; } -QListWidget#trendVarListWidget::item:selected { - background-color: #E3F2FD; - color: #1976D2; -} +/* ==================== 表格整行Hover样式 ==================== */ -QComboBox#trendQuickRangeCombo { - border: 1px solid #D0D0D0; - border-radius: 4px; - padding: 6px 8px; - font: 13px "PingFangSC-Regular"; +/* 通用表格样式 - 整行hover效果 */ +QTableView { + selection-background-color: #E3F2FD; + selection-color: #1976D2; + gridline-color: #E5E7EB; background-color: #FFFFFF; - min-width: 120px; + alternate-background-color: #F8F9FA; + outline: none; } -QComboBox#trendQuickRangeCombo::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - width: 20px; +/* 表格项整行hover效果 */ +QTableView::item { + padding: 8px; border: none; + color: #374151; } -QComboBox#trendQuickRangeCombo::down-arrow { - image: url(./Static/down.png); - width: 12px; - height: 12px; +QTableView::item:hover { + background-color: #F3F4F6; + color: #1F2937; } -QDateTimeEdit#trendStartTimeEdit, QDateTimeEdit#trendEndTimeEdit { - border: 1px solid #D0D0D0; - border-radius: 4px; - padding: 6px 8px; - font: 13px "PingFangSC-Regular"; - background-color: #FFFFFF; +QTableView::item:selected { + background-color: #E3F2FD; + color: #1976D2; } -QDateTimeEdit#trendStartTimeEdit:focus, QDateTimeEdit#trendEndTimeEdit:focus { - border-color: #2277EF; +QTableView::item:selected:hover { + background-color: #BBDEFB; + color: #1565C0; } -QToolButton#trendQueryBtn, QToolButton#trendRefreshBtn, -QToolButton#trendAddBtn, QToolButton#trendClearBtn { - border: 2px solid #2277EF; - border-radius: 6px; - padding: 8px 16px; - font: bold 14px "PingFangSC-Medium"; - color: #2277EF; +/* 变量表格特定样式 */ +QTableView#varView { + selection-background-color: #E3F2FD; + selection-color: #1976D2; + gridline-color: #E5E7EB; background-color: #FFFFFF; - min-height: 32px; + outline: none; + show-decoration-selected: 1; } -QToolButton#trendQueryBtn:hover, QToolButton#trendRefreshBtn:hover, -QToolButton#trendAddBtn:hover, QToolButton#trendClearBtn:hover { - background-color: #2277EF; - color: #FFFFFF; +QTableView#varView::item { + padding: 6px 8px; + border: none; + color: #374151; } -QToolButton#trendQueryBtn:pressed, QToolButton#trendRefreshBtn:pressed, -QToolButton#trendAddBtn:pressed, QToolButton#trendClearBtn:pressed { - background-color: #1A5FCC; - border-color: #1A5FCC; +QTableView#varView::item:hover { + background-color: #F0F9FF; + color: #1F2937; } -QWidget#trendViewerGroup { - background-color: #FFFFFF; - border-radius: 8px; +QTableView#varView::item:selected { + background-color: #E3F2FD; + color: #1976D2; } -QGroupBox#trendChartGroup { - font: bold 16px "PingFangSC-Medium"; - color: #2277EF; - border: 2px solid #E0E0E0; - border-radius: 8px; - margin-top: 12px; - padding-top: 8px; - background-color: #FFFFFF; +QTableView#varView::item:selected:hover { + background-color: #BBDEFB; + color: #1565C0; } -QGroupBox#trendChartGroup::title { - subcontrol-origin: margin; - left: 10px; - padding: 0 8px 0 8px; +/* 项目表格样式 */ +QTableView#projectTable { + selection-background-color: #F0FDF4; + selection-color: #059669; + gridline-color: #E5E7EB; background-color: #FFFFFF; + outline: none; + show-decoration-selected: 1; } -QGroupBox#trendInfoGroup { - font: bold 14px "PingFangSC-Medium"; - color: #555555; - border: 1px solid #E0E0E0; - border-radius: 6px; - margin-top: 8px; - padding-top: 6px; - background-color: #F8F9FA; -} - -QGroupBox#trendInfoGroup::title { - subcontrol-origin: margin; - left: 8px; - padding: 0 6px 0 6px; - background-color: #F8F9FA; +QTableView#projectTable::item { + padding: 8px; + border: none; + color: #374151; } -QLabel#trendVarNameLabel, QLabel#trendDataCountLabel, -QLabel#trendTimeRangeLabel, QLabel#trendStatusLabel { - font: 13px "PingFangSC-Regular"; - color: #666666; - padding: 4px 8px; +QTableView#projectTable::item:hover { + background-color: #F0FDF4; + color: #1F2937; } -QLabel#trendStatusLabel { - color: #2277EF; - font-weight: bold; +QTableView#projectTable::item:selected { + background-color: #D1FAE5; + color: #059669; } -QLabel#trendInfoBubble { - background: rgba(255, 255, 255, 0.95); - border: 2px solid #2277EF; - border-radius: 8px; - padding: 8px 12px; - font: 12px "Consolas", "Monaco", monospace; - color: #333333; +QTableView#projectTable::item:selected:hover { + background-color: #A7F3D0; + color: #047857; } - - -QMessageBox { - background-color: #ffffff; - border-radius: 16px; - border: 2px solid #3498db; - min-width: 400px; - min-height: 180px; - font-size: 18px; - padding: 24px; +/* 用户表格样式 */ +QTableView#userTable { + selection-background-color: #FEF3C7; + selection-color: #D97706; + gridline-color: #E5E7EB; + background-color: #FFFFFF; + outline: none; + show-decoration-selected: 1; } -QMessageBox QLabel { - color: #222; - font-size: 20px; - font-weight: bold; - padding: 16px 0 8px 0; +QTableView#userTable::item { + padding: 8px; + border: none; + color: #374151; } -QMessageBox QPushButton { - min-width: 120px; - min-height: 40px; - font-size: 18px; - border-radius: 8px; - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3498db, stop:1 #2980b9); - color: #fff; - font-weight: bold; - margin: 0 12px; +QTableView#userTable::item:hover { + background-color: #FFFBEB; + color: #1F2937; } -QMessageBox QPushButton:hover { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #5dade2, stop:1 #3498db); +QTableView#userTable::item:selected { + background-color: #FEF3C7; + color: #D97706; } -QMessageBox QPushButton:pressed { - background: #2980b9; +QTableView#userTable::item:selected:hover { + background-color: #FDE68A; + color: #B45309; } -/* Control System Styles */ -QWidget#trendMainWidget { - background-color: #F5F5F5; - border-radius: 8px; +/* 趋势变量列表样式 */ +QListWidget#trendVarListWidget::item:hover { + background-color: #F0F9FF; + color: #1F2937; + border-radius: 4px; } -QGroupBox#trendVariableListGroup, QGroupBox#trendChartGroup { - font: bold 16px "PingFangSC-Medium"; - color: #2277EF; - border: 2px solid #E0E0E0; - border-radius: 8px; - margin-top: 12px; - padding-top: 8px; - background-color: #FFFFFF; +QListWidget#trendVarListWidget::item:selected { + background-color: #E3F2FD; + color: #1976D2; + border-radius: 4px; } -QGroupBox#trendTimeGroupBox, QGroupBox#trendButtonGroupBox, QGroupBox#trendInfoGroup { - font: bold 14px "PingFangSC-Medium"; - color: #555555; - border: 1px solid #D0D0D0; - border-radius: 6px; - margin-top: 10px; - padding-top: 6px; - background-color: #FAFAFA; +QListWidget#trendVarListWidget::item:selected:hover { + background-color: #BBDEFB; + color: #1565C0; + border-radius: 4px; } -QHeaderView::section { - font-size: 10px; -} diff --git a/UI/Main/Main.py b/UI/Main/Main.py index f4cefac..452c33b 100644 --- a/UI/Main/Main.py +++ b/UI/Main/Main.py @@ -174,9 +174,9 @@ class MainWindow(QMainWindow): self.horizontalLayout.addWidget(self.leftWidget) self.horizontalLayout.addWidget(self.rightWidget) self.horizontalLayout.setStretch(0, 1) - self.horizontalLayout.setStretch(1, 7) + self.horizontalLayout.setStretch(1, 10) self.horizontalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout.setSpacing(5) + self.horizontalLayout.setSpacing(1) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.addWidget(self.topWidget) self.verticalLayout.addLayout(self.horizontalLayout) diff --git a/UI/Main/MainLeft.py b/UI/Main/MainLeft.py index 0275ea0..623d217 100644 --- a/UI/Main/MainLeft.py +++ b/UI/Main/MainLeft.py @@ -2,6 +2,7 @@ from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QWidget, QSizePolicy from PyQt5.QtGui import QIcon +import qtawesome as qta class MainLeft(QWidget): def __init__(self): @@ -11,12 +12,10 @@ class MainLeft(QWidget): def setupUi(self): + # 创建导航按钮 self.createProject = QtWidgets.QPushButton(self) self.createProject.setObjectName("createProject") - # self.openProject = QtWidgets.QPushButton(self) - # self.openProject.setObjectName("openProject") - self.varMag = QtWidgets.QPushButton(self) self.varMag.setObjectName("varMag") @@ -30,162 +29,211 @@ class MainLeft(QWidget): self.protocolMag.setObjectName("protocolMag") self.procedureMag = QtWidgets.QPushButton(self) - self.procedureMag.setObjectName("protocolMag") + self.procedureMag.setObjectName("procedureMag") self.controlMag = QtWidgets.QPushButton(self) self.controlMag.setObjectName("controlMag") + # 创建主布局 self.verticalLayout = QtWidgets.QVBoxLayout(self) - self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setContentsMargins(16, 24, 16, 24) + self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") - spacerItem = QtWidgets.QSpacerItem(40, 10, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) + # 顶部间距 + topSpacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + self.verticalLayout.addItem(topSpacer) + + # 添加导航按钮 self.verticalLayout.addWidget(self.createProject) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) - # self.verticalLayout.addWidget(self.openProject) - # self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.varMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.trendMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.userMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.protocolMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.procedureMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) self.verticalLayout.addWidget(self.controlMag) - self.verticalLayout.addWidget(QtWidgets.QSplitter()) - - self.verticalLayout.setStretch(0, 1) - self.verticalLayout.setStretch(1, 4) - self.verticalLayout.setStretch(2, 2) - self.verticalLayout.setStretch(3, 4) - self.verticalLayout.setStretch(4, 2) - self.verticalLayout.setStretch(5, 4) - self.verticalLayout.setStretch(6, 2) - self.verticalLayout.setStretch(7, 4) - self.verticalLayout.setStretch(8, 2) - self.verticalLayout.setStretch(9, 4) - self.verticalLayout.setStretch(10, 2) - self.verticalLayout.setStretch(11, 4) - self.verticalLayout.setStretch(12, 2) - self.verticalLayout.setStretch(13, 4) - self.verticalLayout.setStretch(14, 35) - QtCore.QMetaObject.connectSlotsByName(self) + # 底部弹性空间 + bottomSpacer = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(bottomSpacer) - self.createProject.setText('工程管理') - # self.openProject.setText("打开工程") - self.varMag.setText("变量管理") - self.trendMag.setText("历史趋势") - self.userMag.setText("用户管理") - self.protocolMag.setText("通讯配置") - self.procedureMag.setText("规程管理") - self.controlMag.setText("控制系统") - - self.createProject.setIcon(QIcon('./Static/newH.png')) - # self.openProject.setIcon(QIcon('./Static/open.png')) - self.varMag.setIcon(QIcon('./Static/varMag.png')) - self.trendMag.setIcon(QIcon('./Static/trend.png')) - self.userMag.setIcon(QIcon('./Static/userMag.png')) - self.protocolMag.setIcon(QIcon('./Static/setting.png')) - self.procedureMag.setIcon(QIcon('./Static/procedure.png')) - self.controlMag.setIcon(QIcon('./Static/control.png')) + QtCore.QMetaObject.connectSlotsByName(self) + # 设置按钮文本 + self.createProject.setText(' 工程管理') + self.varMag.setText(" 变量管理") + self.trendMag.setText(" 历史数据") + self.userMag.setText(" 用户管理") + self.protocolMag.setText(" 通讯配置") + self.procedureMag.setText(" 规程管理") + self.controlMag.setText(" 控制系统") + + # 定义工业控制风格的图标配置 + self.icon_config = { + 'createProject': { + 'icon': 'fa5s.project-diagram', # 项目图标 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'varMag': { + 'icon': 'fa5s.server', # 服务器/变量管理 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'trendMag': { + 'icon': 'fa5s.chart-area', # 面积图/历史趋势 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'userMag': { + 'icon': 'fa5s.user-shield', # 用户管理/权限 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'protocolMag': { + 'icon': 'fa5s.ethernet', # 以太网/通讯协议 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'procedureMag': { + 'icon': 'fa5s.clipboard-list', # 清单/规程管理 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + }, + 'controlMag': { + 'icon': 'fa5s.microchip', # 芯片/控制系统 + 'normal_color': '#6B7280', + 'hover_color': '#2277EF', + 'selected_color': '#FFFFFF' + } + } + + # 设置初始图标(正常状态) + self.setButtonIcon(self.createProject, 'createProject', 'normal') + self.setButtonIcon(self.varMag, 'varMag', 'normal') + self.setButtonIcon(self.trendMag, 'trendMag', 'normal') + self.setButtonIcon(self.userMag, 'userMag', 'normal') + self.setButtonIcon(self.protocolMag, 'protocolMag', 'normal') + self.setButtonIcon(self.procedureMag, 'procedureMag', 'normal') + self.setButtonIcon(self.controlMag, 'controlMag', 'normal') + + # 配置所有按钮 for btn in [self.createProject, self.varMag, self.trendMag, self.userMag, self.protocolMag, self.procedureMag, self.controlMag]: self.setBtn(btn) - # self.openProject.clicked.connect(lambda:self.openProject.setIcon(QIcon('./Static/openH.png'))) - self.createProject.clicked.connect(lambda:self.createProject.setIcon(QIcon('./Static/newH.png'))) - self.varMag.clicked.connect(lambda:self.varMag.setIcon(QIcon('./Static/varMagH.png'))) - self.trendMag.clicked.connect(lambda:self.trendMag.setIcon(QIcon('./Static/trendH.png'))) - self.userMag.clicked.connect(lambda:self.userMag.setIcon(QIcon('./Static/userMagH.png'))) - self.protocolMag.clicked.connect(lambda:self.protocolMag.setIcon(QIcon('./Static/settingH.png'))) - self.controlMag.clicked.connect(lambda:self.controlMag.setIcon(QIcon('./Static/controlH.png'))) + # 连接点击事件 - 设置选中时的白色图标 + self.createProject.clicked.connect(lambda: self.setSelectedIcon(self.createProject, 'createProject')) + self.varMag.clicked.connect(lambda: self.setSelectedIcon(self.varMag, 'varMag')) + self.trendMag.clicked.connect(lambda: self.setSelectedIcon(self.trendMag, 'trendMag')) + self.userMag.clicked.connect(lambda: self.setSelectedIcon(self.userMag, 'userMag')) + self.protocolMag.clicked.connect(lambda: self.setSelectedIcon(self.protocolMag, 'protocolMag')) + self.procedureMag.clicked.connect(lambda: self.setSelectedIcon(self.procedureMag, 'procedureMag')) + self.controlMag.clicked.connect(lambda: self.setSelectedIcon(self.controlMag, 'controlMag')) + # 设置默认选中状态 self.createProject.setChecked(True) self.createProject.setDown(True) + self.setSelectedIcon(self.createProject, 'createProject') + + def setButtonIcon(self, button, button_key, state): + """设置按钮图标 + Args: + button: 按钮对象 + button_key: 按钮配置键 + state: 状态 ('normal', 'hover', 'selected') + """ + config = self.icon_config[button_key] + icon_name = config['icon'] + + if state == 'normal': + color = config['normal_color'] + elif state == 'hover': + color = config['hover_color'] + elif state == 'selected': + color = config['selected_color'] + else: + color = config['normal_color'] + + # 创建QtAwesome图标 + icon = qta.icon(icon_name, color=color) + button.setIcon(icon) + + def setSelectedIcon(self, button, button_key): + """设置选中按钮的白色图标""" + self.setButtonIcon(button, button_key, 'selected') def setBtn(self, btn): btn.setCheckable(True) - btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) - btn.setAttribute(Qt.WA_Hover,True) + btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + btn.setAttribute(Qt.WA_Hover, True) btn.installEventFilter(self) - btn.setIconSize(QtCore.QSize(26, 26)) + btn.setIconSize(QtCore.QSize(22, 22)) btn.clicked.connect(self.clearButton) btn.setAutoExclusive(True) - btn.clicked.connect(lambda:btn.setDown(True)) - btn.clicked.connect(lambda:btn.setChecked(True)) + btn.clicked.connect(lambda: btn.setDown(True)) + btn.clicked.connect(lambda: btn.setChecked(True)) + + # 设置固定高度和样式 + btn.setFixedHeight(56) + btn.setStyleSheet(""" + QPushButton { + text-align: left; + padding-left: 20px; + border: none; + outline: none; + } + """) def eventFilter(self, object, event): - if object == self.createProject: - if event.type() == QtCore.QEvent.HoverEnter: - self.createProject.setIcon(QIcon('./Static/newH.png')) - return True - if event.type() == QtCore.QEvent.HoverLeave and not self.createProject.isChecked(): - self.createProject.setIcon(QIcon('./Static/new.png')) - return True - # if object == self.openProject: - # if event.type() == QtCore.QEvent.HoverEnter: - # self.openProject.setIcon(QIcon('./Static/openH.png')) - # return True - # if event.type() == QtCore.QEvent.HoverLeave and not self.openProject.isChecked(): - # self.openProject.setIcon(QIcon('./Static/open.png')) - # return True - if object == self.varMag: - if event.type() == QtCore.QEvent.HoverEnter: - self.varMag.setIcon(QIcon('./Static/varMagH.png')) - return True - if event.type() == QtCore.QEvent.HoverLeave and not self.varMag.isChecked(): - self.varMag.setIcon(QIcon('./Static/varMag.png')) - return True - if object == self.trendMag: - if event.type() == QtCore.QEvent.HoverEnter: - self.trendMag.setIcon(QIcon('./Static/trendH.png')) - return True - if event.type() == QtCore.QEvent.HoverLeave and not self.trendMag.isChecked(): - self.trendMag.setIcon(QIcon('./Static/trend.png')) - return True - if object == self.userMag: - if event.type() == QtCore.QEvent.HoverEnter: - self.userMag.setIcon(QIcon('./Static/userMagH.png')) - return True - if event.type() == QtCore.QEvent.HoverLeave and not self.userMag.isChecked(): - self.userMag.setIcon(QIcon('./Static/userMag.png')) - return True - if object == self.protocolMag: - if event.type() == QtCore.QEvent.HoverEnter: - self.protocolMag.setIcon(QIcon('./Static/settingH.png')) - return True - if event.type() == QtCore.QEvent.HoverLeave and not self.protocolMag.isChecked(): - self.protocolMag.setIcon(QIcon('./Static/setting.png')) - return True - if object == self.controlMag: + # 定义按钮与配置键的映射 + button_mapping = { + self.createProject: 'createProject', + self.varMag: 'varMag', + self.trendMag: 'trendMag', + self.userMag: 'userMag', + self.protocolMag: 'protocolMag', + self.procedureMag: 'procedureMag', + self.controlMag: 'controlMag' + } + + if object in button_mapping: + button_key = button_mapping[object] + if event.type() == QtCore.QEvent.HoverEnter: - self.controlMag.setIcon(QIcon('./Static/controlH.png')) + if not object.isChecked(): + self.setButtonIcon(object, button_key, 'hover') return True - if event.type() == QtCore.QEvent.HoverLeave and not self.controlMag.isChecked(): - self.controlMag.setIcon(QIcon('./Static/control.png')) + + elif event.type() == QtCore.QEvent.HoverLeave: + if object.isChecked(): + self.setButtonIcon(object, button_key, 'selected') + else: + self.setButtonIcon(object, button_key, 'normal') return True + return False def clearButton(self): - self.createProject.setDown(False) - # self.openProject.setDown(False) - self.varMag.setDown(False) - self.trendMag.setDown(False) - self.userMag.setDown(False) - self.protocolMag.setDown(False) - self.controlMag.setDown(False) - - self.createProject.setIcon(QIcon('./Static/new.png')) - # self.openProject.setIcon(QIcon('./Static/open.png')) - self.varMag.setIcon(QIcon('./Static/varMag.png')) - self.trendMag.setIcon(QIcon('./Static/trend.png')) - self.userMag.setIcon(QIcon('./Static/userMag.png')) - self.protocolMag.setIcon(QIcon('./Static/setting.png')) - self.controlMag.setIcon(QIcon('./Static/control.png')) + # 重置所有按钮状态 + buttons = [ + (self.createProject, 'createProject'), + (self.varMag, 'varMag'), + (self.trendMag, 'trendMag'), + (self.userMag, 'userMag'), + (self.protocolMag, 'protocolMag'), + (self.procedureMag, 'procedureMag'), + (self.controlMag, 'controlMag') + ] + + for button, button_key in buttons: + button.setDown(False) + # 重置图标为正常状态 + self.setButtonIcon(button, button_key, 'normal') diff --git a/UI/ProjectManages/ProjectModel.py b/UI/ProjectManages/ProjectModel.py index dee4201..e6cbcf0 100644 --- a/UI/ProjectManages/ProjectModel.py +++ b/UI/ProjectManages/ProjectModel.py @@ -71,11 +71,11 @@ class ProjectTableModel(QAbstractTableModel): if role == Qt.BackgroundRole: if str(self.datas[QModelIndex.row()][1]) == str(Globals.getValue('currentPro')): return QtGui.QColor('#00FF7F') - return QtGui.QColor('#FFFFFF') - if role == Qt.TextColorRole: - if QModelIndex.column() == 6: - return QtGui.QColor('#000000') - return QtGui.QColor('#1A1A1A') + # return QtGui.QColor('#FFFFFF') + # if role == Qt.TextColorRole: + # if QModelIndex.column() == 6: + # return QtGui.QColor('#000000') + # return QtGui.QColor('#1A1A1A') if role == Qt.CheckStateRole: if QModelIndex.column() == 0: return Qt.Checked if self.checkList[QModelIndex.row()] == 'Checked' else Qt.Unchecked @@ -146,7 +146,24 @@ class ProjectButtonDelegate(QItemDelegate): def __init__(self, parent=None): super(ProjectButtonDelegate, self).__init__(parent) + def sizeHint(self, option, index): + """设置项目的尺寸提示,确保足够的高度""" + size = super().sizeHint(option, index) + size.setHeight(max(60, size.height())) # 最小高度60px + return size + def paint(self, painter, option, index): + # 首先绘制背景颜色,与BackgroundDelegate保持一致 + if index.data(QtCore.Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + # 减少高度调整,保持更多的绘制区域 + paint_rect = option.rect + paint_rect.setHeight(max(56, height - 4)) # 最小保持56px高度 + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(QtCore.Qt.BackgroundRole)) + + # 然后创建按钮部件 if not self.parent().indexWidget(index): button1 = QPushButton( qtawesome.icon('fa.exchange', color='#1fbb6f'), @@ -195,6 +212,14 @@ class ProjectButtonDelegate(QItemDelegate): widget = QWidget() widget.setLayout(h_box_layout) widget.setObjectName('projectBtnWidget') + + # 设置widget的背景为透明,让底层的背景颜色显示出来 + widget.setStyleSheet(""" + QWidget#projectBtnWidget { + background-color: transparent; + } + """) + self.parent().setIndexWidget( index, widget @@ -312,11 +337,20 @@ class BackgroundDelegate(QtWidgets.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(60, 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(height - 7) - option.rect.moveTop(top + 6) + # 减少高度调整,保持更多的绘制区域 + option.rect.setHeight(max(56, height - 4)) # 最小保持56px高度 + option.rect.moveTop(top + 2) painter.fillRect(option.rect, index.data(QtCore.Qt.BackgroundRole)) super().paint(painter, option, index) diff --git a/UI/ProjectManages/ProjectTable.py b/UI/ProjectManages/ProjectTable.py index f5444be..4910401 100644 --- a/UI/ProjectManages/ProjectTable.py +++ b/UI/ProjectManages/ProjectTable.py @@ -13,15 +13,23 @@ class ProjectTableView(QTableView): self.setItemDelegateForColumn(4, ProjectButtonDelegate(self)) self.setShowGrid(False) self.setSelectionBehavior(QAbstractItemView.SelectRows) - self.model = ProjectTableModel([' ID', '工程名称', '工程描述', '创建时间', '操作'],[], table = self) + self.model = ProjectTableModel([' ID', '工程名称', '工程描述', '创建时间', '操作'],[], table = self) self.setModel(self.model) - self.header = CheckBoxHeader(changeY = True) + self.header = CheckBoxHeader(changeY = -15) self.header.setStretchLastSection(True) self.header.setSectionResizeMode(QHeaderView.Stretch) self.header.checkClicked.connect(self.model.headerClick) self.header.setObjectName('projectHeader') self.setHorizontalHeader(self.header) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.verticalHeader().setDefaultSectionSize(50) + self.verticalHeader().hide() self.header.setSectionResizeMode(0, QHeaderView.Fixed) self.header.resizeSection(0, 70) + + # 设置行高 + self.verticalHeader().setDefaultSectionSize(60) + self.verticalHeader().setMinimumSectionSize(60) + + # 设置表格属性 + self.setAlternatingRowColors(False) + self.setWordWrap(True) diff --git a/UI/Setting/SearchDeviceWidget.py b/UI/Setting/SearchDeviceWidget.py index 43c847c..600a8c1 100644 --- a/UI/Setting/SearchDeviceWidget.py +++ b/UI/Setting/SearchDeviceWidget.py @@ -160,11 +160,20 @@ class DeviceMasterWidget(QMainWindow): serverLayout.addWidget(self.publicServerInput) publicLayout.addLayout(serverLayout) - # 启动公网服务端按钮 - self.publicConnectButton = QPushButton('启动公网服务端模式') + # 公网服务端控制按钮 + serverButtonLayout = QHBoxLayout() + self.publicConnectButton = QPushButton('启动公网服务端') self.publicConnectButton.clicked.connect(self.startPublicServer) self.publicConnectButton.setObjectName('setButton') - publicLayout.addWidget(self.publicConnectButton) + serverButtonLayout.addWidget(self.publicConnectButton) + + self.publicDisconnectButton = QPushButton('关闭公网服务端') + self.publicDisconnectButton.clicked.connect(self.stopPublicServer) + self.publicDisconnectButton.setObjectName('setButton') + self.publicDisconnectButton.setEnabled(False) + serverButtonLayout.addWidget(self.publicDisconnectButton) + + publicLayout.addLayout(serverButtonLayout) # 公网客户端扫描区域 publicLayout.addWidget(QLabel('公网服务器客户端列表:')) @@ -276,10 +285,39 @@ class DeviceMasterWidget(QMainWindow): # 设置为服务端模式 Globals.getValue('protocolManage').closeClient() Globals.getValue('protocolManage').setServerMode(rabbitmqHost=serverIp) + + # 更新按钮状态 + self.publicConnectButton.setEnabled(False) + self.publicDisconnectButton.setEnabled(True) + QMessageBox.information(self, '成功', f'已启动公网服务端模式\n服务器地址: {serverIp}') except Exception as e: QMessageBox.critical(self, '失败', f'启动公网服务端模式失败: {str(e)}') + def stopPublicServer(self): + """关闭公网服务端模式""" + try: + # 关闭服务端模式 + Globals.getValue('protocolManage').closeServer() + + # 更新按钮状态 + self.publicConnectButton.setEnabled(True) + self.publicDisconnectButton.setEnabled(False) + + # 清空客户端列表 + self.publicClientList.clear() + self.publicClients = [] + self.connectPublicButton.setEnabled(False) + + # 停止自动刷新 + if self.autoRefreshEnabled: + self.autoRefreshCheckBox.setChecked(False) + self.toggleAutoRefresh(0) + + QMessageBox.information(self, '成功', '已关闭公网服务端模式') + except Exception as e: + QMessageBox.critical(self, '失败', f'关闭公网服务端模式失败: {str(e)}') + def scanPublicClients(self): """扫描公网服务器上的客户端""" serverIp = self.publicServerInput.text().strip() @@ -485,9 +523,15 @@ class DeviceMasterWidget(QMainWindow): protocolManage = Globals.getValue('protocolManage') mode = '无' info = '无' + + # 重置按钮状态 + isServerMode = False + isClientMode = False + if protocolManage: if hasattr(protocolManage, 'RpcServer') and protocolManage.RpcServer: mode = '服务端模式' + isServerMode = True try: clients = protocolManage.RpcServer.getClientNames() clientIpMap = protocolManage.RpcServer.getClientIpMap() if hasattr(protocolManage.RpcServer, 'getClientIpMap') else {} @@ -502,6 +546,7 @@ class DeviceMasterWidget(QMainWindow): self.disconnectList.clear() elif hasattr(protocolManage, 'RpcClient') and protocolManage.RpcClient: mode = '客户端模式' + isClientMode = True try: clientName = protocolManage.RpcClient.clientName serverInfo = protocolManage.RpcClient.rabbitHost @@ -510,6 +555,11 @@ class DeviceMasterWidget(QMainWindow): except: info = '客户端信息获取失败' self.disconnectList.clear() + + # 更新公网服务端按钮状态(主站模式) + self.publicConnectButton.setEnabled(not isServerMode) + self.publicDisconnectButton.setEnabled(isServerMode) + self.statusLabel.setText(f'服务器模式: {mode}') self.infoLabel.setText(f'连接信息: {info}') @@ -697,11 +747,20 @@ class DeviceSlaveWidget(QMainWindow): serverLayout.addWidget(self.publicServerInput) publicLayout.addLayout(serverLayout) - # 公网客户端连接按钮 + # 公网客户端控制按钮 + clientButtonLayout = QHBoxLayout() self.publicConnectButton = QPushButton('连接公网服务器') self.publicConnectButton.clicked.connect(self.connectToPublicServer) self.publicConnectButton.setObjectName('setButton') - publicLayout.addWidget(self.publicConnectButton) + clientButtonLayout.addWidget(self.publicConnectButton) + + self.publicDisconnectButton = QPushButton('断开公网连接') + self.publicDisconnectButton.clicked.connect(self.disconnectFromPublicServer) + self.publicDisconnectButton.setObjectName('setButton') + self.publicDisconnectButton.setEnabled(False) + clientButtonLayout.addWidget(self.publicDisconnectButton) + + publicLayout.addLayout(clientButtonLayout) publicGroup.setLayout(publicLayout) mainLayout.addWidget(publicGroup) @@ -744,19 +803,37 @@ class DeviceSlaveWidget(QMainWindow): QMessageBox.warning(self, '警告', '请输入服务器地址') return - # try: + try: # 获取客户端名称 - from protocol.RPC.RpcClient import RpcClient - clientName = RpcClient.getNextClientNameFromRabbitMQ(serverIp) - print(f"获取到客户端名称: {clientName}") - - # 设置为客户端模式 - Globals.getValue('protocolManage').closeServer() - Globals.getValue('protocolManage').setClentMode(clientName, rabbitmqHost=serverIp) - - QMessageBox.information(self, '连接成功', f'已成功连接到公网服务器 {serverIp}\n客户端名称: {clientName}') - # except Exception as e: - # QMessageBox.critical(self, '连接失败', f'连接公网服务器失败: {str(e)}') + from protocol.RPC.RpcClient import RpcClient + clientName = RpcClient.getNextClientNameFromRabbitMQ(serverIp) + print(f"获取到客户端名称: {clientName}") + + # 设置为客户端模式 + Globals.getValue('protocolManage').closeServer() + Globals.getValue('protocolManage').setClentMode(clientName, rabbitmqHost=serverIp) + + # 更新按钮状态 + self.publicConnectButton.setEnabled(False) + self.publicDisconnectButton.setEnabled(True) + + QMessageBox.information(self, '连接成功', f'已成功连接到公网服务器 {serverIp}\n客户端名称: {clientName}') + except Exception as e: + QMessageBox.critical(self, '连接失败', f'连接公网服务器失败: {str(e)}') + + def disconnectFromPublicServer(self): + """断开公网服务器连接""" + try: + # 关闭客户端模式 + Globals.getValue('protocolManage').closeClient() + + # 更新按钮状态 + self.publicConnectButton.setEnabled(True) + self.publicDisconnectButton.setEnabled(False) + + QMessageBox.information(self, '成功', '已断开公网服务器连接') + except Exception as e: + QMessageBox.critical(self, '失败', f'断开公网连接失败: {str(e)}') def toggleUdpServer(self): if self.server.udpRunning: @@ -788,9 +865,14 @@ class DeviceSlaveWidget(QMainWindow): protocolManage = Globals.getValue('protocolManage') mode = '无' info = '无' + + # 重置按钮状态 + isClientMode = False + if protocolManage: if hasattr(protocolManage, 'RpcClient') and protocolManage.RpcClient: mode = '客户端模式' + isClientMode = True try: clientName = protocolManage.RpcClient.clientName serverInfo = protocolManage.RpcClient.rabbitHost @@ -804,5 +886,10 @@ class DeviceSlaveWidget(QMainWindow): else: mode = '无' info = '无' + + # 更新公网客户端按钮状态(从站模式) + self.publicConnectButton.setEnabled(not isClientMode) + self.publicDisconnectButton.setEnabled(isClientMode) + self.statusLabel.setText(f'服务器模式: {mode}') self.infoLabel.setText(f'连接信息: {info}') diff --git a/UI/TrendManage/TrendWidget.py b/UI/TrendManage/TrendWidget.py index 9bdcdc3..faee129 100644 --- a/UI/TrendManage/TrendWidget.py +++ b/UI/TrendManage/TrendWidget.py @@ -7,6 +7,7 @@ from PyQt5.QtWidgets import (QApplication, QGridLayout, QListWidget, QListWidget QCheckBox, QFrame, QSpacerItem, QSizePolicy, QHeaderView, QToolButton) from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage from utils import Globals +import qtawesome import matplotlib.pyplot as plt import matplotlib.dates as mdates @@ -71,7 +72,7 @@ class TrendWidgets(QWidget): self.variableListGroup = QGroupBox("变量列表") self.variableListGroup.setObjectName("trendVariableListGroup") layout = QVBoxLayout(self.variableListGroup) - layout.setSpacing(8) + layout.setSpacing(5) layout.setContentsMargins(8, 8, 8, 8) # 搜索框 @@ -79,14 +80,14 @@ class TrendWidgets(QWidget): self.searchInput.setObjectName("trendSearchInput") self.searchInput.setPlaceholderText("搜索变量...") self.searchInput.textChanged.connect(self.filterVarList) - layout.addWidget(self.searchInput) + layout.addWidget(self.searchInput, 1) # 变量列表 self.varListWidget = QListWidget() self.varListWidget.setObjectName("trendVarListWidget") self.varListWidget.itemDoubleClicked.connect(self.showVarTrend) self.varListWidget.itemSelectionChanged.connect(self.onVarSelected) - layout.addWidget(self.varListWidget) + layout.addWidget(self.varListWidget, 10) # 时间选择区域 timeGroupBox = QGroupBox("时间范围") @@ -125,7 +126,7 @@ class TrendWidgets(QWidget): timeEditLayout.addWidget(self.endTimeEdit) timeLayout.addLayout(timeEditLayout) - layout.addWidget(timeGroupBox) + layout.addWidget(timeGroupBox, 3) # 按钮区域 buttonGroupBox = QGroupBox("操作") @@ -140,6 +141,9 @@ class TrendWidgets(QWidget): self.queryBtn = QToolButton() self.queryBtn.setObjectName("trendQueryBtn") self.queryBtn.setText("查询数据") + self.queryBtn.setIcon(qtawesome.icon('fa.search', color='#FFFFFF')) + self.queryBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.queryBtn.setIconSize(QSize(16, 16)) self.queryBtn.setToolTip("按时间范围查询变量数据") self.queryBtn.clicked.connect(self.onTimeRangeQuery) firstRowLayout.addWidget(self.queryBtn) @@ -147,8 +151,12 @@ class TrendWidgets(QWidget): self.refreshBtn = QToolButton() self.refreshBtn.setObjectName("trendRefreshBtn") self.refreshBtn.setText("刷新列表") + self.refreshBtn.setIcon(qtawesome.icon('fa.refresh', color='#FFFFFF')) + self.refreshBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.refreshBtn.setIconSize(QSize(16, 16)) + self.refreshBtn.setToolTip("刷新变量列表") self.refreshBtn.clicked.connect(self.refreshVarList) - firstRowLayout.addWidget(self.refreshBtn) + firstRowLayout.addWidget(self.refreshBtn, 2) buttonLayout.addLayout(firstRowLayout) @@ -159,6 +167,9 @@ class TrendWidgets(QWidget): self.addToTrendBtn = QToolButton() self.addToTrendBtn.setObjectName("trendAddBtn") self.addToTrendBtn.setText("添加变量") + self.addToTrendBtn.setIcon(qtawesome.icon('fa.plus', color='#FFFFFF')) + self.addToTrendBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.addToTrendBtn.setIconSize(QSize(16, 16)) self.addToTrendBtn.setToolTip("将选中的变量添加到趋势图") self.addToTrendBtn.clicked.connect(self.addSelectedVarsToTrend) secondRowLayout.addWidget(self.addToTrendBtn) @@ -166,6 +177,9 @@ class TrendWidgets(QWidget): self.clearAllBtn = QToolButton() self.clearAllBtn.setObjectName("trendClearBtn") self.clearAllBtn.setText("清除所有") + self.clearAllBtn.setIcon(qtawesome.icon('fa.trash', color='#FFFFFF')) + self.clearAllBtn.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.clearAllBtn.setIconSize(QSize(16, 16)) self.clearAllBtn.setToolTip("清除趋势图中的所有变量") self.clearAllBtn.clicked.connect(self.clearAllVars) secondRowLayout.addWidget(self.clearAllBtn) diff --git a/UI/UserManage/UserTable.py b/UI/UserManage/UserTable.py index 9d6cc81..d8fc10a 100644 --- a/UI/UserManage/UserTable.py +++ b/UI/UserManage/UserTable.py @@ -39,6 +39,7 @@ class UserTableView(QTableView): self.setHorizontalHeader(self.header) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.verticalHeader().setDefaultSectionSize(50) + self.verticalHeader().hide() self.header.setSectionResizeMode(0, QHeaderView.Fixed) diff --git a/UI/VarManages/AnalogModel.py b/UI/VarManages/AnalogModel.py index 1e7d8bd..3d0255d 100644 --- a/UI/VarManages/AnalogModel.py +++ b/UI/VarManages/AnalogModel.py @@ -11,6 +11,7 @@ from UI.VarManages.TCRTDModel import * from utils import Globals + class AnalogModel(VarTableModel): def __init__(self, header, data: list, table = None): ''' @@ -63,10 +64,11 @@ class AnalogModel(VarTableModel): if not QModelIndex.isValid(): print("行或者列有问题") return QVariant() - if role == Qt.BackgroundColorRole: - if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'): - return QtGui.QColor('#EFEFEF') + if role == Qt.BackgroundRole: + # if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'): + # return QtGui.QColor('#EFEFEF') if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'): + # print(111111) return QtGui.QColor('#00FF7F') if role == Qt.TextColorRole: return QtGui.QColor('#1A1A1A') diff --git a/UI/VarManages/HartModel.py b/UI/VarManages/HartModel.py index 888ca60..7ff031d 100644 --- a/UI/VarManages/HartModel.py +++ b/UI/VarManages/HartModel.py @@ -40,7 +40,7 @@ class HartModel(VarTableModel): if not QModelIndex.isValid(): print("行或者列有问题") return QVariant() - if role == Qt.BackgroundColorRole: + if role == Qt.BackgroundRole: if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][1] not in Globals.getValue('forceVars'): return QtGui.QColor('#EFEFEF') if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'): diff --git a/UI/VarManages/HartSimulateModel.py b/UI/VarManages/HartSimulateModel.py index 5ca37a6..2000a88 100644 --- a/UI/VarManages/HartSimulateModel.py +++ b/UI/VarManages/HartSimulateModel.py @@ -43,7 +43,7 @@ class HartSimulateModel(VarTableModel): if not QModelIndex.isValid(): print("行或者列有问题") return QVariant() - if role == Qt.BackgroundColorRole: + if role == Qt.BackgroundRole: if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][1] not in Globals.getValue('forceVars'): return QtGui.QColor('#EFEFEF') if self.datas[QModelIndex.row()][1] in Globals.getValue('forceVars'): @@ -83,6 +83,17 @@ class HartSimulateButtonDelegate(BaseButtonDelegate): super(HartSimulateButtonDelegate, self).__init__(parent) def paint(self, painter, option, index): + # 首先绘制背景颜色,与其他委托保持一致 + if index.data(Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + # 减少高度调整,保持更多的绘制区域 + paint_rect = option.rect + paint_rect.setHeight(max(46, height - 4)) # 最小保持46px高度 + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(Qt.BackgroundRole)) + + # 然后创建按钮部件 if not self.parent().indexWidget(index): button1 = QPushButton( qtawesome.icon('fa.play', color='#1fbb6f'), @@ -91,13 +102,15 @@ class HartSimulateButtonDelegate(BaseButtonDelegate): clicked=self.start_action ) button1.setIconSize(QSize(15, 15)) - button1.setStyleSheet("border:none;") + button1.setToolTip("启动仿真") + button2 = QPushButton( qtawesome.icon('fa.pencil', color='#4c8cf2'), "", self.parent(), clicked=self.edit_action ) + button2.setToolTip("编辑变量") button1.clicked.connect(self.start_action) button2.clicked.connect(self.edit_action) @@ -114,13 +127,41 @@ class HartSimulateButtonDelegate(BaseButtonDelegate): else: button2.isEdit = False button2.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) + button2.setToolTip("保存变量") self.parent().model.editableList.append(button2.index[0]) + h_box_layout = QHBoxLayout() h_box_layout.addWidget(button1) - h_box_layout.setContentsMargins(2, 0, 0, 2) + h_box_layout.addWidget(button2) + h_box_layout.setContentsMargins(2, 2, 2, 2) h_box_layout.setAlignment(Qt.AlignCenter) + h_box_layout.setSpacing(3) + widget = QWidget() widget.setLayout(h_box_layout) + + # 设置widget的背景为透明,让底层的背景颜色显示出来 + widget.setStyleSheet(""" + QWidget { + background-color: transparent; + } + QPushButton { + background-color: rgba(255, 255, 255, 180); + border: 1px solid #E5E7EB; + border-radius: 3px; + padding: 3px; + margin: 1px; + min-height: 20px; + max-height: 24px; + min-width: 24px; + max-width: 28px; + } + QPushButton:hover { + background-color: rgba(255, 255, 255, 255); + border-color: #2277EF; + } + """) + self.parent().setIndexWidget( index, widget diff --git a/UI/VarManages/ModbusModel.py b/UI/VarManages/ModbusModel.py index cafff6e..8ac332f 100644 --- a/UI/VarManages/ModbusModel.py +++ b/UI/VarManages/ModbusModel.py @@ -98,9 +98,8 @@ class VarTableModel(QAbstractTableModel): if not QModelIndex.isValid(): print("行或者列有问题") return QVariant() - if role == Qt.BackgroundColorRole: - if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'): - return QtGui.QColor('#EFEFEF') + if role == Qt.BackgroundRole: + # 只有强制变量显示绿色背景,其他都是白色 if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'): return QtGui.QColor('#00FF7F') if role == Qt.TextColorRole: @@ -181,14 +180,19 @@ class VarTableModel(QAbstractTableModel): def refreshComboBox(self): #功能类型的index是5,通讯类型index是10 - + for i in range(len(self.datas)): cbRow = str('cb' + str(i) + str(5)) index = self.index(i, int(5)) delegate = self.table.itemDelegate(index) - delegate.paint(self.table, QStyleOptionViewItem(), index) + + # 不需要调用paint方法,因为委托会在需要时自动创建控件 + # 直接尝试获取已存在的comboBox try: - comboBox = getattr(delegate, cbRow) + comboBox = getattr(delegate, cbRow, None) + if comboBox is None: + # 如果comboBox不存在,跳过这一行 + continue # print(comboBox, i, num, cbRow) except Exception as e: continue @@ -219,35 +223,31 @@ class ModbusButtonDelegate(BaseButtonDelegate): return self.trendWidgetDict[varName] def paint(self, painter, option, index): + # 首先绘制背景颜色,与其他委托保持一致 + if index.data(Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + paint_rect = option.rect + paint_rect.setHeight(max(46, height - 4)) + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(Qt.BackgroundRole)) + if not self.parent().indexWidget(index): - button1 = QPushButton( - qtawesome.icon('fa.play', color='#1fbb6f'), - "", - self.parent(), - clicked=self.start_action - ) - # button1.setIconSize(QSize(15, 15)) - # button1.setStyleSheet("border:none;") - button2 = QPushButton( - qtawesome.icon('fa.pencil', color='#4c8cf2'), - "", - self.parent(), - clicked=self.edit_action - ) - # button2.setStyleSheet("border:none;") - button3 = QPushButton( - qtawesome.icon('fa.trash', color='#ff6d6d'), - "", - self.parent(), - clicked=self.delete_action - ) - # button3.setStyleSheet("border:none;") - button4 = QPushButton( - qtawesome.icon('fa.line-chart', color='#393c4e'), - "", - self.parent(), - clicked=self.trend_action - ) + button1 = QPushButton() + button1.setIcon(qtawesome.icon('fa.play', color='#1fbb6f')) + button1.setToolTip("启动强制") + + button2 = QPushButton() + button2.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) + button2.setToolTip("编辑变量") + + button3 = QPushButton() + button3.setIcon(qtawesome.icon('fa.trash', color='#ff6d6d')) + button3.setToolTip("删除变量") + + button4 = QPushButton() + button4.setIcon(qtawesome.icon('fa.line-chart', color='#393c4e')) + button4.setToolTip("趋势图") comboBox = QComboBox(self.parent()) comboBox.addItem('int', 0) @@ -255,9 +255,8 @@ class ModbusButtonDelegate(BaseButtonDelegate): comboBox.addItem('CDAB', 2) comboBox.addItem('BADC', 3) comboBox.addItem('DCBA', 4) - comboBox.setCurrentText(str(self.parent().model.datas[index.row()][index.column()])) - # comboBox.setMinimumWidth(30) - + comboBox.setCurrentText(str(self.parent().model.datas[index.row()][index.column()])) + comboBox.setToolTip("字节序") comboBox.currentIndexChanged.connect(self.indexChange) button1.clicked.connect(self.start_action) @@ -269,7 +268,6 @@ class ModbusButtonDelegate(BaseButtonDelegate): button2.isEdit = True button3.editButton = button2 - # button4.setStyleSheet("border:none;") button1.index = [index.row(), index.column()] button2.index = [index.row(), index.column()] button3.index = [index.row(), index.column()] @@ -283,21 +281,27 @@ class ModbusButtonDelegate(BaseButtonDelegate): else: button2.isEdit = False button2.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) + button2.setToolTip("保存变量") self.parent().model.editableList.append(button2.index[0]) + hboxLayout = QHBoxLayout() hboxLayout.addWidget(comboBox) hboxLayout.addWidget(button1) hboxLayout.addWidget(button2) hboxLayout.addWidget(button3) hboxLayout.addWidget(button4) - hboxLayout.setContentsMargins(2, 0, 0, 2) + hboxLayout.setContentsMargins(2, 2, 2, 2) hboxLayout.setAlignment(Qt.AlignCenter) + hboxLayout.setSpacing(3) + widget = QWidget() widget.setLayout(hboxLayout) - self.parent().setIndexWidget( - index, - widget - ) + + # 所有下拉框使用统一样式 + widget.setObjectName('ModbusButtonWidget') + comboBox.setObjectName('ModbusOrderBox') + + self.parent().setIndexWidget(index, widget) def indexChange(self): sender = self.sender() @@ -482,6 +486,15 @@ class ModbusTypeBox(QItemDelegate): super(ModbusTypeBox, self).__init__(parent) def paint(self, painter, option, index): + # 首先绘制背景颜色,与其他委托保持一致 + if index.data(Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + paint_rect = option.rect + paint_rect.setHeight(max(46, height - 4)) + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(Qt.BackgroundRole)) + if (index.column() == 5) and index.row() not in self.parent().model.editableList: data = self.parent().model.datas[index.row()] comBox = str('cb' + str(index.row()) + str(index.column())) @@ -495,35 +508,31 @@ class ModbusTypeBox(QItemDelegate): comboBox.setCurrentIndex(self.parent().model.datas[index.row()][index.column()] - 1) else: comboBox.setCurrentIndex(0) - - comboBox.currentIndexChanged.connect( self.indexChange) + comboBox.currentIndexChanged.connect(self.indexChange) comboBox.setObjectName('ModbusTypeBox') comboBox.setEditable(True) comboBox.lineEdit().setAlignment(Qt.AlignCenter) - # comboBox.setMinimumWidth(200) - # comboBox.setEnabled(False) + hboxLayout = QHBoxLayout() hboxLayout.addWidget(comboBox) - hboxLayout.setContentsMargins(0, 0, 0, 0) - # hboxLayout.setAlignment(Qt.AlignCenter) + hboxLayout.setContentsMargins(2, 2, 2, 2) comboBox.index = [index.row(), index.column()] - row = index.row() - if row % 2 == 0: - comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF; height: 40px; }") - else: - comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3; height: 40px; }") + + # 所有下拉框使用统一样式 + comboBox.setObjectName('ModbusTypeBox') if str(data[index.column()]): comboBox.setEnabled(False) + comboBox.setProperty('disabled', True) else: comboBox.setEnabled(True) + comboBox.setProperty('disabled', False) + widget = QWidget() + # widget.setStyleSheet("QWidget { background-color: transparent; }") widget.setLayout(hboxLayout) - self.parent().setIndexWidget( - index, - widget - ) + self.parent().setIndexWidget(index, widget) self.parent().openPersistentEditor(index) def indexChange(self): @@ -539,9 +548,17 @@ class ModbusVarModelBox(QItemDelegate): super(ModbusVarModelBox, self).__init__(parent) self.comBoxColumn = comBoxColumn - def paint(self, painter, option, index): - #本地值、模拟值、远程值 对应 0, 1, 2 + # 首先绘制背景颜色,与其他委托保持一致 + if index.data(Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + paint_rect = option.rect + paint_rect.setHeight(max(46, height - 4)) + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(Qt.BackgroundRole)) + + # 本地值、模拟值、远程值 对应 0, 1, 2 if (index.column() == self.comBoxColumn) and not self.parent().indexWidget(index): data = self.parent().model.datas[index.row()] comBox = str('cb' + str(index.row()) + str(index.column())) @@ -552,27 +569,19 @@ class ModbusVarModelBox(QItemDelegate): comboBox.addItems(items) comboBox.setCurrentText(str(self.parent().model.datas[index.row()][index.column()])) - - comboBox.currentIndexChanged.connect( self.indexChange) - comboBox.setObjectName('ModbusTypeBox') + comboBox.currentIndexChanged.connect(self.indexChange) + comboBox.setObjectName('ModbusVarModelBox') comboBox.setEditable(True) comboBox.lineEdit().setAlignment(Qt.AlignCenter) + hboxLayout = QHBoxLayout() hboxLayout.addWidget(comboBox) - hboxLayout.setContentsMargins(0, 0, 0, 0) + hboxLayout.setContentsMargins(2, 2, 2, 2) comboBox.index = [index.row(), index.column()] - row = index.row() - if row % 2 == 0: - comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF; height: 40px; }") - else: - comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3; height: 40px; }") - widget = QWidget() + # widget.setStyleSheet("QWidget { background-color: transparent; }") widget.setLayout(hboxLayout) - self.parent().setIndexWidget( - index, - widget - ) + self.parent().setIndexWidget(index, widget) self.parent().openPersistentEditor(index) def indexChange(self): diff --git a/UI/VarManages/TCRTDModel.py b/UI/VarManages/TCRTDModel.py index 68cf10b..7fa0927 100644 --- a/UI/VarManages/TCRTDModel.py +++ b/UI/VarManages/TCRTDModel.py @@ -65,7 +65,7 @@ class TcRtdModel(VarTableModel): if not QModelIndex.isValid(): print("行或者列有问题") return QVariant() - if role == Qt.BackgroundColorRole: + if role == Qt.BackgroundRole: if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'): return QtGui.QColor('#EFEFEF') if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'): @@ -114,6 +114,17 @@ class TcRtdButtonDelegate(BaseButtonDelegate): self.trendWindows = {} # 保存所有打开的趋势窗口 def paint(self, painter, option, index): + # 首先绘制背景颜色,与其他委托保持一致 + if index.data(Qt.BackgroundRole): + height = option.rect.height() + top = option.rect.top() + # 减少高度调整,保持更多的绘制区域 + paint_rect = option.rect + paint_rect.setHeight(max(46, height - 4)) # 最小保持46px高度 + paint_rect.moveTop(top + 2) + painter.fillRect(paint_rect, index.data(Qt.BackgroundRole)) + + # 然后创建按钮部件 if not self.parent().indexWidget(index): button1 = QPushButton( qtawesome.icon('fa.play', color='#1fbb6f'), @@ -122,13 +133,15 @@ class TcRtdButtonDelegate(BaseButtonDelegate): clicked=self.start_action ) button1.setIconSize(QSize(15, 15)) - button1.setStyleSheet("border:none;") + button1.setToolTip("启动强制") + button2 = QPushButton( qtawesome.icon('fa.pencil', color='#4c8cf2'), "", self.parent(), clicked=self.edit_action ) + button2.setToolTip("编辑变量") button3 = QPushButton( qtawesome.icon('fa.line-chart', color='#393c4e'), @@ -136,7 +149,7 @@ class TcRtdButtonDelegate(BaseButtonDelegate): self.parent(), clicked=self.trend_action ) - + button3.setToolTip("趋势图") button1.clicked.connect(self.start_action) button2.clicked.connect(self.edit_action) @@ -149,7 +162,6 @@ class TcRtdButtonDelegate(BaseButtonDelegate): button2.index = [index.row(), index.column()] button3.index = [index.row(), index.column()] - data = self.parent().model.datas[index.row()] for x in data[:-1]: if x != '': @@ -157,15 +169,42 @@ class TcRtdButtonDelegate(BaseButtonDelegate): else: button2.isEdit = False button2.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) + button2.setToolTip("保存变量") self.parent().model.editableList.append(button2.index[0]) + h_box_layout = QHBoxLayout() h_box_layout.addWidget(button1) h_box_layout.addWidget(button2) h_box_layout.addWidget(button3) - h_box_layout.setContentsMargins(2, 0, 0, 2) + h_box_layout.setContentsMargins(2, 2, 2, 2) h_box_layout.setAlignment(Qt.AlignCenter) + h_box_layout.setSpacing(3) + widget = QWidget() widget.setLayout(h_box_layout) + + # 设置widget的背景为透明,让底层的背景颜色显示出来 + widget.setStyleSheet(""" + QWidget { + background-color: transparent; + } + QPushButton { + background-color: rgba(255, 255, 255, 180); + border: 1px solid #E5E7EB; + border-radius: 3px; + padding: 3px; + margin: 1px; + min-height: 20px; + max-height: 24px; + min-width: 24px; + max-width: 28px; + } + QPushButton:hover { + background-color: rgba(255, 255, 255, 255); + border-color: #2277EF; + } + """) + self.parent().setIndexWidget( index, widget @@ -333,10 +372,10 @@ class TcRtdTypeDelegate(TcRtdButtonDelegate): widget = QWidget() widget.setLayout(h_box_layout) row = index.row() - if row % 2 == 0: - comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF }") - else: - comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3 }") + # if row % 2 == 0: + # comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF }") + # else: + # comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3 }") self.parent().setIndexWidget( index, widget diff --git a/UI/VarManages/VarTable.py b/UI/VarManages/VarTable.py index 89beba9..deed53e 100644 --- a/UI/VarManages/VarTable.py +++ b/UI/VarManages/VarTable.py @@ -16,15 +16,38 @@ from .RpcVarModel import RpcVarModel, RpcVarButtonDelegate from model.ProjectModel.VarManage import * from UI.VarManages.Thread import RTDTCThread, AnalogThread, HartSimulateThread +# from UI.ProjectManages.ProjectModel import BackgroundDelegate from utils import Globals +class BackgroundDelegate(QtWidgets.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 CheckBoxHeader(QHeaderView): checkClicked = pyqtSignal(bool) sectionClicked = pyqtSignal(int) - _x_offset = 4 - _y_offset = 0 + _x_offset = 15 + _y_offset = 15 _width = 25 _height = 25 @@ -40,7 +63,7 @@ class CheckBoxHeader(QHeaderView): if self.changeY: - self._y_offset = int((rect.height()-self._width)/2.) + 4 + self._y_offset = int((rect.height()-self._width)/2.) else: self._y_offset = int((rect.height()-self._width)/2.) @@ -83,14 +106,14 @@ class VarTableView(QTableView): self.setItemDelegateForColumn(11, ModbusButtonDelegate(self)) self.setItemDelegateForColumn(5, ModbusTypeBox(self)) self.setItemDelegateForColumn(10, ModbusVarModelBox(self)) - self.model = VarTableModel(['ID', '强制值', '当前值', '变量名', '变量描述', '变量类型', + self.model = VarTableModel([' ID', '强制值', '当前值', '变量名', '变量描述', '变量类型', '从站地址', '寄存器地址', '工程量下限', '工程量上限', '值类型', '操作'], [], modbusType = self.modbusType, table = self) def setupUi(self): - self.setShowGrid(True) - self.setAlternatingRowColors(True) + self.setShowGrid(False) + # self.setAlternatingRowColors(True) self.setSelectionBehavior(QAbstractItemView.SelectRows) @@ -107,9 +130,15 @@ class VarTableView(QTableView): self.header.setSectionResizeMode(0, QHeaderView.Fixed) self.header.resizeSection(0, 70) + + # 设置行高 + self.verticalHeader().setDefaultSectionSize(50) + self.verticalHeader().setMinimumSectionSize(50) + + self.verticalHeader().hide() def setupColumnWidths(self): - self.setCustomColumnWidths([0.9, 0.9, 1, 1, 1, 1.2, 1, 1, 1, 1, 1, 1.2]) + self.setCustomColumnWidths([0.9, 0.9, 0.9, 1, 1, 1.4, 1, 1, 1, 1, 0.9, 1.3]) def setCustomColumnWidths(self, ratios): # 计算总比例 @@ -130,6 +159,8 @@ class HartTableView(VarTableView): super(HartTableView, self).__init__(parent) def setHeader(self): + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) self.setItemDelegateForColumn(9, HartButtonDelegate(self)) self.setItemDelegateForColumn(8, HartVarModelBox(self)) self.model = HartModel([' ID', '仪器名', '描述', '电流值', '参数1', '参数2','参数3', '参数4', '值类型','操作'], [], table=self) @@ -139,7 +170,10 @@ class HartTableView(VarTableView): class TcRtdTableView(VarTableView): def __init__(self, parent=None): super(TcRtdTableView, self).__init__(parent) + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) self.setItemDelegateForColumn(6, TcRtdTypeDelegate(self)) + self.valueList = [0] * 16 self.mvList = [0] * 16 # self.workThread = RTDTCThread(self) @@ -150,7 +184,7 @@ class TcRtdTableView(VarTableView): def setHeader(self): self.setItemDelegateForColumn(11, TcRtdButtonDelegate(self)) self.setItemDelegateForColumn(10, TcRtdVarModelBox(self)) - self.model = TcRtdModel(['ID', '强制值', '当前值', '变量名', '通道序号', '变量描述', '变量类型', '工程量下限', '工程量上限', '补偿值', '值类型','操作'], [], table=self) + self.model = TcRtdModel([' ID', '强制值', '当前值', '变量名', '通道序号', '变量描述', '变量类型', '工程量下限', '工程量上限', '补偿值', '值类型','操作'], [], table=self) class AnalogTableView(VarTableView): def __init__(self, parent=None): @@ -159,6 +193,8 @@ class AnalogTableView(VarTableView): # self.realList = [0] * 8 + [0] * 8 + [0] * 16 + [0] * 8 + [0] * 8 + [0] * 8 # self.workThread = AnalogThread(self) # Globals.setValue('AnalogThread', self.workThread) + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) self.setupColumnWidths() def setupColumnWidths(self): @@ -167,12 +203,14 @@ class AnalogTableView(VarTableView): def setHeader(self): self.setItemDelegateForColumn(10, AnalogButtonDelegate(self)) self.setItemDelegateForColumn(9, AnalogVarModelBox(self)) - self.model = AnalogModel(['ID', '强制值', '当前值', '变量名', '通道序号', '变量描述', '变量类型', '工程量下限', '工程量上限', '值类型', '操作'], [], table=self) + self.model = AnalogModel([' ID', '强制值', '当前值', '变量名', '通道序号', '变量描述', '变量类型', '工程量下限', '工程量上限', '值类型', '操作'], [], table=self) class HartSimulateTableView(VarTableView): def __init__(self, parent=None): super(HartSimulateTableView, self).__init__(parent) + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) self.valueList = [] # self.realList = [0] * 8 + [0] * 8 + [0] * 8 self.workThread = HartSimulateThread(self) @@ -189,8 +227,10 @@ class HartSimulateTableView(VarTableView): class RpcVarTableView(QTableView): def __init__(self, parent=None): super(RpcVarTableView, self).__init__(parent) - self.setShowGrid(True) - self.setAlternatingRowColors(True) + self.delegate = BackgroundDelegate(self) + self.setItemDelegate(self.delegate) + self.setShowGrid(False) + # self.setAlternatingRowColors(True) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setItemDelegateForColumn(7, RpcVarButtonDelegate(self)) self.model = RpcVarModel(['客户端', '变量名', '类型', '下限', '上限', '当前值', '操作'], self) diff --git a/UI/VarManages/VarWidget.py b/UI/VarManages/VarWidget.py index 3d160f2..058ce13 100644 --- a/UI/VarManages/VarWidget.py +++ b/UI/VarManages/VarWidget.py @@ -365,7 +365,7 @@ class HartWidgets(VarWidgets): self.startProtocolBtn.clicked.connect(self.startProtocol) self.varView = HartTableView() - self.varView.setObjectName('HartTable') + self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) @@ -417,7 +417,7 @@ class TcRtdWidgets(VarWidgets): self.clearBtn.clicked.connect(self.clearColour) self.varView = TcRtdTableView() - self.varView.setObjectName('TcRtdTable') + self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) @@ -510,7 +510,7 @@ class AnalogWidgets(VarWidgets): self.clearBtn.clicked.connect(self.clearColour) self.varView = AnalogTableView() - self.varView.setObjectName('AnalogTable') + self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) @@ -589,7 +589,7 @@ class HartSimulateWidgets(VarWidgets): self.startProtocolBtn.clicked.connect(self.startProtocol) self.varView = HartSimulateTableView() - self.varView.setObjectName('HartSimulateTable') + self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) diff --git a/protocol/RPC/RpcServer.py b/protocol/RPC/RpcServer.py index a9f56cf..8bddf47 100644 --- a/protocol/RPC/RpcServer.py +++ b/protocol/RPC/RpcServer.py @@ -548,7 +548,8 @@ class RpcServer: if offlineClients: print(f"本次检查移除了 {len(offlineClients)} 个离线客户端") else: - print("所有客户端状态正常") + # print("所有客户端状态正常") + pass except Exception as e: print(f"自动检测客户端异常: {e}")