QCustomPlot Discussion and Comments

Printing a PlotReturn to overview

I've read everything I can find and somehow I still don't get it. I can see SavePdf and so on but I just can't see how that gets used for actually printing. I'm getting really frustrated and probably now can't see the wood for the trees. Can anyone rescue me?

QCustomPlot allows you to draw itself onto anything that supports a QPainter, see the method QCustomPlot::toPainter

From there it's all standard Qt stuff about printing (i.e. open a QPainter on a QPrinter, set all the printer parameters, etc.)

I thought I'd have a go with this code below. I get the dialog but it's blank. ('plot' is just ui->customplot)

void MainWindow::on_actionPreview_triggered()
{
    // display print preview dialog
    QPrinter             myprinter( QPrinter::HighResolution );
    QPrintPreviewDialog  mypreview( &myprinter, this );
    connect( &mypreview, SIGNAL(paintRequested(QPrinter*)), this, SLOT(printfunction(QPrinter*)) );
    mypreview.exec();
}

void MainWindow::printfunction(QPrinter *aprinter)
{
    // this is the actual put it on the page bit
    // create a PAINTER do the job
    QCPPainter mypainter(aprinter);

    // use the painter to create the page
    int      w = aprinter->pageRect().width();
    int      h = aprinter->pageRect().height();
//    mypainter.begin(aprinter);
    plot->toPainter(&mypainter, w, h);
    mypainter.end();
}

I am an idiot! I had not put 'printfunction' in the slots section of the header file. I should have noticed the Application Output flashing at me earlier. Qt is amazing and CustomPlot is brilliant but you have to keep your wits about you. I need more wits.

Having succeeded in getting a plot on the preview using the code below I now find a new problem.

void MainWindow::printfunction(QPrinter *aprinter)
{
    // this is the actual put it on the page bit
    // create a PAINTER do the job
    aprinter->setPageSize(QPrinter::A4);
    QCPPainter mypainter(aprinter);

    QRectF rf;
    QRect r;

    // use the painter to create the page
    rf = aprinter->pageRect(QPrinter::DevicePixel);
    int      w = rf.width();
    int      h = rf.height();

    r = plot->viewport();
    h = w * r.height()/r.width();

    plot->toPainter(&mypainter, w, h);
    mypainter.end();
}

No matter what I do the page seems to display only the area within the axes rectangle plus a thin margin. The axes numbers and labels are clipped off. Also, where the legend is supposed to be there is a tiny box with something in it.

I have tried adding, subtracting and generally fiddling with all viewports and sizes but nothing seems to cure this.

Two issues are mixing here:
First, I think you're mixing up a bit the concept of viewport size of the plot and page size. If you want the relative sizes of plot elements (texts, legend, pen widths, etc.) to be reproduced on the output medium, you need to keep the passed plot viewport width and height the same. However, you of course want to utilize the higher resolution of the output medium, so you need to introduce a scale factor, so the final size matches the size of the page in the output medium's coordinates. In the savePdf/savePng/... methods you do that with the "scale" parameter. If you do the drawing yourself, you simply call painter.scale(...).

The second issue is Qt's HighResolution printer. It messes things up, especially font sizes, and that's well known unfortunately. The good thing is that you can also print without it specified and get good results. Otherwise you'll have to fiddle with font sizes (beware of the difference between specifying them in points and pixels).

Here's the header of my project:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qcustomplot.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT
  
public:
  explicit MainWindow(QWidget *parent = 0);
  ~MainWindow();
  
private:
  Ui::MainWindow *ui;
  QCustomPlot *mPlot;
  
private slots:
  void printPreview();
  void renderPlot(QPrinter *printer);
};

#endif // MAINWINDOW_H

and here's the cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPrintDialog>

MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow),
  mPlot(new QCustomPlot(this))
{
  ui->setupUi(this);
  
  setCentralWidget(mPlot);
  
  // just setting up a plot, nothing special happening here:
  mPlot->axisRect()->setupFullAxesBox(true);
  mPlot->addGraph()->addData(QVector<double>() << 1 << 2 << 3 << 4, QVector<double>() << 1 << 2 << 4 << 8);
  mPlot->rescaleAxes();
  mPlot->legend->setVisible(true);
  mPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop); // align legend to top left corner
  
  ui->menuBar->addAction("Print Preview", this, SLOT(printPreview()));
}

MainWindow::~MainWindow()
{
  delete ui;
}

void MainWindow::printPreview()
{
  QPrinter printer;
  QPrintPreviewDialog previewDialog(&printer, this);
  connect(&previewDialog, SIGNAL(paintRequested(QPrinter*)), SLOT(renderPlot(QPrinter*)));
  previewDialog.exec();
}

void MainWindow::renderPlot(QPrinter *printer)
{
  printer->setPageSize(QPrinter::A4);
  QCPPainter painter(printer);
  QRectF pageRect = printer->pageRect(QPrinter::DevicePixel);
  
  int plotWidth = mPlot->viewport().width();
  int plotHeight = mPlot->viewport().height();
  double scale = pageRect.width()/(double)plotWidth;
  
  painter.setMode(QCPPainter::pmVectorized);
  painter.setMode(QCPPainter::pmNoCaching);
  painter.setMode(QCPPainter::pmNonCosmetic); // comment this out if you want cosmetic thin lines (always 1 pixel thick independent of pdf zoom level)
  
  painter.scale(scale, scale);
  mPlot->toPainter(&painter, plotWidth, plotHeight);
}

This is the application view, the print preview, and the pdf in two zoom stages:
printing-appview.png
printing-printpreview.png
printing-pdf.png
printing-pdfzoom.png

Let me know if this is any help... I think I'll turn this into a use case tutorial some time. At the moment I'm focusing on pushing QCP2.0 forward though.

This man is a hero. He is kind to idiots. He deserves much praise. My only excuse is that I'm old and losing brain cells at an alarming rate.

DerManu, your code works great except when working on a hiDPI system in Windows, for example, where the screen resolution is set to more than 100%. At 200% display scale, the labels are all cut in half in the printer output. Any hints on how to avoid this problem with printing on hiDPI displays?