diff --git a/API/__pycache__/__init__.cpython-312.pyc b/API/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..1d63abf Binary files /dev/null and b/API/__pycache__/__init__.cpython-312.pyc differ diff --git a/API/__pycache__/client.cpython-312.pyc b/API/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..2a48fec Binary files /dev/null and b/API/__pycache__/client.cpython-312.pyc differ diff --git a/API/__pycache__/mix_weight_api.cpython-312.pyc b/API/__pycache__/mix_weight_api.cpython-312.pyc new file mode 100644 index 0000000..b00c8db Binary files /dev/null and b/API/__pycache__/mix_weight_api.cpython-312.pyc differ diff --git a/config/__pycache__/__init__.cpython-312.pyc b/config/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..2e4a46d Binary files /dev/null and b/config/__pycache__/__init__.cpython-312.pyc differ diff --git a/config/__pycache__/settings.cpython-312.pyc b/config/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..3801a47 Binary files /dev/null and b/config/__pycache__/settings.cpython-312.pyc differ diff --git a/database/__pycache__/__init__.cpython-312.pyc b/database/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..87ebc67 Binary files /dev/null and b/database/__pycache__/__init__.cpython-312.pyc differ diff --git a/database/__pycache__/access_db.cpython-312.pyc b/database/__pycache__/access_db.cpython-312.pyc new file mode 100644 index 0000000..4f32551 Binary files /dev/null and b/database/__pycache__/access_db.cpython-312.pyc differ diff --git a/database/__pycache__/sql_server.cpython-312.pyc b/database/__pycache__/sql_server.cpython-312.pyc new file mode 100644 index 0000000..d55cedc Binary files /dev/null and b/database/__pycache__/sql_server.cpython-312.pyc differ diff --git a/database/__pycache__/sql_server_connection.cpython-312.pyc b/database/__pycache__/sql_server_connection.cpython-312.pyc new file mode 100644 index 0000000..a546da8 Binary files /dev/null and b/database/__pycache__/sql_server_connection.cpython-312.pyc differ diff --git a/services/__pycache__/__init__.cpython-312.pyc b/services/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cb1b2e3 Binary files /dev/null and b/services/__pycache__/__init__.cpython-312.pyc differ diff --git a/services/__pycache__/monitoring_service.cpython-312.pyc b/services/__pycache__/monitoring_service.cpython-312.pyc new file mode 100644 index 0000000..856b1f0 Binary files /dev/null and b/services/__pycache__/monitoring_service.cpython-312.pyc differ diff --git a/services/__pycache__/task_service.cpython-312.pyc b/services/__pycache__/task_service.cpython-312.pyc new file mode 100644 index 0000000..144beb4 Binary files /dev/null and b/services/__pycache__/task_service.cpython-312.pyc differ diff --git a/tcp/__pycache__/__init__.cpython-312.pyc b/tcp/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..5b8f9e1 Binary files /dev/null and b/tcp/__pycache__/__init__.cpython-312.pyc differ diff --git a/tcp/__pycache__/server.cpython-312.pyc b/tcp/__pycache__/server.cpython-312.pyc new file mode 100644 index 0000000..37bdc4b Binary files /dev/null and b/tcp/__pycache__/server.cpython-312.pyc differ diff --git a/utils/__pycache__/__init__.cpython-312.pyc b/utils/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cfa42e7 Binary files /dev/null and b/utils/__pycache__/__init__.cpython-312.pyc differ diff --git a/utils/__pycache__/helpers.cpython-312.pyc b/utils/__pycache__/helpers.cpython-312.pyc new file mode 100644 index 0000000..f0a69b7 Binary files /dev/null and b/utils/__pycache__/helpers.cpython-312.pyc differ diff --git a/zjsh_mixing_sysytem/.idea/.gitignore b/zjsh_mixing_sysytem/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/zjsh_mixing_sysytem/.idea/inspectionProfiles/Project_Default.xml b/zjsh_mixing_sysytem/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..78dfc6d --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/.idea/inspectionProfiles/profiles_settings.xml b/zjsh_mixing_sysytem/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/.idea/misc.xml b/zjsh_mixing_sysytem/.idea/misc.xml new file mode 100644 index 0000000..62ef35e --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/.idea/modules.xml b/zjsh_mixing_sysytem/.idea/modules.xml new file mode 100644 index 0000000..56ad756 --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/.idea/中交三航背景界面.iml b/zjsh_mixing_sysytem/.idea/中交三航背景界面.iml new file mode 100644 index 0000000..53e58ed --- /dev/null +++ b/zjsh_mixing_sysytem/.idea/中交三航背景界面.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-312.pyc b/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-312.pyc new file mode 100644 index 0000000..fb90395 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-312.pyc differ diff --git a/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-39.pyc b/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-39.pyc new file mode 100644 index 0000000..cb173e4 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/bottom_control_widget.cpython-39.pyc differ diff --git a/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-312.pyc b/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-312.pyc new file mode 100644 index 0000000..8fa2c83 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-312.pyc differ diff --git a/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-39.pyc b/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-39.pyc new file mode 100644 index 0000000..0d743f6 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-39.pyc differ diff --git a/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-312.pyc b/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-312.pyc new file mode 100644 index 0000000..6410e16 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-312.pyc differ diff --git a/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-39.pyc b/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-39.pyc new file mode 100644 index 0000000..acbf850 Binary files /dev/null and b/zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-39.pyc differ diff --git a/zjsh_mixing_sysytem/bottom_control_widget.py b/zjsh_mixing_sysytem/bottom_control_widget.py new file mode 100644 index 0000000..49b3025 --- /dev/null +++ b/zjsh_mixing_sysytem/bottom_control_widget.py @@ -0,0 +1,41 @@ +from PySide6.QtWidgets import (QWidget, QHBoxLayout, QLabel, QPushButton, + QMessageBox, QSpacerItem, QSizePolicy) +from PySide6.QtGui import QPixmap, QPainter, QFont, QPen +from PySide6.QtCore import Qt, QDateTime, QEvent, QSize + +class BottomControlWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.initUI() + + def initUI(self): + # 1. 加载背景图并设置控件尺寸 + bg_path = "底部背景.png" + self.bg_pixmap = QPixmap(bg_path) + if self.bg_pixmap.isNull(): + print("警告:底部背景.png 加载失败,请检查路径!") + self.setFixedSize(1280, 66) + else: + self.setFixedSize(self.bg_pixmap.size()) + + # 2. 主布局(水平布局,组件间距6px) + main_layout = QHBoxLayout(self) + main_layout.setContentsMargins(10, 10, 10, 10) # 适当留白避免贴边 + main_layout.setSpacing(6) + + def paintEvent(self, event): + """绘制背景图""" + super().paintEvent(event) + if not self.bg_pixmap.isNull(): + painter = QPainter(self) + painter.drawPixmap(0, 0, self.bg_pixmap) + +if __name__ == "__main__": + import sys + from PySide6.QtWidgets import QApplication + + app = QApplication(sys.argv) + widget = BottomControlWidget() + widget.show() + sys.exit(app.exec()) + \ No newline at end of file diff --git a/zjsh_mixing_sysytem/dispatch_task.py b/zjsh_mixing_sysytem/dispatch_task.py new file mode 100644 index 0000000..8dd5556 --- /dev/null +++ b/zjsh_mixing_sysytem/dispatch_task.py @@ -0,0 +1,531 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +''' +# @Time : 2025/11/10 11:29 +# @Author : reenrr +# @Description : 派单任务的详情按钮点击之后弹出, 显示派单任务的详情 +''' + +from PySide6.QtWidgets import ( + QApplication, + QDialog, + QVBoxLayout, + QHBoxLayout, + QGridLayout, + QLabel, + QWidget, + QPushButton, +) +from PySide6.QtGui import QPixmap, QFont, QPainter, QIcon +from PySide6.QtCore import Qt, QEvent, QSize, QTimer, Slot +from PySide6.QtNetwork import QTcpSocket, QAbstractSocket +import sys +import json +from datetime import datetime + +# ----------- +# 参数配置 +# ----------- +tcp_server_host = "127.0.0.1" +tcp_server_port = 8888 + +MAX_RECONNECT = 3 # 最大重连次数 +RECONNECT_INTERVAL = 2000 # 重连间隔(毫秒) + +class DispatchDetailsDialog(QDialog): + """ + 派单任务的界面 + """ + def __init__(self, parent=None): + super().__init__(parent) + self.setAttribute(Qt.WA_TranslucentBackground) + + self.is_running = False # 系统运行状态标记 + self.latest_json_data = {"erp_id":None,"artifact_id":None} # 缓存服务端发送的最新JSON数据 + + self.statusWidgets = [] # 存储api_field + 对应的value标签 + + # --------------- + # TCP客户端核心配置 + # --------------- + self.tcp_socket = QTcpSocket(self) # TCP socket实例 + self.tcp_server_host = tcp_server_host + self.tcp_server_port = tcp_server_port + self.is_tcp_connected = False # TCP连接状态标记 + self.has_connected_once = False # 连接至服务器至少一次标记(区别首次连接和断开后重连) + self.reconnect_count = 0 # 重连次数计数器 + # 重连定时器,每隔RECONNECT_INTERVAL毫秒重连一次 + self.reconnect_timer = QTimer(self) + self.reconnect_timer.setInterval(RECONNECT_INTERVAL) + self.reconnect_timer.timeout.connect(self._reconnect_to_server) # 绑定重连函数 + + # 绑定TCP信号与槽(事件驱动) + self._bind_tcp_signals() + + # 初始化存储需要修改的控件 + self.id_value_label = None # 对应管片ID值标签 + self.rows = [] # 所有行的单元格列表(包含label、value) + + self._init_ui() + + # ---------------- + # 客户端自动后自动连接服务端 + # ---------------- + print(f"客户端启动,自动连接服务端{self.tcp_server_host}:{self.tcp_server_port}...") + self._connect_to_server() + + # --------------------- + # TCP连接相关函数 + # --------------------- + def _connect_to_server(self): + """主动发起连接(仅在未连接状态下有效""" + if not self.is_tcp_connected: + self.tcp_socket.abort() # 终止现有连接 + self.tcp_socket.connectToHost(self.tcp_server_host, self.tcp_server_port) + + def _reconnect_to_server(self): + """重连执行函数:仅在未连接且未达最大次数时触发""" + if not self.is_tcp_connected and self.reconnect_count < MAX_RECONNECT: + self.reconnect_count += 1 + print(f"第{self.reconnect_count}次重连(共{MAX_RECONNECT}次尝试)...") + self._connect_to_server() + elif self.reconnect_count >= MAX_RECONNECT: + self.reconnect_timer.stop() # 停止重连定时器 + print(f"已达最大重连次数({MAX_RECONNECT}次),停止重连,请检查服务端状态") + + def _bind_tcp_signals(self): + """绑定TCP socket的核心信号(连接、断开、接收数据、错误)""" + # 连接成功信号 + self.tcp_socket.connected.connect(self._on_tcp_connected) + # 断开连接信号 + self.tcp_socket.disconnected.connect(self._on_tcp_disconnected) + # 接收数据信号(有新数据时触发) + self.tcp_socket.readyRead.connect(self._on_tcp_data_received) + # 错误信号(连接/通信出错时触发) + self.tcp_socket.errorOccurred.connect(self._on_tcp_error) + + # ----------------------- + # 界面初始化函数 + # ----------------------- + def _init_ui(self): + self.setWindowFlags(Qt.FramelessWindowHint) + self._load_background() + + main_layout = QVBoxLayout(self) + main_layout.setContentsMargins(32, 20, 32, 50) + main_layout.setSpacing(0) + + # 1. 顶部区域(标题 + 关闭按钮) + self._add_top_area(main_layout) + + # 2. 对应管片ID区域 + self._add_segment_id_area(main_layout) + + # 3. 网格信息区域(单列6行) + self._add_grid_info_area(main_layout) + main_layout.addSpacing(30) + + # 4. 操作按钮区域 + self._init_operation_buttons(main_layout) + + def _init_operation_buttons(self, parent_layout): + """初始化操作按钮(下料完成/生产异常/生产取消)""" + buttonContainer = QWidget() + buttonLayout = QHBoxLayout(buttonContainer) + buttonLayout.setSpacing(100) + + self.errorButton = QPushButton("生产异常") + self.cancelButton = QPushButton("生产取消") + + self.errorButton.setStyleSheet(""" + QPushButton { + color: #1E1E1E; + font-size: 18px; + font-weight: bold; + padding: 15px 50px; + border: 1px solid; + background-color: #FF0000; + border-color: transparent; + + } + QPushButton:hover { + opacity: 0.9; + background-color: #B71C1C; + } + QPushButton:pressed { + opacity: 0.8; + } + + """) + + self.cancelButton.setStyleSheet(""" + QPushButton { + color: #1E1E1E; + font-size: 18px; + font-weight: bold; + padding: 15px 50px; + border: 1px solid; + background-color: #FFD700; + border-color: transparent; + } + QPushButton:hover { + opacity: 0.9; + background-color: #FFB300 + } + QPushButton:pressed { + opacity: 0.8; + } + """) + + self.errorButton.clicked.connect(self.on_error_clicked) + self.cancelButton.clicked.connect(self.on_cancel_clicked) + + # 初始禁用“停止”和“异常”按钮(未连接时不可用) + self.errorButton.setDisabled(True) + self.cancelButton.setDisabled(True) + + buttonLayout.addStretch(1) + buttonLayout.addWidget(self.errorButton) + buttonLayout.addWidget(self.cancelButton) + buttonLayout.addStretch(1) + + parent_layout.addWidget(buttonContainer) + + def _load_background(self): + self.bg_pixmap = QPixmap("详情弹出背景.png") # 修改为派单任务背景图 + if self.bg_pixmap.isNull(): + print("错误:派单任务背景.png 加载失败,请检查路径!") + self.setFixedSize(800, 500) + else: + self.setFixedSize(self.bg_pixmap.size()) + + def _add_top_area(self, parent_layout): + top_layout = QHBoxLayout() + top_layout.setContentsMargins(0, 0, 0, 36) + + top_layout.addStretch() + + # 标题改为“任务派单” + title_label = QLabel("任务派单") + font = QFont() + font.setPixelSize(24) + font.setLetterSpacing(QFont.AbsoluteSpacing, 2) + font.setBold(True) + title_label.setFont(font) + title_label.setStyleSheet("color: #13fffc; font-weight: Bold;") + title_label.setAlignment(Qt.AlignCenter) + top_layout.addWidget(title_label, alignment=Qt.AlignTop) + + # 关闭按钮 + top_layout.addStretch() + + parent_layout.addLayout(top_layout) + + def _add_segment_id_area(self, parent_layout): + id_layout = QHBoxLayout() + + id_label = QLabel("对应管片ID") # 标签文字修改 + id_label.setFixedSize(318, 32) + id_font = QFont() + id_font.setPixelSize(18) + id_font.setLetterSpacing(QFont.AbsoluteSpacing, 2) + id_font.setBold(True) + id_label.setFont(id_font) + id_label.setStyleSheet( + """ + background-image: url("详情标题.png"); + background-repeat: no-repeat; + background-position: center; + color: #13ffff; + """ + ) + id_label.setContentsMargins(16, 0, 0, 0) + id_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + + self.id_value_label = QLabel("") # 初始管片ID值 + value_font = QFont() + value_font.setPixelSize(18) + value_font.setBold(True) + value_font.setLetterSpacing(QFont.AbsoluteSpacing, 2) + self.id_value_label.setFont(value_font) + self.id_value_label.setStyleSheet("color: #13ffff;") + self.id_value_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + + id_layout.addWidget(id_label) + id_layout.addStretch() + id_layout.addWidget(self.id_value_label) + id_layout.setContentsMargins(0, 0, 0, 16) + parent_layout.addLayout(id_layout) + parent_layout.setAlignment(id_layout, Qt.AlignTop) + + def _add_grid_info_area(self, parent_layout): + grid_layout = QGridLayout() + grid_layout.setSpacing(12) + + # 初始化信息条目(6行) + info_items = [ + {"name":"任务单号", "value": "", "api_field": "task_id"}, + {"name": "工程名称", "value": "", "api_field": "project_name"}, + {"name": "配比编号", "value": "", "api_field": "produce_mix_id"}, + {"name": "要料状态", "value": "", "api_field": "flag"}, + {"name": "砼强度", "value": "", "api_field": "beton_grade"}, + {"name": "要料方量", "value": "", "api_field": "adjusted_volume"}, + ] + + self.rows.clear() + self.statusWidgets.clear() # 清空缓存 + + for row, item in enumerate(info_items): + cell_widget = self._create_info_cell(item["name"], item["value"]) + self.rows.append(cell_widget) + grid_layout.addWidget(cell_widget, row, 0) + + self.statusWidgets.append({ + 'api_field': item["api_field"], + 'valueLabel': cell_widget.value # 绑定实际UI控件 + } + ) + + parent_layout.addLayout(grid_layout) + + def _create_info_cell(self, label_text, value_text): + cell_widget = QWidget() + cell_bg = QPixmap("派单任务信息栏1.png") # 正常背景图 + cell_widget.setObjectName("infoCell") + if not cell_bg.isNull(): + cell_widget.setFixedSize(cell_bg.size()) + cell_widget.setStyleSheet( + """ + QWidget { + background-image: url(派单任务信息栏1.png); + background-repeat: no-repeat; + background-position: Center; + } + QWidget:hover { + background-image: url(派单任务信息栏2.png); + } + QWidget QLabel#valueLabel { + color: #9fbfd4; + background: none; + } + """ + ) + + cell_layout = QHBoxLayout(cell_widget) + cell_layout.setContentsMargins(0, 0, 0, 0) + + # 左侧标签 + label = QLabel(label_text) + label.setFixedSize(136, 60) + label_font = QFont() + label_font.setPixelSize(16) + label_font.setLetterSpacing(QFont.AbsoluteSpacing, 2) + label.setFont(label_font) + label.setStyleSheet("background: none;color: #fffffd; font-weight:Bold;") + label.setAlignment(Qt.AlignCenter) + cell_widget.label = label + + # 右侧值标签(设置objectName以便样式选择) + value = QLabel(value_text) + value.setObjectName("valueLabel") + value_font = QFont() + value_font.setPixelSize(20) + value.setFont(value_font) + value.setAlignment(Qt.AlignCenter) + cell_widget.value = value + + cell_layout.addWidget(label) # 左侧的标题标签 + cell_layout.addSpacing(60) + cell_layout.addWidget(value) # 右侧的值标签 + + cell_widget.installEventFilter(self) + return cell_widget + + def eventFilter(self, obj, event): + """ + 实现事件过滤器,动态修改右侧值颜色 + """ + # 只处理父控件(infoCell)的事件 + if obj.objectName() == "infoCell": + # 鼠标进入父控件 → 改#13f0f3 + if event.type() == QEvent.Enter: + if hasattr(obj, "value"): # 确保存在value控件 + obj.value.setStyleSheet("background: none; color: #13f0f3;") + # 鼠标离开父控件 → 恢复默认色 + elif event.type() == QEvent.Leave: + if hasattr(obj, "value"): + obj.value.setStyleSheet("background: none; color: #9fbfd4;") + return super().eventFilter(obj, event) + + def paintEvent(self, event): + if not self.bg_pixmap.isNull(): + painter = QPainter(self) + painter.drawPixmap(self.rect(), self.bg_pixmap) + super().paintEvent(event) + + def _update_ui_from_data(self, data): + """根据TCP获取的数据更新界面状态""" + # 1、更新顶部独立的“对应管片ID” + if "artifact_id" in data: + self.id_value_label.setText(str(data["artifact_id"])) + + # 2、更新网格中的所有字段 + for widget in self.statusWidgets: + api_field = widget['api_field'] + value_label = widget['valueLabel'] + if api_field in data: + new_value = str(data[api_field]) + value_label.setText(new_value) + + # ------------------- 对外修改接口 ------------------- + # row 对应行号(0-6),从0开始 + # -------------------------------------------------- + def set_segment_id(self, new_id): + """修改上方的 对应的管片ID的值""" + if self.id_value_label: + self.id_value_label.setText(str(new_id)) + + def set_row_label(self, row, new_label_text: str): + """修改左侧的显示的标签的文本,如: 创建时间、派单时间等""" + if 0 <= row < len(self.rows): + self.rows[row].label.setText(new_label_text) + + def set_row_value(self, row, new_value_text: str): + """修改右侧的显示的值, 如: 2025年9月9日 9:9:9""" + if 0 <= row < len(self.rows): + self.rows[row].value.setText(new_value_text) + + # ------------------ + # 按钮点击事件 + # ------------------ + def on_error_clicked(self): + """点击“生产异常”:向服务端发送异常指令""" + print("🔘 点击「生产异常」按钮") + self._clear_ui_info() + self._send_tcp_request("production_error") + + def on_cancel_clicked(self): + """点击“生产取消”:向服务端发送取消指令""" + print("🔘 点击「生产取消」按钮") + self._clear_ui_info() + self._send_tcp_request("cancel_feed") + + # -------------------- + # 清空界面信息的通用方法 + # -------------------- + def _clear_ui_info(self): + """清空管片ID和网格信息""" + if self.id_value_label: + self.id_value_label.setText("") + for widget in self.statusWidgets: + widget['valueLabel'].setText("") + print("ℹ️ 界面信息已清空") + + # ------------------ + # TCP客户端核心功能 + # ------------------ + @Slot() + def _on_tcp_connected(self): + """TCP连接成功回调""" + self.is_tcp_connected = True + self.has_connected_once = True + self.reconnect_timer.stop() # 停止重连定时器 + self.reconnect_count = 0 # 重连计数器清零 + self.is_running = True + print(f"TCP连接成功:{self.tcp_server_host}:{self.tcp_server_port}") + + # 连接成功后,向服务器发送“请求初始数据”指令 + self._send_tcp_request("get_initial_data") + + # 更新按钮状态:启用“生产异常”“生产取消” + self.errorButton.setDisabled(False) + self.cancelButton.setDisabled(False) + + @Slot() + def _on_tcp_disconnected(self): + """TCP连接断开回调""" + self.is_tcp_connected = False + self.is_running = False + print(f"TCP连接断开:{self.tcp_server_host}:{self.tcp_server_port}") + + # 启用/禁用按钮 + self.errorButton.setDisabled(True) + self.cancelButton.setDisabled(True) + + # 重置状态指示灯为“未连接”状态 + for widget in self.statusWidgets: + widget['indicator'].setStyleSheet(""" + QLabel { + background-color: #9E9E9E; + border-radius: 10px; + border: 2px solid #555555; + } + """) + + @Slot() + def _on_tcp_data_received(self): + """TCP数据接收回调(服务器发送数据时触发)""" + tcp_data = self.tcp_socket.readAll().data().decode("utf-8").strip() + print(f"TCP数据接收:{tcp_data}") + + # 解析数据 + try: + status_data = json.loads(tcp_data) + self.latest_json_data = status_data + self._update_ui_from_data(status_data) + except json.JSONDecodeError as e: + print(f"TCP数据解析失败(非JSON格式):{e}, 原始数据:{tcp_data}") + except Exception as e: + print(f"TCP数据处理异常:{e}") + + @Slot(QAbstractSocket.SocketError) + def _on_tcp_error(self, error): + """TCP错误回调""" + if not self.is_tcp_connected: + error_str = self.tcp_socket.errorString() + print(f"TCP错误:{error_str}") + self.is_tcp_connected = False + self.is_running = False + + # 启用/禁用按钮 + self.errorButton.setDisabled(True) + self.cancelButton.setDisabled(True) + + # 首次连接失败时,启动重连定时器 + if not self.has_connected_once and self.reconnect_count == 0: + print(f"将在{RECONNECT_INTERVAL / 1000}秒后启动重连(最多{MAX_RECONNECT}次)") + self.reconnect_timer.start() + + def _send_tcp_request(self, request_cmd="get_status"): + """向TCP服务器发送请求指令""" + if not self.is_tcp_connected: + print("TCP连接未建立,无法发送请求") + return + print(self.latest_json_data) + # 构造请求数据 + request_data = json.dumps({ + "cmd": request_cmd, + "erp_id": self.latest_json_data["erp_id"], + "artifact_id":self.latest_json_data["artifact_id"], + "timestamp": self.get_current_time(), + "client_info": "布料系统客户端" + }) + "\n" # 增加换行符作为数据结束标识 + + # 发送请求数据 + self.tcp_socket.write(request_data.encode("utf-8")) + print(f"TCP请求发送:{request_data.strip()}") + + # ------------------ + # 时间相关的通用方法 + # ------------------ + def get_current_time(self): + """获取格式化的当前时间""" + return datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + +# 测试代码 +if __name__ == "__main__": + app = QApplication(sys.argv) + dialog = DispatchDetailsDialog() + dialog.show() + sys.exit(app.exec()) diff --git a/zjsh_mixing_sysytem/main_window.py b/zjsh_mixing_sysytem/main_window.py new file mode 100644 index 0000000..038d2e3 --- /dev/null +++ b/zjsh_mixing_sysytem/main_window.py @@ -0,0 +1,113 @@ +# coding: utf-8 +from typing import List +from PySide6.QtCore import Qt, Signal, QEasingCurve, QUrl, QSize, QTimer, QEvent +from PySide6.QtGui import QIcon, QDesktopServices, QColor, QPalette, QBrush, QImage +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout, + QFrame, QWidget, QSpacerItem, QSizePolicy, QMainWindow, QPushButton) + +from system_nav_bar import SystemNavBar +from bottom_control_widget import BottomControlWidget +from dispatch_task import DispatchDetailsDialog + + +class MainWindow(QWidget): + def __init__(self): + super().__init__() + self.initWindow() + self.createSubWidgets() # 创建子部件 + self.setupLayout() # 设置布局 + + def initWindow(self): + """初始化窗口基本属性""" + self.setWindowTitle("中交三航主界面") # 设置窗口标题 + # 设置最小尺寸(可根据需求调整) + # 触摸屏尺寸为 1280*800 + self.setMinimumSize(1280, 800) + self.setObjectName("MainWindow") + + # Qt.FramelessWindowHint + self.setWindowFlags(Qt.FramelessWindowHint) # 无边框窗口 + + # 设置主界面背景图片 + try: + self.background_image = QImage("主界面背景.png") + if self.background_image.isNull(): + raise Exception("图片为空,可能路径错误或格式不支持") + # print("图片加载成功") + except Exception as e: + print(f"主界面背景图片加载失败: {e}") + self.background_image = None + return # 加载背景失败 + + self.update_background() + + def createSubWidgets(self): + """创建所有子部件实例""" + self.system_nav_bar = SystemNavBar() # 最上方:系统导航栏 + self.bottom_control_widget = BottomControlWidget() # 最下方: 控制的按钮 (系统诊断、系统中心等) + + # 中间:任务派单 + self.dispatch_dialog = DispatchDetailsDialog(self) + + def setupLayout(self): + """设置垂直布局,从上到下排列部件""" + main_layout = QVBoxLayout(self) # 主布局:垂直布局 + main_layout.setSpacing(0) # 部件间距0px + main_layout.setContentsMargins(0, 0, 0, 0) # 左上右下边距0px + + # 添加最上方的 系统导航栏(包括系统标题、中交三航的logo等) + main_layout.addWidget(self.system_nav_bar, alignment=Qt.AlignTop) + # 添加中间内容区 + main_layout.addWidget(self.dispatch_dialog, alignment=Qt.AlignCenter) + # 添加最下方的 控制按钮控件 + main_layout.addWidget(self.bottom_control_widget, alignment=Qt.AlignBottom) + + # 关闭按钮 + self.system_nav_bar.msg_container.close_btn.clicked.connect(self.close) + # 将布局应用到主窗口 + self.setLayout(main_layout) + + def show_dispatch_dialog(self): + """显示派单详细弹窗(居中显示在主界面)""" + # 计算弹窗居中位置 + main_geometry = self.frameGeometry() + dialog_geometry = self.dispatch_dialog.frameGeometry() + dialog_geometry.moveCenter(main_geometry.center()) + self.dispatch_dialog.move(dialog_geometry.topLeft()) + + # 显示弹窗 + self.dispatch_dialog.show() + + def update_background(self): + """更新主界面背景图片""" + if self.background_image and not self.background_image.isNull(): + palette = self.palette() + # 按当前窗口尺寸缩放图片 + scaled_image = self.background_image.scaled( + self.size(), + Qt.IgnoreAspectRatio, + Qt.SmoothTransformation + ) + palette.setBrush(QPalette.Window, QBrush(scaled_image)) + self.setPalette(palette) + self.setAutoFillBackground(True) + + def resizeEvent(self, e): + """窗口大小变化时的回调""" + super().resizeEvent(e) + self.update_background() # 重新加载背景图片 + + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Escape: # 按下Esc键, 退出界面 + self.close() + super().keyPressEvent(event) + + +if __name__ == "__main__": + import sys + app = QApplication([]) + window = MainWindow() + window.show() # 显示主界面 + # window.showFullScreen() # 全屏显示主界面 + sys.exit(app.exec()) diff --git a/zjsh_mixing_sysytem/system_nav_bar.py b/zjsh_mixing_sysytem/system_nav_bar.py new file mode 100644 index 0000000..35ed612 --- /dev/null +++ b/zjsh_mixing_sysytem/system_nav_bar.py @@ -0,0 +1,141 @@ +from PySide6.QtWidgets import (QWidget, QLabel, QHBoxLayout, + QSpacerItem, QSizePolicy, QPushButton) +from PySide6.QtGui import QPixmap, QFont, QPainter, QIcon +from PySide6.QtCore import Qt, QTimer, QDateTime + +# 自定义消息容器, 显示系统消息 +class MsgContainer(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setFixedSize(770, 24) + + # 加载消息区域背景图 + self.bg_pixmap = QPixmap("系统消息背景.png") # 替换为实际路径 + if self.bg_pixmap.isNull(): + print("警告:系统消息背景.png 加载失败") + + # 消息区域内部布局(喇叭+文本) + msg_layout = QHBoxLayout(self) + msg_layout.setContentsMargins(0, 0, 0, 0) # 调整内边距,避免内容贴边 + msg_layout.setSpacing(3) # 喇叭和文本的间距 + + # 消息喇叭图标 + self.msg_icon = QLabel() + self.msg_icon.setFixedSize(13, 18) + # self.msg_icon.setStyleSheet("background-color:red;") + self.msg_icon.setPixmap(QPixmap("系统消息喇叭.png")) # 替换为实际路径 + msg_layout.addWidget(self.msg_icon, alignment=Qt.AlignVCenter | Qt.AlignLeft) + + # 消息文本 + current_time = QDateTime.currentDateTime().toString("hh:mm:ss") + self.msg_text = QLabel(f"{current_time} 开始启动智能浇筑系统") + self.msg_text.setFixedWidth(740) + # self.msg_text.setStyleSheet("color: white; font-size: 14px;background-color:red;") # 文本样式 + self.msg_text.setStyleSheet("color: white; font-size: 16px;font-weight:Bold;") + self.msg_text.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + msg_layout.addWidget(self.msg_text) + + # 关闭按钮 + self._create_close_button(msg_layout) + + def _create_close_button(self, parent_layout): + self.close_btn = QPushButton() + self.close_btn.setFixedSize(20, 20) + + close_icon = QPixmap("关闭图标.png") + if not close_icon.isNull(): + self.close_btn.setIcon(QIcon(close_icon)) + + self.close_btn.setStyleSheet( + """ + QPushButton { + background-color: transparent; + border: none; + padding: 0px; + } + QPushButton:hover { + background-color: red; + border-radius: 2px; + } + """ + ) + + parent_layout.addWidget(self.close_btn) + + def paintEvent(self, event): + # 绘制消息区域背景图 + super().paintEvent(event) # 确保子控件正常绘制 + if self.bg_pixmap.isNull(): + return # 图片加载失败则不绘制 + + painter = QPainter(self) + painter.setRenderHint(QPainter.SmoothPixmapTransform) # 缩放平滑 + painter.drawPixmap(self.rect(), self.bg_pixmap) + +class SystemNavBar(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + # 设置尺寸 + self.setFixedSize(1280, 80) + + # 1. 加载背景图 + self.bg_pixmap = QPixmap("系统主界面导航栏.png") # 替换为实际图片路径 + if self.bg_pixmap.isNull(): + print("警告:背景图加载失败,请检查路径!") + + main_layout = QHBoxLayout(self) + main_layout.setContentsMargins(9, 9, 9, 19) + main_layout.setSpacing(100) # 注意:左侧的logo+系统标题的容器 和 系统消息的间隔 + + # 左侧区域:logo + 系统标题 + left_container = QWidget() + left_container.setFixedSize(400, 53) + left_layout = QHBoxLayout(left_container) # 容器内部的水平布局 + left_layout.setContentsMargins(0, 0, 0, 0) # 容器内边距 + left_layout.setSpacing(6) # 设置logo和标题之间的间隙为6px + # 系统logo + self.logo = QLabel() + self.logo.setFixedSize(53, 53) + self.logo.setPixmap(QPixmap("系统logo.png")) + left_layout.addWidget(self.logo, alignment=Qt.AlignTop) + # 系统总标题 + self.title = QLabel() + self.title.setPixmap(QPixmap("系统总标题.png")) + left_layout.addWidget(self.title, alignment=Qt.AlignCenter) + main_layout.addWidget(left_container, alignment=Qt.AlignTop) + + # 中间区域:系统消息(喇叭+文本+背景) + self.msg_container = MsgContainer() + main_layout.addWidget(self.msg_container, alignment=Qt.AlignBottom | Qt.AlignRight) + + # 右侧区域:实时时间 + self.time_label = QLabel() + self.time_label.setStyleSheet("color: white; font-size: 16px;font-weight:Bold;") + main_layout.addWidget(self.time_label, alignment= Qt.AlignTop | Qt.AlignRight) + + # 启动时间更新定时器 + self.timer = QTimer(self) + self.timer.timeout.connect(self.update_time) + self.timer.start(1000) # 每秒更新一次 + + def paintEvent(self, event): + super().paintEvent(event) + + painter = QPainter(self) + painter.setRenderHint(QPainter.SmoothPixmapTransform) # 缩放时平滑 + + painter.drawPixmap(0, 0, self.bg_pixmap) + + def update_time(self): + current_time = QDateTime.currentDateTime().toString("yyyy/MM/dd hh:mm:ss") + self.time_label.setText(current_time) + +# 测试代码 +if __name__ == "__main__": + import sys + from PySide6.QtWidgets import QApplication, QMainWindow + + app = QApplication(sys.argv) + nav_bar = SystemNavBar() + nav_bar.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/zjsh_mixing_sysytem/主界面背景.png b/zjsh_mixing_sysytem/主界面背景.png new file mode 100644 index 0000000..dc65b12 Binary files /dev/null and b/zjsh_mixing_sysytem/主界面背景.png differ diff --git a/zjsh_mixing_sysytem/关闭图标.png b/zjsh_mixing_sysytem/关闭图标.png new file mode 100644 index 0000000..9b090d6 Binary files /dev/null and b/zjsh_mixing_sysytem/关闭图标.png differ diff --git a/zjsh_mixing_sysytem/底部背景.png b/zjsh_mixing_sysytem/底部背景.png new file mode 100644 index 0000000..3a66c09 Binary files /dev/null and b/zjsh_mixing_sysytem/底部背景.png differ diff --git a/zjsh_mixing_sysytem/派单任务信息栏1.png b/zjsh_mixing_sysytem/派单任务信息栏1.png new file mode 100644 index 0000000..6991534 Binary files /dev/null and b/zjsh_mixing_sysytem/派单任务信息栏1.png differ diff --git a/zjsh_mixing_sysytem/派单任务信息栏2.png b/zjsh_mixing_sysytem/派单任务信息栏2.png new file mode 100644 index 0000000..5397cd4 Binary files /dev/null and b/zjsh_mixing_sysytem/派单任务信息栏2.png differ diff --git a/zjsh_mixing_sysytem/派单最底部.png b/zjsh_mixing_sysytem/派单最底部.png new file mode 100644 index 0000000..92f9b55 Binary files /dev/null and b/zjsh_mixing_sysytem/派单最底部.png differ diff --git a/zjsh_mixing_sysytem/生产取消按钮.png b/zjsh_mixing_sysytem/生产取消按钮.png new file mode 100644 index 0000000..5ff0693 Binary files /dev/null and b/zjsh_mixing_sysytem/生产取消按钮.png differ diff --git a/zjsh_mixing_sysytem/生产异常按钮.png b/zjsh_mixing_sysytem/生产异常按钮.png new file mode 100644 index 0000000..4874222 Binary files /dev/null and b/zjsh_mixing_sysytem/生产异常按钮.png differ diff --git a/zjsh_mixing_sysytem/系统logo.png b/zjsh_mixing_sysytem/系统logo.png new file mode 100644 index 0000000..e50db73 Binary files /dev/null and b/zjsh_mixing_sysytem/系统logo.png differ diff --git a/zjsh_mixing_sysytem/系统主界面导航栏.png b/zjsh_mixing_sysytem/系统主界面导航栏.png new file mode 100644 index 0000000..7db6d8b Binary files /dev/null and b/zjsh_mixing_sysytem/系统主界面导航栏.png differ diff --git a/zjsh_mixing_sysytem/系统总标题.png b/zjsh_mixing_sysytem/系统总标题.png new file mode 100644 index 0000000..64f34be Binary files /dev/null and b/zjsh_mixing_sysytem/系统总标题.png differ diff --git a/zjsh_mixing_sysytem/系统消息喇叭.png b/zjsh_mixing_sysytem/系统消息喇叭.png new file mode 100644 index 0000000..abbc7f2 Binary files /dev/null and b/zjsh_mixing_sysytem/系统消息喇叭.png differ diff --git a/zjsh_mixing_sysytem/系统消息背景.png b/zjsh_mixing_sysytem/系统消息背景.png new file mode 100644 index 0000000..a436582 Binary files /dev/null and b/zjsh_mixing_sysytem/系统消息背景.png differ diff --git a/zjsh_mixing_sysytem/详情弹出背景.png b/zjsh_mixing_sysytem/详情弹出背景.png new file mode 100644 index 0000000..7c003c2 Binary files /dev/null and b/zjsh_mixing_sysytem/详情弹出背景.png differ diff --git a/zjsh_mixing_sysytem/详情标题.png b/zjsh_mixing_sysytem/详情标题.png new file mode 100644 index 0000000..edf08db Binary files /dev/null and b/zjsh_mixing_sysytem/详情标题.png differ