New three-column label format for Brother P-touch EDGE PT-E560BT asset tags:
logo | three static lines + variable field | QR code.
Layout and rendering
- ptouch_label.py: geometry engine with PDF and preview renderers. Both
consume the same geometry, so the preview matches the print exactly.
- Code symbols are snapped to whole printer dots per module. Fractional
modules are the usual reason a small QR code will not scan.
- QR is maximised to the printable height by default (16.4 mm, 4 dots per
module) instead of a fixed 16 mm box (12.3 mm, 3 dots per module).
- Code payload is composed as prefix + inventory + suffix. A legacy
{inventory} template is migrated on first save.
Hardware handling
- print_area.py reads the driver's real printable area via GetDeviceCaps.
The PT-E560BT head is 128 dots at 180 dpi, so the maximum print height is
18.06 mm even on 24 mm tape; the driver reports media as 65x24 mm but only
about 61x18 mm is reachable. Content is fitted to the reachable area,
otherwise the driver silently crops it.
- set_printer_resolution.py reads/switches the driver's resolution mode. The
driver offers 180x180 (symmetric) and 180x360 (anisotropic, 2x across the
tape); the anisotropic mode scaled output incorrectly.
GUI
- label_printer_ptouch_gui.py: 600x300 two-column panel - controls on the
left, live preview on the right. Advanced settings for logo, static lines,
code prefix/suffix and error correction.
- native_file_dialog.py: the standard Windows file picker, run in a
subprocess because Tk and Kivy both want the main event loop.
Printing
- Print failures are reported honestly. print_to_printer previously returned
True unconditionally, so a failed print claimed success and was logged as
SUCCESS.
- SumatraPDF exit codes are now checked (4 = no printer, 5 = driver error).
- The PDF is sent with noscale so driver-side scaling cannot distort the QR
module pitch.
Tests
- validate_ptouch_layout.py decodes the QR back out of the rendered preview
module by module, proving the preview and PDF are the same layout.
- test_ptouch_pipeline.py writes to a temp config so it cannot overwrite
operator settings.
- test_gui_layout.py exercises the real app class headlessly.
- test_print_reporting.py covers the failure paths.
Diagnostics: check_print_area.py, check_print_geometry.py, check_devmode.py,
print_calibration.py, print_measure_scale.py, capture_print_job.py.
6.2 KiB
PT-E560BT Label Format (63 x 20 mm)
Three-column label layout for the Brother P-touch EDGE PT-E560BT.
0 20 mm 45 mm 63 mm
+---------+------------+--------+ 0
| | static 1 | Q R |
| LOGO | static 2 | C O | 1 mm non-printable gutter
| 16x16 | static 3 | D E |
| | VARIABLE | |
+---------+------------+--------+ 20 mm
20 mm 25 mm 18 mm
Interface
Run the new GUI:
python label_printer_ptouch_gui.py
| Element | Purpose |
|---|---|
| Inventory number | The single variable value per label. Becomes the 4th text line and is embedded in the QR code. |
| PRINT LABEL | Generates the PDF and sends it to the selected printer. |
| Preview area | Renders the label at true print proportions. Updates as you type. |
| Advanced settings | Logo image, the three static text lines, QR prefix/suffix, QR error correction. Saved to label_config.json. |
The old three-row format is still available via label_printer_gui.py and is
unchanged.
Hardware constraints
Verified against Brother's published specifications and the installed Windows
driver (via GetDeviceCaps), which agree:
| Spec | Value | Consequence |
|---|---|---|
| Print resolution | 180 dpi, 128-dot head | 128 / 180 x 25.4 = 18.06 mm is the hard height limit |
| Max print height | 18.0 mm, only with 24 mm tape | A 20 mm design cannot fit; it is not a margin setting |
| Max tape width | 24 mm | 24 mm tape required |
Because the head reaches only ~18 mm of the 24 mm tape, ~3 mm is unused on each
side. The label content is sized to the driver's printable area, read at
runtime by print_area.py, rather than to the nominal media size.
Driver resolution modes
The Brother driver offers two modes, and the choice matters:
| Mode | xdpi | ydpi | Notes |
|---|---|---|---|
| 1 | 180 | 180 | symmetric - use this |
| 2 | 180 | 360 | anisotropic, 2x across the tape |
In the anisotropic mode the driver reports twice the horizontal dots, and a page
authored in millimetres can be scaled by the wrong factor and print short.
set_printer_resolution.py reads and switches this setting:
python set_printer_resolution.py # show current mode
python set_printer_resolution.py --mode 180 # symmetric (recommended)
python set_printer_resolution.py --restore # undo
Why the code is dot-aligned
A thermal head cannot print a fraction of a dot. If a QR symbol is scaled to an arbitrary size, its modules end up unevenly sized (some 3 dots wide, some 4), which is the usual reason a small QR code will not scan.
build_code_bitmap() snaps the symbol to an integer number of printer dots per
module and returns the resulting exact physical size. At 16 mm available and 29
modules (a typical short payload), that is 3 dots per module = 12.28 mm,
which scans comfortably.
code_density_warning() estimates density for the current payload and the GUI
shows a warning when the code becomes too dense to be reliable, instead of
silently printing an unscannable label.
Code payload
The code is composed as fixed prefix + inventory + fixed suffix, configured in Advanced settings:
| Prefix | Inventory | Suffix | Encoded |
|---|---|---|---|
INV- |
1234 |
(empty) | INV-1234 |
INV- |
1234 |
-A |
INV-1234-A |
| (empty) | 1234 |
(empty) | 1234 |
The Encodes: line in Advanced settings shows the exact payload live as you type,
so the result can be confirmed before printing.
A legacy {inventory} template (e.g. INV:{inventory}) is still accepted and is
migrated to prefix/suffix on first save, so older config files keep working.
Keep the payload short. The QR is maximised to fill the printable height
(~16.4 mm at 29 modules), which gives 4 printer dots per module for a short
payload. Longer strings raise the QR version, reducing dots per module; the GUI
warns before printing a code that would be too dense to scan reliably. Error
correction L allows a longer string than M/Q/H at the same physical size.
Maximising the QR
By default qr_maximize is enabled: the code column takes the full printable
height so the symbol is as large as the hardware allows, and the text column
shrinks to make room (never below MIN_TEXT_COL_MM, so the three static lines
keep fitting).
| Setting | QR size | Dots/module |
|---|---|---|
qr_maximize: false (16 mm box) |
12.28 mm | 3 |
qr_maximize: true (default) |
16.37 mm | 4 |
The symbol is flush to the right edge of the printable area when maximised. If
your unit feeds the tape slightly off-centre, reduce qr_box_mm and set
qr_maximize: false to leave clearance.
Logo
- Formats: PNG, JPG, JPEG, BMP, GIF. Greyscale and alpha are handled (transparent areas are flattened to white).
- The logo is scaled to fit the 16 x 16 mm cell preserving aspect ratio.
- Thermal transfer printing is 1-bit, so logos should be black-and-white line
art. Photographs dither poorly.
logo_dithercontrols this.
Files
| File | Role |
|---|---|
ptouch_label.py |
Layout engine: geometry, code generation, PDF + preview renderers |
label_config.py |
Settings persistence (label_config.json) |
label_printer_ptouch_gui.py |
Kivy GUI (this format) |
print_label.py |
Printer discovery and silent printing (extended) |
validate_ptouch_layout.py |
Geometry + QR round-trip validation |
test_ptouch_pipeline.py |
End-to-end test (no hardware needed) |
Tests
python validate_ptouch_layout.py # page size, boxes, QR round-trip
python test_ptouch_pipeline.py # config, PDF, printing, QR scaling
validate_ptouch_layout.py re-reads the rendered QR symbol out of the preview
image, module by module, and compares it against the source matrix. That is what
proves the preview and the PDF are the same layout, and that the symbol is
geometrically intact after dot-alignment.
Printing notes
Only SumatraPDF printing honours the scale setting. The PDF is sent with
-print-settings noscale because driver-side scaling ("fit to page") would
distort the QR module pitch and break scanning.
In the printer driver, set the media to the 63 x 20 mm continuous tape and disable any auto-rotate. The TZe tape must be 24 mm.