Mercurial > hg > zebra
annotate zebra.py @ 32:405f0751f09a default tip
Added tag 0.2.0 for changeset f77ca0963d6d
author | Ben Croston <ben@croston.org> |
---|---|
date | Wed, 12 Feb 2025 15:14:03 +0000 |
parents | f77ca0963d6d |
children |
rev | line source |
---|---|
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
1 #!/usr/bin/env python3 |
25 | 2 |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
3 # Copyright (c) 2011-2025 Ben Croston |
25 | 4 # |
5 # Permission is hereby granted, free of charge, to any person obtaining a copy of | |
6 # this software and associated documentation files (the "Software"), to deal in | |
7 # the Software without restriction, including without limitation the rights to | |
8 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
9 # of the Software, and to permit persons to whom the Software is furnished to do | |
10 # so, subject to the following conditions: | |
11 # | |
12 # The above copyright notice and this permission notice shall be included in all | |
13 # copies or substantial portions of the Software. | |
14 # | |
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 # SOFTWARE. | |
22 | |
0 | 23 import os.path |
24 import sys | |
10 | 25 |
0 | 26 if sys.platform.lower().startswith('win'): |
10 | 27 IS_WINDOWS = True |
0 | 28 import win32print |
10 | 29 else: |
30 IS_WINDOWS = False | |
22 | 31 import subprocess |
0 | 32 |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
33 |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
34 class Zebra: |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
35 """A class to communicate with (Zebra) label printers""" |
10 | 36 |
0 | 37 def __init__(self, queue=None): |
10 | 38 """queue - name of the printer queue (optional)""" |
0 | 39 self.queue = queue |
40 | |
41 def _output_unix(self, commands): | |
42 if self.queue == 'zebra_python_unittest': | |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
43 p = subprocess.Popen(['cat', '-'], stdin=subprocess.PIPE) |
0 | 44 else: |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
45 p = subprocess.Popen( |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
46 ['lpr', '-P{}'.format(self.queue), '-oraw'], stdin=subprocess.PIPE |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
47 ) |
17
a9dacd180597
Does not work in python 2.6 after all
Ben Croston <ben@croston.org>
parents:
14
diff
changeset
|
48 p.communicate(commands) |
0 | 49 p.stdin.close() |
50 | |
51 def _output_win(self, commands): | |
8 | 52 if self.queue == 'zebra_python_unittest': |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
53 print(commands) |
8 | 54 return |
55 hPrinter = win32print.OpenPrinter(self.queue) | |
56 try: | |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
57 win32print.StartDocPrinter(hPrinter, 1, ('Label', None, 'RAW')) |
8 | 58 try: |
59 win32print.StartPagePrinter(hPrinter) | |
60 win32print.WritePrinter(hPrinter, commands) | |
61 win32print.EndPagePrinter(hPrinter) | |
62 finally: | |
63 win32print.EndDocPrinter(hPrinter) | |
64 finally: | |
65 win32print.ClosePrinter(hPrinter) | |
26 | 66 |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
67 def output(self, commands, encoding='cp437'): |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
68 """Send raw commands to the label printer |
10 | 69 |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
70 commands - commands to send to the printer. Converted to a byte string if necessary. |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
71 encoding - Encoding used if 'commands' is not a byte string |
10 | 72 """ |
0 | 73 assert self.queue is not None |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
74 if not isinstance(commands, bytes): |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
75 commands = str(commands).encode(encoding=encoding) |
10 | 76 if IS_WINDOWS: |
0 | 77 self._output_win(commands) |
78 else: | |
14 | 79 self._output_unix(commands) |
0 | 80 |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
81 def print_config_label(self): |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
82 """ |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
83 Send an EPL2 command to print label(s) with current config settings |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
84 """ |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
85 self.output('\nU\n') |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
86 |
0 | 87 def _getqueues_unix(self): |
88 queues = [] | |
26 | 89 try: |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
90 output = subprocess.check_output( |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
91 ['lpstat', '-p'], |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
92 universal_newlines=True, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
93 env={"LANG": "en_US.UTF-8"} |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
94 ) |
26 | 95 except subprocess.CalledProcessError: |
96 return [] | |
0 | 97 for line in output.split('\n'): |
98 if line.startswith('printer'): | |
99 queues.append(line.split(' ')[1]) | |
100 return queues | |
101 | |
102 def _getqueues_win(self): | |
8 | 103 printers = [] |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
104 for a, b, name, d in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL): |
8 | 105 printers.append(name) |
106 return printers | |
0 | 107 |
108 def getqueues(self): | |
10 | 109 """Returns a list of printer queues on local machine""" |
110 if IS_WINDOWS: | |
0 | 111 return self._getqueues_win() |
112 else: | |
113 return self._getqueues_unix() | |
114 | |
1 | 115 def setqueue(self, queue): |
10 | 116 """Set the printer queue""" |
0 | 117 self.queue = queue |
118 | |
2
ad9d9bf61243
Change direct transfer to direct thermal
Ben Croston <ben@fuzzyduckbrewery.co.uk>
parents:
1
diff
changeset
|
119 def setup(self, direct_thermal=None, label_height=None, label_width=None): |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
120 """Set up the label printer using EPL2. Parameters are not set if they are None. |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
121 Not necessary if using AutoSense (hold feed button while powering on) |
10 | 122 |
123 direct_thermal - True if using direct thermal labels | |
124 label_height - tuple (label height, label gap) in dots | |
125 label_width - in dots | |
126 """ | |
0 | 127 commands = '\n' |
2
ad9d9bf61243
Change direct transfer to direct thermal
Ben Croston <ben@fuzzyduckbrewery.co.uk>
parents:
1
diff
changeset
|
128 if direct_thermal: |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
129 commands += 'OD\n' |
0 | 130 if label_height: |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
131 commands += 'Q%s,%s\n' % (label_height[0], label_height[1]) |
0 | 132 if label_width: |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
133 commands += 'q%s\n' % label_width |
0 | 134 self.output(commands) |
135 | |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
136 def reset_default(self): |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
137 """Reset the printer to factory settings using EPL2""" |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
138 self.output('\n^default\n') |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
139 |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
140 def reset(self): |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
141 """Resets the printer using EPL2 - equivalent to switching off/on""" |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
142 self.output('\n^@\n') |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
143 |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
144 def autosense(self): |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
145 """Run AutoSense by sending an EPL2 command |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
146 Get the printer to detect label and gap length and set the sensor levels |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
147 """ |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
148 self.output('\nxa\n') |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
149 |
0 | 150 def store_graphic(self, name, filename): |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
151 """Store a 1 bit PCX file on the label printer, using EPL2. |
10 | 152 |
153 name - name to be used on printer | |
154 filename - local filename | |
155 """ | |
156 assert filename.lower().endswith('.pcx') | |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
157 commands = '\nGK"%s"\n' % name |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
158 commands += 'GK"%s"\n' % name |
0 | 159 size = os.path.getsize(filename) |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
160 commands += 'GM"%s"%s\n' % (name, size) |
0 | 161 self.output(commands) |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
162 self.output(open(filename, 'rb').read()) |
0 | 163 |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
164 def print_graphic(self, x, y, width, length, data, qty): |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
165 """Print a label from 1 bit data, using EPL2 |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
166 |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
167 x,y - top left coordinates of the image, in dots |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
168 width - width of image, in dots. Must be a multiple of 8. |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
169 length - length of image, in dots |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
170 data - raw graphical data, in bytes |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
171 qty - number of labels to print |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
172 """ |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
173 assert isinstance(data, bytes) |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
174 assert width % 8 == 0 # make sure width is a multiple of 8 |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
175 assert (width // 8) * length == len(data) |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
176 commands = b'\nN\nGW%d,%d,%d,%d,%s\nP%d\n' % ( |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
177 x, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
178 y, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
179 width // 8, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
180 length, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
181 data, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
182 qty, |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
183 ) |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
184 self.output(commands) |
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
185 |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
186 |
0 | 187 if __name__ == '__main__': |
29
63d1260cc64e
- class name is now 'Zebra' instead of 'zebra'
Ben Croston <ben@croston.org>
parents:
27
diff
changeset
|
188 z = Zebra() |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
189 print('Printer queues found:', z.getqueues()) |
0 | 190 z.setqueue('zebra_python_unittest') |
31
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
191 z.setup( |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
192 direct_thermal=True, label_height=(406, 32), label_width=609 |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
193 ) # 3" x 2" direct thermal label |
f77ca0963d6d
Fix language bug, convert to pyproject.toml
Ben Croston <ben@croston.org>
parents:
29
diff
changeset
|
194 z.store_graphic('logo', 'logo.pcx') |
0 | 195 label = """ |
196 N | |
197 GG419,40,"logo" | |
198 A40,80,0,4,1,1,N,"Tangerine Duck 4.4%" | |
199 A40,198,0,3,1,1,N,"Duty paid on 39.9l" | |
1 | 200 A40,240,0,3,1,1,N,"Gyle: 127 Best Before: 16/09/2011" |
0 | 201 A40,320,0,4,1,1,N,"Pump & Truncheon" |
202 P1 | |
203 """ | |
204 z.output(label) |