如何通过pycups获取打印选项键?求更高效实现方案
我通过反复试错并查看PPD文件,写出了能实现多纸盒打印的脚本,在PPD里找到了job-options对应的正确键值名称。但仅靠conn.getPrinterAttributes(printer)['media-source-supported']没法清晰获取这些选项。我觉得肯定有比直接查看PPD文件更好的方法来获取所有打印作业选项,但没找到相关文档;另外因为pycups底层基于C语言,我对它的Python与C绑定机制也不太清楚。执行pip show pycups只找到cupshelpers和pycups的相关信息,以及以下文件:
cups.cpython-310-x86_64-linux-gnu.so cupsext.cpython-310-x86_64-linux-gnu.so cupshelpers/ cupshelpers-1.0-py3.10.egg-info pycups-2.0.1.egg-info
没找到具体的绑定细节。为了更好地使用pycups并理解其底层绑定机制,特此提问,附上参考代码:
#!/usr/bin/env python3 import cups def list_printers(): conn = cups.Connection() printers = conn.getPrinters() print("Available printers:") for printer in printers: print(f"Printer: {printer}") print("Supported options:") options = conn.getPrinterAttributes(printer)['media-source-supported'] for option in options: cleaned_option = ''.join(c for c in option if c.isalnum()).upper() print(f" {cleaned_option}") print() def print_document(printer_name, document_path, tray_option): conn = cups.Connection() printers = conn.getPrinters() if printer_name not in printers: print(f"Printer '{printer_name}' not found.") return job_options = { 'InputSlot': tray_option, } print(job_options) with open(document_path, 'rb') as document: print_job_id = conn.printFile(printer_name, document_path, "Print Job", job_options) print(f"Document '{document_path}' sent to '{printer_name}' with Job ID: {print_job_id}") if __name__ == "__main__": # Specify the printer name, document paths, and tray options printer_name = "Kyocera-ECOSYS-P3055dn-KPDL" document1_path = "Tray1.pdf" document2_path = "Tray2.pdf" options = ["PF430A", "PF430B"] for option in options: print_document(printer_name, f"{option}.pdf", option)
一、获取完整打印作业选项的替代方法
1. 使用CUPS命令行工具
直接执行lpoptions -p <打印机名称> -l命令,可直接列出打印机支持的所有作业选项及可选值,输出格式更直观,无需手动解析PPD文件。比如针对你的打印机:
lpoptions -p Kyocera-ECOSYS-P3055dn-KPDL -l
示例输出:
InputSlot/Media Source: *Auto PF430A PF430B MediaSize/Media Size: *A4 Letter Legal
你可以在Python中通过subprocess模块调用该命令,解析输出结果得到结构化的选项列表,比仅依赖media-source-supported更全面。
2. 利用cupshelpers封装库
pycups附带的cupshelpers模块提供了上层封装,能更友好地处理打印机选项。比如用cupshelpers.getPrinterOptions方法获取包含选项名称、可选值的字典:
from cupshelpers import getPrinterOptions import cups conn = cups.Connection() printer = "Kyocera-ECOSYS-P3055dn-KPDL" options = getPrinterOptions(conn, printer) # 打印所有支持的选项及对应值 for opt_name, opt_info in options.items(): print(f"{opt_name}: {opt_info['choices']}")
该方法会自动处理PPD中的选项映射,返回结果比直接调用getPrinterAttributes更清晰。
二、理解pycups的Python与C绑定机制
pycups的底层绑定通过SWIG(Simplified Wrapper and Interface Generator)生成,它负责将CUPS的C API转换为Python可调用的接口。你看到的.so文件是编译后的C扩展模块,承载了Python与CUPS C库之间的调用逻辑。
1. 绑定细节的查找方式
pycups源码仓库中包含SWIG接口定义文件(如cups.i),里面明确了哪些CUPS C函数被暴露给Python,以及参数、返回值的转换规则。若想深入了解,可下载pycups源码包,查看其中的cups.i、setup.py等文件——setup.py包含编译C扩展的配置,能看到SWIG生成的代码如何被编译为.so模块。
2. pip安装文件说明
cups.cpython-310-x86_64-linux-gnu.so和cupsext.cpython-310-x86_64-linux-gnu.so:编译后的C扩展,直接实现Python到CUPS C API的调用;cupshelpers/:纯Python编写的辅助模块,封装常用打印机操作,无需直接对接C接口;.egg-info文件:pip用于管理包信息的元数据文件,与绑定机制无关。
优化后的参考代码
结合上述方法,优化后的代码可自动获取并使用正确的纸盒选项:
#!/usr/bin/env python3 import cups from cupshelpers import getPrinterOptions import subprocess def list_printers_with_options(): conn = cups.Connection() printers = conn.getPrinters() print("可用打印机及其支持的选项:") for printer in printers: print(f"\n打印机: {printer}") # 使用cupshelpers获取选项 options = getPrinterOptions(conn, printer) if 'InputSlot' in options: print("支持的纸盒选项:") for choice in options['InputSlot']['choices']: print(f" {choice}") # 或者用lpoptions命令获取完整选项 print("\n通过lpoptions获取的完整选项:") result = subprocess.run( ['lpoptions', '-p', printer, '-l'], capture_output=True, text=True ) print(result.stdout) def print_document(printer_name, document_path, tray_option): conn = cups.Connection() printers = conn.getPrinters() if printer_name not in printers: print(f"未找到打印机 '{printer_name}'") return job_options = { 'InputSlot': tray_option, } print(f"打印选项: {job_options}") try: print_job_id = conn.printFile(printer_name, document_path, "打印任务", job_options) print(f"文档 '{document_path}' 已发送到 '{printer_name}',任务ID: {print_job_id}") except cups.IPPError as e: print(f"打印失败: {e}") if __name__ == "__main__": printer_name = "Kyocera-ECOSYS-P3055dn-KPDL" # 先列出所有选项确认 list_printers_with_options() # 示例打印任务 options = ["PF430A", "PF430B"] for option in options: print_document(printer_name, f"{option}.pdf", option)
内容的提问来源于stack exchange,提问作者Xaver

