Skillversitas

Printing

Qt's printing is the QPrinter (the printer's description - the paper, the resolution, the page rect) plus the QPainter (the draw on the printer's surface - the same QPainter the paintEvent chapter used, the target the printer instead of the widget). The QPrintDialog is the platform's print dialog (the destination, the copies, the range), and the QPrintPreviewWidget is the preview (the same QPainter draw, the target the preview's surface). The model is one: the QPrinter is the target, the QPainter is the draw, and the draw_page function is the page's content - the same function renders the print and the preview, the QPrinter switching the target. This chapter covers the QPrinter (the setPageSize, the pageRect), the QPrintDialog (the exec_ modal), the draw_page pattern (the QPainter on the QPrinter, the page loop), and the QPrintPreviewWidget (the preview's paintRequest).

The QPrinter: The Printer's Description

from PyQt5.QtPrintSupport import (QPrinter, QPrintDialog,
                                  QPrintPreviewWidget)
from PyQt5.QtGui import QPageLayout, QPageSize
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)

# the PRINTER: the description object (the paper, the
# resolution, the printable area)
printer = QPrinter(QPrinter.HighResolution)
# the mode: the HighResolution (the print's quality),
# the ScreenResolution (the preview's, the lighter)
printer.setPageSize(QPageSize(QPageSize.A4))
printer.setPageOrientation(QPageLayout.Portrait)

# the pageRect: the PRINTABLE area (the paper minus the
# margins) - the draw's bound, the content lives inside
# it, never on the paper's full extent
rect = printer.pageRect()   # the QRectF, in the
                            # printer's units

# the system's printers: the name + the list
print(printer.printerName())
print(QPrinter.printerList())

The QPrinter is the printer's description: the mode argument (QPrinter.HighResolution for the print, the ScreenResolution for the preview), the setPageSize (the QPageSize.A4, the Letter), the setPageOrientation (the Portrait, the Landscape), and the pageRect (the printable area - the paper minus the margins, the QRectF the draw is bound to). The printerName is the system's default printer, and the QPrinter.printerList is the available ones. The QPrinter is the target the QPainter draws on, and the pageRect is the draw's bound (the content inside the pageRect adjusted by an app margin, not the paper's full extent). The QPrinter is the app's description of the job, and the QPrintDialog (the next section) is the user's choice of the destination and the copies - the two together are the print's setup.

The draw_page Pattern: The Painter on the Printer

from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
from PyQt5.QtGui import QPainter, QPageSize
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)

# the DRAW: the function (the page's content; the
# QPainter is on the target - the printer or the preview)
def draw_page(painter, printer, page_num):
    rect = printer.pageRect()     # the printable area
    margin = 50                   # the app's margin (pt)
    painter.drawText(
        rect.adjusted(margin, margin, -margin, -margin),
        Qt.AlignLeft | Qt.AlignTop,
        "page %d - the drawn content" % (page_num + 1))
    # the draw is the page's content: the app's text,
    # the shapes, the table - the SAME function for the
    # print and the preview (the next section)

# the PRINT: the QPrintDialog (the user's choice), the
# exec_ (the modal), the Accepted (the go)
def do_print(window):
    printer = QPrinter(QPrinter.HighResolution)
    printer.setPageSize(QPageSize(QPageSize.A4))
    dlg = QPrintDialog(printer, window)
    if dlg.exec_() != QPrintDialog.Accepted:
        return
    painter = QPainter()
    painter.begin(printer)        # the painter ON the
                                  # printer (the target)
    pages = 3
    for i in range(pages):
        draw_page(painter, printer, i)
        if i < pages - 1:
            painter.newPage()     # the next page
    painter.end()

The draw_page pattern is the print's core: the function (draw_page(painter, printer, page_num)) takes the QPainter (the target's surface) and the pageRect (the printable bound), and draws the page's content - the QPainter is the same one the paintEvent chapter used, the target the printer instead of the widget. The QPrintDialog is the user's choice: the QPrintDialog(printer, window) plus the exec_ (the modal, the Accepted the go), and the QPainter begins on the QPrinter (the painter.begin(printer)), the page loop runs (draw_page per page, the painter.newPage() between), and the painter.end() finishes. The pageRect is the draw's bound (the content inside the pageRect adjusted by the app's margin), and the newPage is the page's advance. The pattern is one: the QPrinter the target, the QPainter the draw, the draw_page the content, the QPrintDialog the user's choice - the four that make the print complete.

The QPrintPreviewWidget: The Preview

from PyQt5.QtPrintSupport import QPrintPreviewWidget
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
app = QApplication(sys.argv)
window = QMainWindow()

# the PREVIEW: the widget that renders the draw_page on
# the preview's surface (the paintRequest signal fires
# per page; the draw_page is the render)
preview = QPrintPreviewWidget(window)
# the preview's QPrinter is the ScreenResolution (the
# preview's quality, not the print's HighResolution);
# the draw_page is the SAME function (the QPainter the
# preview's, the pageRect the preview's) - the preview
# and the print share the draw, the QPrinter switches
window.setCentralWidget(preview)
window.show()
sys.exit(app.exec_())

The QPrintPreviewWidget is the preview: it renders the draw_page on the preview's surface (the paintRequest signal fires per page, the draw_page is the render), and its QPrinter is the ScreenResolution (the preview's quality, not the print's). The QPrintPreviewWidget is a QWidget (the central widget, the dialog's content), and the draw_page is the same function (the QPainter the preview's, the pageRect the preview's) - the preview and the print share the draw_page, and the QPrinter is the switch (the print's HighResolution, the preview's ScreenResolution). The QPrintPreviewDialog is the dialog form (the exec_ modal, the print button triggers the QPrinter print), and the QPrintPreviewWidget is the embedded form (the central widget, the always-visible). The preview is the draw_page on the preview's QPainter, and the print is the same draw_page on the print's QPrinter - one function, two targets, the QPrinter the switch.

The Working Rules

  • The QPrinter is the target, the QPainter is the draw, the draw_page is the content. The QPrinter describes the job (the paper, the resolution, the pageRect), the QPainter begins on it (painter.begin(printer)), and the draw_page is the page's content - the same function for the print and the preview, the QPrinter the switch.
  • The pageRect is the draw's bound, the margin is the app's. The content is the pageRect adjusted by the app's margin (the rect.adjusted(m, m, -m, -m)), never the paper's full extent - the pageRect is the printable area, and the margin keeps the content off the edge.
  • The QPrintDialog is the user's choice, the exec_ is the modal. The QPrintDialog(printer, window) plus the exec_ (the Accepted the go) - the destination, the copies, and the range are the user's, and the QPrinter carries the user's choices into the painter.begin.
  • The newPage is the page's advance, the loop is the document. The for over the pages (the draw_page per page, the painter.newPage() between, the painter.end() after) - the document is the loop, and the draw_page is the per-page render.
  • The preview shares the draw_page, the QPrinter is the ScreenResolution. The QPrintPreviewWidget (the embedded) or the QPrintPreviewDialog (the modal) renders the same draw_page on the preview's surface (the ScreenResolution) - the preview is the print's proof, and the one draw_page is the consistency the two share.

Summary

Qt's printing is the QPrinter (the target - the HighResolution the print's, the ScreenResolution the preview's, the setPageSize the paper, the pageRect the printable bound) plus the QPainter (the draw - the painter.begin(printer) on the target, the draw_page the page's content, the newPage the advance, the end the finish). The QPrintDialog is the user's choice (the exec_ modal, the Accepted the go, the destination and the copies the user's), and the QPrintPreviewWidget (the embedded) or the QPrintPreviewDialog (the modal) is the preview - the same draw_page on the preview's surface, the QPrinter the switch between the print and the preview. The model is one: the QPrinter the target, the QPainter the draw, the draw_page the content, and the pageRect adjusted by the app's margin the bound - the same QPainter the paintEvent chapter used, the target the printer, and the preview the print's proof.