打印具有特定扩展名的文件
print a file with certain extension
我想打印使用文件选择器(或以某种方式)选择的文件
某些扩展名,以便 PyQt
或打印机将自动识别
格式(例如 pdf
、ms word
、excel
、txt
、html
、jpg
等)
到目前为止,我已经找到here如何打印内容
的 TextEdit,但我想打印各种格式的文件。
可以使用 PyQt5
还是我应该在别处搜索?
打印纯文本文档不需要 查看器,因为 print_()
function actually calls the internal QDocument's 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 文件打印几乎相似,但您需要使用 QWebEnginePage class from QtWebEngineWidgets. See .
不要 使用 QTextDocument.setHtml()
,因为 Qt 对 html 标签的支持有限。
同样适用于 PDF 文件,不同之处在于文件必须通过 setUrl()
and the QWebEngineSettings.PluginsEnabled setting has to be enabled through page.settings().setAttribute(setting, bool)
加载以防万一。
阅读有关 PDF File Viewing.
的文档
可以通过两种方法打印图像。
第一个也是更简单的方法是创建一个嵌入图像的临时 html 文件并加载到上面的网络引擎页面(您可以为 zoom/scaling 添加控件)。
或者,您可以直接使用 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()
)。
如评论中所述,打印为其他(可能是专有)格式需要外部模块。
我想打印使用文件选择器(或以某种方式)选择的文件
某些扩展名,以便 PyQt
或打印机将自动识别
格式(例如 pdf
、ms word
、excel
、txt
、html
、jpg
等)
到目前为止,我已经找到here如何打印内容 的 TextEdit,但我想打印各种格式的文件。
可以使用 PyQt5
还是我应该在别处搜索?
打印纯文本文档不需要 查看器,因为 print_()
function actually calls the internal QDocument's 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 文件打印几乎相似,但您需要使用 QWebEnginePage class from QtWebEngineWidgets. See
不要 使用 QTextDocument.setHtml()
,因为 Qt 对 html 标签的支持有限。
同样适用于 PDF 文件,不同之处在于文件必须通过 setUrl()
and the QWebEngineSettings.PluginsEnabled setting has to be enabled through page.settings().setAttribute(setting, bool)
加载以防万一。
阅读有关 PDF File Viewing.
可以通过两种方法打印图像。
第一个也是更简单的方法是创建一个嵌入图像的临时 html 文件并加载到上面的网络引擎页面(您可以为 zoom/scaling 添加控件)。
或者,您可以直接使用 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()
)。
如评论中所述,打印为其他(可能是专有)格式需要外部模块。