在 QPdfWriter 上结合 QPainter 和 QTextDocument
Combining QPainter and QTextDocument on QPdfWriter
在另一个问题中,我了解到 QTextDocument
,我 可以在 PDF 的同一页上使用 QPainter
和 QTextDocument
。但是,当我尝试这样做时,他们各自重新启动了文档,抹去了对方的内容。
from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
from PySide6.QtWidgets import QApplication
def main():
app = QApplication()
pdf = QPdfWriter('example.pdf')
pdf.setPageSize(QPageSize.Letter)
# Whichever of these goes second, overwrites the first.
draw_diagram(pdf)
print_document(pdf)
app.exit(0)
def draw_diagram(pdf: QPdfWriter):
painter = QPainter(pdf)
painter.drawArc(painter.window().width()//4,
painter.window().height()//2 - painter.window().width()//4,
painter.window().width()//2,
painter.window().width()//2,
0,
5760)
painter.drawText(0,
painter.window().height()//2,
painter.window().width(),
painter.window().height()//10,
Qt.AlignHCenter | Qt.AlignTop,
'https://donkirkby.github.io')
print(pdf.newPage())
painter.drawText(painter.window().width()//2,
painter.window().height()//2,
'Bar')
print(pdf.newPage())
painter.end()
def print_document(pdf: QPdfWriter):
html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"
document = QTextDocument()
document.setHtml(html)
document.print_(pdf)
main()
理想情况下,我希望在同一页上显示文本和绘图,但此代码试图将它们分开,以免它们相互覆盖。这两种方式都行不通。
如何将绘图与文本文档结合起来? QTextCursor
有帮助吗?
问题是每次设置 QPainter 时都会重置 QPdfWriter。一个可能的解决方案是使用相同的 QPainter 而不是打印方法,您应该使用 drawContents,您还必须手动处理分页。
from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
from PySide6.QtWidgets import QApplication
def main():
app = QApplication()
pdf = QPdfWriter("example.pdf")
pdf.setPageSize(QPageSize.Letter)
painter = QPainter(pdf)
draw_diagram(painter, pdf)
print_document(painter, pdf)
painter.end()
def draw_diagram(painter: QPainter, pdf: QPdfWriter):
painter.drawArc(
painter.window().width() // 4,
painter.window().height() // 2 - painter.window().width() // 4,
painter.window().width() // 2,
painter.window().width() // 2,
0,
5760,
)
painter.drawText(
0,
painter.window().height() // 2,
painter.window().width(),
painter.window().height() // 10,
Qt.AlignHCenter | Qt.AlignTop,
"https://donkirkby.github.io",
)
print(pdf.newPage())
painter.drawText(
painter.window().width() // 2, painter.window().height() // 2, "Bar"
)
print(pdf.newPage())
def print_document(painter: QPainter, pdf: QPdfWriter):
document = QTextDocument()
document.documentLayout().setPaintDevice(pdf)
html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"
document.setHtml(html)
document.drawContents(painter)
if __name__ == "__main__":
main()
虽然我最后用了 eyllanesc's answer of switching from document.print_()
to document.drawContents()
, I also experimented with QTextCursor
and QPyTextObject
. This TextObject example 介绍的很好。我想,如果我需要分布在多个页面上,它会很有用。
这是我从问题转换为使用 QPyTextObject
:
的示例
from PySide6.QtCore import QSizeF, QRectF
from PySide6.QtGui import (QPdfWriter, QPainter, QPageSize, QTextDocument, Qt, QPyTextObject, QTextFormat, QTextCursor,
QTextCharFormat)
from PySide6.QtWidgets import QApplication
DIAGRAM_TEXT_FORMAT = QTextFormat.UserObject + 1
DIAGRAM_DATA = 1
OBJECT_REPLACEMENT = chr(0xfffc)
def main():
app = QApplication()
pdf = QPdfWriter('example.pdf')
pdf.setPageSize(QPageSize.Letter)
print_document(pdf)
app.exit(0)
class Diagram(QPyTextObject):
def __init__(self, parent=None):
super().__init__(parent)
# noinspection PyPep8Naming,PyShadowingBuiltins
def intrinsicSize(self,
doc: QTextDocument,
posInDocument: int,
format: QTextFormat) -> QSizeF:
diameter = doc.textWidth()/4
return QSizeF(doc.textWidth(), diameter)
# noinspection PyPep8Naming,PyShadowingBuiltins
def drawObject(self,
painter: QPainter,
rect: QRectF,
doc: QTextDocument,
posInDocument: int,
format: QTextFormat):
diameter = rect.height()
message = format.property(DIAGRAM_DATA)
painter.drawArc((rect.width() - diameter) // 2,
rect.y(),
diameter,
diameter,
0,
5760)
painter.drawText(rect,
Qt.AlignHCenter | Qt.AlignVCenter,
message)
def print_document(pdf: QPdfWriter):
html = """\
<h1>Title</h1>
<p>Lorem ipsum
<a href='https://donkirkby.github.io'>donkirkby.github.io</a>
dolores sit amet.</p>
"""
document = QTextDocument()
document.setPageSize(QSizeF(pdf.width(), pdf.height()))
font = document.defaultFont()
font.setPixelSize(pdf.height()//60)
document.setDefaultFont(font)
diagram_handler = Diagram()
doc_layout = document.documentLayout()
doc_layout.registerHandler(DIAGRAM_TEXT_FORMAT, diagram_handler)
document.setHtml(html)
cursor = QTextCursor(document)
cursor.movePosition(cursor.End)
cursor.insertText('\n')
diagram_format = QTextCharFormat()
diagram_format.setObjectType(DIAGRAM_TEXT_FORMAT)
for i in range(1, 11):
cursor.insertHtml(f'<h3>Heading {i}</h3>')
cursor.insertText('\n')
diagram_format.setProperty(DIAGRAM_DATA, f'Message {i} in a circle')
cursor.insertText(OBJECT_REPLACEMENT, diagram_format)
document.print_(pdf)
main()
我仍然不喜欢这种方法的一点是可以在标题和图表之间添加分页符。我会问一个跟进的问题。
在另一个问题中,我了解到 QTextDocument
,我 QPainter
和 QTextDocument
。但是,当我尝试这样做时,他们各自重新启动了文档,抹去了对方的内容。
from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
from PySide6.QtWidgets import QApplication
def main():
app = QApplication()
pdf = QPdfWriter('example.pdf')
pdf.setPageSize(QPageSize.Letter)
# Whichever of these goes second, overwrites the first.
draw_diagram(pdf)
print_document(pdf)
app.exit(0)
def draw_diagram(pdf: QPdfWriter):
painter = QPainter(pdf)
painter.drawArc(painter.window().width()//4,
painter.window().height()//2 - painter.window().width()//4,
painter.window().width()//2,
painter.window().width()//2,
0,
5760)
painter.drawText(0,
painter.window().height()//2,
painter.window().width(),
painter.window().height()//10,
Qt.AlignHCenter | Qt.AlignTop,
'https://donkirkby.github.io')
print(pdf.newPage())
painter.drawText(painter.window().width()//2,
painter.window().height()//2,
'Bar')
print(pdf.newPage())
painter.end()
def print_document(pdf: QPdfWriter):
html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"
document = QTextDocument()
document.setHtml(html)
document.print_(pdf)
main()
理想情况下,我希望在同一页上显示文本和绘图,但此代码试图将它们分开,以免它们相互覆盖。这两种方式都行不通。
如何将绘图与文本文档结合起来? QTextCursor
有帮助吗?
问题是每次设置 QPainter 时都会重置 QPdfWriter。一个可能的解决方案是使用相同的 QPainter 而不是打印方法,您应该使用 drawContents,您还必须手动处理分页。
from PySide6.QtGui import QPdfWriter, QPainter, QPageSize, QTextDocument, Qt
from PySide6.QtWidgets import QApplication
def main():
app = QApplication()
pdf = QPdfWriter("example.pdf")
pdf.setPageSize(QPageSize.Letter)
painter = QPainter(pdf)
draw_diagram(painter, pdf)
print_document(painter, pdf)
painter.end()
def draw_diagram(painter: QPainter, pdf: QPdfWriter):
painter.drawArc(
painter.window().width() // 4,
painter.window().height() // 2 - painter.window().width() // 4,
painter.window().width() // 2,
painter.window().width() // 2,
0,
5760,
)
painter.drawText(
0,
painter.window().height() // 2,
painter.window().width(),
painter.window().height() // 10,
Qt.AlignHCenter | Qt.AlignTop,
"https://donkirkby.github.io",
)
print(pdf.newPage())
painter.drawText(
painter.window().width() // 2, painter.window().height() // 2, "Bar"
)
print(pdf.newPage())
def print_document(painter: QPainter, pdf: QPdfWriter):
document = QTextDocument()
document.documentLayout().setPaintDevice(pdf)
html = "<a href='https://donkirkby.github.io'>donkirkby.github.io</a>"
document.setHtml(html)
document.drawContents(painter)
if __name__ == "__main__":
main()
虽然我最后用了 eyllanesc's answer of switching from document.print_()
to document.drawContents()
, I also experimented with QTextCursor
and QPyTextObject
. This TextObject example 介绍的很好。我想,如果我需要分布在多个页面上,它会很有用。
这是我从问题转换为使用 QPyTextObject
:
from PySide6.QtCore import QSizeF, QRectF
from PySide6.QtGui import (QPdfWriter, QPainter, QPageSize, QTextDocument, Qt, QPyTextObject, QTextFormat, QTextCursor,
QTextCharFormat)
from PySide6.QtWidgets import QApplication
DIAGRAM_TEXT_FORMAT = QTextFormat.UserObject + 1
DIAGRAM_DATA = 1
OBJECT_REPLACEMENT = chr(0xfffc)
def main():
app = QApplication()
pdf = QPdfWriter('example.pdf')
pdf.setPageSize(QPageSize.Letter)
print_document(pdf)
app.exit(0)
class Diagram(QPyTextObject):
def __init__(self, parent=None):
super().__init__(parent)
# noinspection PyPep8Naming,PyShadowingBuiltins
def intrinsicSize(self,
doc: QTextDocument,
posInDocument: int,
format: QTextFormat) -> QSizeF:
diameter = doc.textWidth()/4
return QSizeF(doc.textWidth(), diameter)
# noinspection PyPep8Naming,PyShadowingBuiltins
def drawObject(self,
painter: QPainter,
rect: QRectF,
doc: QTextDocument,
posInDocument: int,
format: QTextFormat):
diameter = rect.height()
message = format.property(DIAGRAM_DATA)
painter.drawArc((rect.width() - diameter) // 2,
rect.y(),
diameter,
diameter,
0,
5760)
painter.drawText(rect,
Qt.AlignHCenter | Qt.AlignVCenter,
message)
def print_document(pdf: QPdfWriter):
html = """\
<h1>Title</h1>
<p>Lorem ipsum
<a href='https://donkirkby.github.io'>donkirkby.github.io</a>
dolores sit amet.</p>
"""
document = QTextDocument()
document.setPageSize(QSizeF(pdf.width(), pdf.height()))
font = document.defaultFont()
font.setPixelSize(pdf.height()//60)
document.setDefaultFont(font)
diagram_handler = Diagram()
doc_layout = document.documentLayout()
doc_layout.registerHandler(DIAGRAM_TEXT_FORMAT, diagram_handler)
document.setHtml(html)
cursor = QTextCursor(document)
cursor.movePosition(cursor.End)
cursor.insertText('\n')
diagram_format = QTextCharFormat()
diagram_format.setObjectType(DIAGRAM_TEXT_FORMAT)
for i in range(1, 11):
cursor.insertHtml(f'<h3>Heading {i}</h3>')
cursor.insertText('\n')
diagram_format.setProperty(DIAGRAM_DATA, f'Message {i} in a circle')
cursor.insertText(OBJECT_REPLACEMENT, diagram_format)
document.print_(pdf)
main()
我仍然不喜欢这种方法的一点是可以在标题和图表之间添加分页符。我会问一个跟进的问题。