<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - export</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/export.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2020-07-05T09:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>Exporting Widgets to PDF and Controlling Position in PyQt5 — How to render a QWidget to PDF and position it at the top of the page</title><link href="https://www.pythonguis.com/faq/exporting-widgets-and-setting-the-position-of-to-the-top-of-the-paper-in-pyqt5/" rel="alternate"/><published>2020-07-05T09:00:00+00:00</published><updated>2020-07-05T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2020-07-05:/faq/exporting-widgets-and-setting-the-position-of-to-the-top-of-the-paper-in-pyqt5/</id><summary type="html">When you're building a PyQt5 application that displays information &amp;mdash; student records, reports, invoices &amp;mdash; you'll often want to export that content to PDF. PyQt5 makes this possible by using &lt;code&gt;QPrinter&lt;/code&gt; and &lt;code&gt;QPainter&lt;/code&gt; to render any &lt;code&gt;QWidget&lt;/code&gt; directly onto a virtual "page." The tricky part is controlling &lt;em&gt;where&lt;/em&gt; the widget appears on that page.</summary><content type="html">
            &lt;p&gt;When you're building a PyQt5 application that displays information &amp;mdash; student records, reports, invoices &amp;mdash; you'll often want to export that content to PDF. PyQt5 makes this possible by using &lt;code&gt;QPrinter&lt;/code&gt; and &lt;code&gt;QPainter&lt;/code&gt; to render any &lt;code&gt;QWidget&lt;/code&gt; directly onto a virtual "page." The tricky part is controlling &lt;em&gt;where&lt;/em&gt; the widget appears on that page.&lt;/p&gt;
&lt;p&gt;In this tutorial, we'll walk through how to render a widget to PDF, understand how the coordinate translation system works, and adjust the position so your widget appears at the top of the page (or anywhere else you want).&lt;/p&gt;
&lt;h2 id="rendering-a-qwidget-to-pdf"&gt;Rendering a QWidget to PDF&lt;/h2&gt;
&lt;p&gt;The basic approach is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up a &lt;code&gt;QPrinter&lt;/code&gt; configured for PDF output.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;QPainter&lt;/code&gt; that paints onto that printer.&lt;/li&gt;
&lt;li&gt;Use coordinate transforms (translate, scale) to position and size the widget on the page.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;widget.render(painter)&lt;/code&gt; to paint the widget's contents.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here's a starting example that renders a widget &lt;strong&gt;centered&lt;/strong&gt; on the page:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtCore import QFileInfo


def print_widget(self, widget, filename):
    printer = QPrinter(QPrinter.HighResolution)
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setOutputFileName(filename)

    painter = QPainter(printer)

    # Calculate scale to fit the widget onto the page
    xscale = printer.pageRect().width() * 1.0 / widget.width()
    yscale = printer.pageRect().height() * 1.0 / widget.height()
    scale = min(xscale, yscale)

    # Move origin to the center of the paper
    painter.translate(printer.paperRect().center())
    painter.scale(scale, scale)
    # Shift back by half the widget size so it's centered
    painter.translate(-widget.width() / 2, -widget.height() / 2)

    widget.render(painter)
    painter.end()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This works well, and the widget ends up centered both horizontally and vertically on the page. But what if you want the widget at the &lt;strong&gt;top&lt;/strong&gt; of the page instead?&lt;/p&gt;
&lt;p&gt;To understand how to change the position, we need to look at what those &lt;code&gt;translate&lt;/code&gt; calls are actually doing.&lt;/p&gt;
&lt;h2 id="understanding-the-coordinate-transforms"&gt;Understanding the Coordinate Transforms&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;QPainter&lt;/code&gt; coordinate system starts with the origin (0, 0) at the top-left corner of the page. When you call &lt;code&gt;widget.render(painter)&lt;/code&gt;, the widget is drawn starting at whatever the current origin is. If you're new to QPainter and how its coordinate system works, our &lt;a href="https://www.pythonguis.com/tutorials/bitmap-graphics/"&gt;bitmap graphics tutorial&lt;/a&gt; covers these concepts in more detail.&lt;/p&gt;
&lt;p&gt;The centering code uses three steps:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;painter.translate(printer.paperRect().center())       # Step A
painter.scale(scale, scale)                            # Step B
painter.translate(-widget.width() / 2, -widget.height() / 2)  # Step C
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Step A&lt;/strong&gt; moves the origin to the exact center of the paper &amp;mdash; both horizontally and vertically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step B&lt;/strong&gt; applies a uniform scale so the widget fits on the page without being clipped.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step C&lt;/strong&gt; shifts the origin back by half the widget's width and half its height. Since the widget draws from the origin point, this adjustment means the &lt;em&gt;center&lt;/em&gt; of the widget lines up with the &lt;em&gt;center&lt;/em&gt; of the page.&lt;/p&gt;
&lt;p&gt;The result: a perfectly centered widget.&lt;/p&gt;
&lt;h2 id="moving-the-widget-to-the-top-of-the-page"&gt;Moving the Widget to the Top of the Page&lt;/h2&gt;
&lt;p&gt;To position the widget at the &lt;strong&gt;top&lt;/strong&gt; of the page (still centered horizontally), we only want to shift the origin horizontally to the center &amp;mdash; we don't want to move it down to the vertical center.&lt;/p&gt;
&lt;p&gt;Here's the modified version:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Move origin to the horizontal center only (no vertical shift)
xshift = printer.paperRect().width() / 2
painter.translate(xshift, 0)

painter.scale(scale, scale)

# Shift back horizontally by half the widget width (no vertical shift)
painter.translate(-widget.width() / 2, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That's it. By passing &lt;code&gt;0&lt;/code&gt; for the y-component in both &lt;code&gt;translate&lt;/code&gt; calls, the widget stays at the top of the page. The horizontal centering still works exactly as before.&lt;/p&gt;
&lt;h2 id="complete-working-example"&gt;Complete Working Example&lt;/h2&gt;
&lt;p&gt;Here's a full, runnable example that creates a simple widget with some labels and exports it to PDF, positioned at the top of the page. The example uses a &lt;code&gt;QMainWindow&lt;/code&gt; with a form layout &amp;mdash; if you need a refresher on how layouts work in PyQt5, see our &lt;a href="https://www.pythonguis.com/tutorials/pyqt-layouts/"&gt;PyQt5 layouts tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt5.QtCore import QFileInfo
from PyQt5.QtGui import QPainter, QFont
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtWidgets import (
    QApplication,
    QFileDialog,
    QFormLayout,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Export Widget to PDF")

        # Main layout
        central = QWidget()
        layout = QVBoxLayout(central)
        self.setCentralWidget(central)

        # The widget we want to export
        self.student_card = QWidget()
        self.student_card.setStyleSheet(
            "background-color: white; padding: 10px;"
        )
        form_layout = QFormLayout(self.student_card)
        form_layout.addRow("Name:", QLabel("Jane Doe"))
        form_layout.addRow("Student ID:", QLabel("2024-00142"))
        form_layout.addRow("Program:", QLabel("Computer Science"))
        form_layout.addRow("Year:", QLabel("3rd Year"))
        form_layout.addRow("Status:", QLabel("Active"))

        layout.addWidget(self.student_card)

        # Export button
        export_btn = QPushButton("Export to PDF")
        export_btn.clicked.connect(self.pdf_export)
        layout.addWidget(export_btn)

    def print_widget(self, widget, filename):
        """Render a widget to a PDF file, positioned at the top center."""
        printer = QPrinter(QPrinter.HighResolution)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(filename)

        painter = QPainter(printer)

        # Calculate scale factor to fit widget width on the page
        xscale = printer.pageRect().width() * 1.0 / widget.width()
        yscale = printer.pageRect().height() * 1.0 / widget.height()
        scale = min(xscale, yscale)

        # Center horizontally, keep at top vertically
        xshift = printer.paperRect().width() / 2
        painter.translate(xshift, 0)
        painter.scale(scale, scale)
        painter.translate(-widget.width() / 2, 0)

        widget.render(painter)
        painter.end()

    def pdf_export(self):
        fn, _ = QFileDialog.getSaveFileName(
            self, "Export PDF", None, "PDF files (*.pdf);;All Files(*)"
        )
        if fn:
            if QFileInfo(fn).suffix() == "":
                fn += ".pdf"
            self.print_widget(self.student_card, fn)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this, click &lt;strong&gt;Export to PDF&lt;/strong&gt;, and open the resulting file. The student card content will appear at the top of the page, centered horizontally.&lt;/p&gt;
&lt;h2 id="positioning-the-widget-anywhere"&gt;Positioning the Widget Anywhere&lt;/h2&gt;
&lt;p&gt;Now that you understand the pattern, you can position the widget wherever you like by adjusting the &lt;code&gt;translate&lt;/code&gt; values. Here are a few common positions:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top-left corner&lt;/strong&gt; (no translation at all):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;painter.scale(scale, scale)
# Widget draws from (0, 0) &amp;mdash; top-left
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Top-center&lt;/strong&gt; (what we used above):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;xshift = printer.paperRect().width() / 2
painter.translate(xshift, 0)
painter.scale(scale, scale)
painter.translate(-widget.width() / 2, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Center of the page&lt;/strong&gt; (the original code):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;painter.translate(printer.paperRect().center())
painter.scale(scale, scale)
painter.translate(-widget.width() / 2, -widget.height() / 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Custom position&lt;/strong&gt; &amp;mdash; for example, 100 pixels from the top and left, in printer coordinates:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;painter.translate(100, 100)
painter.scale(scale, scale)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The translate-scale-translate pattern gives you full control. The first translate sets where the widget's anchor point will be on the page, the scale adjusts the size, and the second translate offsets the widget relative to that anchor.&lt;/p&gt;
&lt;h2 id="common-pitfalls"&gt;Common Pitfalls&lt;/h2&gt;
&lt;p&gt;There are a couple of easy mistakes to watch out for when working with this code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;QPrinter&lt;/code&gt; instead of &lt;code&gt;QPainter&lt;/code&gt;.&lt;/strong&gt; When creating the painter, make sure you write &lt;code&gt;QPainter(printer)&lt;/code&gt; and not &lt;code&gt;QPrinter(printer)&lt;/code&gt;. They look very similar but are completely different classes. The printer defines the output device; the painter is what draws onto it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forgetting to call &lt;code&gt;painter.end()&lt;/code&gt;.&lt;/strong&gt; If you don't end the painter, the PDF file may not be written correctly &amp;mdash; or at all. Always call &lt;code&gt;painter.end()&lt;/code&gt; when you're done rendering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mixing up &lt;code&gt;pageRect()&lt;/code&gt; and &lt;code&gt;paperRect()&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;pageRect()&lt;/code&gt; is the printable area (inside the margins), while &lt;code&gt;paperRect()&lt;/code&gt; is the full physical page. For centering calculations, think about which one makes sense for your layout &amp;mdash; usually &lt;code&gt;paperRect()&lt;/code&gt; for centering on the physical page and &lt;code&gt;pageRect()&lt;/code&gt; for staying inside the margins.&lt;/p&gt;
&lt;p&gt;With this approach, you can export any widget in your PyQt5 application to a cleanly positioned PDF &amp;mdash; whether it's a student profile, a report, or any other visual layout you've built with widgets. If you want to distribute your finished application, take a look at our guide to &lt;a href="https://www.pythonguis.com/tutorials/packaging-pyqt5-pyside2-applications-windows-pyinstaller/"&gt;packaging PyQt5 applications with PyInstaller&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt5"/><category term="pdf"/><category term="qprinter"/><category term="qpainter"/><category term="widget"/><category term="export"/><category term="printing"/><category term="python"/><category term="qt"/><category term="qt5"/></entry></feed>