Graph Printing

Printing a 2D or 3D Graph to a PDF.

The Graph Printing example demonstrates how to print or export to PDF 2D and 3D graphs.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, see Qt Creator: Tutorial: Build and run.

GraphPrinter class

The printing functionality is implemented in the GraphPrinter class. The class exposes these functions:

  • The generatePDF function, which works as follows.
    • Sets up the output PDF file.

      The function instantiates QPdfWriter with a "graph.pdf" file pointed in the specified folder. The function also specifies the options for the exported PDF file: title, resolution, page size, and margins.

      const QFile file = QFile(path.toLocalFile() + QStringLiteral("/graph.pdf"));
      
      QPdfWriter writer(file.fileName());
      writer.setResolution(90);
      writer.setTitle("Graph");
      writer.setPageSize(QPageSize(image.size()));
      writer.setPageMargins(QMarginsF(0, 0, 0, 0));
      writer.newPage();
    • Sets up image processing.

      The function creates a QPainter referring to the previously created QPdfWriter.

      To ensure the graph is printed correctly, it is scaled to the painter's viewport size with the original aspect ratio.

      The painter's rendering hint is set to lossless image rendering. After this, the function draws the image to the PDF file.

      QPainter painter(&writer);
      const QImage finalImage = image.scaled(painter.viewport().size(), Qt::KeepAspectRatio);
      painter.setRenderHint(QPainter::LosslessImageRendering);
      painter.drawImage(finalImage.rect(), finalImage);
  • The print function, which works like the generatePDF function, but creates a QPainter referring a QPrinter instance:
    void GraphPrinter::print(const QImage &image, const QString printerName)
    {
        QPrinterInfo printInfo = QPrinterInfo::printerInfo(printerName);
        if (printInfo.isNull()) {
            qWarning("%ls is not a valid printer", qUtf16Printable(printerName));
            return;
        }
    
        QPrinter printer(printInfo, QPrinter::HighResolution);
        printer.setOutputFormat(QPrinter::NativeFormat);
    
        QPainter painter(&printer);
        const QImage finalImage = image.scaled(painter.viewport().size(), Qt::KeepAspectRatio);
        painter.setRenderHint(QPainter::LosslessImageRendering);
        painter.drawImage(finalImage.rect(), finalImage);
    
        qInfo("printed image with %ls", qUtf16Printable(printerName));
    }
  • The getPrinters function returns a list of available printers.
    QStringList GraphPrinter::getPrinters()
    {
        return QPrinterInfo::availablePrinterNames();
    }

Application setup

In addition to the application setup code, the main.cpp file contains code that creates a new instance of the GraphPrinter class and makes it reachable from the QML code.

GraphPrinter graphPrinter;
viewer.rootContext()->setContextProperty("graphPrinter", &graphPrinter);

Setting up the layout and image capture

The 2D and 3D graphs are laid out in a Stacklayout. Users can navigate it with a TabBar.

TabBar {
    id: tabBar
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right

    TabButton {
        text: "2D Graph"
        implicitHeight: 50
    }

    TabButton {
        text: "3D Graph"
        implicitHeight: 50
    }
}

StackLayout {
    id: stackLayout
    anchors.top: tabBar.bottom
    anchors.bottom: parent.bottom
    width: parent.width
    currentIndex: tabBar.currentIndex

    Graph2D {}

    Graph3D {}
}

The FolderDialog component is used to select a folder for saving an exported file. This component has no visual representation in the application layout, but its API is accessible from the current QML file.

The Button invokes a folder dialog.

FolderDialog {
    id: dialog
    onAccepted: console.log("Saving to " + currentFolder)
}
    ...
Button {
    id: setFolderButton
    onClicked: dialog.open()
    text: "Set save location"
    Layout.margins: 5
}

A custom printing dialog is created for selecting a printer. The Dialog retrieves the list of available printers and displays them in a list view.

Dialog {
    id: printerDialog
    x: parent.width * 0.5 - width * 0.5
    y: parent.height * 0.5;
    contentHeight: 200
    contentWidth: 200

    title: qsTr("Available printers")
    modal: true

    property var item: stackLayout.itemAt(stackLayout.currentIndex)

    onOpened: {
        printerModel.clear()
        var printers = graphPrinter.getPrinters()
        printers.forEach((x,i) =>
                         printerModel.append({"name": x}))
    }
    ...
contentItem: Item {
    id: printerItem
    height: 200
    width: parent.width
    ListView {
        id: printerListView
        height: 200
        width: 200
        clip: true

        model: printerModel
        delegate: printerDelegate
        highlight: Rectangle {color: "darkgrey"}
    }
}

The Save to PDF button and Print button in the printing dialog run the following code:

  • Capture an image using the grabToImage method. The current graph is the Stacklayout's item at the current index.
  • In the grabToImage parameters, we specify the callback as the generatePDF or print function in the GraphPrinter class.

    For the size, the code makes the image render at a 7282 by 4096 resolution. For 3D graphs, the item must also be expanded for the duration of printing.

Button {
    id: captureButton
    text: "Save to PDF"
    Layout.margins: 5
    property var item: stackLayout.itemAt(stackLayout.currentIndex)

    onPressed: {
        if (stackLayout.currentIndex === 1) {
            item.width = 7282
            item.height = 4096
        }
        item.grabToImage(function(result) {
            graphPrinter.generatePDF(dialog.currentFolder, result.image)
        }, Qt.size(7282, 4096))
    }

    onReleased: {
        if (stackLayout.currentIndex === 1) {
            item.width = mainView.width
            item.height = mainView.height
        }
    }
}
    ...
onAccepted: {
    var selectedPrinter = printerModel.get(printerListView.currentIndex)
    if (stackLayout.currentIndex === 1) {
        item.width = 7282
        item.height = 4096
    }
    item.grabToImage(function(result) {
        graphPrinter.print(result.image, selectedPrinter.name)
    }, Qt.size(7282, 4096))
}
onClosed: {
    if (stackLayout.currentIndex === 1) {
        item.width = mainView.width
        item.height = mainView.height
    }
}

Example project @ code.qt.io

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.