I am using QGraphicsSvgItem
subclass, that reads some content from a file, places content into a QDomDocument
, does some initial processing, then sets the processed DOM onto a renderer.
During program processing, additional changes are required on a copy of pre-processed DOM, so the DOM is stored in class. After changes, the DOM is placed on renderer.
class MyGraphicsSvgItem : public QGraphicsSvgItem
{
public:
MyGraphicsSvgItem (QGraphicsItem *parent = 0):
QGraphicsSvgItem(parent),
_svgXML() {}
~MyGraphicsSvgItem () { delete renderer(); }
void CheckAndChangeSomeThings() {}
void LoadStuff (QString fileName)
{
QFile file(fileName);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
QString svgContent = in.readAll();
file.close();
_svgXML.setContent(svgContent);
CheckAndChangeSomeThings(); // this modifies _svgXML
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data)); // very slow
}
void ChangeThingslater();
void ChangeSomeThingslater()
{
ChangeThingslater(); // this modifies _svgXML
renderer()->load(_svgXML.toByteArray()); // very slow - no file involved
}
protected:
QDomDocument _svgXML;
};
There seems to be a significant slow processing during the lines that assign the DOM to the renderer.
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data));
If I skip the DOM processing - if I set the renderer to the file - the code speeds up considerably:
Leaving all the code in, but replacing
setSharedRenderer(new QSvgRenderer(_data)); // VERY SLOW
with
setSharedRenderer(new QSvgRenderer(fileName)); // FAST
So it seems the bottleneck is loading the svg renderer from QByteArray
.
I looked for alternatives... there is no mention of performance in documentation
QSvgRenderer::QSvgRenderer(const QString & filename, QObject * parent = 0)
Constructs a new renderer with the given parent and loads the contents of the SVG file with the specified filename.QSvgRenderer::QSvgRenderer(const QByteArray & contents, QObject * parent = 0)
Constructs a new renderer with the given parent and loads the SVG data from the byte array specified by contents.QSvgRenderer::QSvgRenderer(QXmlStreamReader * contents, QObject * parent = 0)
Constructs a new renderer with the given parent and loads the SVG data using the stream reader specified by contents.
Looking in QXmlStreamReader Class, I find that its constructors are similar ! In addition, it says
In some cases it might also be a faster and more convenient alternative for use in applications that would otherwise use a DOM tree
I seem to be going in circles, and even though having already a well formed xml in the DOM, it seems I cannot take advantage of it !
What are my alternatives, to either loading the renderer from the pre-processed DOM, or to a different way of pre-processing the xml - using something other than DOM that the renderer can read fast ?
qt 4.8. c++
You can do all the DOM processing in a worker method that's executed on the thread queue using
QtConcurrent::run
.You can use the
QSvgRenderer
directly in your item. Initialize it in the worker method, and load fromQByteArray
and not a file. You can then pass the renderer to the GUI thread and use it to render the graphics item by setting it on theQGraphicsSvgItem
.Caveats:
Since you create the renderer in a worker thread, you must move it to a null thread after you've done using it in the worker thread. Conversely, you must move it to the GUI thread once it has been received by the GUI thread.
Recall that
moveToThread
can only be called from the object's current thread, or any thread ifthread() == 0
.In Qt 4.8, there's a bug in
QGraphicsSvgItem::setSharedRenderer
: it doesn't properly connect the renderer'srepaintNeeded
signal to itsupdate
method. The work around this to connect the signal manually to your own update slot.This will prevent the GUI from getting blocked by the long processing.
Reentering the event loop, as you do, from within the item is a source of bugs and just a very bad idea from the design standpoint. Use the non-blocking API of the file dialog instead.
Below is an example that demonstrates this technique. It also displays a small spinner when the item is being loaded/processed. There's a simulated delay for this purpose.