add support for dynamic printer selection and template resolution in label printing modules

This commit is contained in:
edo-neo 2025-09-03 11:40:34 +02:00
parent eac50a9ec8
commit 2f423f4281
8 changed files with 215 additions and 95 deletions

View File

@ -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

View File

@ -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)

View File

@ -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", "")),
},
}

View File

@ -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}},

View File

@ -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)

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>750</width>
<height>332</height>
<height>426</height>
</rect>
</property>
<property name="windowTitle">
@ -37,45 +37,17 @@
<string>Etichetta</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Modello</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QComboBox" name="label_template_cb"/>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Campo testo 1</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>Campo testo 2</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Campo testo 3</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>Campo testo 4</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QLineEdit" name="labeltxt_1">
<property name="minimumSize">
<size>
@ -85,7 +57,14 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>Campo testo 2</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="labeltxt_2">
<property name="minimumSize">
<size>
@ -95,7 +74,14 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Campo testo 3</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="labeltxt_3">
<property name="minimumSize">
<size>
@ -105,7 +91,14 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>Campo testo 4</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="labeltxt_4">
<property name="minimumSize">
<size>
@ -115,7 +108,14 @@
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="0">
<widget class="QLabel" name="label_35">
<property name="text">
<string>Campo testo 5</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="labeltxt_5">
<property name="minimumSize">
<size>
@ -125,21 +125,14 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_35">
<property name="text">
<string>Campo testo 5</string>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_34">
<property name="text">
<string>Etichette supplementari</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QLineEdit" name="extra_label">
<property name="minimumSize">
<size>
@ -149,17 +142,17 @@
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Formato barcode</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QLineEdit" name="barcode"/>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
@ -177,42 +170,21 @@
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>{PART}: Codice Ricetta selezionata</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>{SN5}:Numero incrementale di 5 cifre</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>{MO}:Mese espresso con 2 cifre </string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>{YY}:Anno espresso con 2 cifre</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>{DD}:Giorno espresso con 2 cifre</string>
</property>
</widget>
</item>
<item row="11" column="0">
<item row="12" column="0">
<widget class="QLabel" name="label_8">
<property name="font">
<font>
@ -226,6 +198,44 @@
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>{MO}:Mese espresso con 2 cifre </string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>{YY}:Anno espresso con 2 cifre</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>{DD}:Giorno espresso con 2 cifre</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Modello</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Stampante</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="printer_selection"/>
</item>
</layout>
</widget>
</item>

View File

@ -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):

View File

@ -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