Embedding plots in a QTextDocument

Report generation often requires inserting plots and charts inside a text document. This tutorial demonstrates how QCustomPlot can interact with QTextDocument, to achieve this easily.

The example project accompanying this tutorial is called text-document-integration and is part of the full package download.

QCPDocumentObject

The interface between QCustomPlot and QTextDocument is QCPDocumentObject. Note that this class is not in the standard qcustomplot.cpp/.h files but is defined in qcpdocumentobject.cpp/.h in the example project of this tutorial.

It serves two purposes:

  • Generation of a text char format from a QCustomPlot. This allows inserting plots into the QTextDocument, e.g. at the position of the cursor.
  • Rendering of the static plot inside the QTextDocument, whenever it is redrawn or exported.

In case you are wondering what a text char format is good for: Inserting a QChar::ObjectReplacementCharacter with a custom format is the way Qt allows insertion of custom objects into a text document.

let's assume our QCustomPlot is ui->plot, and our QTextEdit with the text document is ui->textEdit. The first step is to register the QCPDocumentObject as a handler for plot objects in the text document:

  // register the plot document object (only needed once, no matter how many plots will be in the QTextDocument):
  QCPDocumentObject *plotObjectHandler = new QCPDocumentObject(this);
  ui->textEdit->document()->documentLayout()->registerHandler(QCPDocumentObject::PlotTextFormat, plotObjectHandler);

After this call, we can start inserting plots into the text document. This is what the static method QCPDocumentObject::generatePlotFormat(QCustomPlot *plot, int width, int height) is good for. It takes a vectorized snapshot of the plot with the given width and height (if left to be 0, the current width and height of the plot is used) and attaches it to a QTextCharFormat. The returned QTextCharFormat can subsequently be used to format a QChar::ObjectReplacementCharacter, which then appears as the plot object. The insertion of a plot at the current cursor position can thus be done as follows:

  QTextCursor cursor = ui->textEdit->textCursor();
  
  // insert the current plot at the cursor position. QCPDocumentObject::generatePlotFormat creates a
  // vectorized snapshot of the passed plot (with the specified width and height) which gets inserted
  // into the text document.
  double width = ui->cbUseCurrentSize->isChecked() ? 0 : ui->sbWidth->value();
  double height = ui->cbUseCurrentSize->isChecked() ? 0 : ui->sbHeight->value();
  cursor.insertText(QString(QChar::ObjectReplacementCharacter), QCPDocumentObject::generatePlotFormat(ui->plot, width, height));
  
  ui->textEdit->setTextCursor(cursor);

The components cbUseCurrentSize, sbWidth and sbHeight are part of the user interface of the example project. As stated, the plot object inside the text document maintains its vectorized nature. So exporting it to PDF (or other formats capable of vectorized content) results in a maximum quality output. Saving the above document to a PDF file and opening it in a PDF viewer results in the following display

As can be seen, zooming in to the inserted plot reveals smooth lines.