C++ 在预处理的svg xml文档上设置QGraphicsSvgItem的渲染器非常慢
我使用的是C++ 在预处理的svg xml文档上设置QGraphicsSvgItem的渲染器非常慢,c++,xml,performance,qt,svg,C++,Xml,Performance,Qt,Svg,我使用的是QGraphicsSvgItem子类,它从文件中读取一些内容,将内容放入QDomDocument,进行一些初始处理,然后将处理过的DOM设置到渲染器上 在程序处理期间,需要对预处理DOM的副本进行额外更改,因此DOM存储在类中。更改后,DOM将放置在渲染器上 class MyGraphicsSvgItem : public QGraphicsSvgItem { public: MyGraphicsSvgItem (QGraphicsItem *parent = 0):
QGraphicsSvgItem
子类,它从文件中读取一些内容,将内容放入QDomDocument
,进行一些初始处理,然后将处理过的DOM设置到渲染器上
在程序处理期间,需要对预处理DOM的副本进行额外更改,因此DOM存储在类中。更改后,DOM将放置在渲染器上
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;
};
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data));
在将DOM分配给渲染器的过程中,处理速度似乎非常慢
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;
};
QByteArray _data = _svgXML.toByteArray();
setSharedRenderer(new QSvgRenderer(_data));
如果我跳过DOM处理—如果我将渲染器设置为文件—代码将大大加快:
保留所有代码,但替换
setSharedRenderer(new QSvgRenderer(_data)); // VERY SLOW
与
因此,瓶颈似乎是从QByteArray
加载svg渲染器
我在寻找替代品。。。文档中没有提到性能
qsvgrender::qsvgrender(常量QString&filename,QObject*parent
=0)使用给定的父级构造新的渲染器,并使用指定的文件名加载SVG文件的内容 qsvgrender::qsvgrender(const QByteArray&contents,QObject* 父项=0)
使用给定的父对象和负载构造新的渲染器 内容指定的字节数组中的SVG数据 QSVGrender::QSVGrender(QXmlStreamReader*内容,QObject* 父项=0)
使用给定的父对象和负载构造新的渲染器 SVG数据使用内容指定的流读取器 往里看,我发现它的构造函数是相似的!此外,它说 在某些情况下,它也可能是一种更快、更方便的替代方法,可用于使用DOM树的应用程序中 我似乎在兜圈子,尽管DOM中已经有了格式良好的xml,但似乎我无法利用它 除了从预处理的DOM加载渲染器,或者使用渲染器可以快速读取的DOM以外的其他方式预处理xml,我还有什么选择
qt 4.8。c++您可以使用
QtConcurrent::run
在线程队列上执行的辅助方法中执行所有DOM处理
您可以在项目中直接使用QSVGrender
。在worker方法中初始化它,并从QByteArray
加载,而不是从文件加载。然后,您可以将渲染器传递给GUI线程,并通过在QGraphicsSvgItem
上设置它来使用它渲染图形项目
注意事项:
moveToThread
只能从对象的当前线程调用,如果thread()==0,则可以从任何线程调用
重新绘制所需
信号连接到其更新
方法。解决此问题的方法是手动将信号连接到您自己的更新插槽
#include <QGraphicsView>
#include <QGraphicsSvgItem>
#include <QGraphicsSceneMouseEvent>
#include <QFileDialog>
#include <QSvgRenderer>
#include <QDomDocument>
#include <QtConcurrentRun>
#include <QFutureWatcher>
#include <QThread>
#include <QApplication>
struct Thread : public QThread { using QThread::sleep; }; // Needed for Qt 4 only
class RendererGenerator {
QString m_fileName;
void process(QDomDocument &) {
Thread::sleep(3); /* let's pretend we process the DOM for a long time here */
}
QByteArray generate(const QByteArray & data) {
QDomDocument dom;
dom.setContent(data);
process(dom);
return dom.toByteArray();
}
public:
typedef QSvgRenderer * result_type;
RendererGenerator(const QString & fileName) : m_fileName(fileName) {}
QSvgRenderer * operator()() {
QFile file(m_fileName);
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
QScopedPointer<QSvgRenderer> renderer(new QSvgRenderer);
renderer->load(generate(data));
renderer->moveToThread(0);
return renderer.take();
}
return 0;
}
};
class UserSvgItem : public QGraphicsSvgItem {
Q_OBJECT
QSvgRenderer m_spinRenderer, * m_lastRenderer;
QScopedPointer<QSvgRenderer> m_renderer;
QFuture<QSvgRenderer*> m_future;
QFutureWatcher<QSvgRenderer*> m_watcher;
QGraphicsView * aView() const {
QList<QGraphicsView*> views = scene()->views();
return views.isEmpty() ? 0 : views.first();
}
Q_SLOT void update() { QGraphicsSvgItem::update(); }
void mousePressEvent(QGraphicsSceneMouseEvent * event) {
if (event->button() == Qt::LeftButton) askForFile();
}
void setRenderer(QSvgRenderer * renderer) {
if (m_lastRenderer) disconnect(m_lastRenderer, SIGNAL(repaintNeeded()), this, SLOT(update()));
setSharedRenderer(renderer);
m_lastRenderer = renderer;
connect(renderer, SIGNAL(repaintNeeded()), SLOT(update()));
if (aView()) aView()->centerOn(this);
}
void askForFile() {
QFileDialog * dialog = new QFileDialog(aView());
connect(dialog, SIGNAL(fileSelected(QString)), SLOT(loadFile(QString)));
dialog->setAcceptMode(QFileDialog::AcceptOpen);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
}
Q_SLOT void loadFile(const QString & file) {
if (m_future.isRunning()) return;
setRenderer(&m_spinRenderer);
m_future = QtConcurrent::run(RendererGenerator(file));
m_watcher.setFuture(m_future);
}
Q_SLOT void rendererReady() {
m_renderer.reset(m_future.result());
m_renderer->moveToThread(thread());
setRenderer(m_renderer.data());
}
public:
UserSvgItem(const QString & fileName = QString(), QGraphicsItem *parent = 0) :
QGraphicsSvgItem(fileName, parent), m_lastRenderer(0) {
connect(&m_watcher, SIGNAL(finished()), SLOT(rendererReady()));
setFlags(QGraphicsItem::ItemClipsToShape);
setCacheMode(QGraphicsItem::NoCache);
}
void setWaitAnimation(const QByteArray & data) { m_spinRenderer.load(data); }
};
namespace {
const char svgCircle[] =
"<svg height=\"100\" width=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" /></svg>";
const char svgRectangle[] =
"<svg width=\"400\" height=\"110\"><rect width=\"300\" height=\"100\" style=\"fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)\"></svg>";
const char svgThrobber[] =
"<svg width=\"16\" height=\"16\" viewBox=\"0 0 300 300\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"><path d=\"M 150,0 a 150,150 0 0,1 106.066,256.066 l -35.355,-35.355 a -100,-100 0 0,0 -70.711,-170.711 z\" fill=\"#3d7fe6\"><animateTransform attributeName=\"transform\" attributeType=\"XML\" type=\"rotate\" from=\"0 150 150\" to=\"360 150 150\" begin=\"0s\" dur=\"1s\" fill=\"freeze\" repeatCount=\"indefinite\" /></path></svg>";
void write(const char * str, const QString & fileName) {
QFile out(fileName);
if (out.open(QIODevice::WriteOnly | QIODevice::Truncate)) out.write(str);
}
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
write(svgRectangle, "rectangle.svg"); // Put svg resources into the working directory
write(svgCircle, "circle.svg");
QGraphicsScene scene;
UserSvgItem item("circle.svg");
QGraphicsView view(&scene);
scene.addItem(&item);
item.setWaitAnimation(QByteArray::fromRawData(svgThrobber, sizeof(svgThrobber)-1));
view.show();
return app.exec();
}
#include "main.moc"
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
结构线程:公共QThread{使用QThread::sleep;};//仅Qt 4需要
类渲染器生成器{
QString m_文件名;
作废流程(QDomDocument&){
线程::sleep(3);/*让我们假设在这里处理DOM很长时间*/
}
QByteArray生成(常量QByteArray和数据){
qdomdom文档;
setContent(数据);
进程(dom);
返回dom.toByteArray();
}
公众:
typedef QSVGrender*结果类型;
RenderGenerator(const QString&fileName):m_fileName(fileName){}
QSVGrender*运算符()(){
QFile文件(m_文件名);
if(file.open(QIODevice::ReadOnly)){
QByteArray data=file.readAll();
QScopedPointer渲染器(新QSVGrender);
渲染器->加载(生成(数据));
渲染器->移动到线程(0);
返回renderer.take();
}
返回0;
}
};
类UserSvgItem:publicqgraphicssvgitem{
Q_对象
QSvgRenderer m_spinRenderer,*m_lastRenderer;
QScopedPointer m_渲染器;
未来,未来;
QFutureWatcher m_watcher;
QGraphicsView*aView()常量{
QList views=场景()->views();
返回views.isEmpty()?0:views.first();
}
Q_插槽无效更新(){QGraphicsSvgItem::update();}
无效鼠标压力事件(QGraphicsSceneMouseEvent*事件){
如果(event->button()==Qt::LeftButton)askForFile();
}
void setRenderer(QSvgRenderer*渲染器){
如果(m_lastRenderer)断开(m_lastRenderer,SIGNAL(repainneeded()),则此插槽(update());
设置共享渲染器(渲染器);
m_lastrender=渲染器;
连接(渲染器、信号(需要重新绘制())、插槽(更新());
如果(aView())aView()->centerOn(此);
}
void askForFile(){
QFileDialog*dialog=新建QFileDialog(aView());
连接(对话框、信号(fileSelected(QString))、插槽(loadFile(QString));
对话框->设置接受模式(QFileDialog::AcceptOpen);
对话框->设置属性(Qt::WA_DeleteOnClose);
对话框->显示();
}
Q_插槽无效加载文件(常量Q字符串和文件){
if(m_future.isRunning())返回;
setRenderer(&m_spinRenderer);
m_future=QtConcurrent::run(rendergenerato