Files
Feeding_control_system/view/widgets/hopper_widget.py
2025-11-01 18:52:19 +08:00

426 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
from .clamp_widget import ClampWidget
import resources.resources_rc
from utils.image_paths import ImagePaths
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.setFixedSize(356, 516)
# 创建上位和下位料斗
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.setFixedSize(332, 222) # 调整!!
# 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(117, 23)
self.upper_title_label.setAlignment(Qt.AlignCenter)
# self.upper_title_label.setStyleSheet("color: #2fd3f2; font-size: 14px; font-weight: bold;")
# self.upper_title_label.setStyleSheet("color: #0bffff; font-size: 18px;")
self.upper_title_label.setStyleSheet(f"""
color: #0bffff;
font-size: 18px;
background-image: url({ImagePaths.TEXT_TITLE_BG});
background-repeat: no-repeat;
background-position: center;
margin-bottom: 4px;
""")
layout.addWidget(self.upper_title_label, alignment=Qt.AlignCenter)
# 加载外框图片
outer_img = ImagePaths.HOPPER1
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 = ImagePaths.HOPPER2
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 = ImagePaths.HOPPER_STATUS_GREEN
status_pixmap = QPixmap(status_img)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(13, 13, 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(22, 12)
self.upper_status_label.setScaledContents(False)
self.upper_status_label.setStyleSheet("background: none;")
# 破拱图片(上位)
arch_img = ImagePaths.ARCH_BREAK
arch_pixmap = QPixmap(arch_img)
if not arch_pixmap.isNull():
arch_pixmap = arch_pixmap.scaled(16, 13, 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 - 39, 12)
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: #003669; color: #16ffff; font-size: 18px;")
# self.upper_weight_label.setFixedSize(93, 22)
self.upper_weight_label.setFixedSize(120, 29)
self.upper_weight_label.move(outer_width//2 - 60, outer_height//2 - 46)
# 额外文字(上位)
self.upper_extra_label = QLabel("2.0方(预估)", self.upper_bg_widget)
self.upper_extra_label.setAlignment(Qt.AlignCenter)
# #262c38 #16ffff #131427 #003669
self.upper_extra_label.setStyleSheet("background: none; background-color: #003669; color: #16ffff; font-size: 18px;")
self.upper_extra_label.setFixedSize(120, 29)
self.upper_extra_label.move(outer_width//2 - 60, outer_height//2 - 13)
# 夹具容器
self.upper_clamp_widget = ClampWidget()
layout.addWidget(self.upper_clamp_widget, alignment=Qt.AlignCenter)
self.upper_bg_widget.raise_() # 夹具图层位于 upper_bg_widget之下
# 按钮(上位料斗)
self.upper_open_btn = CircularButton("", group)
# self.upper_open_btn.move(60, 153) # 上位料斗的开按钮位置
self.upper_open_btn.move(290, 90)
# 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)
self.upper_open_btn.clicked.connect(self.onUpperClampOpen)
self.lower_open_btn.clicked.connect(self.onLowerClampOpen)
@Slot()
def onUpperClampOpen(self):
self.upper_clamp_widget.testAnimation(target_angle=60, duration=6) # 测试6秒打开60度
@Slot()
def onLowerClampOpen(self):
self.lower_clamp_widget.testAnimation(target_angle=25, duration=6) # 测试6秒打开30度
@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.setFixedSize(332, 222)
# 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(117, 23)
self.lower_title_label.setAlignment(Qt.AlignCenter)
# self.lower_title_label.setStyleSheet("color: #2fd3f2; font-size: 14px; font-weight: bold;")
# self.lower_title_label.setStyleSheet("color: #0bffff; font-size: 18px;")
self.lower_title_label.setStyleSheet(f"""
color: #0bffff;
font-size: 18px;
background-image: url({ImagePaths.TEXT_TITLE_BG});
background-repeat: no-repeat;
background-position: center;
margin-bottom: 4px;
""")
layout.addWidget(self.lower_title_label, alignment=Qt.AlignCenter)
# 加载外框图片
outer_img = ImagePaths.HOPPER1
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 = ImagePaths.HOPPER2
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 = ImagePaths.HOPPER_STATUS_GREEN
status_pixmap = QPixmap(status_img)
if not status_pixmap.isNull():
status_pixmap = status_pixmap.scaled(13, 13, 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(22, 12)
self.lower_status_label.setScaledContents(False)
self.lower_status_label.setStyleSheet("background: none;")
# 破拱图片(下位)
arch_img = ImagePaths.ARCH_BREAK
arch_pixmap = QPixmap(arch_img)
if not arch_pixmap.isNull():
arch_pixmap = arch_pixmap.scaled(16, 13, 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 - 39, 12)
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.setStyleSheet("background: none; background-color: #003669; color: #16ffff; font-size: 18px;")
self.lower_weight_label.setAlignment(Qt.AlignCenter)
# self.lower_weight_label.setFixedSize(93, 22)
self.lower_weight_label.setFixedSize(120, 29)
# self.lower_weight_label.move(outer_width//2 - 46, outer_height//2 - 46)
self.lower_weight_label.move(outer_width//2 - 60, 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.setStyleSheet("background: none; background-color: #003669; color: #16ffff; font-size: 18px;")
# self.lower_extra_label.setFixedSize(93, 22)
self.lower_extra_label.setFixedSize(120, 29)
# self.lower_extra_label.move(outer_width//2 - 46, outer_height//2 - 20)
self.lower_extra_label.move(outer_width//2 - 60, outer_height//2 - 13)
# 夹具容器
self.lower_clamp_widget = ClampWidget()
layout.addWidget( self.lower_clamp_widget, alignment=Qt.AlignCenter)
self.lower_bg_widget.raise_() # 夹具图层位于 self.lower_bg_widget之下
# 按钮(下位)
self.lower_open_btn = CircularButton("", group)
# self.lower_open_btn.move(60, 233) # 下位料斗的开按钮位置
self.lower_open_btn.move(290, 90)
# 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}°")
self.lower_clamp_widget.set_angle(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: ImagePaths.HOPPER_STATUS_GREEN,
1: ImagePaths.HOPPER_STATUS_YELLOW,
2: ImagePaths.HOPPER_STATUS_RED
}
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: ImagePaths.HOPPER_STATUS_GREEN,
1: ImagePaths.HOPPER_STATUS_YELLOW,
2: ImagePaths.HOPPER_STATUS_RED
}
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)
# 隐藏上料斗 (用于上料斗移动)
def hideUpperHopper(self):
self.upper_title_label.hide()
self.upper_bg_widget.hide()
self.upper_clamp_widget.hide()
self.upper_open_btn.hide()
self.upper_arch_btn.hide()
# 显示上料斗 (用于上料斗移动完成后恢复显示)
def showUpperHopper(self):
self.upper_title_label.setHidden(False)
self.upper_bg_widget.setHidden(False)
self.upper_clamp_widget.setHidden(False)
self.upper_open_btn.setHidden(False)
self.upper_arch_btn.setHidden(False)
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())