From 2f423f4281cdee54b397c02b579ba3256b0c2b18 Mon Sep 17 00:00:00 2001 From: edo-neo Date: Wed, 3 Sep 2025 11:40:34 +0200 Subject: [PATCH] add support for dynamic printer selection and template resolution in label printing modules --- config/machine_settings/test-linux.ini | 8 +- src/components/os_label_printer.py | 37 +++-- src/lib/helpers/recipe_manager.py | 1 + src/main.py | 2 +- src/ui/print_step_editor/print_step_editor.py | 51 ++++++ src/ui/print_step_editor/print_step_editor.ui | 154 ++++++++++-------- src/ui/recipe_selection/recipe_selection.py | 40 +++++ src/ui/test/test.py | 17 +- 8 files changed, 215 insertions(+), 95 deletions(-) diff --git a/config/machine_settings/test-linux.ini b/config/machine_settings/test-linux.ini index 25158db..62fd068 100644 --- a/config/machine_settings/test-linux.ini +++ b/config/machine_settings/test-linux.ini @@ -9,6 +9,7 @@ archive_synchronizer: present galaxy_camera: absent uvc_camera: absent label_printer: present +label_printer_2: present neo_pixels: absent remote_api: absent tecna_t3: present @@ -45,9 +46,14 @@ description_field: descrizione [label_printer] platform: linux -printer: ZTC-ZD421-300dpi-ZPL-2 +printer: ZTC-ZD421-300dpi-ZPL risoluzione: 300 +[label_printer_2] +platform: linux +printer: ZTC-ZD421-203dpi-ZPL +risoluzione: 203 + [recipes_defaults] tester_discharge_enable: yes codice_ricetta: specificare ricetta diff --git a/src/components/os_label_printer.py b/src/components/os_label_printer.py index f754f7c..51d8108 100644 --- a/src/components/os_label_printer.py +++ b/src/components/os_label_printer.py @@ -20,7 +20,8 @@ class Os_Label_Printer(Component): self.simulate = True def config_changed(self): - self.platform = self.config[self.name]["platform"] + cfg = self.config.get(self.name, {}) + self.platform = cfg.get("platform", platform.system().lower()) # for windows: # cmd # wmic printer list brief @@ -28,33 +29,35 @@ class Os_Label_Printer(Component): # Get-Printer # for cups (linux, osx) # lpstat -p -d - self.printer = self.config[self.name]["printer"] + # read only the 'printer' key from this section + self.printer = cfg.get("printer", "") @Component.reconfig_on_error def print_label(self, template, context=None): if context is None: context = {} - if self.config["label_printer"]["risoluzione"] == "300": - custom_label_folder=f"config/label_templates/{str(self.config.machine_id)}/300/" - standard_label_folder=f"config/label_templates/300/" - elif self.config["label_printer"]["risoluzione"]== "203": + # Use section resolution only + cfg = self.config.get(self.name, {}) + try: + res = int(str(cfg.get("risoluzione", "300")).strip()) + except Exception: + res = 300 + # Resolve folders per inferred resolution + if res == 300: + custom_label_folder = f"config/label_templates/{str(self.config.machine_id)}/300/" + standard_label_folder = f"config/label_templates/300/" + else: custom_label_folder = f"config/label_templates/{str(self.config.machine_id)}/203/" standard_label_folder = f"config/label_templates/203/" - if os.path.exists(custom_label_folder): - label_folder = custom_label_folder - else: - label_folder = standard_label_folder - label_path = label_folder+str(template) - if os.path.exists(label_path): - pass - else: - label_path = standard_label_folder+str(template) + label_folder = custom_label_folder if os.path.exists(custom_label_folder) else standard_label_folder + label_path = label_folder + str(template) + if not os.path.exists(label_path): + label_path = standard_label_folder + str(template) # LOAD LABEL TEMPLATE with open(label_path, "r", errors="surrogateescape", encoding='iso-8859-1') as f: label_file_contents = f.read() - # LABEL PRINT - #label = label.format(**context) + # Replace placeholders with context values for key, val in context.items(): key = "{" + key + "}" label_file_contents = label_file_contents.replace(key, val) diff --git a/src/lib/helpers/recipe_manager.py b/src/lib/helpers/recipe_manager.py index 6a7fb07..44289db 100644 --- a/src/lib/helpers/recipe_manager.py +++ b/src/lib/helpers/recipe_manager.py @@ -145,6 +145,7 @@ def read_steps(row, config, defaults=None, unsupported_steps=None): "labeltxt_5": row.get("barcode_input_finelinea", ""), "extra_label": row.get("etichette_supplementari", ""), "barcode": row.get("barcode_stampato",defaults["barcode_format"]), + "printer_selection": row.get("printer_selection", config.get("label_printer", {}).get("printer", "")), }, } diff --git a/src/main.py b/src/main.py index 633c092..342286e 100644 --- a/src/main.py +++ b/src/main.py @@ -111,7 +111,7 @@ try: "pipe_cutter": {"c": PipeCutterComponent, "k": {"paused": False}}, "label_printer": {"c": Os_Label_Printer, "t": False}, "extra_label_printer": {"c": Os_Label_Printer, "t": False}, - "label_printer_2": {"c": BrotherLabelPrinter, "t": False}, + "label_printer_2": {"c": Os_Label_Printer, "t": False}, "multicomp": {"c": Multicomp730424, "k": {"paused": True}}, "remote_api": {"c": RemoteAPI, "k": {"main": self}}, "screwdriver": {"c": TecnaScrewdriver, "k": {"paused": True}}, diff --git a/src/ui/print_step_editor/print_step_editor.py b/src/ui/print_step_editor/print_step_editor.py index fc6eb6e..6aade30 100644 --- a/src/ui/print_step_editor/print_step_editor.py +++ b/src/ui/print_step_editor/print_step_editor.py @@ -4,7 +4,11 @@ from ui.editor import Editor class Print_Step_Editor(Editor): def __init__(self, action=None, cell_widget=None): super().__init__(action=action, cell_widget=cell_widget) + self._templates_203 = [] + self._templates_300 = [] + self._printers_resolution = {} self.spec.update({ + "printer_selection": self.printer_selection, "template": self.label_template_cb, "labeltxt_1": self.labeltxt_1, "labeltxt_2": self.labeltxt_2, @@ -14,3 +18,50 @@ class Print_Step_Editor(Editor): "extra_label": self.extra_label, "barcode": self.barcode, }) + # Update templates on printer change + try: + self.printer_selection.currentTextChanged.connect(self._on_printer_changed) + except Exception: + pass + + def do_autocomplete(self, autocomplete): + # Grab templates lists if provided for dynamic switching + if autocomplete is None: + return + self._templates_203 = list(map(str, autocomplete.get("templates_203", []))) + self._templates_300 = list(map(str, autocomplete.get("templates_300", []))) + self._printers_resolution = dict(autocomplete.get("printers_resolution", {})) + super().do_autocomplete(autocomplete) + # After filling combos, ensure templates shown match current printer + self._on_printer_changed(self.printer_selection.currentText()) + + def render(self, data, field_name=None, row_number=None, crud=None): + # Preserve current template choice from data then adjust list for selected printer + current_template = data.get("template", None) + super().render(data, field_name=field_name, row_number=row_number, crud=crud) + # Re-apply templates list based on printer and restore selection + self._on_printer_changed(self.printer_selection.currentText(), initial_template=current_template) + + def _on_printer_changed(self, printer_name, initial_template=None): + # If no mapping for this printer, keep current list (use machine's default) + if not printer_name or printer_name not in self._printers_resolution: + return + # Decide resolution from mapping provided by recipe_selection + try: + res = int(self._printers_resolution.get(printer_name, 300)) + except Exception: + res = 300 + # Build the list accordingly + new_items = self._templates_300 if res == 300 else self._templates_203 + # If no lists provided, leave as-is + if not new_items: + return + # Preserve current selection + prev = initial_template if initial_template is not None else self.label_template_cb.currentText() + self.label_template_cb.blockSignals(True) + self.label_template_cb.clear() + self.label_template_cb.addItems(list(map(str, new_items))) + # Restore previous selection if present in new list + if prev in new_items: + self.label_template_cb.setCurrentText(prev) + self.label_template_cb.blockSignals(False) diff --git a/src/ui/print_step_editor/print_step_editor.ui b/src/ui/print_step_editor/print_step_editor.ui index 8538e73..f71f765 100644 --- a/src/ui/print_step_editor/print_step_editor.ui +++ b/src/ui/print_step_editor/print_step_editor.ui @@ -7,7 +7,7 @@ 0 0 750 - 332 + 426 @@ -37,45 +37,17 @@ Etichetta - - - - Modello - - - - + - + Campo testo 1 - - - - Campo testo 2 - - - - - - - Campo testo 3 - - - - - - - Campo testo 4 - - - - + @@ -85,7 +57,14 @@ - + + + + Campo testo 2 + + + + @@ -95,7 +74,14 @@ - + + + + Campo testo 3 + + + + @@ -105,7 +91,14 @@ - + + + + Campo testo 4 + + + + @@ -115,7 +108,14 @@ - + + + + Campo testo 5 + + + + @@ -125,21 +125,14 @@ - - - - Campo testo 5 - - - - + Etichette supplementari - + @@ -149,17 +142,17 @@ - + Formato barcode - + - + @@ -177,42 +170,21 @@ - + {PART}: Codice Ricetta selezionata - + {SN5}:Numero incrementale di 5 cifre - - - - {MO}:Mese espresso con 2 cifre - - - - - - - {YY}:Anno espresso con 2 cifre - - - - - - - {DD}:Giorno espresso con 2 cifre - - - - + @@ -226,6 +198,44 @@ + + + + {MO}:Mese espresso con 2 cifre + + + + + + + {YY}:Anno espresso con 2 cifre + + + + + + + {DD}:Giorno espresso con 2 cifre + + + + + + + Modello + + + + + + + Stampante + + + + + + diff --git a/src/ui/recipe_selection/recipe_selection.py b/src/ui/recipe_selection/recipe_selection.py index 65c9e19..7b083c5 100755 --- a/src/ui/recipe_selection/recipe_selection.py +++ b/src/ui/recipe_selection/recipe_selection.py @@ -74,6 +74,40 @@ class Recipe_Selection(Widget): else: label_folder = standard_label_folder + # Build both 203 and 300 template lists for dynamic switching in the editor + # Machine-specific overrides if available + custom_203 = f"config/label_templates/{str(self.config.machine_id)}/203/" + custom_300 = f"config/label_templates/{str(self.config.machine_id)}/300/" + std_203 = "config/label_templates/203/" + std_300 = "config/label_templates/300/" + label_folder_203 = custom_203 if os.path.exists(custom_203) else std_203 + label_folder_300 = custom_300 if os.path.exists(custom_300) else std_300 + templates_203 = sorted(map(os.path.basename, glob(f"{label_folder_203}*.prn"))) + templates_300 = sorted(map(os.path.basename, glob(f"{label_folder_300}*.prn"))) + + # Available printers from both sections (only 'printer' key) and mapping to resolution + lp1 = self.config.get("label_printer", {}) + lp2 = self.config.get("label_printer_2", {}) + lp1_p = lp1.get("printer", "") + lp2_p = lp2.get("printer", "") + printers_list = [] + seen = set() + for p in [lp1_p, lp2_p]: + if p and p not in seen: + seen.add(p) + printers_list.append(p) + printers_resolution = {} + if lp1_p: + try: + printers_resolution[lp1_p] = int(str(lp1.get("risoluzione", "300")).strip()) + except Exception: + printers_resolution[lp1_p] = 300 + if lp2_p: + try: + printers_resolution[lp2_p] = int(str(lp2.get("risoluzione", "300")).strip()) + except Exception: + printers_resolution[lp2_p] = 300 + step_defaults.update({ "vision": { # "recipe": sorted(glob("*.ini", root_dir="./config/vision/recipes/")), # only in python3.10 @@ -82,6 +116,10 @@ class Recipe_Selection(Widget): "print": { # "template": sorted(glob("*.prn", root_dir="./config/label_templates/")), # only in python3.10 "template": sorted(map(os.path.basename, glob(f"{label_folder}*.prn"))), + "templates_203": templates_203, + "templates_300": templates_300, + "printer_selection": printers_list, + "printers_resolution": printers_resolution, }, }), self.crud = Crud( @@ -329,6 +367,7 @@ class Recipe_Selection(Widget): "labeltxt_4": row.get("testo_etich_4", ""), "labeltxt_5": row.get("barcode_input_finelinea", ""), "extra_label": row.get("etichette_supplementari", ""), + "printer_selection": row.get("printer_selection", self.config.get("label_printer", {}).get("printer", "")), }, } @@ -486,6 +525,7 @@ class Recipe_Selection(Widget): "labeltxt_3": steps.get("print", Step()).spec.get("labeltxt_3", ""), "labeltxt_4": steps.get("print", Step()).spec.get("labeltxt_4", ""), "labeltxt_5": steps.get("print", Step()).spec.get("labeltxt_5", ""), + "printer_selection": steps.get("print",Step()).spec.get("printer_selection",""), } data.append(exportable) if len(data): diff --git a/src/ui/test/test.py b/src/ui/test/test.py index f739e90..20bb643 100755 --- a/src/ui/test/test.py +++ b/src/ui/test/test.py @@ -992,10 +992,19 @@ class Test(Widget): context[field.upper()] = printer_fields[field] # PRINT MAIN PRODUCT LABEL - if self.config["hardware_config"]["tecna_t3"] == "absent" and self.config["hardware_config"]["furness_controls"] == "absent": - compiled_label = self.components["label_printer_2"].print_label(barcode) # Printing with Brother label printer - else: - compiled_label = self.components["label_printer"].print_label(label, context=context) + # Determine which OS label printer to use based on per-recipe selection + selected_printer = printer_fields.get("printer_selection", "") + lp2_cfg = self.config.get("label_printer_2", {}) + lp2_printer = lp2_cfg.get("printer", "") + use_comp_name = "label_printer_2" if selected_printer and lp2_printer and selected_printer == lp2_printer else "label_printer" + comp = self.components.get(use_comp_name) or self.components.get("label_printer") or self.components.get("label_printer_2") + # Set the target device name to selected printer if provided + if selected_printer: + try: + comp.printer = selected_printer + except Exception: + pass + compiled_label = comp.print_label(label, context=context) self.log.info(f"Main label printed: {context!r}") # return fields used to print label for saving into test archive return context