完成派单程序
BIN
API/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
API/__pycache__/client.cpython-312.pyc
Normal file
BIN
API/__pycache__/mix_weight_api.cpython-312.pyc
Normal file
BIN
config/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
config/__pycache__/settings.cpython-312.pyc
Normal file
BIN
database/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
database/__pycache__/access_db.cpython-312.pyc
Normal file
BIN
database/__pycache__/sql_server.cpython-312.pyc
Normal file
BIN
database/__pycache__/sql_server_connection.cpython-312.pyc
Normal file
BIN
services/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
services/__pycache__/monitoring_service.cpython-312.pyc
Normal file
BIN
services/__pycache__/task_service.cpython-312.pyc
Normal file
BIN
tcp/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tcp/__pycache__/server.cpython-312.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
utils/__pycache__/helpers.cpython-312.pyc
Normal file
3
zjsh_mixing_sysytem/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
18
zjsh_mixing_sysytem/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="5">
|
||||
<item index="0" class="java.lang.String" itemvalue="scipy" />
|
||||
<item index="1" class="java.lang.String" itemvalue="numpy" />
|
||||
<item index="2" class="java.lang.String" itemvalue="snap7" />
|
||||
<item index="3" class="java.lang.String" itemvalue="jsonchema" />
|
||||
<item index="4" class="java.lang.String" itemvalue="werkzeung" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
zjsh_mixing_sysytem/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
7
zjsh_mixing_sysytem/.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="pyside6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="pyside6" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
zjsh_mixing_sysytem/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/中交三航背景界面.iml" filepath="$PROJECT_DIR$/.idea/中交三航背景界面.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
8
zjsh_mixing_sysytem/.idea/中交三航背景界面.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="pyside6" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
BIN
zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-312.pyc
Normal file
BIN
zjsh_mixing_sysytem/__pycache__/dispatch_task.cpython-39.pyc
Normal file
BIN
zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-312.pyc
Normal file
BIN
zjsh_mixing_sysytem/__pycache__/system_nav_bar.cpython-39.pyc
Normal file
41
zjsh_mixing_sysytem/bottom_control_widget.py
Normal file
@ -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())
|
||||
|
||||
531
zjsh_mixing_sysytem/dispatch_task.py
Normal file
@ -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())
|
||||
113
zjsh_mixing_sysytem/main_window.py
Normal file
@ -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())
|
||||
141
zjsh_mixing_sysytem/system_nav_bar.py
Normal file
@ -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())
|
||||
BIN
zjsh_mixing_sysytem/主界面背景.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
zjsh_mixing_sysytem/关闭图标.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
zjsh_mixing_sysytem/底部背景.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
zjsh_mixing_sysytem/派单任务信息栏1.png
Normal file
|
After Width: | Height: | Size: 510 B |
BIN
zjsh_mixing_sysytem/派单任务信息栏2.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
zjsh_mixing_sysytem/派单最底部.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
zjsh_mixing_sysytem/生产取消按钮.png
Normal file
|
After Width: | Height: | Size: 226 B |
BIN
zjsh_mixing_sysytem/生产异常按钮.png
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
zjsh_mixing_sysytem/系统logo.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
zjsh_mixing_sysytem/系统主界面导航栏.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
zjsh_mixing_sysytem/系统总标题.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
zjsh_mixing_sysytem/系统消息喇叭.png
Normal file
|
After Width: | Height: | Size: 348 B |
BIN
zjsh_mixing_sysytem/系统消息背景.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
zjsh_mixing_sysytem/详情弹出背景.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
zjsh_mixing_sysytem/详情标题.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |