qltool 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qltool/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ def main() -> None:
2
+ print("Hello from qltool!")
qltool/main.py ADDED
@@ -0,0 +1,561 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ #---------text
4
+ rm /tmp/tmp_qrcode.png ; uv run src/qltool/main.py "Ahoj
5
+ aaa
6
+ 456 8jgqy 789 456 456 " --font-size 55 --font-size 45
7
+
8
+ # ---- create QR
9
+
10
+ uv run src/qltool/main.py "ahoj
11
+ momo
12
+ 123 456 789 jjj" -p -q
13
+
14
+ # -------- use any image/qrcode
15
+ uv run src/qltool/main.py "Užaj jetů" --qr /tmp/baba.png
16
+
17
+ uv run src/qltool/main.py "Už je tuná" --qr /tmp/baba.png -p
18
+
19
+ """
20
+ import shlex
21
+ import subprocess as sp
22
+ import glob
23
+ import tempfile
24
+ import sys
25
+
26
+ import click
27
+ from importlib_resources import files, as_file
28
+ from PIL import Image, ImageDraw, ImageFont
29
+ # uv add Pillow
30
+ import cv2
31
+ # uv add opencv-python
32
+ import numpy as np
33
+ from console import fg, bg
34
+
35
+ import os
36
+ import re
37
+
38
+ import qrcode
39
+
40
+ # =====================================================================
41
+ #
42
+ # ----------------------------------------------------------------------
43
+ def is_user_in_lp_group():
44
+ """
45
+ Return True if 'lp' appears in the output of the `groups` command.
46
+ Uses runme() to call `groups`.
47
+ """
48
+ try:
49
+ out = runme("groups", reallyrun=True)
50
+ except Exception:
51
+ return False
52
+ tokens = re.findall(r"\w+", out)
53
+ return "lp" in tokens
54
+
55
+ # --- helpers (kept similar to your original) ---
56
+ # =====================================================================
57
+ #
58
+ # ----------------------------------------------------------------------
59
+ def get_data_path():
60
+ src = files("data").joinpath("ql570")
61
+ with as_file(src) as p:
62
+ return str(p)
63
+
64
+ # =====================================================================
65
+ #
66
+ # ----------------------------------------------------------------------
67
+ def check_lpx():
68
+ prs = glob.glob("/dev/usb/lp*")
69
+ if not prs:
70
+ raise RuntimeError("No LP device found under /dev/usb/lp*")
71
+ return prs[0]
72
+
73
+ # =====================================================================
74
+ #
75
+ # ----------------------------------------------------------------------
76
+ def runme(CMDi, silent=False, reallyrun=False):
77
+ print("D...", fg.orange, CMDi, fg.default)
78
+ CMD = shlex.split(CMDi)
79
+ if not reallyrun:
80
+ return "ok"
81
+ res = sp.check_output(CMD).decode("utf8")
82
+ if not silent:
83
+ print("i... RESULT:", fg.darkslategray, res, fg.default)
84
+ return res
85
+
86
+ # =====================================================================
87
+ #
88
+ # ----------------------------------------------------------------------
89
+ def get_tmp_fname():
90
+ temp_file = tempfile.NamedTemporaryFile(suffix=".png", dir="/tmp", delete=False)
91
+ name = temp_file.name
92
+ temp_file.close()
93
+ return name
94
+
95
+ # =====================================================================
96
+ #
97
+ # ----------------------------------------------------------------------
98
+ # --- image creation ---
99
+ def make_bw_text_image(lines, width=714,
100
+ # font_path="/usr/share/fonts/truetype/freefont/FreeMono.ttf",
101
+ font_path="/usr/share/fonts/truetype/clear-sans/ClearSans-Regular.ttf",
102
+ font_size=None,
103
+ pad=13):
104
+ # lines: list of 1-3 strings
105
+ if not (1 <= len(lines) <= 3):
106
+ raise ValueError("Provide 1 to 3 lines.")
107
+ # choose font
108
+ FSIZ = font_size or 36
109
+ try:
110
+ if font_path:
111
+ font = ImageFont.truetype(font_path, FSIZ)
112
+ else:
113
+ # try a reasonably sized PIL default or fallback
114
+ font = ImageFont.load_default()
115
+ except Exception:
116
+ font = ImageFont.load_default()
117
+
118
+ PAD = int( (FSIZ / 36) * pad) # for 36 pad 12 is OK
119
+ # compute sizes
120
+ # use a big temporary image to measure text
121
+ tmp = Image.new("L", (width, 2000), 255)
122
+ draw = ImageDraw.Draw(tmp)
123
+ #line_heights = []
124
+ #line_widths = []
125
+ line_sizes = []
126
+ for ln in lines:
127
+ bbox = draw.textbbox((0, 0), ln, font=font)
128
+ w = bbox[2] - bbox[0]
129
+ h = bbox[3] - bbox[1]
130
+ line_sizes.append((w, h))
131
+ #w, h = draw.textsize(ln, font=font)
132
+ #line_widths.append(w)
133
+ #line_heights.append(h)
134
+ # compute total height with small spacing
135
+ spacing = int(0.2 * (line_sizes[0][1] if line_sizes else 12)) or 4
136
+ total_h = sum(h for _, h in line_sizes) + spacing * (len(lines) - 1) + PAD * 2
137
+ # create final image
138
+ im = Image.new("L", (width, total_h), 255) # white background
139
+ draw = ImageDraw.Draw(im)
140
+ # center horizontally, stack lines vertically
141
+ y = PAD
142
+ for (w, h), ln in zip(line_sizes, lines):
143
+ x = max((width - w) // 2, 0)
144
+ draw.text((x, y), ln, font=font, fill=0)
145
+ y += h + spacing
146
+ return im.convert("RGB")
147
+
148
+ # =====================================================================
149
+ #
150
+ # ----------------------------------------------------------------------
151
+ def pil_to_cv2(im):
152
+ arr = np.array(im)[:, :, ::-1] # RGB->BGR
153
+ return arr
154
+
155
+
156
+ # =====================================================================
157
+ #
158
+ # ----------------------------------------------------------------------
159
+ # --- mono conversion with Floyd-Steinberg and gray bias ---
160
+ def convert_to_mono_floyd(img_rgb, gray_percent=50):
161
+ """
162
+ img_rgb: PIL RGB image
163
+ gray_percent: 0..100, default 50. 50 means no bias. Values>50 bias lighter, <50 bias darker.
164
+ Returns a PIL Image in mode '1' (mono).
165
+ """
166
+ if not (0 <= gray_percent <= 100):
167
+ raise ValueError("gray_percent must be 0..100")
168
+ # convert to grayscale
169
+ im_l = img_rgb.convert("L")
170
+ # compute offset: 50 => 0, >50 => positive (lighter), <50 => negative (darker)
171
+ offset = int(round((gray_percent - 50) * 255 / 100.0))
172
+ if offset != 0:
173
+ lut = [min(255, max(0, i + offset)) for i in range(256)]
174
+ im_l = im_l.point(lut)
175
+ # convert to 1-bit using Floyd-Steinberg
176
+ mono = im_l.convert("1", dither=Image.FLOYDSTEINBERG)
177
+ if mono.mode != "1":
178
+ # fallback: force mode to '1'
179
+ mono = mono.convert("1")
180
+ return mono
181
+
182
+ # =====================================================================
183
+ #
184
+ # ----------------------------------------------------------------------
185
+ def rotate_mono_90(img, clockwise=True):
186
+ """
187
+ Rotate a mono (1-bit) image by 90 degrees.
188
+ - img: PIL.Image or path to image file.
189
+ - clockwise: True => rotate 90° clockwise, False => 90° counter-clockwise.
190
+ Returns a PIL.Image in mode '1'.
191
+ """
192
+ if isinstance(img, str):
193
+ img = Image.open(img)
194
+ if img.mode != "1":
195
+ img = img.convert("1")
196
+ # Use transpose for exact 90° rotation without resampling
197
+ return img.transpose(Image.ROTATE_270 if clockwise else Image.ROTATE_90)
198
+
199
+
200
+
201
+
202
+ # =====================================================================
203
+ #
204
+ # ----------------------------------------------------------------------
205
+ def create_qr(qr_path, lines, width=5):
206
+ qrtag = ";".join(lines)#f"{lines}"
207
+ qrtag = qrtag.replace(r"\n", ";")
208
+ qrtag = qrtag.replace(" ", " ")
209
+ qrtag = qrtag.replace(" ", " ")
210
+ qrtag = qrtag.replace(" ", " ")
211
+ qrtag = qrtag.replace(" ", " ")
212
+ qrtag = qrtag.replace(" ;", ";")
213
+ print(f"i... QRTAG /{fg.cyan}{qrtag}{fg.default}/")
214
+ # I will join Name: asoidj ; owner: OKjd ; location: asd o; ID: 8998244;
215
+ qr = qrcode.QRCode(
216
+ version=1,
217
+ error_correction=qrcode.constants.ERROR_CORRECT_M,
218
+ box_size=width,
219
+ border=3,
220
+ )
221
+ qr.add_data(qrtag)
222
+ qr.make(fit=True)
223
+ img = qr.make_image(fill_color="darkblue", back_color="white")
224
+ img.save(qr_path)
225
+ # ----------------------- QRDONE -------------
226
+ # #qr = qrcode.make( qrtag, width=width)
227
+ # qrs = qrtag.split(";")
228
+ # # I have nlabel and slabel: what about midlabel
229
+ # nlabel, midlabel, slabel= None, None, None
230
+ # for q in qrs:
231
+ # nam, *val= q.split(":")
232
+ # if len(val) == 0:continue
233
+ # # EXTRACT ID and NAME
234
+ # if nam.strip().find( "id") == 0:
235
+ # nlabel = val[0].strip()
236
+ # if nam.strip().find( "name") == 0:
237
+ # slabel = val[0].strip()
238
+ # if nam.strip().find( "owner") == 0:
239
+ # midlabel = val[0].strip()
240
+ # print(f"i... LABELS SN: /{fg.green}{nlabel}{fg.default}/ x /{fg.yellow}{midlabel}{fg.default}/ x /{fg.green}{slabel}{fg.default}/")
241
+
242
+
243
+
244
+ def make_bw_text_with_qr(lines,
245
+ qr_path,
246
+ width=714,
247
+ # font_path="/usr/share/fonts/truetype/freefont/FreeMono.ttf",
248
+ font_path="/usr/share/fonts/truetype/clear-sans/ClearSans-Regular.ttf",
249
+ font_size=None,
250
+ pad=13,
251
+ qr_max_width=350):
252
+ """
253
+ Compose an RGB image (width x dynamic height) with:
254
+ - QR image placed at left (must be < qr_max_width),
255
+ - text block (1-3 lines) placed on the right of the QR.
256
+ Behavior of text layout follows your make_bw_text_image(), but horizontal
257
+ placement is centered inside the area to the right of the QR. x_offset is
258
+ computed as (qr_image_width + PAD) where PAD ~= scaled pad value.
259
+ Returns a PIL.Image in RGB.
260
+ """
261
+ if not (1 <= len(lines) <= 3):
262
+ raise ValueError("Provide 1 to 3 lines.")
263
+ if not os.path.exists(qr_path):
264
+ raise FileNotFoundError(qr_path)
265
+
266
+ # load QR and check width
267
+ qr = Image.open(qr_path).convert("RGB")
268
+ if qr.width >= qr_max_width:
269
+ #raise ValueError(f"QR image width {qr.width}px >= limit {qr_max_width}px")
270
+ qr = qr.resize( (qr_max_width, qr_max_width) )
271
+
272
+ # font choice
273
+ FSIZ = font_size or 36
274
+ try:
275
+ font = ImageFont.truetype(font_path, FSIZ) if font_path else ImageFont.load_default()
276
+ except Exception:
277
+ font = ImageFont.load_default()
278
+
279
+ PAD = int((FSIZ / 36) * pad) # scaled pad
280
+
281
+ # Measure text lines using a temp image (same as original)
282
+ tmp = Image.new("L", (width, 2000), 255)
283
+ draw_tmp = ImageDraw.Draw(tmp)
284
+ line_sizes = []
285
+ for ln in lines:
286
+ bbox = draw_tmp.textbbox((0, 0), ln, font=font)
287
+ w = bbox[2] - bbox[0]
288
+ h = bbox[3] - bbox[1]
289
+ line_sizes.append((w, h))
290
+
291
+ spacing = int(0.2 * (line_sizes[0][1] if line_sizes else 12)) or 4
292
+ text_content_h = sum(h for _, h in line_sizes) + spacing * (len(lines) - 1)
293
+ text_total_h = text_content_h + PAD * 2
294
+ qr_total_h = qr.height + PAD * 2
295
+
296
+ # canvas height must fit both QR and text
297
+ canvas_h = max(text_total_h, qr_total_h)
298
+ canvas = Image.new("RGB", (width, canvas_h), "white")
299
+ draw = ImageDraw.Draw(canvas)
300
+
301
+ # place QR at left with left padding PAD, vertically centered
302
+ qr_x = PAD
303
+ qr_y = (canvas_h - qr.height) // 2
304
+ canvas.paste(qr, (qr_x, qr_y))
305
+
306
+ # compute x_offset = qr_image width + PAD (as requested)
307
+ x_offset = qr.width + PAD
308
+
309
+ # available width for text: leave right PAD margin
310
+ available_for_text = width - x_offset - PAD
311
+ if available_for_text < 0:
312
+ available_for_text = 0
313
+
314
+ # vertical start for text: center the text block vertically in canvas
315
+ y = (canvas_h - text_content_h) // 2
316
+
317
+ # draw each line centered inside the available area starting at x_offset
318
+ for (w, h), ln in zip(line_sizes, lines):
319
+ x = x_offset + max((available_for_text - w) // 2, 0)
320
+ draw.text((x, y), ln, font=font, fill=0)
321
+ y += h + spacing
322
+
323
+ return canvas
324
+
325
+
326
+
327
+ # =====================================================================
328
+ #
329
+ # ----------------------------------------------------------------------
330
+
331
+ # --- CLI ---
332
+ @click.command()
333
+ @click.argument("lines", nargs=-1, required=True)
334
+ @click.option("-p", "--print", "do_print", is_flag=True, default=False, help="Send to printer (calls ql570).")
335
+ @click.option("-d", "--device", "device", default=None, help="Printer device (defaults to first /dev/usb/lp*).")
336
+ @click.option("-f", "--format", "fmt", default=62, help="Format code for ql570 (default 62 at the width of 714).")
337
+ @click.option("--width", default=714, show_default=True, help="Total image width.")
338
+ @click.option("--gray", "gray", default=50, show_default=True, help="Gray bias percent (0..100), default 50.")
339
+ @click.option("--font-size", "font_size", default=72, show_default=True, help="Font size for text.")
340
+ @click.option("--qr", "qr_path", default=None, show_default=True, help="QR image path.")
341
+ @click.option("-q", "qr_create", is_flag=True, default=False, show_default=True, help="do QR")
342
+ def cli(lines, do_print, device, fmt, width, gray, font_size, qr_path, qr_create):
343
+ """
344
+ Create a black-on-white PNG from 1-3 lines of text.
345
+ By default displays the image with OpenCV. Use -p to print via ql570.
346
+ """
347
+ #print(lines, type(lines), len(lines))
348
+ if type(lines)is not tuple:
349
+ print("X... the text should be python-tuple for some reason....")
350
+ sys.exit(1)
351
+
352
+ if len(lines) > 3: # "A" "B" "C"
353
+ print("Error: max 3 lines allowed.", file=sys.stderr)
354
+ sys.exit(2)
355
+
356
+ if len(lines) == 1:
357
+ #print("Q", lines[0].find("\n"))
358
+ if lines[0].find("\n") > 0:
359
+ print("Q... contains several lines, I understand...")
360
+ lines = lines[0].split("\n")
361
+ else:
362
+ print("Q... lines ...I do not understand")
363
+
364
+ if len(lines) > 3: # "A" "B" "C"
365
+ print("Error: max 3 lines allowed.", file=sys.stderr)
366
+ sys.exit(2)
367
+
368
+
369
+ # ------- decide on qr
370
+ use_qr_version = False
371
+ my_qr_path = None
372
+ if qr_path is not None: # Existing PATH
373
+ if os.path.exists(qr_path):
374
+ use_qr_version = True
375
+ my_qr_path = qr_path
376
+ #create_qr(qr_path, lines)
377
+
378
+ if qr_create:
379
+ my_qr_path = "/tmp/tmp_qrcode.png"
380
+ create_qr(my_qr_path, lines)
381
+ use_qr_version = True
382
+
383
+ # ----------------------------------- create image
384
+ lines = ["\n".join(lines) ] # BACK TO NICE \n
385
+
386
+ im = None
387
+ if use_qr_version:
388
+ im = make_bw_text_with_qr(list(lines), my_qr_path, width=714, font_size=font_size)
389
+ else:
390
+ im = make_bw_text_image(list(lines), width=714, font_size=font_size)
391
+
392
+ mono = convert_to_mono_floyd(im, gray_percent=int(gray))
393
+ mono = rotate_mono_90(mono, clockwise=True)
394
+
395
+ out = get_tmp_fname()
396
+ print(f"D.... {out}")
397
+ mono.save(out, format="PNG")
398
+ if not do_print:
399
+ # display with cv2
400
+ cvim = pil_to_cv2(im)
401
+ cv2.imshow("Preview - press any key to close", cvim)
402
+ cv2.waitKey(0)
403
+ cv2.destroyAllWindows()
404
+ print("i... Saved:", out)
405
+ return
406
+ # printing path
407
+ exe = get_data_path()
408
+ dev = device if device else check_lpx()
409
+ CMD = f"{exe} {dev} {fmt} {out}"
410
+
411
+ if not is_user_in_lp_group():
412
+ print("X... ", fg.red, "you are not in lp group", fg.default)
413
+ sys.exit(1)
414
+ runme(CMD, reallyrun=True)
415
+ print("i... Printed (and saved):", out)
416
+
417
+ # =====================================================================
418
+ #
419
+ # ----------------------------------------------------------------------
420
+ # =====================================================================
421
+ #
422
+ # ----------------------------------------------------------------------
423
+ # =====================================================================
424
+ #
425
+ # ----------------------------------------------------------------------
426
+
427
+ if __name__ == "__main__":
428
+ cli()
429
+
430
+
431
+ # from importlib_resources import files, as_file
432
+ # import shlex # split correctly CMD
433
+ # import subprocess as sp
434
+ # import glob
435
+ # import tempfile
436
+ # from console import fg, bg
437
+
438
+ # def get_data_path():
439
+ # exe_path1 = None
440
+
441
+ # source = files('data').joinpath('ql570')
442
+ # with as_file(source) as eml:
443
+ # exe_path1 = str(eml)
444
+ # return exe_path1
445
+
446
+ # def check_lpx():
447
+ # prs = glob.glob("/dev/usb/lp*")
448
+ # print(f"D... LP devices : {prs}")
449
+ # return prs[0]
450
+
451
+
452
+ # def runme(CMDi, silent = False, reallyrun=False):
453
+ # """
454
+ # run with the help of safe shlex
455
+ # """
456
+ # print("D...", "_"*(90-len(CMDi)), fg.yellow, CMDi, fg.default) # Just show on terminal
457
+ # CMD = shlex.split(CMDi)# .split()
458
+ # if not reallyrun:
459
+ # return "ok"
460
+ # res=sp.check_output( CMD ).decode("utf8")
461
+ # if not silent:
462
+ # print("i... RESULT:", res)
463
+ # return res
464
+
465
+ # def get_tmp_fname():
466
+ # """
467
+ # the printer understands to PNG
468
+ # """
469
+ # suffix = '.png'
470
+ # tmp_dir = '/tmp'
471
+ # temp_file = tempfile.NamedTemporaryFile(suffix=suffix, dir=tmp_dir, delete=False)
472
+ # temp_filename = temp_file.name
473
+ # temp_file.close()
474
+ # return temp_filename
475
+
476
+
477
+ # def main():
478
+ # print("i... STARTING")
479
+ # exe = get_data_path()
480
+ # lpx = check_lpx()
481
+ # myformat = 62
482
+ # image = get_tmp_fname()
483
+ # CMD = f"{exe} {lpx} {myformat} {image}"
484
+ # runme(CMD)
485
+ # print("i... FINISHED")
486
+
487
+ # if __name__ == "__main__":
488
+ # main()
489
+ ################################################################################
490
+ # # Compose QR (left) + text block (right)
491
+ # def compose_qr_and_text(qr_path, lines, total_width=714, pad=10, gap=10, font_path=None, font_size=36, qr_max_size=350):
492
+ # if not (1 <= len(lines) <= 3):
493
+ # raise ValueError("Provide 1..3 lines.")
494
+ # if not os.path.exists(qr_path):
495
+ # raise FileNotFoundError(qr_path)
496
+ # # load font
497
+ # try:
498
+ # font = ImageFont.truetype(font_path, font_size) if font_path else ImageFont.load_default()
499
+ # except Exception:
500
+ # font = ImageFont.load_default()
501
+ # # measure text lines
502
+ # tmp_h = 4000
503
+ # tmp = Image.new("L", (total_width, tmp_h), 255)
504
+ # draw_tmp = ImageDraw.Draw(tmp)
505
+ # line_sizes = []
506
+ # for ln in lines:
507
+ # bbox = draw_tmp.textbbox((0, 0), ln, font=font)
508
+ # w = bbox[2] - bbox[0]
509
+ # h = bbox[3] - bbox[1]
510
+ # line_sizes.append((w, h))
511
+ # spacing = max(int(0.2 * (line_sizes[0][1] if line_sizes else 12)), 4)
512
+ # text_block_w = max(w for w, _ in line_sizes)
513
+ # text_block_h = sum(h for _, h in line_sizes) + spacing * (len(lines) - 1)
514
+ # # open qr and possibly scale
515
+ # qr = Image.open(qr_path).convert("RGB")
516
+ # # constrain QR to qr_max_size and to available width
517
+ # available_for_qr = total_width - (text_block_w + gap + pad*2)
518
+ # if available_for_qr <= 0:
519
+ # # if no room, allocate half-half
520
+ # available_for_qr = min(qr_max_size, total_width // 2 - pad - gap)
521
+ # qr_target_h = min(qr_max_size, max(text_block_h, qr.height))
522
+ # # scale QR to match text_block_h (if bigger) but not exceed available width/height
523
+ # # compute scale preserving aspect ratio
524
+ # resample = getattr(Image, "Resampling", Image).LANCZOS
525
+ # scale_h = text_block_h
526
+ # # ensure we don't exceed available_for_qr width
527
+ # # try target height = text_block_h, compute width
528
+ # w_ratio = qr.width / qr.height if qr.height else 1
529
+ # qr_w_by_h = int(scale_h * w_ratio)
530
+ # if qr_w_by_h > available_for_qr:
531
+ # # reduce height to fit available width
532
+ # qr_w = available_for_qr
533
+ # qr_h = max(1, int(qr_w / w_ratio))
534
+ # else:
535
+ # qr_w = qr_w_by_h
536
+ # qr_h = max(1, int(scale_h))
537
+ # # also ensure not exceeding qr_max_size
538
+ # if qr_h > qr_max_size:
539
+ # qr_h = qr_max_size
540
+ # qr_w = max(1, int(qr_h * w_ratio))
541
+ # qr = qr.resize((qr_w, qr_h), resample=resample)
542
+ # # recompute text area width to fill remaining space
543
+ # text_area_w = total_width - (pad*2 + gap + qr_w)
544
+ # if text_area_w < 1:
545
+ # text_area_w = max(1, total_width - (pad*2) - qr_w - gap)
546
+ # # create canvas height as max(qr_h, text_block_h) + pad*2
547
+ # canvas_h = max(qr_h, text_block_h) + pad*2
548
+ # canvas = Image.new("RGB", (total_width, canvas_h), "white")
549
+ # # paste QR at left, vertically centered
550
+ # qr_y = pad + (canvas_h - pad*2 - qr_h) // 2
551
+ # canvas.paste(qr, (pad, qr_y))
552
+ # # draw text block on right of QR
553
+ # draw = ImageDraw.Draw(canvas)
554
+ # text_x = pad + qr_w + gap
555
+ # # vertical starting position to center the text block
556
+ # text_y = pad + (canvas_h - pad*2 - text_block_h) // 2
557
+ # for (w, h), ln in zip(line_sizes, lines):
558
+ # x = text_x + max((text_area_w - w) // 2, 0)
559
+ # draw.text((x, text_y), ln, font=font, fill=0)
560
+ # text_y += h + spacing
561
+ # return canvas
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.4
2
+ Name: qltool
3
+ Version: 0.1.2
4
+ Summary: PRINT simple labels on ql700
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: click>=8.2.1
7
+ Requires-Dist: console>=0.9911
8
+ Requires-Dist: importlib-resources>=6.5.2
9
+ Requires-Dist: opencv-python>=4.12.0.88
10
+ Requires-Dist: pillow>=11.3.0
11
+ Requires-Dist: qrcode>=8.2
12
+ Description-Content-Type: text/markdown
13
+
14
+ # qltool
15
+
16
+ *tool to print some useful stuff with ql700 brother printer*
17
+
18
+ - It is just a wrapper around the great `ql570.c` of Asbjørn Sloth
19
+ Tønnesen, Marc Juul and Luca Zimmermann
20
+
21
+ ## Installation
22
+
23
+ ``` {.bash org-language="sh"}
24
+ pip3 install qltool
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ``` {.bash org-language="sh"}
30
+ #---------text
31
+ rm /tmp/tmp_qrcode.png ; uv run src/qltool/main.py "Ahoj
32
+ aaa
33
+ 456 8jgqy 789 456 456 " --font-size 55 --font-size 45
34
+
35
+ # ---- create QR
36
+
37
+ uv run src/qltool/main.py "ahoj
38
+ momo
39
+ 123 456 789 jjj" -p -q
40
+
41
+ # -------- use any image/qrcode
42
+ uv run src/qltool/main.py "Užaj jetů" --qr /tmp/baba.png
43
+
44
+ uv run src/qltool/main.py "Už je tuná" --qr /tmp/baba.png -p
45
+ ```
46
+
47
+ ## Org-mode inventory - broken now
48
+
49
+ If this function is somewhere in the `.emacs` configfiles ...
50
+
51
+ It selects two fields fromthe TABLE and puts -q option.
52
+
53
+ You point to a line of a table and `c-c q`{.verbatim} or
54
+ `c-c w`{.verbatim} will view/print the code for inventory.
55
+
56
+ whatever id name owner location
57
+ ---------- ------------- --------------------------- ------- ----------
58
+ 123 D4-12345678 najaka blbost z inventury Pepa Home
59
+
60
+ and this is run
61
+ `./bin_qltool.py "id: D4-12345678; name: najaka blbost z inventury owner:Pepa " -q`
@@ -0,0 +1,6 @@
1
+ qltool/__init__.py,sha256=tPmKiA61zeZ_78r716j0_ZNtoNy3pQfaPX2oKmE1TZQ,52
2
+ qltool/main.py,sha256=AL6RuYBQUDCYARvqr-7nA3lWIUgRYFdl5g1Wf8nfiC8,20065
3
+ qltool-0.1.2.dist-info/METADATA,sha256=v3db_jHi_rYM1HxfnZ3ZVInUtvzYCrdsipcultEBWTQ,1611
4
+ qltool-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
5
+ qltool-0.1.2.dist-info/entry_points.txt,sha256=q5ksREBeeGS17aFhE-z2qUWXW5TcoTi5nO938fGiLhE,39
6
+ qltool-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ qltool = qltool:main