在实际打印文档之前预览PDF文件并选择打印机

问题描述 投票:1回答:1

我目前正在使用Python3 / tkinter开发一个主要在Mac上运行的应用程序,需要打印PDF文件。

在过去,我已经设法通过使用bash脚本中的一些命令自动将PDF文件发送到打印机,所以我想我最终可能会使用这种方法作为最后的手段。

但是,我从未找到指定一台打印机的方法,它总是将其直接发送到当前选定的打印机(通常是最后使用的打印机)。问题是所有这些计算机都连接到多台打印机,而某些打印机不适合A4文档(例如,标签打印机)。

有没有人知道是否有任何方法从命令行启动预览应用程序,或任何允许我们在实际打印之前预览和选择打印机的外部模块?

python macos printing tkinter printers
1个回答
1
投票

这是我刚刚创建的一个程序,允许您选择打印机并进行打印。

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)上。

我只测试了.py文件。不幸的是,印刷文件的格式并不是真正理想的(因为我不是真正的专家,如何使用lpr打印文件),但你可以检查lpr的选项,看看你能用它做什么。

此外,我没有创建所选文件的任何预览,但您可以使用PIL(至少对于图像)实现此目的...

注意:注意,不要在print按钮上单击100次,否则队列中将有100个文件要打印!你可能想以某种方式解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.