增加重量更新显示,hopper_controller 提供了料斗模块的数据接口

This commit is contained in:
2025-11-06 10:55:29 +08:00
parent 84382b0cc0
commit d2603cec4d
16 changed files with 278 additions and 94 deletions

2
.gitignore vendored
View File

@ -35,3 +35,5 @@ PyQt6_Fluent_Widgets.egg-info/
PySide2_Fluent_Widgets.egg-info/
/hardware/__pycache__
__pycache__
/core/__pycache__
/vision/__pycache__

View File

@ -0,0 +1,153 @@
from PySide6.QtCore import QTimer, Signal, QObject, Slot
import threading
from hardware.transmitter import TransmitterController
from hardware.relay import RelayController
from view.widgets.hopper_widget import HopperWidget
from view.widgets.conveyor_system_widget import ConveyorSystemWidget
# 信号类:后台线程向主线程传递数据
class HopperSignals(QObject):
upper_weight_updated = Signal(int) # 上料斗重量更新信号
class HopperController:
def __init__(self, hopper_view:HopperWidget, conveyor_view:ConveyorSystemWidget):
self.hopper_view = hopper_view
self.conveyor_view = conveyor_view # 控制传送带中的上料斗
self.signals = HopperSignals() # 信号
# 下料斗夹爪测试用例数据
# 注意:目前只控制 下料斗的夹爪角度变化
self.angle = 10 # 夹爪当前角度
self.max_angle = 60 # 夹爪最大张开角度
self.min_angle = 10 # 夹爪最小张开角度
self.is_add = True # 角度增加/减小 控制
self.timer_angle = QTimer() # 角度更新定时器
self.timer_angle.setInterval(1000) # 1秒更新一次角度
# 重量读取定时器
self.timer_weight = QTimer() # 重量读取定时器
self.timer_weight.setInterval(2000) # 每2秒读取一次重量
# 绑定信号
self._connect_signals()
# 开启定时器
self.timer_angle.start()
self.timer_weight.start()
def _connect_signals(self):
# 更新上料斗重量
self.signals.upper_weight_updated.connect(self.onUpdateUpperHopperWeight)
# 上料斗重量定时读取
self.timer_weight.timeout.connect(self.handleReadUpperHopperWeight)
# 下料斗夹爪定时更新
self.timer_angle.timeout.connect(self.handleLowerClampAngleUpdate)
# 上料斗 "开"按钮点击
self.hopper_view.upper_open_btn.clicked.connect(self.onUpperClampOpenBottonClicked)
# 上料斗 "破拱"按钮
self.hopper_view.upper_arch_breaking_signal.connect(self.onUpperArchBreaking)
# 下料斗 "开"按钮点击
self.hopper_view.lower_open_btn.clicked.connect(self.onLowerClampOpenBottonClicked)
# 下料斗 "破拱"按钮
self.hopper_view.lower_arch_breaking_signal.connect(self.onLowerArchBreaking)
def handleLowerClampAngleUpdate(self):
"""处理下料斗夹爪开合"""
# 角度增减逻辑
if self.is_add:
self.angle += 1
else:
self.angle -= 1
# 边界控制
if self.angle > self.max_angle:
self.is_add = False
self.angle = self.max_angle
if self.angle <= self.min_angle:
self.is_add = True
self.angle = self.min_angle
# 更新下料斗夹爪角度
self.onUpdateLowerClampAngle(self.angle)
@Slot(int)
def onUpdateUpperHopperWeight(self, weight:int):
"更新上料斗重量"
self.hopper_view.setUpperHopperWeight(weight)
# 注意:此时需要同步更新传送带中的上料斗的重量
self.conveyor_view.setConveyorHopperWeight(weight)
@Slot(int)
def onUpdateLowerHopperWeight(self, weight:int):
"更新下料斗重量"
self.hopper_view.setLowerHopperWeight(weight)
@Slot()
def handleReadUpperHopperWeight(self):
# 后台读取上料斗重量
def upper_weight_task():
loc_tra = TransmitterController(RelayController())
# 上料斗重量 (目前只有上料斗安装变送器, 可以读取到重量)
upper_weight = loc_tra.read_data(1)
# 发送信号到主线程更新UI
if upper_weight is not None:
self.signals.upper_weight_updated.emit(upper_weight)
threading.Thread(target=upper_weight_task, daemon=True).start()
@Slot(float)
def onUpdateLowerClampAngle(self, angle:float):
"""更新下料斗夹爪角度"""
self.hopper_view.setLowerHopperOpeningAngle(angle)
@Slot(float)
def onUpdateUpperClampAngle(self, angle:float):
"""更新上料斗夹爪角度"""
self.hopper_view.setUpperHopperClampAngle(angle)
@Slot()
def onUpperClampOpenBottonClicked(self):
# 上料斗 夹爪 "开"按钮点击
print("hopper_controller: onUpperClampOpenBottonClicked")
# 测试上料斗夹爪6秒打开60度
self.hopper_view.upper_clamp_widget.testAnimation(target_angle=60, duration=6)
@Slot(bool)
def onUpperArchBreaking(self, status:bool):
"""上料斗破拱: status 为True表示 开启破拱, 为False表示 关闭破拱"""
print("hopper_controller: onUpperArchBreaking ", status)
@Slot(int)
def onUpperHopperStatusChanged(self, status:int):
"""上料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """
# 料斗中的状态指示器
self.hopper_view.setUpperHopperStatus(status)
@Slot(float)
def onUpdateUpperHopperVolume(self, volume: float):
"""更新上料斗显示的方量,如: 2.0"""
self.hopper_view.setUpperHopperVolume(volume)
@Slot()
def onLowerClampOpenBottonClicked(self):
# 下料斗 夹爪 "开"按钮点击
print("hopper_controller: onLowerClampOpenBottonClicked")
@Slot(bool)
def onLowerArchBreaking(self, status:bool):
"""下料斗破拱: status 为True表示 开启破拱, 为False表示 关闭破拱"""
print("hopper_controller: onLowerArchBreaking ", status)
@Slot(int)
def onLowerHopperStatusChanged(self, status:int):
"""下料斗状态改变: status为 0=绿(正常), 1=黄(警告), 2=红(异常) """
# 料斗中的状态指示器
self.hopper_view.setLowerHopperStatus(status)

View File

@ -5,78 +5,17 @@ from hardware import transmitter
from view.main_window import MainWindow
from .camera_controller import CameraController
from .bottom_control_controller import BottomControlController
from hardware.transmitter import TransmitterController
from hardware.relay import RelayController
# 定义信号类(用于后台线程向 UI主线程传递数据
class Signals(QObject):
weight_updated = Signal(int) # 传递上料斗重量
from .hopper_controller import HopperController
class MainController:
def __init__(self):
# 主界面
self.main_window = MainWindow()
# 1. 用于更新下料斗夹爪角度的QTimer
self.timer_angle = QTimer()
self.timer_angle.setInterval(1000) # 1秒触发一次
self.timer_angle.timeout.connect(self._onTimer2) # 连接到角度更新函数
self.timer_angle.start()
# 下料斗夹具角度相关参数
self.angle = 10
self.max_angle = 60
self.min_angle = 10
self.is_add = True
# 2. 用于更新重量的 QTimer
self.timer_weight = QTimer()
self.timer_weight.setInterval(2000) # 2秒触发一次
self.timer_weight.timeout.connect(self._read_weight_in_background)
self.timer_weight.start()
# 信号实例(用于后台线程传递数据到主线程)
self.signals = Signals()
self.signals.weight_updated.connect(self._update_upper_weight) # 主线程更新UI
# 初始化子界面和控制器
self._initSubViews()
self._initSubControllers()
def _read_weight_in_background(self):
"""在后台线程中读取重量"""
def weight_task():
loc_tra = TransmitterController(RelayController())
# 上料斗重量 (目前只有上料斗安装变送器, 可以读取到重量)
upper_weight = loc_tra.read_data(1)
# 发送信号到主线程更新UI
if upper_weight is not None:
self.signals.weight_updated.emit(upper_weight)
# 启动后台线程执行重量读取操作
threading.Thread(target=weight_task, daemon=True).start()
def _update_upper_weight(self, weight):
"""主线程中 更新界面上料斗重量数据"""
self.main_window.hopper_widget.setUpperHopperWeight(weight)
def _onTimer2(self):
"""QTimer触发的 料斗夹爪角度更新"""
if self.is_add:
self.angle += 1
else:
self.angle -= 1
if self.angle > self.max_angle:
self.is_add = False
self.angle = self.max_angle
if self.angle <= self.min_angle:
self.is_add = True
self.angle = 10
# 直接更新UI
self.main_window.hopper_widget.setLowerHopperOpeningAngle(self.angle)
def showMainWindow(self):
self.main_window.showFullScreen()
self.main_window.dispatch_task_widget.set_task_time("task1","15:44 PM")
@ -85,13 +24,23 @@ class MainController:
self.main_window.segment_task_widget.set_task_time("task2","17:24 PM")
def _initSubControllers(self):
# 右侧视频显示控制模块
self.camera_controller = CameraController(
video_view=self.main_window.vibration_video
)
# 底部按钮控制模块
self.bottom_control_controller = BottomControlController(
bottom_control_widget=self.main_window.bottom_control_widget,
main_window=self.main_window
)
# 料斗控制模块(包括 夹爪开合、拱等按钮)
self.hopper_controller = HopperController(
hopper_view = self.main_window.hopper_widget,
conveyor_view = self.main_window.conveyor_system_widget
)
def _initSubViews(self):
pass

View File

@ -74,13 +74,48 @@ class ConveyorSystemWidget(QWidget):
inner_img = ImagePaths.HOPPER2
inner_pixmap = QPixmap(inner_img)
if not inner_pixmap.isNull():
upper_inner_label = QLabel(upper_bg_widget)
upper_inner_label.setPixmap(inner_pixmap)
upper_inner_label.setFixedSize(inner_pixmap.width(), inner_pixmap.height())
upper_inner_label.move(14, 9) # 保持原位置
self.upper_inner_label = QLabel(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.setScaledContents(False)
self.upper_inner_label.setStyleSheet("background: none;")
self.upper_inner_label.move(14, 9)
self.upper_inner_label.setAlignment(Qt.AlignBottom)
return group
def _update_upper_inner_height(self, total_weight, current_weight: float):
"""根据当前重量占比, 更新upper_inner_label的高度, 实现动态进度的效果"""
# 1、处理边界值超过总重量按100%低于0按0%
clamped_weight = max(0.0, min(current_weight, total_weight))
# 2、计算占比0~1之间
weight_ratio = clamped_weight / (total_weight * 1.0)
# 3、根据占比计算实际高度
inner_img_height = 100 # 内部的料斗阴影的高度为100px
target_height = int(weight_ratio * inner_img_height)
# print("target_height: ", target_height)
# 4、设置标签高度动态变化
self.upper_inner_label.setFixedHeight(target_height)
# 5、计算标签位置确保标签底部与父容器底部对齐
container_bottom = 117 # 容器的高固定为了 117px (背景图片"料斗1"的高)
label_y = container_bottom - target_height - 8 # 标签顶部y坐标 (减去底部8px)
self.upper_inner_label.move(14, label_y) # x固定y动态计算
# print("label_y", label_y)
# 6、强制刷新UI确保立即显示变化
self.upper_inner_label.update()
def setConveyorHopperWeight(self, weight:float):
# 1、更新传送带中的 上料斗内部进度显示
# 假设上料斗装满之后,总的重量为 5100kg
total_weight = 5100
self._update_upper_inner_height(total_weight, weight)
def create_conveyor(self):
"""创建传送带组件包含左右齿轮group容器背景为传送带图片"""
group = QWidget()

View File

@ -89,18 +89,21 @@ class HopperWidget(QWidget):
# 背景容器(上位)
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;")
self.upper_bg_widget.setStyleSheet(f"background-image: url({outer_img});background-repeat: no-repeat; background-position: center;")
# self.upper_bg_widget.setStyleSheet(f"background-color:red; 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.setFixedSize(inner_pixmap.width(), inner_pixmap.height()) # 初始宽高
self.upper_inner_label.setScaledContents(False) # 禁用缩放(避免图片拉伸)
self.upper_inner_label.setStyleSheet("background: none;")
self.upper_inner_label.move(14, 9)
self.upper_inner_label.setAlignment(Qt.AlignBottom)
# 状态图片(上位,绿色)
status_img = ImagePaths.HOPPER_STATUS_GREEN
@ -162,17 +165,6 @@ class HopperWidget(QWidget):
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: # 不破拱状态
@ -251,7 +243,10 @@ class HopperWidget(QWidget):
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.setScaledContents(False) # 禁用图片缩放
self.lower_inner_label.setStyleSheet("background: none;")
self.lower_inner_label.move(14, 9)
self.lower_inner_label.setAlignment(Qt.AlignBottom)
# 状态图片(下位)
status_img = ImagePaths.HOPPER_STATUS_GREEN
@ -313,10 +308,42 @@ class HopperWidget(QWidget):
return group
def _update_upper_inner_height(self, total_weight, current_weight: float):
"""根据当前重量占比, 更新upper_inner_label的高度, 实现动态进度的效果"""
# 1、处理边界值超过总重量按100%低于0按0%
clamped_weight = max(0.0, min(current_weight, total_weight))
# 2、计算占比0~1之间
weight_ratio = clamped_weight / (total_weight * 1.0)
# 3、根据占比计算实际高度
inner_img_height = 100 # 内部的料斗阴影的高度为100px
target_height = int(weight_ratio * inner_img_height)
# print("target_height: ", target_height)
# 4、设置标签高度动态变化
self.upper_inner_label.setFixedHeight(target_height)
# 5、计算标签位置确保标签底部与父容器底部对齐
container_bottom = self.upper_bg_widget.y() + self.upper_bg_widget.height()
label_y = container_bottom - target_height - 8 # 标签顶部y坐标 (减去底部8px)
self.upper_inner_label.move(14, label_y) # x固定y动态计算
# print("label_y", label_y)
# 6、强制刷新UI确保立即显示变化
self.upper_inner_label.update()
# 上料斗重量设置
def setUpperHopperWeight(self, weight:float):
# 1、更新上料斗重量标签显示最新重量
self.upper_weight_label.setText(f"{weight}kg")
# 2、更新上料斗内部进度显示
# 假设上料斗装满之后,总的重量为 5100kg
total_weight = 5100
self._update_upper_inner_height(total_weight, weight)
# 上料斗方量设置
def setUpperHopperVolume(self, volume: float):
"""Args:
@ -334,8 +361,36 @@ class HopperWidget(QWidget):
# 下料斗重量设置
def setLowerHopperWeight(self, weight:float):
# 1、更新下料斗显示标签显示的重量
self.lower_weight_label.setText(f"{weight}kg")
# 2、更新下料斗的进度显示
# 假设下料斗装满之后 总重量为 5100kg
total_weight = 5100
self._update_lower_inner_height(total_weight, weight)
def _update_lower_inner_height(self, total_weight, current_weight: float):
# 1、处理边界值
clamped_weight = max(0.0, min(current_weight, total_weight))
# 2、计算占比
weight_ratio = clamped_weight / (total_weight * 1.0)
# 3、根据占比计算当前的实际高度
inner_img_height = 100 # 内部料斗阴影的高度为100px
target_height = int(weight_ratio * inner_img_height)
# 4、设置内部阴影标签的高度
self.lower_inner_label.setFixedHeight(target_height)
# 5、计算标签位置
container_bottom = self.lower_bg_widget.y() + self.lower_bg_widget.height()
label_y = container_bottom - target_height - 8
self.lower_inner_label.move(14, label_y)
# 6、强制刷新UI确保立即显示变化
self.lower_inner_label.update()
# 下料斗开合角度设置 (包括 夹爪和标签)
def setLowerHopperOpeningAngle(self, angle: float):
"""Args:
@ -347,7 +402,7 @@ class HopperWidget(QWidget):
# ------------------------------
# 设置上料斗状态0=绿1=黄2=红)
# ------------------------------
def setUpperArchStatus(self, status: int):
def setUpperHopperStatus(self, status: int):
"""
设置上料斗状态图片
Args:
@ -369,17 +424,12 @@ class HopperWidget(QWidget):
# 加载并缩放图片
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):
def setLowerHopperStatus(self, status: int):
"""
设置下料斗状态图片
Args:
@ -398,11 +448,6 @@ class HopperWidget(QWidget):
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)
# 隐藏上料斗 (用于上料斗移动)
@ -428,7 +473,7 @@ if __name__ == "__main__":
window.setLowerHopperWeight(2000)
window.setUpperHopperVolume(3.0)
window.setLowerHopperOpeningAngle(45)
window.setUpperArchStatus(2)
window.setLowerArchStatus(1)
window.setUpperHopperStatus(2)
window.setLowerHopperStatus(1)
window.show()
sys.exit(app.exec())