Почему этот файл SVG пуст, когда генерируется QSvgGenerator из QGLWidget?

Я отображаю график данных с QwtPlot3D, используя Qt5. Графики отображаются в несколько раз подклассифицированном QGLWidget. Рассматриваемые графики выглядят примерно так:

Пример участка

Я использую следующий код для записи файлов, как указано в других источниках, путем рендеринга QGLWidget в QPainter, который принимает QSvgGenerator при вызове QPainter::begin, Аналогичный процесс работает для записи растровых изображений, но файлы SVG кажутся пустыми (но они пишут).

Обратите внимание, что переменная currentPlot относится к классу Plot, который наследует SurfacePlot, который наследует Plot3D, который наследует QGLWidget.

void PlotWindow::writeCurrentPlotToSVG(const QString& directory,
                                       const QString& filename)
{  
    QSvgGenerator generator;
    QString ext = ".svg";
    QString filepath = buildPath(directory, filename, ext);
    generator.setFileName(filepath);
    generator.setSize(currentPlot->size());
    generator.setViewBox(currentPlot->rect());
    generator.setTitle(currentPlot->title());
    generator.setDescription("description");

    QPainter painter;
    painter.begin(&generator);
    currentPlot->render(&painter);
    painter.end();
}

Сгенерированный файл выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="225.778mm" height="169.333mm"
 viewBox="0 0 640 480"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
<title>Plot_0</title>
<desc>description</desc>
<defs>
</defs>
<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >

</g>
</svg>

Просмотр файла в Internet Explorer и Mozilla Firefox ничего не отображает. Я проверил правильность отображаемого прямоугольника и значений размера, передаваемых в генератор. На данный момент, я не уверен, что еще делать.

Заранее спасибо за помощь.

1 ответ

SVG выводит векторную графику, OpenGL выводит растровые данные в кадровый буфер, поэтому напрямую получить масштабируемую векторную графику из QGLWidget не представляется возможным. Я ожидал бы, что QSvgGenerator также получит контент OpenGL (по крайней мере, как растровое изображение), но, похоже, он этого не делает...
У меня была похожая проблема с отображением закадрового QGLWidget. Мой обходной путь - сначала render() для всего виджета (у вас не будет содержимого OpenGL), затем используйте отдельный FBO для визуализации QGLWidget, а затем нарисуйте изображение, полученное из этого FBO, поверх первого рендера.
Что-то в этом роде:

//find your QGL-sub-widget
qglWidget = ...
//render parent widget into the svg
QPainter painter(&svgGenerator);
widget->render(&painter);
//create/resize FBO. make the OpenGL context current, so the FBO is created in the correct context
qglChild->makeCurrent();
QOpenGLFramebufferObjectFormat fboFormat;
fboFormat.setSamples(4);
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
fbo = new QOpenGLFramebufferObject(newWindowRect.size(), fboFormat);
//get the position and size of the QGLWidget within the widget
QRect newWindowRect = GetChildRectInParent(widget, qglChild);
//the position is reliably off by one pixel in x and y, so adjust it
newWindowRect.adjust(-1, -1, -1, -1);
//make the OpenGL context current, so the FBO is created in the correct context
qglChild->makeCurrent();
//bind FBO for rendering to it
fbo->bind();
//Painting to an FBO using OpenGL content in Qt is only possible with a QOpenGLPaintDevice
QOpenGLPaintDevice fboPaintDev(newWindowRect.size());
QPainter fboPainter(&fboPaintDev);
fboPainter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing);
//now render the QGraphicsView
qglChild->render(&fboPainter);
fboPainter.end();
fbo->release();
//grab image from FBO and paint it at the correct position in the pixmap we grabbed from the parent widget
QPixmap openGLPixmap = QPixmap::fromImage(fbo->toImage());
painter.drawPixmap(newWindowRect, openGLPixmap);
painter.end();
delete fbo;
Другие вопросы по тегам