Docs: screenshots and full label designer guide; icon category tabs

- README: overview, installation, designer walkthrough with light/dark
  screenshots and sample output, library usage, D101 pairing note
- Emoji picker tabs use emoji icons so the sidebar fits at 1280px;
  file actions and theme toggle move to the bottom bar
This commit is contained in:
Gracious
2026-09-21 08:27:48 +01:00
parent 76e6732285
commit 41b4d4ce64
5 changed files with 127 additions and 56 deletions
+77 -23
View File
@@ -1,6 +1,18 @@
# `niimprint` — Niimbot Printer Client
# `niimprint` — Niimbot Printer Client + Label Designer
**Fork changelog & differences from original version:**
Print to Niimbot label printers (B1, B18, B21, D11, D101, D110) over Bluetooth or USB, from a CLI or a desktop label designer with full emoji support.
![Label designer, light theme](docs/app-light.png)
Fork of [AndBondStyle/niimprint](https://github.com/AndBondStyle/niimprint). Changes in this fork:
- **Label designer app** (`label_app.py`): drag-and-drop text and emoji, rotation, live 1-bit preview, light/dark themes
- Printer model table (`niimprint/models.py`) with per-model width / density / protocol quirks; adds **D101**
- Ported upstream PRs [#28](https://github.com/AndBondStyle/niimprint/pull/28) and [#12](https://github.com/AndBondStyle/niimprint/pull/12): D-series `ALLOW_PRINT_CLEAR`/`SET_QUANTITY` (fixes tall labels), print-status polling instead of a fixed sleep
- `PrinterError` raised on timeouts / rejected commands instead of returning `None`
- Tested on a D101 (firmware 20.51) over Bluetooth
Upstream's own changelog vs. the original project:
- Tested on Niimbot B1, B18, B21, D11, D110 and Python 3.11
- Added transport abstraction: switch between bluetooth and USB (serial)
@@ -8,11 +20,24 @@
- Switched to [click](https://click.palletsprojects.com/) CLI library instead of argparse
- Integrated [pyproject.toml](https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/) and [poetry](https://python-poetry.org)
- Integrated [pre-commit](https://pre-commit.com/) and [ruff](https://docs.astral.sh/ruff/), re-formatted all files
- Miscellaneous refactoring / file renaming / etc.
## Installation
Recommended method is to use [poetry](https://python-poetry.org) and install with `poetry install`. However `requirements.txt` is also provided for convenience. Project is tested on Python 3.11, but should work on other versions.
```
git clone https://git.gracious.one/gracious_admin/NIIMBOT-D101.git
cd NIIMBOT-D101
python -m venv --system-site-packages .venv # system Python gives you tkinter
.venv/bin/pip install -r requirements.txt
```
Or with [poetry](https://python-poetry.org): `poetry install`. Python 3.10+ (uses `match`); tested on 3.13.
The label designer needs two fonts, both standard on most distros:
- DejaVu Sans — `/usr/share/fonts/TTF/DejaVuSans*.ttf` (Arch) or `/usr/share/fonts/truetype/dejavu/` (Debian)
- Noto Color Emoji — `/usr/share/fonts/noto/NotoColorEmoji.ttf`
Edit `TEXT_FONTS` / `EMOJI_FONT` at the top of `label_app.py` if yours live elsewhere.
## Usage
@@ -58,6 +83,8 @@ It seems like B21 and B1 (and maybe other models?) have two bluetooth adresses.
To identify which address is the correct one, run `bluetoothctl info` on the address you want to check. The incorrect one might list `UUID: Generic Access Profile` and `UUID: Generic Attribute Profile`, while the correct one will list `UUID: Serial Port`.
The D101 does the same: it advertises as `D101-<serial>` on two addresses (e.g. `94:11:02:…` and `11:02:94:…`); use the one whose `bluetoothctl info` lists `UUID: Serial Port`, then `pair` and `trust` it once.
## Examples
**B21, USB connection, 30x15 mm (240x120 px) label**
@@ -76,25 +103,52 @@ python niimprint -c bluetooth -a "E2:E1:08:03:09:87" -r 90 -i examples/B21_80x50
[![](examples/B21_80x50_result.png)]()
## Label designer app
```
.venv/bin/python label_app.py
```
| Light | Dark |
|---|---|
| ![light](docs/app-light.png) | ![dark](docs/app-dark.png) |
The 40 × 12 mm label above, exactly as sent to the printer (320 × 96 px, 1-bit):
![sample label](docs/sample-label.png)
### Designing
- **Label size** — pick a preset (12 × 40, 12 × 30, 12 × 22, 15 × 30 mm) or *Custom* and type width / length in mm. 8 px per mm.
- **+ Text** adds a text block. Type in the *Selected item* box; multi-line is fine, and emoji typed inline are rendered too.
- **Emoji** — click any emoji in the picker to add it as its own item. The tabs are 😀 Smileys, 👍 People, 🐶 Animals, 🍎 Food, 🚗 Travel, ⚽ Activities, 💡 Objects, ❤️ Symbols, 🏁 Flags (~1,900 in total); the *Search* box filters by name; hovering shows the name.
- **Move** — drag on the canvas, arrow keys nudge 1 px (Shift = 10 px), or type X / Y. **Center H / V** snap to the middle.
- **Rotate** — ⟲ / ⟳ 90° buttons, or any angle in the *Angle* spinbox (15° steps).
- **Size / Bold / Align** — per item. **Fit** sizes the selected item to fill the label.
- **Duplicate / Delete** (or the Delete key).
- The canvas shows the dithered black-and-white image the printer will actually produce, at 24× zoom depending on label size. Emoji are darkened (gamma 3) so pale colours don't dither away on thermal paper.
### Printing & files
- **Print** — connects lazily and prints in the background; *Copies* repeats the label. Errors show in a dialog.
- **Save PNG…** exports the 1-bit image; **Save… / Open…** store the design as JSON; **New** clears the label.
- Printer model, connection, address, density, theme, and the last design are remembered in `~/.config/niimprint-label/settings.json`.
- ☾ / ☀ toggles light and dark themes ([ttkbootstrap](https://ttkbootstrap.readthedocs.io) *flatly* / *darkly*).
### Library use
```python
from PIL import Image
from niimprint import BluetoothTransport, PrinterClient, prepare_print
image = Image.open("label.png") # landscape, 8 px/mm
image, density = prepare_print("d101", image, 3, rotate=90)
printer = PrinterClient(BluetoothTransport("94:11:02:66:16:4D"))
printer.print_image(image, density=density, model="d101")
```
`prepare_print` clamps density to the model's maximum, rotates, and raises `ValueError` if the image is wider than the print head. `print_image` raises `PrinterError` if the printer stops responding or rejects a command.
## Licence
[MIT](https://choosealicense.com/licenses/mit/). Originally developed by [kjy00302](https://github.com/kjy00302), forked & enhanced by [AndBondStyle](https://github.com/AndBondStyle)
## Label designer app
`label_app.py` is a Tkinter app for designing and printing labels with text and emoji:
```
python label_app.py
```
- Pick a label size preset (or custom mm)
- **+ Text** adds a text block; click any emoji in the picker (all ~1,900, searchable by name) to add it
- Click an item to select it, drag to move, arrow keys nudge (Shift = 10 px), Delete removes
- Selected item panel: edit text, size, angle (any degrees, or the ⟲/⟳ 90° buttons), X/Y, bold, alignment
- **Fit** resizes the item to fill the label; **Center H/V** aligns it
- The canvas shows the dithered 1-bit image exactly as the printer will produce it
- **Save…/Open…** store designs as JSON; the last design and printer settings are restored on launch
- ☾/☀ in the toolbar switches between light and dark themes ([ttkbootstrap](https://ttkbootstrap.readthedocs.io))
Fonts: DejaVu Sans (`/usr/share/fonts/TTF/`) for text and Noto Color Emoji (`/usr/share/fonts/noto/`) for emoji; edit `TEXT_FONTS` / `EMOJI_FONT` at the top of the file for other distros. Emoji are rendered by Pillow with the same font that prints, so the picker is WYSIWYG.