refactor: 移除局域网发送至打印机功能
This commit is contained in:
@@ -59,7 +59,6 @@ except ImportError as exc: # pragma: no cover
|
||||
|
||||
from . import APP_NAME, APP_TITLE
|
||||
from .i18n import t, switch as i18n_switch, current_locale
|
||||
from .printer import PrinterConfig, send_to_printer
|
||||
from .batch import IndividualBuildTask, individual_batch_worker_count, run_individual_batch_builds
|
||||
from .core import (
|
||||
BuildOptions,
|
||||
@@ -689,35 +688,6 @@ class MainWindow(QMainWindow):
|
||||
self.output_preview_label_title = QLabel(t("gui.output.preview"))
|
||||
output_layout.addRow(self.output_preview_label_title, self.output_preview_label)
|
||||
|
||||
# ---- 发送至打印机 ----
|
||||
self.send_to_printer_check = QCheckBox(t("gui.output.printer.send"))
|
||||
self.send_to_printer_check.setChecked(False)
|
||||
self.send_to_printer_check.setToolTip(t("gui.output.printer.send.tooltip"))
|
||||
self.printer_label = QLabel(t("gui.output.printer.send"))
|
||||
output_layout.addRow(self.printer_label, self.send_to_printer_check)
|
||||
|
||||
# 打印机配置行(IP、访问码、序列号)
|
||||
printer_config_widget = QWidget()
|
||||
printer_config_layout = QHBoxLayout(printer_config_widget)
|
||||
printer_config_layout.setContentsMargins(0, 0, 0, 0)
|
||||
printer_config_layout.setSpacing(6)
|
||||
|
||||
self.printer_ip_edit = QLineEdit()
|
||||
self.printer_ip_edit.setPlaceholderText(t("gui.output.printer.ip"))
|
||||
self.printer_ip_edit.setMaximumWidth(140)
|
||||
self.printer_access_code_edit = QLineEdit()
|
||||
self.printer_access_code_edit.setPlaceholderText(t("gui.output.printer.access_code"))
|
||||
self.printer_access_code_edit.setMaximumWidth(120)
|
||||
self.printer_serial_edit = QLineEdit()
|
||||
self.printer_serial_edit.setPlaceholderText(t("gui.output.printer.serial"))
|
||||
self.printer_serial_edit.setMaximumWidth(180)
|
||||
|
||||
printer_config_layout.addWidget(self.printer_ip_edit)
|
||||
printer_config_layout.addWidget(self.printer_access_code_edit)
|
||||
printer_config_layout.addWidget(self.printer_serial_edit)
|
||||
printer_config_layout.addStretch(1)
|
||||
output_layout.addRow("", printer_config_widget)
|
||||
|
||||
root.addWidget(self.output_group)
|
||||
|
||||
# =====================================================================
|
||||
@@ -778,10 +748,6 @@ class MainWindow(QMainWindow):
|
||||
self.individual_batch_check.stateChanged.connect(self.on_individual_batch_toggled)
|
||||
self.clear_after_build_check.stateChanged.connect(self.save_current_settings)
|
||||
self.skip_duplicates_check.stateChanged.connect(self.save_current_settings)
|
||||
self.send_to_printer_check.stateChanged.connect(self.save_current_settings)
|
||||
self.printer_ip_edit.textChanged.connect(self.save_current_settings)
|
||||
self.printer_access_code_edit.textChanged.connect(self.save_current_settings)
|
||||
self.printer_serial_edit.textChanged.connect(self.save_current_settings)
|
||||
self.output_dir_edit.textChanged.connect(self.on_output_rule_changed)
|
||||
self.output_name_edit.textChanged.connect(self.on_output_rule_changed)
|
||||
self.language_combo.currentIndexChanged.connect(self.on_language_changed)
|
||||
@@ -806,10 +772,6 @@ class MainWindow(QMainWindow):
|
||||
locale_index = self.language_combo.findData(saved_locale)
|
||||
if locale_index >= 0:
|
||||
self.language_combo.setCurrentIndex(locale_index)
|
||||
self.send_to_printer_check.setChecked(bool(options.get("send_to_printer_enabled", False)))
|
||||
self.printer_ip_edit.setText(str(options.get("printer_ip", "")))
|
||||
self.printer_access_code_edit.setText(str(options.get("printer_access_code", "")))
|
||||
self.printer_serial_edit.setText(str(options.get("printer_serial", "")))
|
||||
self.output_dir_edit.setText(str(options.get("output_directory", "")))
|
||||
self.output_name_edit.setText(str(options.get("output_filename_rule", DEFAULT_OUTPUT_PATTERN)))
|
||||
metadata_mode = options.get("metadata_mode", "sum")
|
||||
@@ -849,10 +811,6 @@ class MainWindow(QMainWindow):
|
||||
"clear_after_build": self.clear_after_build_check.isChecked(),
|
||||
"skip_duplicates": self.skip_duplicates_check.isChecked(),
|
||||
"locale": self.language_combo.currentData() or "zh_CN",
|
||||
"send_to_printer_enabled": self.send_to_printer_check.isChecked(),
|
||||
"printer_ip": self.printer_ip_edit.text().strip(),
|
||||
"printer_access_code": self.printer_access_code_edit.text().strip(),
|
||||
"printer_serial": self.printer_serial_edit.text().strip(),
|
||||
"output_directory": self.output_dir_edit.text().strip(),
|
||||
"output_filename_rule": self.output_name_edit.text().strip() or DEFAULT_OUTPUT_PATTERN,
|
||||
}
|
||||
@@ -1402,21 +1360,6 @@ class MainWindow(QMainWindow):
|
||||
"""显示构建成功的浮动提示条。"""
|
||||
self.success_toast.show_message(message)
|
||||
|
||||
def _send_to_printer(self, file_path: Path) -> None:
|
||||
"""将构建好的 3MF 文件发送至打印机。"""
|
||||
config = PrinterConfig(
|
||||
ip=self.printer_ip_edit.text().strip(),
|
||||
access_code=self.printer_access_code_edit.text().strip(),
|
||||
serial=self.printer_serial_edit.text().strip(),
|
||||
)
|
||||
try:
|
||||
send_to_printer(file_path, config)
|
||||
self.log.append(t("gui.toast.send_success", name=file_path.name))
|
||||
except ImportError:
|
||||
self.log.append(t("gui.log.printer_missing"))
|
||||
except Exception as exc:
|
||||
self.log.append(t("gui.toast.send_failed", error=str(exc)))
|
||||
|
||||
def build_output(self) -> None:
|
||||
"""主构建入口:根据批处理模式分发到合并构建或独立构建。"""
|
||||
jobs = self.collect_jobs()
|
||||
@@ -1440,8 +1383,6 @@ class MainWindow(QMainWindow):
|
||||
options = self.build_options_for_output(output_path)
|
||||
result = build_packed_3mf(jobs, options)
|
||||
self.log_build_result(result)
|
||||
if self.send_to_printer_check.isChecked():
|
||||
self._send_to_printer(result.output_3mf)
|
||||
self.show_success_toast(t("gui.toast.combined_success"))
|
||||
if self.clear_after_build_check.isChecked():
|
||||
self.remove_all()
|
||||
@@ -1471,8 +1412,6 @@ class MainWindow(QMainWindow):
|
||||
batch_result = run_individual_batch_builds(tasks, max_workers=worker_count)
|
||||
for result in batch_result.results:
|
||||
self.log_build_result(result)
|
||||
if self.send_to_printer_check.isChecked():
|
||||
self._send_to_printer(result.output_3mf)
|
||||
success_count = len(batch_result.results)
|
||||
for failure in batch_result.failures:
|
||||
errors.append(f"{failure.job.source_3mf.name}: {failure.error}")
|
||||
@@ -1589,12 +1528,6 @@ class MainWindow(QMainWindow):
|
||||
self.output_dir_label.setText(t("gui.output.dir.label"))
|
||||
self.output_filename_rule_label.setText(t("gui.output.filename_rule"))
|
||||
self.output_preview_label_title.setText(t("gui.output.preview"))
|
||||
self.printer_label.setText(t("gui.output.printer.send"))
|
||||
self.send_to_printer_check.setText(t("gui.output.printer.send"))
|
||||
self.send_to_printer_check.setToolTip(t("gui.output.printer.send.tooltip"))
|
||||
self.printer_ip_edit.setPlaceholderText(t("gui.output.printer.ip"))
|
||||
self.printer_access_code_edit.setPlaceholderText(t("gui.output.printer.access_code"))
|
||||
self.printer_serial_edit.setPlaceholderText(t("gui.output.printer.serial"))
|
||||
|
||||
# 语言标签
|
||||
self.language_label.setText(t("gui.options.language") + ":")
|
||||
|
||||
@@ -71,11 +71,6 @@
|
||||
"gui.output.filename_rule.help.tooltip": "Show filename token help",
|
||||
"gui.output.preview": "Preview",
|
||||
"gui.output.batch_preview": "{first} | {count} output file(s)",
|
||||
"gui.output.printer.send": "Send to printer after build",
|
||||
"gui.output.printer.ip": "IP Address",
|
||||
"gui.output.printer.access_code": "Access Code",
|
||||
"gui.output.printer.serial": "Serial",
|
||||
"gui.output.printer.send.tooltip": "Send the generated 3MF to an A1 printer over LAN and start printing (LAN only — computer and printer must be on the same local network)",
|
||||
|
||||
"gui.dialog.individual_batch.title": "Individual Batch Mode",
|
||||
"gui.dialog.individual_batch.text": "Individual batch mode treats every input row as a separate build.\n\nExample: if you add 20 single-plate 3MF files and set copies to 5, Build 3MF will create 20 separate packed files. Each output contains only that source file repeated 5 times.\n\nThis is useful for quickly batch-converting many independent 3MF jobs into multi-copy SwapMod packs. It does not combine all input files into one 3MF.",
|
||||
@@ -93,8 +88,6 @@
|
||||
|
||||
"gui.toast.combined_success": "The packed 3MF file was created.",
|
||||
"gui.toast.batch_success": "Created {count} packed 3MF file(s).",
|
||||
"gui.toast.send_success": "Sent to printer: {name}",
|
||||
"gui.toast.send_failed": "Failed to send to printer: {error}",
|
||||
|
||||
"gui.log.added_files": "Added {added} input file(s). Skipped {skipped}.",
|
||||
"gui.log.skipped_file": "Skipped {path}: {reason}",
|
||||
@@ -106,7 +99,6 @@
|
||||
"gui.log.filament": "Estimated source filament: {filament}",
|
||||
"gui.log.md5": "G-code MD5: {md5}",
|
||||
"gui.log.build_error": "Error building {path}: {error}",
|
||||
"gui.log.printer_missing": "Error: bambulabs_api not installed, run: pip install bambulabs_api",
|
||||
|
||||
"format.unknown": "Unknown"
|
||||
}
|
||||
|
||||
@@ -71,11 +71,6 @@
|
||||
"gui.output.filename_rule.help.tooltip": "显示文件名标记帮助",
|
||||
"gui.output.preview": "预览",
|
||||
"gui.output.batch_preview": "{first} | {count} 个输出文件",
|
||||
"gui.output.printer.send": "构建后发送至打印机",
|
||||
"gui.output.printer.ip": "IP 地址",
|
||||
"gui.output.printer.access_code": "访问码",
|
||||
"gui.output.printer.serial": "序列号",
|
||||
"gui.output.printer.send.tooltip": "通过局域网将生成的 3MF 发送到 A1 打印机并开始打印(仅支持本地局域网,电脑与打印机需在同一网络下)",
|
||||
|
||||
"gui.dialog.individual_batch.title": "独立批处理模式",
|
||||
"gui.dialog.individual_batch.text": "独立批处理模式将每一行输入作为独立的构建任务。\n\n示例:如果你添加了 20 个单板 3MF 文件并设置份数为 5,点击\"构建 3MF\"将创建 20 个独立的打包文件。每个输出文件仅包含该源文件重复 5 次的内容。\n\n这个功能适用于快速将大量独立的 3MF 作业批量转换为多份数的 SwapMod 包。它不会将所有输入文件合并到一个 3MF 中。",
|
||||
@@ -93,8 +88,6 @@
|
||||
|
||||
"gui.toast.combined_success": "已创建打包的 3MF 文件。",
|
||||
"gui.toast.batch_success": "已创建 {count} 个打包的 3MF 文件。",
|
||||
"gui.toast.send_success": "已发送至打印机:{name}",
|
||||
"gui.toast.send_failed": "发送至打印机失败:{error}",
|
||||
|
||||
"gui.log.added_files": "已添加 {added} 个输入文件。跳过 {skipped} 个。",
|
||||
"gui.log.skipped_file": "已跳过 {path}:{reason}",
|
||||
@@ -106,7 +99,6 @@
|
||||
"gui.log.filament": "预估源文件耗材:{filament}",
|
||||
"gui.log.md5": "G-code MD5:{md5}",
|
||||
"gui.log.build_error": "构建 {path} 时出错:{error}",
|
||||
"gui.log.printer_missing": "错误:未安装 bambulabs_api,请执行 pip install bambulabs_api",
|
||||
|
||||
"format.unknown": "未知"
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
"""打印机发送模块:通过局域网将 3MF 发送到 Bambu A1 并开始打印。"""
|
||||
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrinterConfig:
|
||||
"""打印机连接配置。"""
|
||||
ip: str = "" # 打印机 IP 地址
|
||||
access_code: str = "" # 局域网访问码
|
||||
serial: str = "" # 打印机序列号
|
||||
|
||||
|
||||
def check_printer_ready(config: PrinterConfig) -> bool:
|
||||
"""检查打印机是否空闲可接收新任务。
|
||||
|
||||
返回 True 表示打印机空闲,False 表示正忙或离线。
|
||||
"""
|
||||
from bambulabs_api import Printer as BambuPrinter
|
||||
from bambulabs_api.states_info import GcodeState
|
||||
printer = BambuPrinter(config.ip, config.access_code, config.serial)
|
||||
try:
|
||||
printer.connect()
|
||||
state = printer.get_state()
|
||||
return state == GcodeState.IDLE
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
printer.disconnect()
|
||||
|
||||
|
||||
def _start_print_for_a1(printer, file_name: str, plate_number: int = 1) -> None:
|
||||
"""发送打印机开始打印的 MQTT 指令,使用 A1 专用的 sdcard URL 格式。
|
||||
|
||||
bambulabs_api 的 start_print 方法硬编码了 ftp:/// 前缀,
|
||||
这对 A1 不生效。A1 需要 file:///sdcard/ 前缀。
|
||||
本函数绕过 start_print,直接构造正确的 MQTT 载荷。
|
||||
"""
|
||||
plate_location = f"Metadata/plate_{int(plate_number)}.gcode"
|
||||
payload = {
|
||||
"print": {
|
||||
"command": "project_file",
|
||||
"param": plate_location,
|
||||
"file": file_name,
|
||||
"bed_leveling": True,
|
||||
"bed_type": "textured_plate",
|
||||
"flow_cali": True,
|
||||
"vibration_cali": True,
|
||||
"url": f"file:///sdcard/{file_name}",
|
||||
"layer_inspect": False,
|
||||
"sequence_id": "10000000",
|
||||
"use_ams": True,
|
||||
"ams_mapping": [0],
|
||||
}
|
||||
}
|
||||
# 调用名称改写后的私有方法发送原始 JSON 荷载
|
||||
printer.mqtt_client._PrinterMQTTClient__publish_command(payload)
|
||||
|
||||
|
||||
def send_to_printer(file_path: Path, config: PrinterConfig) -> bool:
|
||||
"""将 3MF 文件上传至 A1 打印机并通过局域网开始打印。
|
||||
|
||||
发送前会检查打印机状态;正忙则拒绝发送。
|
||||
⚠️ 仅支持本地局域网,电脑与打印机必须在同一网络下。
|
||||
|
||||
Args:
|
||||
file_path: 要发送的 3MF 文件路径
|
||||
config: 打印机连接配置
|
||||
|
||||
Returns:
|
||||
True 表示成功
|
||||
|
||||
Raises:
|
||||
ImportError: bambulabs_api 未安装
|
||||
RuntimeError: 打印机正忙或上传/打印失败
|
||||
"""
|
||||
from bambulabs_api import Printer as BambuPrinter
|
||||
from bambulabs_api.states_info import GcodeState
|
||||
|
||||
printer = BambuPrinter(config.ip, config.access_code, config.serial)
|
||||
try:
|
||||
printer.connect()
|
||||
|
||||
# 检查打印机是否空闲
|
||||
state = printer.get_state()
|
||||
if state != GcodeState.IDLE:
|
||||
raise RuntimeError(
|
||||
f"打印机当前状态为 {state.value},"
|
||||
f"无法开始新任务。请等待当前打印完成。"
|
||||
)
|
||||
|
||||
file_name = file_path.name
|
||||
with open(file_path, "rb") as f:
|
||||
import io
|
||||
result = printer.upload_file(io.BytesIO(f.read()), file_name)
|
||||
if "226" not in result:
|
||||
raise RuntimeError(f"上传文件失败: {result}")
|
||||
|
||||
# 使用 A1 专用的 MQTT 指令开始打印,而非内置的 start_print
|
||||
_start_print_for_a1(printer, file_name, plate_number=1)
|
||||
return True
|
||||
finally:
|
||||
printer.disconnect()
|
||||
Reference in New Issue
Block a user