add main_ui.py(中交三航主界面)
35
.gitignore
vendored
Normal 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/
|
||||
21
controller/main_controller.py
Normal 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
|
After Width: | Height: | Size: 342 B |
BIN
images/上位料斗1.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
images/上位料斗2.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
images/下位料斗1.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
images/下位料斗2.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
images/任务刷新.png
Normal file
|
After Width: | Height: | Size: 582 B |
BIN
images/在线.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
images/拱1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
images/拱2.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/拱3.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
images/料斗夹具.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
images/料斗状态红.png
Normal file
|
After Width: | Height: | Size: 726 B |
BIN
images/料斗状态绿.png
Normal file
|
After Width: | Height: | Size: 732 B |
BIN
images/料斗状态黄.png
Normal file
|
After Width: | Height: | Size: 731 B |
BIN
images/破拱.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
images/离线.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
images/系统停止.png
Normal file
|
After Width: | Height: | Size: 159 B |
BIN
images/系统启动.png
Normal file
|
After Width: | Height: | Size: 271 B |
BIN
images/系统复位.png
Normal file
|
After Width: | Height: | Size: 525 B |
8
main_ui.py
Normal 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
23
resources/resources.qrc
Normal 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
@ -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())
|
||||
239
view/widgets/arc_progress_widget.py
Normal 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())
|
||||
36
view/widgets/circular_button.py
Normal 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())
|
||||
407
view/widgets/hopper_widget.py
Normal 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())
|
||||
169
view/widgets/production_progress_widget.py
Normal 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())
|
||||
241
view/widgets/status_monitor_widget.py
Normal 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())
|
||||
76
view/widgets/system_button_widget.py
Normal 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())
|
||||