Search code examples
c++qtopenglqchart

Can't properly save QChartView as image when using QLineSeries with openGL on


i am trying to create an application which can plot large data sets (so using of OpenGl is important to me). I use QChartView, QChart and QLineSeries. Also for QLineSeries i turn using openGL on. But when i'm trying to save chart as an image i get plot without data. I know that when QLineSeries using openGL it creates a QOpenGLWidget on top of the chart plot area, but i don't know ho to access it.

So a question is: how to save a chart as an image with drawn lines?

Some images:

What i want (Plot without using openGL):

Plot without using openGL

What i get (Plot with openGL):

Plot with openGL

Here is an example of code:

MainWindow constructor:

chartView = new QChartView(generate_sin_chart(), ui->centralWidget);
ui->centralWidget->layout()->addWidget(chartView);

generate_sin_chart():

QLineSeries *series = new QLineSeries();
series->setUseOpenGL(true); //this line cause a problem

constexpr double PI = 3.14159265359;
for(int i = 0; i < 100; ++i)
{
    double temp = i*PI/6;
    series->append(temp, sin(temp));
}

QChart *chart = new QChart();
chart->legend()->hide();
chart->addSeries(series);
chart->createDefaultAxes();
chart->setTitle("Simple line chart example");

return chart;

Function for saving:

QString filename = QFileDialog::getSaveFileName(this, tr("Save file"), "", tr("Images (*.png)"));
QPixmap p = chartView->grab();
p.save(filename, "PNG");

Solution

  • According to the docs:

    useOpenGL : bool

    Specifies whether or not drawing the series is accelerated by using OpenGL.

    Acceleration using OpenGL is supported only for QLineSeries and QScatterSeries. A line series used as an edge series for QAreaSeries cannot use OpenGL acceleration. When a chart contains any series that are drawn with OpenGL, a transparent QOpenGLWidget is created on top of the chart plot area. The accelerated series are not drawn on the underlying QGraphicsView, but are instead drawn on the created QOpenGLWidget.

    [...]

    So when you use grab() only take a picture of the QChartView, the solution is to find the QOpenGLWidget object and record its image on top of the QChartView image, the following code does it:

    QPixmap p = chartView->grab();
    QOpenGLWidget *glWidget  = chartView->findChild<QOpenGLWidget*>();
    if(glWidget){
        QPainter painter(&p);
        QPoint d = glWidget->mapToGlobal(QPoint())-chartView.mapToGlobal(QPoint());
        painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
        painter.drawImage(d, glWidget->grabFramebuffer());
        painter.end();
    }
    p.save("test", "PNG");
    

    As you should use QOpenGLWidget you must add QT += opengl to your .pro

    A complete example can be found in the following link