karlphillip
karlphillip

Reputation: 93410

Qt Charts rendering problems on a PDF

I'm using Qt charts module to draw a pie chart directly on a PDF file.

Here's the problem:

(black borders were added to these images to improve visualization)

Displaying all charts on a window before they are rendered to the PDF is not an option. The fact that the chart needs to execute show() for QPainter to draw it to the PDF correctly seems to indicate that without it, QPainter ignores the chart's dimension.

On a side note, show() opens the window but it takes several seconds for the chart to appear, so rendering is very very slow, another reason for me not to want to display the charts.

So here are my main questions:

Here is a Minimal, Complete, and Verifiable example...

main.cpp:

#include <QApplication>
#include <QtCharts/QChartView>
#include <QtCharts/QPieSeries>
#include <QtCharts/QPieSlice>
#include <QPainter>
#include <QPdfWriter>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QtCharts::QChartView* chartView = new QtCharts::QChartView();
    chartView->setRenderHint(QPainter::Antialiasing);
    chartView->resize(640, 480);

    QtCharts::QChart* chart = chartView->chart();
    chart->setTitle("Beautiful Pie Chart");
    chart->legend()->hide();

    QtCharts::QPieSeries* series = new QtCharts::QPieSeries();
    float hits = 73.0f, misses = 27.0f;
    series->append("Hits", hits);
    series->append("Misses", misses);

    QtCharts::QPieSlice* hit_slice = series->slices().at(0);
    hit_slice->setBrush(QColor(87, 147, 243));  // blue

    QtCharts::QPieSlice* miss_slice = series->slices().at(1);
    miss_slice->setBrush(QColor(221, 68, 68)); // red

    chart->addSeries(series);

    // Due to Qt bug, must show() the chart before render()
    // or it will be draw too tiny in the PDF by QPainter
    chartView->show();

    QPdfWriter writer("out.pdf");
    writer.setCreator("https://stackoverflow.com/users/176769/karlphillip");
    writer.setPageSize(QPagedPaintDevice::A4);
    QPainter painter(&writer);

    chartView->render(&painter);

    painter.end();

    return a.exec();
}

QtCharts_PDF.pro:

QT       += core gui charts

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = QtCharts_PDF
TEMPLATE = app
SOURCES += main.cpp

Upvotes: 13

Views: 5878

Answers (3)

Jonathan Starr
Jonathan Starr

Reputation: 254

I find that grabbing the pixmap is not great because it may not look good or have good resolution. However, you can do this:

chartView->grab(); chartView->render (&painter, ... etc.

where chartView is a QChartView object. Then the painter properly renders the chart. It works as well as show(), but you don't have to have the chart displayed in the GUI. Apparently both grab() and show() cause the chart to be rendered internally and somehow work around the Qt bug.

Upvotes: 2

the_mandrill
the_mandrill

Reputation: 30842

I think this could be a scaling issue as I encountered a similar problem with my output being printed much smaller than expected. QPdfWriter has a logical unit of a 'dot' where the default resolution is 1200 dots per inch. You need to decide how to map between the size of the QChartView and its printed appearance. The QPdfWriter will map a pixel to a dot by default. You want to set a scaling of 1200/pixelsPerInch

For sample code see my other answer here: QTextDocument, QPdfWriter - how to scale output

Upvotes: 2

karlphillip
karlphillip

Reputation: 93410

One way to bypass this problem is to create a QPixmap from the QChartView and draw the pixmap into the PDF:

    QPixmap pix = chartView->grab();
    int h = painter.window().height()*0.4;
    int w = h * 1.3;
    int x = (painter.window().width() / 2) - (w/2);
    int y = (painter.window().height() / 2) - (h/2);
    chartView->resize(w, h);
    painter.drawPixmap(x, y, w, h, pix);

This is kinda like taking a screenshot of the widget and rendering it to the file.

QPainter.drawPixmap() let's you specify the size and location of the drawing in the PDF. It's not ideal, but it will do for now. I know, it's a hack, it works, but I'm still looking for a better solution.

Upvotes: 8

Related Questions