add main_ui.py(中交三航主界面)

This commit is contained in:
2025-10-18 18:29:40 +08:00
parent 2c16ef2a7e
commit dc038b0c0d
31 changed files with 7601 additions and 0 deletions

35
.gitignore vendored Normal file
View File

@ -0,0 +1,35 @@
# 忽略.vscode文件夹
.vscode/
.VSCodeCounter
# 忽略工作区文件
*.code-workspace
.DS_Store
# 忽略 .idea
.idea/
# 忽略python缓存文件
*/__pycache__
*.py[cod]
# 忽略日志文件
log/
*.log
# 忽略测试文件
test.py
# 忽略配置文件
config.json
# 忽略下载文件夹
download/
# 忽略构建文件夹
build/
dist/
PyQt_Fluent_Widgets.egg-info/
PySide6_Fluent_Widgets.egg-info/
PyQt6_Fluent_Widgets.egg-info/
PySide2_Fluent_Widgets.egg-info/

View File

@ -0,0 +1,21 @@
from view.main_window import MainWindow
class MainController:
def __init__(self):
# 主界面
self.main_window = MainWindow()
# 初始化子界面
self._initSubViews()
# 初始化子控制器
# self._initSubControllers()
# self.__connectSignals()
def showMainWindow(self):
self.main_window.show()
def _initSubViews(self):
pass

BIN
images/AI检测.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

BIN
images/上位料斗1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
images/上位料斗2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/下位料斗1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
images/下位料斗2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
images/任务刷新.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

BIN
images/在线.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
images/拱1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
images/拱2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/拱3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
images/料斗夹具.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
images/料斗状态红.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

BIN
images/料斗状态绿.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

BIN
images/料斗状态黄.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

BIN
images/破拱.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
images/离线.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
images/系统停止.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

BIN
images/系统启动.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

BIN
images/系统复位.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

8
main_ui.py Normal file
View File

@ -0,0 +1,8 @@
from PySide6.QtWidgets import QApplication
from controller.main_controller import MainController
if __name__ == "__main__":
app = QApplication([])
controller = MainController()
controller.showMainWindow()
app.exec()

6258
resources/resource_rc.py Normal file

File diff suppressed because it is too large Load Diff

23
resources/resources.qrc Normal file
View File

@ -0,0 +1,23 @@
<RCC>
<qresource prefix="/icons">
<file>../images/AI检测.png</file>
<file>../images/拱1.png</file>
<file>../images/拱2.png</file>
<file>../images/拱3.png</file>
<file>../images/离线.png</file>
<file>../images/料斗夹具.png</file>
<file>../images/料斗状态红.png</file>
<file>../images/料斗状态黄.png</file>
<file>../images/料斗状态绿.png</file>
<file>../images/破拱.png</file>
<file>../images/任务刷新.png</file>
<file>../images/上位料斗1.png</file>
<file>../images/上位料斗2.png</file>
<file>../images/系统复位.png</file>
<file>../images/系统启动.png</file>
<file>../images/系统停止.png</file>
<file>../images/下位料斗1.png</file>
<file>../images/下位料斗2.png</file>
<file>../images/在线.png</file>
</qresource>
</RCC>

88
view/main_window.py Normal file
View File

@ -0,0 +1,88 @@
# coding: utf-8
from typing import List
from PySide6.QtCore import Qt, Signal, QEasingCurve, QUrl, QSize, QTimer
from PySide6.QtGui import QIcon, QDesktopServices, QColor
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout,
QFrame, QWidget, QSpacerItem, QSizePolicy)
from .widgets.status_monitor_widget import StatusMonitorWidget
from .widgets.hopper_widget import HopperWidget
from .widgets.arc_progress_widget import ArcProgressWidget
from .widgets.production_progress_widget import ProductionProgressWidget
from .widgets.system_button_widget import SystemButtonWidget
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initWindow()
self.createSubWidgets() # 创建子部件
self.setupLayout() # 设置布局
self.connectSignalToSlot()
def connectSignalToSlot(self):
# 可添加信号槽连接
self.system_button_widget.buttons["系统启动"].clicked.connect(self.handleSystemStart)
self.system_button_widget.buttons["系统停止"].clicked.connect(self.handleSystemStop)
def handleSystemStart(self):
# 测试
self.production_progress.testProgress(60)
self.arc_progress.testProgress(60)
def handleSystemStop(self):
# 测试
self.production_progress.animation.stop()
self.arc_progress.animation.stop()
def initWindow(self):
"""初始化窗口基本属性"""
self.setWindowTitle("中交三航主界面") # 设置窗口标题
self.setMinimumSize(1280, 1040) # 设置最小尺寸(可根据需求调整)
# 设置主界面窗口背景色
self.setStyleSheet("background-color: #ffffff;")
def createSubWidgets(self):
"""创建所有子部件实例"""
self.status_monitor = StatusMonitorWidget() # 最上方:状态监控部件
self.hopper_widget = HopperWidget() # 中间1料斗部件
self.arc_progress = ArcProgressWidget() # 中间2弧形进度部件
self.production_progress = ProductionProgressWidget() # 生产进度部件
self.system_button_widget = SystemButtonWidget() # 系统控制按钮
def setupLayout(self):
"""设置垂直布局,从上到下排列部件"""
main_layout = QVBoxLayout(self) # 主布局:垂直布局
# 设置布局间距(部件之间的距离)和边距(布局与窗口边缘的距离)
main_layout.setSpacing(0) # 部件间距0px
main_layout.setContentsMargins(15, 15, 15, 15) # 上下左右边距15px
# 依次添加部件到布局(从上到下)
main_layout.addWidget(self.status_monitor, alignment=Qt.AlignHCenter)
main_layout.addWidget(self.hopper_widget, alignment=Qt.AlignHCenter)
main_layout.addWidget(self.arc_progress, alignment=Qt.AlignHCenter)
main_layout.addWidget(self.production_progress, alignment=Qt.AlignHCenter)
main_layout.addWidget(self.system_button_widget, alignment=Qt.AlignHCenter)
# 将布局应用到主窗口
self.setLayout(main_layout)
def resizeEvent(self, e):
"""窗口大小变化时的回调"""
super().resizeEvent(e)
def closeEvent(self, e):
"""窗口关闭时的回调"""
super().closeEvent(e)
if __name__ == "__main__":
import sys
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec())

View File

@ -0,0 +1,239 @@
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QWidget, QVBoxLayout, QSizePolicy
from PySide6.QtGui import QPixmap
from PySide6.QtCore import Qt, QPropertyAnimation, Property
import os
import resources.resource_rc
# 拱形进度条
class OverlapArcProgress(QWidget):
def __init__(self, bg_img_path, fg_img_path, parent=None):
super().__init__(parent)
self._progress = 0 # 进度0-100
self.setStyleSheet("background: transparent; border: none;")
# 加载并统一图片尺寸
self.bg_pixmap = QPixmap(bg_img_path)
self.fg_pixmap = QPixmap(fg_img_path)
if self.bg_pixmap.isNull():
print(f"错误:拱进度条背景图 {bg_img_path} 加载失败")
if self.fg_pixmap.isNull():
print(f"错误:拱进度条前景图 {fg_img_path} 加载失败")
if self.bg_pixmap.size() != self.fg_pixmap.size():
self.fg_pixmap = self.fg_pixmap.scaled(
self.bg_pixmap.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
)
# 控件尺寸与图片一致(避免缩放失真)
self.setFixedSize(self.bg_pixmap.size())
self.total_height = self.bg_pixmap.height() # 缓存总高度(背景/前景高度一致)
# 背景标签(下层,完整显示背景)
self.bg_label = QLabel(self)
self.bg_label.setPixmap(self.bg_pixmap)
self.bg_label.setScaledContents(False) # 禁用缩放
# 前景标签(上层,初始加载完整前景图,通过高度控制显示区域)
self.fg_label = QLabel(self)
self.fg_label.setPixmap(self.fg_pixmap) # 直接加载完整前景图
self.fg_label.setScaledContents(False) # 禁用缩放(避免图片拉伸)
self.fg_label.setAlignment(Qt.AlignBottom) # 图片在标签内底部对齐
self.fg_label.raise_() # 确保在背景上层
# 初始状态高度为0位置在底部不显示任何内容
self.fg_label.setGeometry(0, self.total_height, self.bg_pixmap.width(), 0)
# ---------- 进度显示标签 ----------
self.progress_label = QLabel(self)
self.progress_label.setGeometry(217, 17, 121, 34)
self.progress_label.setStyleSheet("""
background-color: #444750;
color: #A2EF4D;
font-size: 20px;
font-weight: bold;
""")
# 文本在标签内部居中显示
self.progress_label.setAlignment(Qt.AlignCenter)
self.progress_label.raise_() # 确保在前景上层(最上层)
self.progress_label.setText("0%") # 初始文本
# ---------- 进度属性 ----------
@Property(int)
def progress(self):
return self._progress
@progress.setter
def progress(self, value):
if 0 <= value <= 100 and value != self._progress:
self._progress = value
self.update_foreground()
# 标签高度调整方式实现
def update_foreground(self):
"""通过调整前景标签的高度和Y坐标, 实现从底部向上填充的进度条"""
# 同步更新进度标签文本
self.progress_label.setText(f"{self._progress}%")
# print(f"{self._progress}%")
progress_ratio = self._progress / 100.0
k = 8 # 调整此系数k越大“快慢差异”越明显推荐范围 3~10
# 公式height_ratio = x + k*(x³/3 - x²/2 + x/6)
height_ratio = progress_ratio + k * (
(progress_ratio ** 3) / 3 -
(progress_ratio ** 2) / 2 +
progress_ratio / 6
)
height_ratio = max(0.0, min(1.0, height_ratio)) # 限制范围0-1
# 计算目标高度(随进度变化)
target_height = int(self.total_height * height_ratio)
# fg_label标签Y坐标 = 总高度 - 目标高度(实现底部对齐,向上延伸)
target_y = self.total_height - target_height
# 标签宽度与背景一致,高度为目标高度
self.fg_label.setGeometry(
0, # X坐标与背景左对齐
target_y, # Y坐标底部对齐
self.bg_pixmap.width(), # 宽度(与背景一致)
target_height # 高度(随进度变化)
)
class ArcProgressWidget(QWidget):
def __init__(self):
super().__init__()
# 加载MainWidget背景图
# 添加了 拱3.png 作为背景图
bg_img = ":/icons/images/拱3.png" # 需要修改为实际的图片的路径
# self.setStyleSheet(f"background-image: url({bg_img}); background-repeat: no-repeat; background-position: center;")
self.bg_pixmap = QPixmap(bg_img)
# # 设置控件widget大小与背景图一致
self.setFixedSize(self.bg_pixmap.size())
self.setSizePolicy(
QSizePolicy.Fixed, # 水平方向固定
QSizePolicy.Fixed # 垂直方向固定
)
# 创建背景标签
background_label = QLabel(self)
background_label.setPixmap(self.bg_pixmap)
background_label.setScaledContents(True)
background_label.setFixedSize(self.bg_pixmap.size())
# 创建拱形进度条控件
arc_bg_img = ":/icons/images/拱2.png" # 拱进度条背景(空心) # 需要修改为实际的图片的路径
arc_fg_img = ":/icons/images/拱1.png" # 拱进度条前景(实心) # 需要修改为实际的图片的路径
self.arc_progress = OverlapArcProgress(arc_bg_img, arc_fg_img, self)
# 显示重量的标签,单位是 kg
self.weight_label = QLabel(self)
self.weight_label.setGeometry(217, 118, 118, 29)
self.weight_label.setStyleSheet("""
background-color: #3a2528;
color: #A2EF4D;
font-size: 20px;
""")
# 文本在标签内部居中显示
self.weight_label.setAlignment(Qt.AlignCenter)
self.weight_label.setText("2000kg") # 初始文本
# 显示管片型号的标签 如 B1
self.segment_model_label = QLabel(self)
self.segment_model_label.setGeometry(98, 150, 116, 29)
self.segment_model_label.setStyleSheet("""
background-color: #3a2528;
color: #A2EF4D;
font-size: 20px;
""")
# 文本在标签内部居中显示
self.segment_model_label.setAlignment(Qt.AlignCenter)
self.segment_model_label.setText("B1") # 初始文本
# 显示频率的标签,单位是 Hz
self.frequency_label = QLabel(self)
self.frequency_label.setGeometry(217, 150, 118, 29)
self.frequency_label.setStyleSheet("""
background-color: #3a2528;
color: #A2EF4D;
font-size: 20px;
""")
# 文本在标签内部居中显示
self.frequency_label.setAlignment(Qt.AlignCenter)
self.frequency_label.setText("230Hz") # 初始文本
# 显示管片编号的标签 如 SHRB1-3
self.segment_number_label = QLabel(self)
self.segment_number_label.setGeometry(338, 150, 116, 29)
self.segment_number_label.setStyleSheet("""
background-color: #3a2528;
color: #A2EF4D;
font-size: 20px;
""")
# 文本在标签内部居中显示
self.segment_number_label.setAlignment(Qt.AlignCenter)
self.segment_number_label.setText("SHRB1-3") # 初始文本
# 显示状态的标签 (如:振动中等)
self.state_label = QLabel(self)
self.state_label.setGeometry(217, 182, 118, 29)
self.state_label.setStyleSheet("""
background-color: #3a2528;
color: #A2EF4D;
font-size: 20px;
""")
# 文本在标签内部居中显示
self.state_label.setAlignment(Qt.AlignCenter)
self.state_label.setText("振动中") # 初始文本
# 进度条测试
def testProgress(self, seconds: float):
# 启动动画从0%到100% (模拟进度加载)
self.animation = QPropertyAnimation(self.arc_progress, b"progress")
self.animation.setDuration(seconds * 1000)
self.animation.setStartValue(0)
self.animation.setEndValue(100)
self.animation.start()
# 进度条设置
def setProgress(self, progress:int):
"""
设置progress之后, 会根据该值调整进度条
Args:
progress: 传入去掉百分号之后的数值, 如80%, 传入80
"""
self.arc_progress.progress = progress
# 重量设置 (单位kg)
def setWeight(self, weight:float):
self.weight_label.setText(f"{weight}kg")
# 频率设置单位Hz
def setFrequency(self, frequency:float):
self.frequency_label.setText(f"{frequency}Hz")
# 状态设置 (...中)
def setState(self, stateStr:str):
self.state_label.setText(stateStr)
# 管片类型设置 (B1、B2等)
def setSegmentModel(self, segmentModelStr:str):
self.segment_model_label.setText(segmentModelStr)
# 管片编号设置
def setSegmentNumber(self, segmentNumberStr:str):
self.segment_number_label.setText(segmentNumberStr)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = ArcProgressWidget()
window.testProgress(120)
window.setState("测试中")
window.show()
sys.exit(app.exec())

View File

@ -0,0 +1,36 @@
from PySide6.QtWidgets import QPushButton
from PySide6.QtGui import QPainter, QColor, QFont, Qt, QBrush
class CircularButton(QPushButton):
"""自定义圆形按钮,处理 hover 颜色变化"""
def __init__(self, text, parent=None):
super().__init__(text, parent)
self.setFixedSize(40, 40)
# self.normal_color = QColor(1, 18, 98) # 正常颜色 (RGB: 1,18,98)
self.normal_color = QColor(86, 119, 252)
self.hover_color = QColor(24, 253, 255) # hover 颜色 (RGB: 24,253,255)
self.current_color = self.normal_color
self.setStyleSheet("background: transparent; border: none;")
def enterEvent(self, event):
self.current_color = self.hover_color
self.update()
super().enterEvent(event)
def leaveEvent(self, event):
self.current_color = self.normal_color
self.update()
super().leaveEvent(event)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# 绘制圆形背景
painter.setBrush(QBrush(self.current_color))
painter.setPen(Qt.NoPen)
painter.drawEllipse(0, 0, self.width(), self.height())
# 绘制白色文字
painter.setPen(Qt.white)
painter.setFont(QFont("Arial", 10, QFont.Bold))
painter.drawText(self.rect(), Qt.AlignCenter, self.text())

View File

@ -0,0 +1,407 @@
from PySide6.QtWidgets import (QApplication, QWidget, QPushButton, QLabel,
QVBoxLayout, QHBoxLayout)
from PySide6.QtGui import QPainter, QColor, QFont, QPixmap, Qt, QBrush
from PySide6.QtCore import QPoint, Signal, Slot
import sys
# 圆形按钮
from .circular_button import CircularButton
import resources.resource_rc
class HopperWidget(QWidget):
# 上料斗破拱信号
# 开启破拱 True关闭破拱 False
upper_arch_breaking_signal = Signal(bool)
# 下料斗破拱信号
# 开启破拱 True关闭破拱 False
lower_arch_breaking_signal = Signal(bool)
def __init__(self):
super().__init__()
self.setWindowTitle("料斗控制界面")
# 破拱状态 (True/False)
self.upper_arch_breaking_status = False # 初始为不破拱状态
self.lower_arch_breaking_status = False # 初始为不破拱状态
# 料斗控制界面的固定大小为 332x482
# 需要根据具体的料斗的图片来调整
self.setFixedSize(356, 496)
# 创建上位和下位料斗
self.upper_hopper = self.create_upper_hopper()
self.lower_hopper = self.create_lower_hopper()
# 主布局
main_layout = QVBoxLayout(self)
main_layout.addWidget(self.upper_hopper)
main_layout.addWidget(self.lower_hopper)
main_layout.setSpacing(0)
main_layout.setContentsMargins(10, 10, 0, 10)
self.connectSignalToSlot()
def create_upper_hopper(self):
"""创建上位料斗Widget"""
group = QWidget()
# 上位的 料斗控件的固定尺寸 !!!!
# 注意: 这里需要根据具体的料斗图片来修改
group.setFixedSize(332, 202)
# group.setStyleSheet("background-color: green")
layout = QVBoxLayout(group)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
# 标题标签(上位)
self.upper_title_label = QLabel("上位料斗")
self.upper_title_label.setFixedSize(79, 17)
self.upper_title_label.setAlignment(Qt.AlignCenter)
self.upper_title_label.setStyleSheet("color: #2fd3f2; font-size: 14px; font-weight: bold;")
layout.addWidget(self.upper_title_label, alignment=Qt.AlignCenter)
# 加载外框图片
outer_img = ":/icons/images/上位料斗1.png"
outer_pixmap = QPixmap(outer_img)
if outer_pixmap.isNull():
print(f"警告:图片 {outer_img} 加载失败,请检查路径!")
return group
outer_width = outer_pixmap.width()
outer_height = outer_pixmap.height()
# 背景容器(上位)
self.upper_bg_widget = QWidget()
self.upper_bg_widget.setFixedSize(outer_width, outer_height)
self.upper_bg_widget.setStyleSheet(f"background-image: url({outer_img}); background-repeat: no-repeat; background-position: center;")
layout.addWidget(self.upper_bg_widget, alignment=Qt.AlignCenter)
# 内框图片(上位)
inner_img = ":/icons/images/上位料斗2.png"
inner_pixmap = QPixmap(inner_img)
if not inner_pixmap.isNull():
self.upper_inner_label = QLabel(self.upper_bg_widget)
self.upper_inner_label.setPixmap(inner_pixmap)
self.upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height())
self.upper_inner_label.move(14, 9)
# 状态图片(上位,绿色)
status_img = ":/icons/images/料斗状态绿.png"
status_pixmap = QPixmap(status_img)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(22, 22, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.upper_status_label = QLabel(self.upper_bg_widget)
self.upper_status_label.setPixmap(status_pixmap)
self.upper_status_label.move(32, 18)
self.upper_status_label.setScaledContents(False)
self.upper_status_label.setStyleSheet("background: none;")
# 破拱图片(上位)
arch_img = ":/icons/images/破拱.png"
arch_pixmap = QPixmap(arch_img)
if not arch_pixmap.isNull():
arch_pixmap = arch_pixmap.scaled(24, 21, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.upper_arch_label = QLabel(self.upper_bg_widget)
self.upper_arch_label.setPixmap(arch_pixmap)
self.upper_arch_label.move(outer_width - 56, 18)
self.upper_arch_label.setStyleSheet("background: none;")
self.upper_arch_label.setHidden(True) # 初始,不破拱状态,隐藏
# 重量文字(上位)
self.upper_weight_label = QLabel("5000kg", self.upper_bg_widget)
self.upper_weight_label.setAlignment(Qt.AlignCenter)
self.upper_weight_label.setStyleSheet("background: none; background-color: #262c38; color: #79c053; font-size: 14px; font-weight: bold;")
self.upper_weight_label.setFixedSize(93, 22)
self.upper_weight_label.move(outer_width//2 - 46, outer_height//2 - 30)
# 额外文字(上位)
self.upper_extra_label = QLabel("2.0方(预估)", self.upper_bg_widget)
self.upper_extra_label.setAlignment(Qt.AlignCenter)
self.upper_extra_label.setStyleSheet("background: none; background-color: #262c38; color: #79c053; font-size: 14px; font-weight: bold;")
self.upper_extra_label.setFixedSize(93, 22)
self.upper_extra_label.move(outer_width//2 - 46, outer_height//2)
# 料斗夹具图片(上位)
# clamp_img = ":/icons/images/料斗夹具.png"
# clamp_pixmap = QPixmap(clamp_img)
# if not clamp_pixmap.isNull():
# self.upper_clamp_label = QLabel()
# self.upper_clamp_label.setPixmap(clamp_pixmap)
# self.upper_clamp_label.setFixedSize(clamp_pixmap.width(), clamp_pixmap.height())
# layout.addWidget(self.upper_clamp_label, alignment=Qt.AlignCenter)
# 料斗夹具图片上位固定152x60尺寸图片自适应缩放
# 注意:目前由于给出的 料斗夹具图片尺寸大了,只能够先缩放,后期图片对了,可以用上面的
clamp_img = ":/icons/images/料斗夹具.png"
clamp_pixmap = QPixmap(clamp_img)
if not clamp_pixmap.isNull():
self.upper_clamp_label = QLabel()
# 1. 固定QLabel尺寸为152x60
self.upper_clamp_label.setFixedSize(152, 60)
# 2. 图片缩放到152x60保持比例平滑缩放
scaled_pixmap = clamp_pixmap.scaled(
152, 60,
Qt.KeepAspectRatio, # 保持图片原始比例,避免变形
Qt.SmoothTransformation # 平滑缩放,提升显示效果
)
self.upper_clamp_label.setPixmap(scaled_pixmap)
# 3. 确保图片在QLabel中居中显示
self.upper_clamp_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.upper_clamp_label, alignment=Qt.AlignCenter)
# 按钮(上位)
self.upper_open_btn = CircularButton("", group)
self.upper_open_btn.move(60, 153) # 上位料斗的开按钮位置
self.upper_close_btn = CircularButton("", group)
self.upper_close_btn.move(233, 153) # 上位料斗的关按钮位置
self.upper_arch_btn = CircularButton("", group)
self.upper_arch_btn.move(290, 37) # 上位料斗的破拱按钮位置
return group
def connectSignalToSlot(self):
self.upper_arch_btn.clicked.connect(self.onUpperArchBreaking)
self.lower_arch_btn.clicked.connect(self.onLowerArchBreaking)
@Slot()
def onUpperArchBreaking(self):
if self.upper_arch_breaking_status == False: # 不破拱状态
# 此时,点击按钮为开启破拱
self.upper_arch_breaking_status = True
self.upper_arch_label.setHidden(False)
self.upper_arch_breaking_signal.emit(self.upper_arch_breaking_status)
else: # 破拱状态
# 此时,点击按钮为关闭破拱
self.upper_arch_breaking_status = False
self.upper_arch_label.setHidden(True)
self.upper_arch_breaking_signal.emit(self.upper_arch_breaking_status)
@Slot()
def onLowerArchBreaking(self):
if self.lower_arch_breaking_status == False: # 不破拱状态
# 此时,点击按钮为开启破拱
self.lower_arch_breaking_status = True
self.lower_arch_label.setHidden(False)
self.lower_arch_breaking_signal.emit(self.lower_arch_breaking_status)
else: # 破拱状态
# 此时,点击按钮为关闭破拱
self.lower_arch_breaking_status = False
self.lower_arch_label.setHidden(True)
self.upper_arch_breaking_signal.emit(self.lower_arch_breaking_status)
def create_lower_hopper(self):
"""创建下位料斗Widget"""
group = QWidget()
# 下位的 料斗控件的固定尺寸 !!!!
# 注意: 这里需要根据具体的料斗图片来修改
group.setFixedSize(332, 280)
# group.setStyleSheet("background-color: black")
layout = QVBoxLayout(group)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
# 标题标签(下位)
self.lower_title_label = QLabel("低位料斗")
self.lower_title_label.setFixedSize(79, 17)
self.lower_title_label.setAlignment(Qt.AlignCenter)
self.lower_title_label.setStyleSheet("color: #2fd3f2; font-size: 14px; font-weight: bold;")
layout.addWidget(self.lower_title_label, alignment=Qt.AlignCenter)
# 加载外框图片
outer_img = ":/icons/images/下位料斗1.png"
outer_pixmap = QPixmap(outer_img)
if outer_pixmap.isNull():
print(f"警告:图片 {outer_img} 加载失败,请检查路径!")
return group
outer_width = outer_pixmap.width()
outer_height = outer_pixmap.height()
# 背景容器(下位)
self.lower_bg_widget = QWidget()
self.lower_bg_widget.setFixedSize(outer_width, outer_height)
self.lower_bg_widget.setStyleSheet(f"background-image: url({outer_img}); background-repeat: no-repeat; background-position: center;")
layout.addWidget(self.lower_bg_widget, alignment=Qt.AlignCenter)
# 内框图片(下位)
inner_img = ":/icons/images/下位料斗2.png"
inner_pixmap = QPixmap(inner_img)
if not inner_pixmap.isNull():
self.lower_inner_label = QLabel(self.lower_bg_widget)
self.lower_inner_label.setPixmap(inner_pixmap)
self.lower_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height())
self.lower_inner_label.move(14, 9)
# 状态图片(下位,红色)
status_img = ":/icons/images/料斗状态绿.png"
status_pixmap = QPixmap(status_img)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(22, 22, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.lower_status_label = QLabel(self.lower_bg_widget)
self.lower_status_label.setPixmap(status_pixmap)
self.lower_status_label.move(32, 18)
self.lower_status_label.setScaledContents(False)
self.lower_status_label.setStyleSheet("background: none;")
# 破拱图片(下位)
arch_img = ":/icons/images/破拱.png"
arch_pixmap = QPixmap(arch_img)
if not arch_pixmap.isNull():
arch_pixmap = arch_pixmap.scaled(24, 21, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.lower_arch_label = QLabel(self.lower_bg_widget)
self.lower_arch_label.setPixmap(arch_pixmap)
self.lower_arch_label.move(outer_width - 56, 18)
self.lower_arch_label.setStyleSheet("background: none;")
self.lower_arch_label.setHidden(True) # 初始,不破拱状态,隐藏
# 重量文字(下位)
self.lower_weight_label = QLabel("5000kg", self.lower_bg_widget)
self.lower_weight_label.setStyleSheet("background: none; background-color: #262c38; color: #79c053; font-size: 14px;font-weight: bold;")
self.lower_weight_label.setAlignment(Qt.AlignCenter)
self.lower_weight_label.setFixedSize(93, 22)
self.lower_weight_label.move(outer_width//2 - 46, outer_height//2 - 46)
# 额外文字(下位)
self.lower_extra_label = QLabel("开: 25°", self.lower_bg_widget)
self.lower_extra_label.setAlignment(Qt.AlignCenter)
self.lower_extra_label.setStyleSheet("background: none; background-color: #262c38; color: #79c053; font-size: 14px;font-weight: bold;")
self.lower_extra_label.setFixedSize(93, 22)
self.lower_extra_label.move(outer_width//2 - 46, outer_height//2 - 20)
# 料斗夹具图片(下位)
# clamp_img = ":/icons/images/料斗夹具.png"
# clamp_pixmap = QPixmap(clamp_img)
# if not clamp_pixmap.isNull():
# self.lower_clamp_label = QLabel()
# self.lower_clamp_label.setPixmap(clamp_pixmap)
# self.lower_clamp_label.setFixedSize(clamp_pixmap.width(), clamp_pixmap.height())
# layout.addWidget(self.lower_clamp_label, alignment=Qt.AlignCenter)
# 目前由于给出的 料斗夹具图片尺寸大了,只能够先缩放,后期图片对了,可以用上面的
clamp_img = ":/icons/images/料斗夹具.png"
clamp_pixmap = QPixmap(clamp_img)
if not clamp_pixmap.isNull():
self.lower_clamp_label = QLabel()
# 1. 固定QLabel尺寸为152x60
self.lower_clamp_label.setFixedSize(152, 60)
# 2. 图片缩放到152x60保持比例平滑缩放
scaled_pixmap = clamp_pixmap.scaled(
152, 60,
Qt.KeepAspectRatio, # 保持图片原始比例,避免变形
Qt.SmoothTransformation # 平滑缩放,提升显示效果
)
self.lower_clamp_label.setPixmap(scaled_pixmap)
# 3. 确保图片在QLabel中居中显示
self.lower_clamp_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.lower_clamp_label, alignment=Qt.AlignCenter)
# 按钮(下位)
self.lower_open_btn = CircularButton("", group)
self.lower_open_btn.move(60, 233) # 下位料斗的开按钮位置
self.lower_close_btn = CircularButton("", group)
self.lower_close_btn.move(233, 233) # 下位料斗的关按钮位置
self.lower_arch_btn = CircularButton("", group)
self.lower_arch_btn.move(290, 34) # 下位料斗的破拱按钮位置
return group
# 上料斗重量设置
def setUpperHopperWeight(self, weight:float):
self.upper_weight_label.setText(f"{weight}kg")
# 上料斗方量设置
def setUpperHopperVolume(self, volume: float):
"""Args:
volume : 传入多少方
"""
self.upper_extra_label.setText(f"{volume}方(预估)")
# 下料斗重量设置
def setLowerHopperWeight(self, weight:float):
self.lower_weight_label.setText(f"{weight}kg")
# 下料斗开合角度设置
def setLowerHopperOpeningAngle(self, angle: float):
"""Args:
angle : 传入多少度 (单位°)
"""
self.lower_extra_label.setText(f"开: {angle}°")
# ------------------------------
# 设置上料斗状态0=绿1=黄2=红)
# ------------------------------
def setUpperArchStatus(self, status: int):
"""
设置上料斗状态图片
Args:
status: 状态值0=绿1=黄2=红),非指定值不操作
"""
# 过滤无效状态不在0/1/2范围内则直接返回保持当前图片
if status not in [0, 1, 2]:
return
# 状态-图片路径映射
# 注意:需要替换为实际的图片路径
status_img_map = {
0: ":/icons/images/料斗状态绿.png",
1: ":/icons/images/料斗状态黄.png",
2: ":/icons/images/料斗状态红.png"
}
img_path = status_img_map[status]
# 加载并缩放图片
status_pixmap = QPixmap(img_path)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(
22, 22,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
self.upper_status_label.setPixmap(status_pixmap)
# ------------------------------
# 设置下料斗状态0=绿1=黄2=红)
# ------------------------------
def setLowerArchStatus(self, status: int):
"""
设置下料斗状态图片
Args:
status: 状态值0=绿1=黄2=红),非指定值不操作
"""
if status not in [0, 1, 2]:
return
# 注意:需要替换为实际的图片路径
status_img_map = {
0: ":/icons/images/料斗状态绿.png",
1: ":/icons/images/料斗状态黄.png",
2: ":/icons/images/料斗状态红.png"
}
img_path = status_img_map[status]
status_pixmap = QPixmap(img_path)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(
22, 22,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
self.lower_status_label.setPixmap(status_pixmap)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = HopperWidget()
window.setLowerHopperWeight(2000)
window.setUpperHopperVolume(3.0)
window.setLowerHopperOpeningAngle(45)
window.setUpperArchStatus(2)
window.setLowerArchStatus(1)
window.show()
sys.exit(app.exec())

View File

@ -0,0 +1,169 @@
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout
from PySide6.QtGui import QPixmap,QRegion,QPainter, QColor, QPainterPath
from PySide6.QtCore import Qt, QPropertyAnimation, Property, QSize, QRectF
import sys
class MaskedLabel(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.setFixedSize(28, 20) # 遮罩大小
self.setStyleSheet("background-color: transparent; border: none;")
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
parent_widget = self.parent()
bg_color = QColor("#f3f3f3") # 默认颜色
if parent_widget:
# 从父控件的调色板中提取背景色
bg_color = parent_widget.palette().color(parent_widget.backgroundRole())
# 用父控件背景色绘制遮罩
painter.setBrush(bg_color)
painter.setPen(Qt.NoPen)
# 步骤3绘制遮罩路径逻辑不变
path = QPainterPath()
mask_rect = QRectF(0, 0, 28, 20)
circle_radius = 10
circle_center_x = 28
circle_center_y = 10
path.addRect(mask_rect)
path.addEllipse(
circle_center_x - circle_radius,
circle_center_y - circle_radius,
circle_radius * 2,
circle_radius * 2
)
path.setFillRule(Qt.OddEvenFill)
painter.drawPath(path)
class LinearProductionProgress(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self._progress = 0 # 进度0-100
self.setFixedSize(450 + 18, 20)
self.setStyleSheet(
"""
border-radius: 9px;
"""
)
# 底层背景
self.bg_label = QLabel(self)
self.bg_label.setFixedSize(450, 20)
self.bg_label.setStyleSheet(
"""
background-color: #e7e7e7;
"""
)
self.bg_label.move(18, 0)
# 上层进度填充
self.fg_label = QLabel(self)
self.fg_label.setStyleSheet(
"""
background-color: #0052d9;
min-width: 18px;
"""
)
self.fg_label.setFixedHeight(20)
self.fg_label.move(0, 0)
self.fg_label.raise_()
# 百分比标签宽33px高19px右偏9px
self.percent_label = QLabel(self)
self.percent_label.setText("0%")
self.percent_label.setAlignment(Qt.AlignCenter)
self.percent_label.setFixedSize(33, 19)
self.percent_label.setStyleSheet(
"""
color: white;
font-size: 12px;
font-weight: bold;
background-color: transparent;
"""
)
self.percent_label.move(18, 0) # 百分比标签初始位置
self.percent_label.raise_()
# 遮盖左侧区域
self.mask_label = MaskedLabel(self)
self.mask_label.move(0, 0)
# 进度属性
@Property(int)
def progress(self):
return self._progress
@progress.setter
def progress(self, value):
if 0 <= value <= 100 and value != self._progress:
self._progress = value
self.update_foreground()
# 线性更新进度显示
def update_foreground(self):
# 注意为了实现前4%的动态效果,需要从 fd_width的4%起开始计算
fg_width = int(450 * (self._progress + 4) / 100)
self.fg_label.setFixedWidth(fg_width)
# 计算百分比标签位置:进度条右边缘 - 9px偏移 - 标签宽度33px
if fg_width > 60: # 当上层进度条宽度大于60px开始移动
label_x = fg_width - 9 - 33
# 移动百分比标签
self.percent_label.move(label_x, 0)
else:
# 复原百分比标签
# 移动回初始位置
self.percent_label.move(18, 0)
# 设置百分比标签
self.percent_label.setText(f"{self._progress}%")
class ProductionProgressWidget(QWidget):
def __init__(self):
super().__init__()
self.setFixedSize(620, 49) # 进度条控件大小
# 左侧文字标签
self.text_label = QLabel("生产进度")
self.text_label.setFixedSize(112, 29)
self.text_label.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
self.text_label.setStyleSheet("font-family: 'Microsoft YaHei';font-size: 18px;")
# 右侧进度条
self.linear_progress = LinearProductionProgress()
self.main_layout = QHBoxLayout(self)
self.main_layout.addWidget(self.text_label)
self.main_layout.addWidget(self.linear_progress)
self.main_layout.setContentsMargins(0, 0, 0, 0)
def testProgress(self, seconds: float):
self.animation = QPropertyAnimation(self.linear_progress, b"progress")
self.animation.setDuration(seconds * 1000)
self.animation.setStartValue(0)
self.animation.setEndValue(100)
self.animation.start()
def setProgress(self, progress: float):
"""
设置progress之后, 会根据该值调整进度条
Args:
progress: 传入去掉百分号之后的数值, 如80%, 传入80.0
"""
self.linear_progress.progress = progress
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ProductionProgressWidget()
# window.testProgress(60) # 进度条测试
window.show()
sys.exit(app.exec())

View File

@ -0,0 +1,241 @@
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QMessageBox, QLabel, QGridLayout, QDialog
from PySide6.QtGui import QPainter, QColor, QBrush, QFont, QPixmap
from PySide6.QtCore import Qt
import sys
import datetime
import resources.resource_rc
class DeviceStatusPopup(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("设备状态")
self.setFixedSize(898, 354) # 固定总尺寸为898x354
# 通用字体设置:微软雅黑 18px
font = QFont()
font.setFamily("Microsoft YaHei")
font.setPointSize(18)
# 初始化UI组件
self.ip_label = QLabel("ip地址")
self.ip_value = QLabel()
self.status_label = QLabel("连接状态")
self.status_img = QLabel()
self.status_img.setFixedSize(80, 41) # 状态图片尺寸
self.time_label = QLabel("更新时间")
self.time_value = QLabel()
self.algorithm_label = QLabel("算法结果")
self.algorithm_value = QLabel()
# 右侧大图片369x185
self.right_image = QLabel()
# self.right_image.setFixedSize(369, 185)
self.right_image.setFixedSize(520, 320)
# 临时设置占位图(实际使用时替换为你的图片路径)
# placeholder = QPixmap(520, 340)
# placeholder.fill(QColor(229, 229, 229)) # 灰色占位
# self.right_image.setPixmap(placeholder)
self.right_image.setText("图片未加载")
self.right_image.setStyleSheet("background-color: rgb(229,229,229)")
self.right_image.setAlignment(Qt.AlignCenter) # 图片居中显示
# 左侧栏字体设置:微软雅黑 18px
left_font = QFont("Microsoft YaHei", 18)
# 右侧栏字体设置:微软雅黑 15px
right_font = QFont("Microsoft YaHei", 15)
# 设置左侧文本标签的字体和左对齐
for label in [self.ip_label, self.status_label,
self.time_label, self.algorithm_label]:
label.setFont(left_font)
label.setAlignment(Qt.AlignLeft) # 左对齐
# 设置右侧文本标签的字体和左对齐
for label in [self.ip_value, self.time_value, self.algorithm_value]:
label.setFont(right_font)
label.setAlignment(Qt.AlignLeft) # 左对齐
# 三列布局:左侧标签列 + 中间内容列 + 右侧图片列
layout = QGridLayout()
# 设置布局边距和间距(避免内容贴边)
layout.setContentsMargins(10, 10, 10, 10) # 上下左右边距
layout.setSpacing(15) # 控件间距
# 第一列:标签(左对齐)
layout.addWidget(self.ip_label, 0, 0, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.status_label, 1, 0, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.time_label, 2, 0, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.algorithm_label, 3, 0, alignment=Qt.AlignLeft | Qt.AlignVCenter)
# 第二列:内容(左对齐)
layout.addWidget(self.ip_value, 0, 1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.status_img, 1, 1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.time_value, 2, 1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
layout.addWidget(self.algorithm_value, 3, 1, alignment=Qt.AlignLeft | Qt.AlignVCenter)
# 第三列右侧大图片跨4行垂直居中
layout.addWidget(self.right_image, 0, 2, 4, 1, alignment=Qt.AlignCenter)
# 列宽分配总宽度898 = 左列 + 中列 + 右列(369) + 边距(40) + 间距(30)
# 计算可用宽度898 - 10*2(边距) - 15*2(列间距) - 520(右列) = 328
layout.setColumnStretch(0, 1) # 左列占1份
layout.setColumnStretch(1, 2) # 中列占2份总3份分配328宽度
self.setLayout(layout)
# 初始状态设置
self.update_connection_status("在线")
self.update_algorithm_result("ok")
self.update_ip_address("192.168.1.5")
def update_connection_status(self, status: str):
"""更新连接状态,切换显示图片"""
img_path = ":/icons/images/在线.png" if status == "在线" else ":/icons/images/离线.png"
pixmap = QPixmap(img_path)
self.status_img.setPixmap(pixmap.scaled(
self.status_img.width(),
self.status_img.height(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
))
self._update_time()
def _update_time(self):
"""刷新当前时间"""
now = datetime.datetime.now()
self.time_value.setText(now.strftime("%Y-%m-%d %H:%M:%S"))
def update_ip_address(self, ip: str):
"""更新IP地址"""
self.ip_value.setText(ip)
def update_algorithm_result(self, result: str):
"""更新算法结果"""
self.algorithm_value.setText(result)
def update_right_image(self, img_path: str):
"""更新右侧大图片的接口"""
pixmap = QPixmap(img_path)
self.right_image.setPixmap(pixmap.scaled(
self.right_image.width(),
self.right_image.height(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
))
class CircularPushButton(QPushButton):
"""圆形按钮组件,点击触发弹窗"""
def __init__(self, text, parent=None):
super().__init__(text, parent)
self.setFixedSize(66, 66) # 固定尺寸60x60
self.setFont(QFont("Microsoft YaHei", 9)) # 字体适配尺寸
# 状态颜色定义
self.color_map = {
0: QColor(109, 218, 99), # 正常-绿色
1: QColor(222, 182, 61), # 警告-黄色
2: QColor(241, 63, 61) # 异常-红色
}
self.current_status = 0 # 默认正常状态
def set_status(self, status: int):
"""设置状态0-正常1-警告2-异常"""
if status in self.color_map:
self.current_status = status
self.update() # 触发重绘
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿
# 绘制圆形背景
brush = QBrush(self.color_map[self.current_status])
painter.setBrush(brush)
painter.setPen(Qt.NoPen)
painter.drawEllipse(0, 0, self.width(), self.height())
# 绘制文字
painter.setPen(Qt.black)
painter.drawText(self.rect(), Qt.AlignCenter, self.text())
class StatusMonitorWidget(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("设备状态监控面板")
# self.resize(600, 120) # 窗口大小适配按钮尺寸
self.setFixedSize(720, 100)
layout = QHBoxLayout(self)
layout.setSpacing(15) # 组件间距
layout.setContentsMargins(20, 20, 20, 20) # 窗口边距
# 初始化所有圆形按钮
self.buttons = [
CircularPushButton("RFID对比"),
CircularPushButton("盖板对齐"),
CircularPushButton("溢料状态"),
CircularPushButton("下料度"),
CircularPushButton("IOT平台"),
CircularPushButton("任务平台"),
CircularPushButton("搅拌楼"),
CircularPushButton("LED"),
CircularPushButton("控制设备")
]
# 为每个按钮连接点击弹窗事件
for idx, btn in enumerate(self.buttons):
btn.clicked.connect(lambda checked, btn_idx=idx: self.show_popup(btn_idx))
# 将按钮添加到布局
for btn in self.buttons:
layout.addWidget(btn)
def show_popup(self, btn_idx):
"""点击按钮后弹出提示窗口"""
btn_text = self.buttons[btn_idx].text()
status_map = {0: "正常", 1: "警告", 2: "异常"}
status = status_map[self.buttons[btn_idx].current_status]
# QMessageBox.information(
# self,
# f"{btn_text} 详情",
# f"设备:{btn_text}\n状态{status}\n"
# )
statusWin = DeviceStatusPopup(self)
# 此处 可以更新设备的状态,如 设备ip、算法结果等
# 需要连接检测相关代码
statusWin.exec()
def setButtonStatus(self, button_name: str, status: int):
"""
根据按钮名称设置状态
Args:
button_name: 按钮显示的文本(如"RFID对比"
status: 状态值0-正常1-警告2-异常)
"""
# 遍历按钮列表,匹配名称
for btn in self.buttons:
if btn.text() == button_name:
btn.set_status(status) # 调用按钮的set_status方法
return # 找到后退出循环
# 未找到对应按钮,打印提示
print(f"警告:未找到名为「{button_name}」的按钮")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = StatusMonitorWidget()
window.setButtonStatus("溢料状态", 1)
window.setButtonStatus("LED", 2)
window.show()
sys.exit(app.exec())

View File

@ -0,0 +1,76 @@
from PySide6.QtWidgets import QApplication, QPushButton, QHBoxLayout, QWidget
from PySide6.QtGui import QIcon
from PySide6.QtCore import QSize, Qt
import sys
import resources.resource_rc
class SystemButtonWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# 创建水平布局并设置按钮间距为27px
layout = QHBoxLayout()
layout.setSpacing(27) # 按钮之间的间距
layout.setContentsMargins(6, 6, 6, 6) # 布局边缘留白
self.setWindowTitle("系统按钮")
self.setFixedSize(776, 65) # widget大小
# 按钮样式表
button_style = """
QPushButton {
width: 120px; /* 宽120px */
height: 40px; /* 高40px */
background-color: #7f95bf; /* 平时颜色 */
color: white; /* 文字颜色(白色更清晰) */
font-size: 20px; /* 字号20px */
border: none; /* 去掉默认边框 */
border-radius: 4px; /* 轻微圆角(可选,更美观) */
}
QPushButton:hover {
background-color: #2573d3; /* 鼠标悬停颜色 */
}
QPushButton:pressed {
background-color: #1d5cb8; /* 点击时颜色(可选增强) */
}
"""
# 创建按钮列表(图标+文字)
# 注意:需要替换为实际的图片路径
buttons_info = [
("系统启动", ":/icons/images/系统启动.png"),
("系统停止", ":/icons/images/系统停止.png"),
("系统复位", ":/icons/images/系统复位.png"),
("AI检测", ":/icons/images/AI检测.png"),
("任务刷新", ":/icons/images/任务刷新.png")
]
# 存放按钮控件的字典
self.buttons = dict()
for text, icon_path in buttons_info:
btn = QPushButton(text)
# 设置图标
icon = QIcon(icon_path)
btn.setIcon(icon)
btn.setIconSize(QSize(24, 24)) # 图标尺寸(与按钮比例协调)
# 设置固定尺寸(确保宽高生效)
btn.setFixedSize(120, 40)
# 应用样式
btn.setStyleSheet(button_style)
# 添加到布局
layout.addWidget(btn)
self.buttons[text] = btn
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SystemButtonWidget()
window.show()
sys.exit(app.exec())