(第一篇文章,可能不太好):
我在一家汽车制造公司工作,目前正在为我们的零件信息开发一个新的跟踪系统。我正在编写一个简单的软件,安装在低级线路端计算机上,用于输入信息和打印标签以进行内部跟踪。
目标是使用我选择的 Python GUI 库从操作员处获取零件信息,然后使用
QRCode
和 PIL.Image
模块生成标签作为 .JPG
文件。所有这些都运行良好。标签完全按照我想要的方式生成并保存到机器等。
我似乎无法得到的部分是打印。我找到并尝试的大多数解决方案都是在打印对话框中打开文件,并要求操作员单击“打印”或进行某种输入操作。我不想要这个;它为操作员错误和其他标签格式问题打开了大门。
TL;博士:
有没有一种好方法可以使用系统对话框或提示符从 Python WITHOUT 向操作系统发送打印命令?我包括我尝试过的内容和我当前的解决方案。
操作系统:Windows;语言:Python 3.12.6
第一个解决方案:
我尝试使用
os.startfile()
方法和 'print'
操作,但这创建了上述对话框问题:
from os import path, getcwd, startfile
...
# Attempt to open label.jpg file with 'print' operation
try:
startfile(file_path, "print")
# label.jpg file couldn't be found/opened
except FileNotFoundError:
...
第二种解决方案(当前):
之后,我研究了使用
win32print
库。这让我更进一步,但我被一个错误难住了......
from PIL import Image, ImageDraw, ImageFont, ImageWin
from os import path, getcwd, startfile
import win32print
...
# prints a passed label
def print_label(filepath: str | bytes) -> None:
"""
Accepts a filepath that is opened and printed.
"""
# print the label at the passed filepath
try:
# open the label from the passed filepath
label = Image.open(filepath)
# get the default printer
printer_name = win32print.GetDefaultPrinter()
# create a printer from the default printer name
printer = win32print.OpenPrinter(printer_name)
# create a document printer
doc_printer = win32print.StartDocPrinter(printer, 1, (filepath, None, "RAW"))
# start the printer
win32print.StartPagePrinter(doc_printer)
# rotate the image to improve orientation
label = label.rotate(90, expand = True)
# save the new width and height
width, height = label.size
# draw the image on the printer
bitmap = ImageWin.Dib(label)
bitmap.draw(doc_printer, (0, 0, width, height))
# end the print job
win32print.EndPagePrinter(doc_printer)
win32print.EndDocPrinter(doc_printer)
win32print.ClosePrinter(printer)
# there was an error printing the label
except OSError as ex:
# if the code is 1155 (no printing application assigned)
if ex.winerror == 1155:
# show a popup warning the user
...
# else show a popup with the error
else:
...
输出与错误:
Default Printer: \\[servername]\[printername]
Printer Object: <PyPrinterHANDLE:2151737373152>
Initialized Printer: 199
Traceback (most recent call last):
File "\\...\wip_label_generator.py", line 290, in generate_labels
print_label(label)
File "\\...\wip_label_generator.py", line 36, in print_label
win32print.StartPagePrinter(doc_printer)
pywintypes.error: (6, 'StartPagePrinter', 'The handle is invalid.')
结论:
这不起作用有什么原因吗?我似乎无法找到在此范围内发生的此错误的简洁或适用的解释。我感觉好像它妨碍了我初始化
DocPrinter
,但老实说我很困惑。
如果有更好的方法来实现这种直接打印操作,请告诉我。我确信可以使用
subprocess.run()
或类似的东西。预先感谢!
经过大量研究,我针对这个具体案例提出了一个非常好的解决方案。感谢 @user2357112、@MSalters 和 @Mark Ransom 的反馈,我能够找到一些新资源。
研究与解释:
结合这些来源的信息:
https://python-forum.io/thread-13884.html
https://stackoverflow.com/a/12725233/25419268
https://timgolden.me.uk/python/win32_how_do_i/print.html
我的解决方案很大程度上基于 Tim Golden 网站上的 Single Image 代码片段。虽然他的代码竭尽全力以编程方式检查打印机的物理打印方面,但我不需要这个。当 Tim 提到设备独立位图功能时,主要突破就出现了:
无需任何额外工具,即可在 Windows 机器上打印图像 几乎极其困难,涉及至少三个设备上下文 所有这些都在不同层次上相互关联,并且有相当多的 反复试验。幸运的是,有这样一种东西 设备无关位图 (DIB),让您快刀斩乱麻—— 或者至少是其中的一部分。更幸运的是,Python Imaging 图书馆支持野兽。
这倾向于 Mark Ransom 提到的,将打印机用作窗口。
我的改编只是采用预先生成的
.PNG
文件,将其放大,将其对齐到页面的左上角,然后打印该文件。
代码:
from PIL import ImageWin
import win32print
import win32ui
# prints a passed label
def print_label(filepath: str | bytes) -> None:
"""
Accepts a filepath that is opened and printed.
"""
# set an error flag
print_error = False
# print the label at the passed filepath
try:
# access the OS' default printer
printer_name = win32print.GetDefaultPrinter()
# create a device context from a named printer and assess the printable size of the paper.
hDC = win32ui.CreateDC()
# convert from a basic device context to a printer device context
hDC.CreatePrinterDC(printer_name)
# open the image bitmap
label = Image.open(filepath)
if label.size[0] > label.size[4]:
label = label.rotate(90)
# start the print job
hDC.StartDoc(filepath)
hDC.StartPage()
# draw the bitmap to the printer device (uses the device-independent bitmap feature)
dib = ImageWin.Dib(label)
# scale the label image up
scaled_width = label.size[0] * 3
scaled_height = label.size[4] * 3
# set the left horizontal bound of the image on the page
x1 = 0
# set the top vertical bound of the image on the page
y1 = 0
# set the right horizontal bound of the image on the page
x2 = x1 + scaled_width
# set the top vertical bound of the image on the page
y2 = y1 + scaled_height
# draw the label image onto the device context's handle using the device-independent bitmap
dib.draw(hDC.GetHandleOutput(), (x1, y1, x2, y2))
# end the print job
hDC.EndPage()
hDC.EndDoc()
hDC.DeleteDC()
# there was an error printing the label
except OSError as ex:
# if the code is 1155 (no printing application assigned)
if ex.winerror == 1155:
# show a popup warning the user
...
# else show a popup with the error
else:
...
# if the error flag was not set
if not print_error:
# show popup to confirm printing
...
感谢所有反馈,我希望这对将来的人有所帮助!