From 79127a4bfd03367462ba08ac510eb4aaec9ae9b3 Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Sat, 14 Mar 2026 04:03:16 +0800 Subject: [PATCH] no message --- python/Bag1.py | 86 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/python/Bag1.py b/python/Bag1.py index e98c153..dce3502 100644 --- a/python/Bag1.py +++ b/python/Bag1.py @@ -26,6 +26,11 @@ try: except ImportError: serial = None # type: ignore +try: + import win32print # type: ignore[import] +except ImportError: + win32print = None # type: ignore[assignment] + DEFAULT_BASE_URL = os.environ.get("FPSMS_BASE_URL", "http://localhost:8090/api") # When run as PyInstaller exe, save settings next to the exe; otherwise next to script if getattr(sys, "frozen", False): @@ -42,7 +47,8 @@ DEFAULT_SETTINGS = { "dabag_port": "3008", "laser_ip": "192.168.17.10", "laser_port": "45678", - "label_com": "COM3", + # For 標簽機 on Windows, this is the Windows printer name, e.g. "TSC TTP-246M Pro" + "label_com": "TSC TTP-246M Pro", } @@ -97,13 +103,26 @@ def try_printer_connection(printer_name: str, sett: dict) -> bool: except (socket.error, ValueError, OSError): return False if printer_name == "標簽機": - if serial is None: + target = (sett.get("label_com") or "").strip() + if not target: return False - com = (sett.get("label_com") or "").strip() - if not com: + # On Windows, allow using a Windows printer name (e.g. "TSC TTP-246M Pro") + # as an alternative to a COM port. If it doesn't look like a COM port, + # try opening it via the Windows print spooler. + if os.name == "nt" and not target.upper().startswith("COM"): + if win32print is None: + return False + try: + handle = win32print.OpenPrinter(target) + win32print.ClosePrinter(handle) + return True + except Exception: + return False + # Fallback: treat as serial COM port (original behaviour) + if serial is None: return False try: - ser = serial.Serial(com, timeout=1) + ser = serial.Serial(target, timeout=1) ser.close() return True except (serial.SerialException, OSError): @@ -190,12 +209,12 @@ def generate_zpl_label_small( item_id: Optional[int] = None, stock_in_line_id: Optional[int] = None, lot_no: Optional[str] = None, - font: str = "ARIALR.TTF", + font: str = "MingLiUHKSCS", ) -> str: """ ZPL for 標簽機. Row 1: item name. Row 2: QR left | item code + lot no (or batch) right. QR contains {"itemId": xxx, "stockInLineId": xxx} when both present; else batch_no. - Font: ARIALR.TTF. + Unicode (^CI28); font set for Big-5 (e.g. MingLiUHKSCS). """ desc = _zpl_escape((item_name or "—").strip()) code = _zpl_escape((item_code or "—").strip()) @@ -232,13 +251,42 @@ def send_zpl_to_dataflex(ip: str, port: int, zpl: str) -> None: sock.close() -def send_zpl_to_label_printer(com_port: str, zpl: str) -> None: - """Send ZPL to 標簽機 via serial COM port. Raises on error.""" +def send_zpl_to_label_printer(target: str, zpl: str) -> None: + """ + Send ZPL to 標簽機. + + On Windows, if target is not a COM port (e.g. "TSC TTP-246M Pro"), + send raw ZPL to the named Windows printer via the spooler. + Otherwise, treat target as a serial COM port (original behaviour). + """ + dest = (target or "").strip() + if not dest: + raise ValueError("Label printer destination is empty.") + + # Unicode (^CI28); send UTF-8 to 標簽機 + raw_bytes = zpl.encode("utf-8") + + # Windows printer name path (USB printer installed as normal printer) + if os.name == "nt" and not dest.upper().startswith("COM"): + if win32print is None: + raise RuntimeError("pywin32 not installed. Run: pip install pywin32") + handle = win32print.OpenPrinter(dest) + try: + job = win32print.StartDocPrinter(handle, 1, ("FPSMS Label", None, "RAW")) + win32print.StartPagePrinter(handle) + win32print.WritePrinter(handle, raw_bytes) + win32print.EndPagePrinter(handle) + win32print.EndDocPrinter(handle) + finally: + win32print.ClosePrinter(handle) + return + + # Fallback: serial COM port if serial is None: raise RuntimeError("pyserial not installed. Run: pip install pyserial") - ser = serial.Serial(com_port, timeout=5) + ser = serial.Serial(dest, timeout=5) try: - ser.write(zpl.encode("utf-8")) + ser.write(raw_bytes) finally: ser.close() @@ -733,9 +781,17 @@ def main() -> None: ) grid_row[0] += 1 if key_single: - ttk.Label(f, text="COM:").grid(row=grid_row[0], column=0, sticky=tk.W, pady=2) + ttk.Label( + f, + text="列印機名稱 (Windows):", + ).grid( + row=grid_row[0], + column=0, + sticky=tk.W, + pady=2, + ) var = tk.StringVar(value=sett.get(key_single, "")) - e = tk.Entry(f, textvariable=var, width=14, font=get_font(FONT_SIZE), bg="white") + e = tk.Entry(f, textvariable=var, width=22, font=get_font(FONT_SIZE), bg="white") e.grid(row=grid_row[0], column=1, sticky=tk.W, pady=2) _ensure_dot_in_entry(e) grid_row[0] += 1 @@ -762,7 +818,7 @@ def main() -> None: all_vars.extend(add_section("API 伺服器", "api_ip", "api_port", None)) all_vars.extend(add_section("打袋機 DataFlex", "dabag_ip", "dabag_port", None)) all_vars.extend(add_section("激光機", "laser_ip", "laser_port", None)) - all_vars.extend(add_section("標簽機 COM 埠", None, None, "label_com")) + all_vars.extend(add_section("標簽機 (USB)", None, None, "label_com")) def on_save(): for key, var in all_vars: @@ -933,7 +989,7 @@ def main() -> None: elif printer_var.get() == "標簽機": com = (settings.get("label_com") or "").strip() if not com: - messagebox.showerror("標簽機", "請在設定中填寫標簽機 COM 埠。") + messagebox.showerror("標簽機", "請在設定中填寫標簽機名稱 (例如:TSC TTP-246M Pro)。") else: count = ask_label_count(root) if count is not None: