Mercurial > hg > zebra
comparison zebra.py @ 31:f77ca0963d6d 0.2.0
Fix language bug, convert to pyproject.toml
author | Ben Croston <ben@croston.org> |
---|---|
date | Wed, 12 Feb 2025 15:13:51 +0000 |
parents | 63d1260cc64e |
children |
comparison
equal
deleted
inserted
replaced
30:ddc251fb92aa | 31:f77ca0963d6d |
---|---|
1 #!/usr/bin/env python3 | 1 #!/usr/bin/env python3 |
2 | 2 |
3 # Copyright (c) 2011-2020 Ben Croston | 3 # Copyright (c) 2011-2025 Ben Croston |
4 # | 4 # |
5 # Permission is hereby granted, free of charge, to any person obtaining a copy of | 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 | 6 # this software and associated documentation files (the "Software"), to deal in |
7 # the Software without restriction, including without limitation the rights to | 7 # the Software without restriction, including without limitation the rights to |
8 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | 8 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
28 import win32print | 28 import win32print |
29 else: | 29 else: |
30 IS_WINDOWS = False | 30 IS_WINDOWS = False |
31 import subprocess | 31 import subprocess |
32 | 32 |
33 | |
33 class Zebra: | 34 class Zebra: |
34 """A class to communicate with (Zebra) label printers""" | 35 """A class to communicate with (Zebra) label printers""" |
35 | 36 |
36 def __init__(self, queue=None): | 37 def __init__(self, queue=None): |
37 """queue - name of the printer queue (optional)""" | 38 """queue - name of the printer queue (optional)""" |
38 self.queue = queue | 39 self.queue = queue |
39 | 40 |
40 def _output_unix(self, commands): | 41 def _output_unix(self, commands): |
41 if self.queue == 'zebra_python_unittest': | 42 if self.queue == 'zebra_python_unittest': |
42 p = subprocess.Popen(['cat','-'], stdin=subprocess.PIPE) | 43 p = subprocess.Popen(['cat', '-'], stdin=subprocess.PIPE) |
43 else: | 44 else: |
44 p = subprocess.Popen(['lpr','-P{}'.format(self.queue),'-oraw'], stdin=subprocess.PIPE) | 45 p = subprocess.Popen( |
46 ['lpr', '-P{}'.format(self.queue), '-oraw'], stdin=subprocess.PIPE | |
47 ) | |
45 p.communicate(commands) | 48 p.communicate(commands) |
46 p.stdin.close() | 49 p.stdin.close() |
47 | 50 |
48 def _output_win(self, commands): | 51 def _output_win(self, commands): |
49 if self.queue == 'zebra_python_unittest': | 52 if self.queue == 'zebra_python_unittest': |
50 print(commands) | 53 print(commands) |
51 return | 54 return |
52 hPrinter = win32print.OpenPrinter(self.queue) | 55 hPrinter = win32print.OpenPrinter(self.queue) |
53 try: | 56 try: |
54 hJob = win32print.StartDocPrinter(hPrinter, 1, ('Label',None,'RAW')) | 57 win32print.StartDocPrinter(hPrinter, 1, ('Label', None, 'RAW')) |
55 try: | 58 try: |
56 win32print.StartPagePrinter(hPrinter) | 59 win32print.StartPagePrinter(hPrinter) |
57 win32print.WritePrinter(hPrinter, commands) | 60 win32print.WritePrinter(hPrinter, commands) |
58 win32print.EndPagePrinter(hPrinter) | 61 win32print.EndPagePrinter(hPrinter) |
59 finally: | 62 finally: |
66 | 69 |
67 commands - commands to send to the printer. Converted to a byte string if necessary. | 70 commands - commands to send to the printer. Converted to a byte string if necessary. |
68 encoding - Encoding used if 'commands' is not a byte string | 71 encoding - Encoding used if 'commands' is not a byte string |
69 """ | 72 """ |
70 assert self.queue is not None | 73 assert self.queue is not None |
71 if type(commands) != bytes: | 74 if not isinstance(commands, bytes): |
72 commands = str(commands).encode(encoding=encoding) | 75 commands = str(commands).encode(encoding=encoding) |
73 if IS_WINDOWS: | 76 if IS_WINDOWS: |
74 self._output_win(commands) | 77 self._output_win(commands) |
75 else: | 78 else: |
76 self._output_unix(commands) | 79 self._output_unix(commands) |
82 self.output('\nU\n') | 85 self.output('\nU\n') |
83 | 86 |
84 def _getqueues_unix(self): | 87 def _getqueues_unix(self): |
85 queues = [] | 88 queues = [] |
86 try: | 89 try: |
87 output = subprocess.check_output(['lpstat','-p'], universal_newlines=True) | 90 output = subprocess.check_output( |
91 ['lpstat', '-p'], | |
92 universal_newlines=True, | |
93 env={"LANG": "en_US.UTF-8"} | |
94 ) | |
88 except subprocess.CalledProcessError: | 95 except subprocess.CalledProcessError: |
89 return [] | 96 return [] |
90 for line in output.split('\n'): | 97 for line in output.split('\n'): |
91 if line.startswith('printer'): | 98 if line.startswith('printer'): |
92 queues.append(line.split(' ')[1]) | 99 queues.append(line.split(' ')[1]) |
93 return queues | 100 return queues |
94 | 101 |
95 def _getqueues_win(self): | 102 def _getqueues_win(self): |
96 printers = [] | 103 printers = [] |
97 for (a,b,name,d) in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL): | 104 for a, b, name, d in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL): |
98 printers.append(name) | 105 printers.append(name) |
99 return printers | 106 return printers |
100 | 107 |
101 def getqueues(self): | 108 def getqueues(self): |
102 """Returns a list of printer queues on local machine""" | 109 """Returns a list of printer queues on local machine""" |
119 """ | 126 """ |
120 commands = '\n' | 127 commands = '\n' |
121 if direct_thermal: | 128 if direct_thermal: |
122 commands += 'OD\n' | 129 commands += 'OD\n' |
123 if label_height: | 130 if label_height: |
124 commands += 'Q%s,%s\n'%(label_height[0],label_height[1]) | 131 commands += 'Q%s,%s\n' % (label_height[0], label_height[1]) |
125 if label_width: | 132 if label_width: |
126 commands += 'q%s\n'%label_width | 133 commands += 'q%s\n' % label_width |
127 self.output(commands) | 134 self.output(commands) |
128 | 135 |
129 def reset_default(self): | 136 def reset_default(self): |
130 """Reset the printer to factory settings using EPL2""" | 137 """Reset the printer to factory settings using EPL2""" |
131 self.output('\n^default\n') | 138 self.output('\n^default\n') |
133 def reset(self): | 140 def reset(self): |
134 """Resets the printer using EPL2 - equivalent to switching off/on""" | 141 """Resets the printer using EPL2 - equivalent to switching off/on""" |
135 self.output('\n^@\n') | 142 self.output('\n^@\n') |
136 | 143 |
137 def autosense(self): | 144 def autosense(self): |
138 """Run AutoSense by sending an EPL2 command | 145 """Run AutoSense by sending an EPL2 command |
139 Get the printer to detect label and gap length and set the sensor levels | 146 Get the printer to detect label and gap length and set the sensor levels |
140 """ | 147 """ |
141 self.output('\nxa\n') | 148 self.output('\nxa\n') |
142 | 149 |
143 def store_graphic(self, name, filename): | 150 def store_graphic(self, name, filename): |
144 """Store a 1 bit PCX file on the label printer, using EPL2. | 151 """Store a 1 bit PCX file on the label printer, using EPL2. |
145 | 152 |
146 name - name to be used on printer | 153 name - name to be used on printer |
147 filename - local filename | 154 filename - local filename |
148 """ | 155 """ |
149 assert filename.lower().endswith('.pcx') | 156 assert filename.lower().endswith('.pcx') |
150 commands = '\nGK"%s"\n'%name | 157 commands = '\nGK"%s"\n' % name |
151 commands += 'GK"%s"\n'%name | 158 commands += 'GK"%s"\n' % name |
152 size = os.path.getsize(filename) | 159 size = os.path.getsize(filename) |
153 commands += 'GM"%s"%s\n'%(name,size) | 160 commands += 'GM"%s"%s\n' % (name, size) |
154 self.output(commands) | 161 self.output(commands) |
155 self.output(open(filename,'rb').read()) | 162 self.output(open(filename, 'rb').read()) |
156 | 163 |
157 def print_graphic(self, x, y, width, length, data, qty): | 164 def print_graphic(self, x, y, width, length, data, qty): |
158 """Print a label from 1 bit data, using EPL2 | 165 """Print a label from 1 bit data, using EPL2 |
159 | 166 |
160 x,y - top left coordinates of the image, in dots | 167 x,y - top left coordinates of the image, in dots |
161 width - width of image, in dots. Must be a multiple of 8. | 168 width - width of image, in dots. Must be a multiple of 8. |
162 length - length of image, in dots | 169 length - length of image, in dots |
163 data - raw graphical data, in bytes | 170 data - raw graphical data, in bytes |
164 qty - number of labels to print | 171 qty - number of labels to print |
165 """ | 172 """ |
166 assert type(data) == bytes | 173 assert isinstance(data, bytes) |
167 assert width % 8 == 0 # make sure width is a multiple of 8 | 174 assert width % 8 == 0 # make sure width is a multiple of 8 |
168 assert (width//8) * length == len(data) | 175 assert (width // 8) * length == len(data) |
169 commands = b"\nN\nGW%d,%d,%d,%d,%s\nP%d\n"%(x, y, width//8, length, data, qty) | 176 commands = b'\nN\nGW%d,%d,%d,%d,%s\nP%d\n' % ( |
177 x, | |
178 y, | |
179 width // 8, | |
180 length, | |
181 data, | |
182 qty, | |
183 ) | |
170 self.output(commands) | 184 self.output(commands) |
185 | |
171 | 186 |
172 if __name__ == '__main__': | 187 if __name__ == '__main__': |
173 z = Zebra() | 188 z = Zebra() |
174 print('Printer queues found:',z.getqueues()) | 189 print('Printer queues found:', z.getqueues()) |
175 z.setqueue('zebra_python_unittest') | 190 z.setqueue('zebra_python_unittest') |
176 z.setup(direct_thermal=True, label_height=(406,32), label_width=609) # 3" x 2" direct thermal label | 191 z.setup( |
177 z.store_graphic('logo','logo.pcx') | 192 direct_thermal=True, label_height=(406, 32), label_width=609 |
193 ) # 3" x 2" direct thermal label | |
194 z.store_graphic('logo', 'logo.pcx') | |
178 label = """ | 195 label = """ |
179 N | 196 N |
180 GG419,40,"logo" | 197 GG419,40,"logo" |
181 A40,80,0,4,1,1,N,"Tangerine Duck 4.4%" | 198 A40,80,0,4,1,1,N,"Tangerine Duck 4.4%" |
182 A40,198,0,3,1,1,N,"Duty paid on 39.9l" | 199 A40,198,0,3,1,1,N,"Duty paid on 39.9l" |
183 A40,240,0,3,1,1,N,"Gyle: 127 Best Before: 16/09/2011" | 200 A40,240,0,3,1,1,N,"Gyle: 127 Best Before: 16/09/2011" |
184 A40,320,0,4,1,1,N,"Pump & Truncheon" | 201 A40,320,0,4,1,1,N,"Pump & Truncheon" |
185 P1 | 202 P1 |
186 """ | 203 """ |
187 z.output(label) | 204 z.output(label) |
188 |