【问题标题】:How to set painter of printer correctly?如何正确设置打印机的painter?
【发布时间】:2020-11-19 04:38:37
【问题描述】:

我正在打印一组表格,每个表格都应该有自己的页面并且可能很长。基础工作正常,但我没有画出页脚。问题是页脚将被绘制在一个额外的文档中。

根据文档,我必须将画家设置为设备。该设备是画家,这是正确的,但我如何将画家设置为正确的块?还是这样做是不对的?

目标是使用此文档两次。第一次尝试是打印,第二次是QTextDocument,我可以在其中找到QTextTable,并与其他文档元素一起编译。

工作示例

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtPrintSupport import *

content = [['section 1', [1,2,3,4]],['section2', [5,6,7,8]]]

app = QApplication(sys.argv)

document = QTextDocument ()
printer = QPrinter()
painter = QPainter(printer)
pageRect = printer.pageRect ()

tableFormat = QTextTableFormat ()
cellBlockFormat = QTextBlockFormat ()
cellCharFormat = QTextCharFormat ()
cellCharFormat.setFont (QFont ("Arial", 10))

for rownr, line in enumerate(content):
    cursor = QTextCursor (document)
    mainFrame = cursor.currentFrame ()
    # header
    cursor.setPosition (mainFrame.firstPosition ())
    cursor.insertHtml ("This is the table for  %s"%line[0])

    # table
    table = cursor.insertTable (3, 4, tableFormat)
    for colnr, col in enumerate(line[1]):
        print("col:", col)
        cellCursor = table.cellAt (rownr + 1, colnr).firstCursorPosition ()
        cellCursor.setBlockFormat (cellBlockFormat)
        cellCursor.insertText (str (col))

    #footer
    painter.begin(printer)
    painter.drawText (0, pageRect.bottom(), "I may be the footer")
    painter.end()
    # section finished
    cursor.setPosition (mainFrame.lastPosition ())
    tableFormat.setPageBreakPolicy (QTextFormat.PageBreak_AlwaysAfter)
    cursor.insertBlock (cellBlockFormat, cellCharFormat)
document.print_(printer)

【问题讨论】:

  • 你能澄清最后一段吗?我的意思是“我可以在其中获取 QTextTable 并与另一个文档元素一起编译它的部分。”。考虑到在打印时更改文档结构不是一个好主意。
  • 我从QTextDocument is a container for structured rich text documents 的文档中了解到,我可以通过A QTextDocument can be edited programmatically using a QTextCursor, and its contents can be examined by traversing the document structure. 访问document element,如rich text structure 中所述。所以,我的想法是构建诸如首页、序言、目录、列表、报告表之类的元素,并将它们存储在QTextBlock, QTextFrame, QTextTable, and QTextList 类中,这些类可以使用insertBlock() 插入
  • 我理解错了吗?基本上我正在努力混合绘制元素和文本块。
  • 您必须明白,“绘画”始终是相对结构化格式的绝对和最终结果。如果你想在打印的时候添加元素,你不能依赖自动的print方法,因为它已经绘制了整个文档,所以你需要手动实现绘制,通过扩展drawContents所做的。
  • 感谢您的澄清,这意味着我必须在列表中创建和收集块(文本块、框架、表格......),例如计算它的文本长度,除以 pageRect 长度,然后我可以绘制页脚,然后绘制页面,如其他问题所示。听起来很复杂。特别是对于内容列表,如果其结果超过 1 页。老实说,我无法相信这一点,因为这或多或少是一种标准行为,应该以更简单的方式得到支持。在我看来。

标签: python-3.x pyqt5 qpainter qprinter qtextdocument


【解决方案1】:

前提:这与其说是一种解决方案,不如说是一种破解,因为它是一种肮脏的解决方法。

这个想法是继承 QPrinter,覆盖 newPage 方法并相应地绘制页脚。这需要使用页脚手动更新printer 实例。

不幸的是,还有另一个重要问题:只要只有一页,我就无法打印页脚。

在接下来的几天里,我会再次尝试研究它,看看是否有解决方案。

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtPrintSupport import *


class FooterPrinter(QPrinter):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.footers = {}

    def paintEngine(self, *args):
        engine = super().paintEngine(*args)
        self.currentPage = 0
        return engine

    def drawFooter(self):
        text = self.footers.get(self.currentPage)
        if not text:
            return
        painter = super().paintEngine().painter()
        dpiy = painter.device().logicalDpiY()
        margin = dpiy * (2/2.54)
        rect = QRectF(margin, margin, self.width() - margin * 2, self.height() - margin * 2)
        fm = QFontMetrics(painter.font(), painter.device())
        size = fm.size(0, text)
        painter.drawText(rect.left(), rect.bottom() - size.height(), 
            size.width(), size.height(), Qt.AlignLeft|Qt.AlignTop, text)

    def newPage(self):
        self.drawFooter()
        newPage = super().newPage()
        if newPage:
            self.currentPage += 1
            self.drawFooter()
        return newPage

content = [['section 1', [1,2,3,4]],['section2', [5,6,7,8]]]

app = QApplication(sys.argv)

document = QTextDocument()
printer = FooterPrinter()
printer.setOutputFileName('/tmp/test.pdf')
pageRect = printer.pageRect ()

tableFormat = QTextTableFormat ()
cellBlockFormat = QTextBlockFormat ()
cellCharFormat = QTextCharFormat ()
cellCharFormat.setFont (QFont ("Arial", 10))

for rownr, line in enumerate(content):
    cursor = QTextCursor (document)
    mainFrame = cursor.currentFrame ()
    # header
    cursor.setPosition (mainFrame.firstPosition ())
    cursor.insertHtml ("This is the table for  %s"%line[0])

    # table
    table = cursor.insertTable (3, 4, tableFormat)
    for colnr, col in enumerate(line[1]):
        cellCursor = table.cellAt (rownr + 1, colnr).firstCursorPosition ()
        cellCursor.setBlockFormat (cellBlockFormat)
        cellCursor.insertText (str (col))
    cursor.setPosition (mainFrame.lastPosition ())
    tableFormat.setPageBreakPolicy (QTextFormat.PageBreak_AlwaysAfter)
    cursor.insertBlock (cellBlockFormat, cellCharFormat)

    printer.footers[rownr] = 'Note for page {}: some text.\nNew line\nAnother new line'.format(rownr + 1)

document.print_(printer)

【讨论】:

  • 子类化 Qprinter 的好主意。关于单页,也许另一个包含一个空页面并在之后删除它的黑客会有所帮助。后天不能做,尽快继续。但我不认为这是一个肮脏的黑客,更像是一个天才的解决方案。
  • 不幸的是,您无法在之后删除页面(如果不使用外部工具),因为一旦print_ 返回文件已经写入并关闭(这也意味着您不能调用@987654326 @ 然后)。此外,print_ 所做的几乎所有事情都是私有的,我很“幸运”,因为我意识到 paintEnginenewPage 被显式调用。
  • 我的解决方法是在超过 1 页时决定使用上述类,并仅对 1 页使用第一个解决方案。不完美,但它有效。
猜你喜欢
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
相关资源
最近更新 更多