【问题标题】:print a file with certain extension打印具有特定扩展名的文件
【发布时间】:2020-06-06 08:09:36
【问题描述】:

我想打印使用文件选择器(或以某种方式)选择的文件 某些扩展名,以便PyQt 或打印机会自动识别 格式(例如pdfms wordexceltxthtmljpg 等)

到目前为止,我已经找到here如何打印内容 TextEdit 的,但我想打印各种格式的文件。

PyQt5 可以吗?还是我应该在其他地方搜索?

【问题讨论】:

  • 您的问题到底是什么?因为网络打印机与本地打印机没有区别(只要它们对底层系统打印系统可用)。另一方面,使用您列出的格式进行打印可能会因格式而异很多,并且特定/专有格式需要使用外部库。
  • @musicamante 好的,打印 txt 和 pdf 格式就足够了,如果我需要更多格式,我会问另一个问题。现在,我了解(正如您所写),我可能需要使用各种格式的库。
  • 从文件选择器中选择文件并单击按钮将其打印为 pdf 或 txt(根据文件扩展名)就足够了。
  • @xralf 如果您的问题发生了变化,那么您应该编辑问题的标题和内容
  • @xralf 您不能也不应该在帖子中提出 2 个这样不同的问题:打印机网络和打印不同的格式,因为它们是正交的。

标签: python python-3.x pyqt5 qprinter


【解决方案1】:

打印纯文本文档不需要查看器,因为print_() 函数实际上调用了内部QDocument 的print_() 函数:

filePath, filter = QFileDialog.getOpenFileName(self, 'Open file', '', 'Text (*.txt)')
if not filePath:
    return
doc = QtGui.QTextDocument()
try:
    with open(filePath, 'r') as txtFile:
        doc.setPlainText(txtFile.read())
    printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution)
    if not QtPrintSupport.QPrintDialog(printer, self).exec_():
        return
    doc.print_(printer)
except Exception as e:
    print('Error trying to print: {}'.format(e))

在实际打印之前,您可能想要添加一些功能来设置页面大小、文档边距、字体大小等(只需阅读 QTextDocument 文档),我将把这些留给您。


从 html 文件打印几乎相似,但您需要使用 QtWebEngineWidgets 中的 QWebEnginePage 类。见this answer
不要使用QTextDocument.setHtml(),因为Qt对html标签的支持有限。

同样适用于 PDF 文件,不同之处在于文件必须通过setUrl() 加载,QWebEngineSettings.PluginsEnabled 设置必须通过page.settings().setAttribute(setting, bool) 启用以防万一。
阅读有关PDF File Viewing 的文档。


可以通过两种方法打印图像。

第一个也是更简单的方法是创建一个嵌入图像的临时 html 文件并加载到上述 Web 引擎页面(您可以添加缩放/缩放控件)。

或者,您可以使用 QPainter 直接打印,但您必须与打印机分辨率和图像大小相关,因此您可能希望在实际打印图像之前有一个预览对话框,否则它可能太小了(或太大)。

虽然比普通的<html><img src=""></html> 更复杂,但它可以更好地控制图像[s] 的定位和大小。

class ImagePrintPreview(QtWidgets.QDialog):
    def __init__(self, parent, printer, pixmap):
        super().__init__(parent)

        self.printer = printer
        self.pixmap = pixmap

        layout = QtWidgets.QGridLayout(self)

        self.viewer = QtWidgets.QLabel()
        layout.addWidget(self.viewer, 0, 0, 1, 2)

        self.resoCombo = QtWidgets.QComboBox()
        layout.addWidget(self.resoCombo, 1, 0)

        self.zoom = QtWidgets.QSpinBox(minimum=50, maximum=200, suffix='%')
        self.zoom.setValue(100)
        self.zoom.setAccelerated(True)
        layout.addWidget(self.zoom, 1, 1)
        self.zoom.valueChanged.connect(self.updatePreview)

        self.buttonBox = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel)
        layout.addWidget(self.buttonBox)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        default = printer.resolution()
        self.resoCombo.addItem(str(default), default)
        for dpi in (150, 300, 600, 1200):
            if dpi == default:
                continue
            self.resoCombo.addItem(str(dpi), dpi)
        self.resoCombo.currentIndexChanged.connect(self.updatePreview)

        self.updatePreview()

    def updatePreview(self):
        # create a preview to show how the image will be printed
        self.printer.setResolution(self.resoCombo.currentData())
        paperRect = self.printer.paperRect(self.printer.DevicePixel)
        printRect = self.printer.pageRect(self.printer.DevicePixel)

        # a temporary pixmap that will use the printer's page size
        # note that page/paper are QRectF, they have a QSizeF which has to
        # be converted to a QSize
        pm = QtGui.QPixmap(paperRect.size().toSize())
        # new pixmap have allocated memory for their contents, which usually
        # result in some random pixels, just fill it with white
        pm.fill(QtCore.Qt.white)
        # start a qpainter on the pixmap
        qp = QtGui.QPainter(pm)
        # scale the pixmap to the wanted zoom value
        zoom = self.zoom.value() * .01
        scaled = self.pixmap.scaledToWidth(int(self.pixmap.width() * zoom), QtCore.Qt.SmoothTransformation)
        # paint the pixmap aligned to the printing margins
        qp.drawPixmap(printRect.topLeft(), scaled)

        # other possible alternatives:

        # Center the image:
        #   qp.translate(printRect.center())
        #   delta = QtCore.QPointF(scaled.rect().center())
        #   qp.drawPixmap(-delta, scaled)

        # To also rotate 90° clockwise, add this to the above:
        #   qp.rotate(90)
        # *after* qp.translate() and before qp.drawPixmap()


        # when painting to a non QWidget device, you always have to end the
        # painter before being able to use it
        qp.end()
        # scale the temporary pixmap to a fixed width
        self.viewer.setPixmap(pm.scaledToWidth(300, QtCore.Qt.SmoothTransformation))

    def exec_(self):
        if super().exec_():
            self.printer.setResolution(self.resoCombo.currentData())
            # do the same as above, but paint directly on the printer device
            printRect = self.printer.pageRect(self.printer.DevicePixel)
            qp = QtGui.QPainter(self.printer)
            zoom = self.zoom.value() * .01
            scaled = self.pixmap.scaledToWidth(int(self.pixmap.width() * zoom), QtCore.Qt.SmoothTransformation)
            qp.drawPixmap(printRect.topLeft(), scaled)
            # as above, that's important!
            qp.end()


class ImagePrinter(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QVBoxLayout(self)
        selBtn = QtWidgets.QPushButton('Open image')
        layout.addWidget(selBtn)
        selBtn.clicked.connect(self.selectFile)

    def selectFile(self):
        filePath, filter = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '/tmp', 'Images (*.jpg *.png)')
        if not filePath:
            return
        pixmap = QtGui.QPixmap(filePath)
        if pixmap.isNull():
            return

        printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution)
        if QtPrintSupport.QPrintDialog(printer, self).exec_():
            ImagePrintPreview(self, printer, pixmap).exec_()

请注意,我无法在 Windows 下对此进行测试,因此可能需要更改与分辨率相关的内容(可能通过使用 printer.supportedResolutions())。


正如 cmets 中已经解释的,打印到其他(可能是专有的)格式需要外部模块。

【讨论】:

  • 能否请您出示 PDF 文件?该文档适用于 C++。如果你很清楚(当然)。谢谢
  • 还有一个缺点。我无法从网络上的可用打印机中选择打印机。
  • @xralf “显示 PDF 文件”是什么意思?关于网络打印,很遗憾我无法帮助您,因为我在 Linux 上;如果您使用的是 Windows 并且指的是网络上共享的打印机(“活动目录”),我相信默认情况下不会列出它们,除非它们默认打印机。尝试创建新打印机并使用完整的 `\` 路径手动设置打印机名称。
  • 我的意思是,显示打印 PDF 文件,现在它只打印文本文档 (txt)。
  • @xralf 我从来没有机会实际测试 PDF 打印,但它应该以与 html 相同的方式工作(只要您启用了插件):将 url 设置为文件路径。关于导入,我只是导入了 PyQt5 的基本子模块,这些子模块已经在示例中使用的类的前缀中:QtCore、QtGui、QtWidgets 和 QtPrintSupport。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
相关资源
最近更新 更多