预览PDF文件并在实际打印文档之前选择打印机 [英] Preview a PDF file and choose printer before actually printing the document
问题描述
我目前正在使用Python3/tkinter开发一个应用程序,该应用程序将主要在Mac上运行,并且需要打印PDF文件.
I am currently developing an application in Python3/tkinter that will run primarily on Macs and will need to print PDF files.
过去,我已经设法通过在bash脚本中使用一些命令来自动将PDF文件发送到打印机,所以我认为我最终可能会使用该方法.
In the past, I have managed to send PDF files to printer automatically by using some commands in a bash script, so I think I could end up using that approach as a last resort.
但是,我从未找到一种指定打印机的方法,它总是将其直接发送到当前选择的打印机(通常是最后使用的打印机)中.问题在于所有这些计算机都连接到多于一台打印机,并且某些打印机不适合A4文档(例如,标签打印机).
However, I never found a way to specify one printer, it would always send it right into the currently selected printer (usually the last used one). The problem is that all those computers are connected to more that one printer, and some of the printers are not suitable for A4 documents (for instance, label printers).
有人知道是否有任何方法可以从命令行启动预览应用程序,或者有任何外部模块可以让我们在实际打印之前预览并选择打印机?
Does anyone know if there is any way to launch the Preview application from command line, or any external module that allows us to preview and choose printers before actually printing?
推荐答案
这是我刚刚创建的程序,允许您选择打印机并进行打印.
Here's a program I've just created that allows you to select a printer and print.
import tkinter as tk
from tkinter.filedialog import askopenfilename
import subprocess
from pprint import pprint
import platform
import sys
def which(program):
# http://stackoverflow.com/a/377028/3924118
import os
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
class FilePrinterDialog(tk.Toplevel):
def __init__(self, root, *args, **kwargs):
tk.Toplevel.__init__(self, root, *args, **kwargs)
self.root = root
self.body = tk.Frame(self, bg="lightblue")
self.body.pack(expand=True, fill="both")
self.title_frame = tk.Frame(self.body, pady=5)
self.title_frame.pack(fill="both", pady=(15, 5))
self.title = tk.Label(self.title_frame, text="Let's print!")
self.title.pack(fill="x")
# Current selected printer of your system
self.system_default_destination = self._find_system_default_destination()
# Finds printer names
self.printers_names = self._find_printers_names()
self.selected_file = None # To hold the selected file's name
self.data_bytes = None # Bytes read from the selected file to print
self.selected_printer = None # Hols name of selected printer
# Display them
self.printers_frame = tk.Frame(self.body, bg="lightblue", padx=10, pady=10)
self.printers_frame.pack(expand=True, fill="both")
self._display_printers()
self.bottom_frame = tk.Frame(self.body, pady=5)
self.bottom_frame.pack(fill="both", pady=(5, 16))
self.open_file_chooser = tk.Button(self.bottom_frame,
text="Open file chooser",
command=self._select_file)
self.open_file_chooser.pack(side="left", padx=10)
self.print_file = tk.Button(self.bottom_frame,
text="Print",
command=self._print_selected_file)
self.print_file.pack(side="right", padx=10)
self._make_modal()
def _read_file(self):
# NOT USED!
if not self.selected_file:
raise ValueError("No file chosen")
with open(self.selected_file, "rb") as in_file: # opening for [r]eading as [b]inary
return in_file.read() # if you only wanted to read 512 bytes, do .read(512)
def _print_selected_file(self):
if not self.selected_file:
print("No file selected yet!")
else:
subprocess.call(["lpr", self.selected_file])
def _select_file(self):
self.selected_file = askopenfilename(title = "Choose file to print")
print(self.selected_file)
def _on_listbox_selection(self, event):
self.selected_printer = self._find_current_selected_printer()
# Sets the printer on your system
subprocess.call(["lpoptions", "-d", self.selected_printer])
print("Selected printer:", self.selected_printer)
def _find_current_selected_printer(self):
curselection = self.listbox.curselection()
if len(curselection) > 0:
return self.listbox.get(curselection[0])
else:
return None
def _display_printers(self):
self.scrollbar = tk.Scrollbar(self.printers_frame)
self.scrollbar.pack(side="right", fill="y")
self.listbox = tk.Listbox(self.printers_frame,
yscrollcommand=self.scrollbar.set,
selectbackground="yellow",
selectmode="single",
height=6)
for printer_name in self.printers_names:
self.listbox.insert("end", printer_name)
# Keep track of selected listbox
self.listbox.bind("<<ListboxSelect>>", self._on_listbox_selection)
# Sets first listbox as selected
self.listbox.select_set(0) # Sets focus
self.listbox.event_generate("<<ListboxSelect>>")
self.listbox.pack(side="left", fill="both", expand=True)
self.scrollbar.config(command=self.listbox.yview)
def _find_system_default_destination(self):
return subprocess.getoutput("lpstat -d").split(": ")[1]
def _find_printers_names(self):
# Command to obtain printer names based on: https://superuser.com/a/1016825/317323
return subprocess.getoutput("lpstat -a | awk '{print $1}'").split("\n")
def _make_modal(self):
# Makes the window modal
self.transient(self.root)
self.grab_set()
self.wait_window(self)
if __name__ == "__main__":
if not which("lpoptions") or not which("lpr") or not which("awk") or not which("lpstat"):
sys.stderr.write("Requirements: lopotions, lpr, lpstat and awk not satisfied")
else:
root = tk.Tk()
opener = tk.Button(root, text="Open printer chooser", command=lambda: FilePrinterDialog(root))
opener.pack()
root.mainloop()
仅当您具有我在程序中指定的必需依赖项时,此程序才有效.你应该有这些,因为我也在 Mac OS X (Sierra) 上.
This program only works if you have the required dependencies which I specify in the program. You should have those since I'm also on a Mac OS X (Sierra).
我仅使用 .py
文件进行了测试.不幸的是,打印文档的格式并不是真正想要的(因为我不是真正的如何使用 lpr
来打印文件的专家),但是您可以检查的选项lpr
,看看你能用它做什么.
I've tested only with a .py
file. Unfortunately, the format of the printed documented isn't really as desired (since I'm not really an expert on how to use lpr
to print files), but you can check the options of lpr
and see what you can do with it.
此外,我还没有为所选文件创建任何预览,但是您可以使用PIL(至少对于图像而言)来实现此目的...
Also, I've not created any preview of the selected file, but you may achieve this using PIL (at least for images)...
注意:请小心,不要在 print
按钮上单击100次,否则队列中将要打印100个文件!您可能想以某种方式解决此问题.
Note: be careful, do not click 100 times on the print
button, otherwise you will have 100 files in the queue to be printed! You may want to fix this somehow.
这篇关于预览PDF文件并在实际打印文档之前选择打印机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!