clean useless files
This commit is contained in:
Binary file not shown.
Binary file not shown.
26
pages/control_page.py
Normal file
26
pages/control_page.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
|
||||
from PyQt6.QtCore import Qt
|
||||
class ControlPage(QWidget):
|
||||
def __init__(self, api_client, parent=None):
|
||||
super().__init__(parent)
|
||||
self.api_client = api_client
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
label = QLabel("控制页面")
|
||||
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
layout.addWidget(label)
|
||||
|
||||
# 添加一些控制按钮
|
||||
button_layout = QHBoxLayout()
|
||||
start_button = QPushButton("开始打印")
|
||||
stop_button = QPushButton("停止打印")
|
||||
button_layout.addWidget(start_button)
|
||||
button_layout.addWidget(stop_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
|
||||
|
||||
self.setLayout(layout)
|
||||
@@ -1,8 +1,10 @@
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
from PyQt6.QtWidgets import (QWidget, QHBoxLayout, QVBoxLayout,
|
||||
QPushButton, QLabel, QFrame)
|
||||
from PyQt6.QtCore import Qt, QTimer
|
||||
QPushButton, QLabel, QFrame, QGraphicsView, QGraphicsScene, QGraphicsPathItem)
|
||||
from PyQt6.QtCore import Qt, QTimer, QThread, pyqtSignal, QUrl, QObject, pyqtProperty, QRectF
|
||||
from PyQt6.QtGui import QColor, QPen, QPainter, QPainterPath
|
||||
|
||||
def get_gcode_dir():
|
||||
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
|
||||
@@ -15,116 +17,210 @@ def get_gcode_dir():
|
||||
|
||||
GCODE_DIR = get_gcode_dir()
|
||||
|
||||
class GCode2DPreviewWidget(QGraphicsView):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.scene = QGraphicsScene(self)
|
||||
self.setScene(self.scene)
|
||||
self.setStyleSheet("background-color: #111111; border-radius: 5px; border: 1px solid #666;")
|
||||
self.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
# 翻转Y轴让(0,0)位于左下角,适配物理坐标系
|
||||
self.scale(1, -1)
|
||||
|
||||
def draw_paths(self, lines_data):
|
||||
self.scene.clear()
|
||||
bounding_rect = QRectF()
|
||||
|
||||
for color_str, data in lines_data.items():
|
||||
points = data.get("points", [])
|
||||
line_width = data.get("line_width", 2)
|
||||
|
||||
if not points:
|
||||
continue
|
||||
|
||||
path = QPainterPath()
|
||||
|
||||
# 判断是一组连续点的坐标(单线条)[[x1,y1], [x2,y2]]
|
||||
# 还是包含多根独立线条 [[[x1,y1], [x2,y2]], [[x3,y3], [x4,y4]]]
|
||||
if isinstance(points[0][0], (int, float)):
|
||||
path.moveTo(float(points[0][0]), float(points[0][1]))
|
||||
for pt in points[1:]:
|
||||
path.lineTo(float(pt[0]), float(pt[1]))
|
||||
else:
|
||||
for line_pts in points:
|
||||
if not line_pts:
|
||||
continue
|
||||
path.moveTo(float(line_pts[0][0]), float(line_pts[0][1]))
|
||||
for pt in line_pts[1:]:
|
||||
path.lineTo(float(pt[0]), float(pt[1]))
|
||||
|
||||
path_item = QGraphicsPathItem(path)
|
||||
|
||||
# 安全获取颜色
|
||||
pen_color = QColor(color_str) if QColor.isValidColor(color_str) else QColor("white")
|
||||
|
||||
pen = QPen(pen_color)
|
||||
pen.setWidth(int(line_width))
|
||||
# 保持 Cosmetic,以确保视图缩放时线条粗细在屏幕上看起来一致
|
||||
pen.setCosmetic(True)
|
||||
path_item.setPen(pen)
|
||||
|
||||
self.scene.addItem(path_item)
|
||||
bounding_rect = bounding_rect.united(path.boundingRect())
|
||||
|
||||
# 设置场景范围以便居中缩放
|
||||
if bounding_rect.width() < 1 or bounding_rect.height() < 1:
|
||||
bounding_rect = QRectF(0, 0, 220, 220)
|
||||
|
||||
bounding_rect.adjust(-10, -10, 10, 10)
|
||||
self.scene.setSceneRect(bounding_rect)
|
||||
self.fitInView(self.scene.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio)
|
||||
|
||||
class StatusPage(QWidget):
|
||||
def __init__(self, api_client, parent=None):
|
||||
super().__init__(parent)
|
||||
self.api_client = api_client
|
||||
self.current_file = None
|
||||
|
||||
self.file_name = "None"
|
||||
self.progress = 0
|
||||
self.display_name = "None"
|
||||
self.state = "Unknown"
|
||||
|
||||
self.init_ui()
|
||||
|
||||
# 定时器刷新状态
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.update_status)
|
||||
self.timer.start(2000)
|
||||
self.timer.start(1000)
|
||||
self.update_status()
|
||||
|
||||
def init_ui(self):
|
||||
main_layout = QHBoxLayout(self)
|
||||
|
||||
# --- 左侧:状态与控制 ---
|
||||
self.left_frame = QFrame()
|
||||
self.left_frame.setStyleSheet("background-color: #444444; border-radius: 10px;")
|
||||
left_layout = QVBoxLayout(self.left_frame)
|
||||
|
||||
self.lbl_status = QLabel("状态: 未知")
|
||||
self.lbl_status.setStyleSheet("color: white; font-size: 24px; border: none;")
|
||||
self.lbl_job = QLabel("文件: 无")
|
||||
self.lbl_job.setStyleSheet("color: white; font-size: 18px; border: none;")
|
||||
self.lbl_progress = QLabel("进度: 0%")
|
||||
self.lbl_progress.setStyleSheet("color: white; font-size: 18px; border: none;")
|
||||
|
||||
left_layout.addWidget(self.lbl_status)
|
||||
left_layout.addWidget(self.lbl_job)
|
||||
left_layout.addWidget(self.lbl_progress)
|
||||
left_layout.addStretch()
|
||||
|
||||
btn_layout = QHBoxLayout()
|
||||
self.btn_pause = QPushButton("暂停/恢复")
|
||||
self.btn_pause.setStyleSheet(self.btn_style())
|
||||
self.btn_pause.clicked.connect(self.toggle_pause)
|
||||
self.btn_pause.setMinimumHeight(60)
|
||||
|
||||
self.btn_stop = QPushButton("停止")
|
||||
self.btn_stop.setStyleSheet(self.btn_style())
|
||||
self.btn_stop.clicked.connect(self.stop_print)
|
||||
self.btn_stop.setMinimumHeight(60)
|
||||
|
||||
btn_layout.addWidget(self.btn_pause)
|
||||
btn_layout.addWidget(self.btn_stop)
|
||||
left_layout.addLayout(btn_layout)
|
||||
|
||||
# --- 右侧:预览 ---
|
||||
self.right_frame = QFrame()
|
||||
self.right_frame.setStyleSheet("background-color: #444444; border-radius: 10px;")
|
||||
right_layout = QVBoxLayout(self.right_frame)
|
||||
self.lbl_preview = QLabel("暂无预览...")
|
||||
self.lbl_preview.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.lbl_preview.setStyleSheet("color: white; font-size: 18px; border: none;")
|
||||
right_layout.addWidget(self.lbl_preview)
|
||||
|
||||
main_layout.addWidget(self.left_frame, 1)
|
||||
main_layout.addWidget(self.right_frame, 1)
|
||||
|
||||
def btn_style(self):
|
||||
return """
|
||||
QPushButton {
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
border: 2px solid #666666;
|
||||
border-radius: 5px;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #333333;
|
||||
border: 2px solid #4CAF50;
|
||||
}
|
||||
"""
|
||||
|
||||
def toggle_pause(self):
|
||||
self.api_client.pause_print()
|
||||
|
||||
def stop_print(self):
|
||||
self.api_client.stop_print()
|
||||
|
||||
def update_status(self):
|
||||
def fresh_status_valve(self):
|
||||
data = self.api_client.get_status()
|
||||
# print("Status Data:", data)
|
||||
if data:
|
||||
status = data.get("status", {})
|
||||
job = data.get("job", {})
|
||||
self.file_name = job.get("job", {}).get("file", {}).get("name", "None")
|
||||
self.progress = job.get("progress", {}).get("completion", 0)
|
||||
self.display_name = job.get("job", {}).get("file", {}).get("display_name", "None")
|
||||
self.state = status.get("state", {}).get("text", "Offline")
|
||||
self.print_time = job.get("progress", {}).get("printTime", 0)
|
||||
self.print_time_left = job.get("progress", {}).get("printTimeLeft", 0)
|
||||
|
||||
state_str = status.get("state", {}).get("text", "离线")
|
||||
self.lbl_status.setText(f"状态: {state_str}")
|
||||
# print(f"Updated Status: state={self.state}, file_name={self.file_name}, progress={self.progress:.1f}%, display_name={self.display_name}")
|
||||
def format_time(self, seconds):
|
||||
if seconds is None:
|
||||
return "N/A"
|
||||
m, s = divmod(seconds, 60)
|
||||
h, m = divmod(m, 60)
|
||||
if h > 0:
|
||||
return f"{int(h)}h {int(m)}m {int(s)}s"
|
||||
elif m > 0:
|
||||
return f"{int(m)}m {int(s)}s"
|
||||
else:
|
||||
return f"{int(s)}s"
|
||||
|
||||
file_name_node = job.get("job", {}).get("file", {})
|
||||
file_name = file_name_node.get("name") if file_name_node else "无"
|
||||
if not file_name:
|
||||
file_name = "无"
|
||||
self.lbl_job.setText(f"文件: {file_name}")
|
||||
|
||||
progress_node = job.get("progress", {})
|
||||
progress = progress_node.get("completion") if progress_node else 0
|
||||
if progress is None: progress = 0
|
||||
self.lbl_progress.setText(f"进度: {progress:.1f}%")
|
||||
|
||||
if file_name and file_name != "无" and file_name != self.current_file:
|
||||
self.current_file = file_name
|
||||
self.render_preview(file_name)
|
||||
|
||||
def render_preview(self, filename):
|
||||
filepath = os.path.join(GCODE_DIR, filename)
|
||||
if not os.path.exists(filepath):
|
||||
self.lbl_preview.setText(f"未找到对应的GCode文件:\n{filename}")
|
||||
return
|
||||
def init_ui(self):
|
||||
self.fresh_status_valve()
|
||||
main_layout = QHBoxLayout(self)
|
||||
|
||||
self.lbl_preview.setText(f"预览渲染成功:\n{filename}")
|
||||
self.left_frame = QFrame()
|
||||
self.left_frame.setStyleSheet("background-color: #444444; border-radius: 10px; color: white;")
|
||||
left_layout = QVBoxLayout(self.left_frame)
|
||||
|
||||
self.lbl_status = QLabel(f"Status: {self.state}")
|
||||
left_layout.addWidget(self.lbl_status)
|
||||
self.lbl_job = QLabel(f"File: {self.display_name}")
|
||||
left_layout.addWidget(self.lbl_job)
|
||||
self.lbl_progress = QLabel(f"Progress: {self.progress if self.progress else 0:.1f}%")
|
||||
left_layout.addWidget(self.lbl_progress)
|
||||
self.lbl_print_time = QLabel(f"Print Time: {self.format_time(self.print_time)}")
|
||||
left_layout.addWidget(self.lbl_print_time)
|
||||
self.lbl_print_time_left = QLabel(f"Print Time Left: {self.format_time(self.print_time_left)}")
|
||||
left_layout.addWidget(self.lbl_print_time_left)
|
||||
left_layout.addStretch()
|
||||
|
||||
#TODO: 3D Gcode View in right frame, use QML and QtQuick3D to render the Gcode vertices, pass the progress to QML to show the current layer
|
||||
# Due to the complexity of parsing Gcode and rendering it in 3D, this part will be implemented in a separate thread to avoid blocking the UI, and the vertices will be passed to QML for rendering. The progress will also be passed to QML to show the current layer being printed.
|
||||
# Load QtQuick3D View
|
||||
# self.right_frame = QFrame()
|
||||
# right_layout = QVBoxLayout(self.right_frame)
|
||||
# self.view = QQuickView()
|
||||
# self.view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
|
||||
# qml_file = os.path.join(os.path.dirname(__file__), "gcode_view2.qml")
|
||||
|
||||
# self.view.setSource(QUrl.fromLocalFile(qml_file))
|
||||
# gcode_data = self.load_gcode_vertices(os.path.join(GCODE_DIR, self.file_name))
|
||||
# self.view.rootContext().setContextProperty("gcodeData", gcode_data)
|
||||
# container = QWidget.createWindowContainer(self.view, self)
|
||||
# right_layout.addWidget(container)
|
||||
|
||||
self.right_frame = QFrame()
|
||||
self.right_frame.setStyleSheet("background-color: #444444; border-radius: 10px;")
|
||||
right_layout = QVBoxLayout(self.right_frame)
|
||||
|
||||
self.gcode_view = GCode2DPreviewWidget()
|
||||
right_layout.addWidget(self.gcode_view)
|
||||
|
||||
main_layout.addWidget(self.left_frame, 1)
|
||||
main_layout.addWidget(self.right_frame, 2)
|
||||
|
||||
def update_status(self):
|
||||
self.fresh_status_valve()
|
||||
self.lbl_status.setText(f"Status: {self.state}")
|
||||
|
||||
self.lbl_job.setText(f"File: {self.display_name}")
|
||||
|
||||
self.lbl_progress.setText(f"Progress: {self.progress if self.progress else 0:.1f}%")
|
||||
|
||||
# Pass progress to QML
|
||||
# root_obj = self.view.rootObject()
|
||||
# if root_obj:
|
||||
# root_obj.setProperty("progress", prog if prog else 0)
|
||||
|
||||
|
||||
|
||||
#TODO: Better Gcode Parser, this one is too slow for large files, need to optimize or use a separate thread to load
|
||||
# def load_gcode_vertices(self, path):
|
||||
# vertices = []
|
||||
|
||||
# x = 0
|
||||
# y = 0
|
||||
# z = 0
|
||||
|
||||
# with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
||||
# for line in f:
|
||||
# line = line.strip()
|
||||
|
||||
# if not line:
|
||||
# continue
|
||||
|
||||
# if line.startswith("G0") or line.startswith("G1"):
|
||||
# old_x = x
|
||||
# old_y = y
|
||||
# old_z = z
|
||||
|
||||
# mx = re.search(r"X([-0-9.]+)", line)
|
||||
# my = re.search(r"Y([-0-9.]+)", line)
|
||||
# mz = re.search(r"Z([-0-9.]+)", line)
|
||||
|
||||
# if mx:
|
||||
# x = float(mx.group(1))
|
||||
|
||||
# if my:
|
||||
# y = float(my.group(1))
|
||||
|
||||
# if mz:
|
||||
# z = float(mz.group(1))
|
||||
|
||||
# vertices.append({
|
||||
# "x1": old_x,
|
||||
# "y1": old_y,
|
||||
# "z1": old_z,
|
||||
# "x2": x,
|
||||
# "y2": y,
|
||||
# "z2": z,
|
||||
# })
|
||||
|
||||
# return vertices
|
||||
|
||||
Reference in New Issue
Block a user