diff --git a/.gitignore b/.gitignore index 4570eb6..62b0bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ PyQt6_Fluent_Widgets.egg-info/ PySide2_Fluent_Widgets.egg-info/ /hardware/__pycache__ __pycache__ +/core/__pycache__ +/vision/__pycache__ diff --git a/controller/hopper_controller.py b/controller/hopper_controller.py new file mode 100644 index 0000000..029ed4d --- /dev/null +++ b/controller/hopper_controller.py @@ -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) \ No newline at end of file diff --git a/controller/main_controller.py b/controller/main_controller.py index 586def9..e45aeb9 100644 --- a/controller/main_controller.py +++ b/controller/main_controller.py @@ -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 \ No newline at end of file diff --git a/core/__pycache__/__init__.cpython-39.pyc b/core/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 893971b..0000000 Binary files a/core/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/core/__pycache__/state.cpython-39.pyc b/core/__pycache__/state.cpython-39.pyc deleted file mode 100644 index 75b8544..0000000 Binary files a/core/__pycache__/state.cpython-39.pyc and /dev/null differ diff --git a/core/__pycache__/system.cpython-39.pyc b/core/__pycache__/system.cpython-39.pyc deleted file mode 100644 index e8b6a2b..0000000 Binary files a/core/__pycache__/system.cpython-39.pyc and /dev/null differ diff --git a/view/widgets/conveyor_system_widget.py b/view/widgets/conveyor_system_widget.py index 9f8b5c2..b1c1942 100644 --- a/view/widgets/conveyor_system_widget.py +++ b/view/widgets/conveyor_system_widget.py @@ -74,12 +74,47 @@ 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容器背景为传送带图片)""" diff --git a/view/widgets/hopper_widget.py b/view/widgets/hopper_widget.py index 3390208..1dfc5f3 100644 --- a/view/widgets/hopper_widget.py +++ b/view/widgets/hopper_widget.py @@ -89,9 +89,9 @@ 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 @@ -99,8 +99,11 @@ class HopperWidget(QWidget): 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()) \ No newline at end of file diff --git a/vision/__pycache__/__init__.cpython-39.pyc b/vision/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index f4f43ef..0000000 Binary files a/vision/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/alignment_detector.cpython-39.pyc b/vision/__pycache__/alignment_detector.cpython-39.pyc deleted file mode 100644 index f0422c8..0000000 Binary files a/vision/__pycache__/alignment_detector.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/anger_caculate.cpython-39.pyc b/vision/__pycache__/anger_caculate.cpython-39.pyc deleted file mode 100644 index 6913261..0000000 Binary files a/vision/__pycache__/anger_caculate.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/angle_detector.cpython-39.pyc b/vision/__pycache__/angle_detector.cpython-39.pyc deleted file mode 100644 index 652bdbf..0000000 Binary files a/vision/__pycache__/angle_detector.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/camera.cpython-39.pyc b/vision/__pycache__/camera.cpython-39.pyc deleted file mode 100644 index 874614b..0000000 Binary files a/vision/__pycache__/camera.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/detector.cpython-39.pyc b/vision/__pycache__/detector.cpython-39.pyc deleted file mode 100644 index 1a9c6da..0000000 Binary files a/vision/__pycache__/detector.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/overflow_detector.cpython-39.pyc b/vision/__pycache__/overflow_detector.cpython-39.pyc deleted file mode 100644 index 1d303ac..0000000 Binary files a/vision/__pycache__/overflow_detector.cpython-39.pyc and /dev/null differ diff --git a/vision/__pycache__/resize_tuili_image_main.cpython-39.pyc b/vision/__pycache__/resize_tuili_image_main.cpython-39.pyc deleted file mode 100644 index 97f4798..0000000 Binary files a/vision/__pycache__/resize_tuili_image_main.cpython-39.pyc and /dev/null differ