Custom look svg GUI widgets in QT, very bad performance
So I'm trying to make a gui for a guitar effects program. One goal is to allow others to design svg "skins" to customize the look. I made widgets that inherited each widget (i.e. qdial) and in the initializer load the svg file specified by the skin into a qGraphicsSvgItem. This is put into a scene and a view and I overloaded the resize and repaint appropriately. This works.
When I load several of these custom svg widgets (5 dials, a button, and an led) into a parent widget the cpu rails and my program freezes. Am I going about this entirely the wrong way? Should I even be using QT? It seemed easier than trying to do everything with a stylesheet (especially I couldn't figure out how to change the dial appearance). Would it improve things to leave the widgets as qGraphicsSvgItems and put them all into the parent widget's scene and view?
This is meant to be a real time signal processing program so I don't want to burn up a lot of cpu in the GUI. I tried to do some research on it but couldn't find a lot for svg. I also don't see any performance checker in qtCreator or I'd try to see what the bottleneck is. Anyway I'd really appreciate any advice you could offer before I spend more time trying to do something the wrong way.
Thanks so much! _ssj71
p.s. Here is some of the code (hopefully enough) an svg widget:
#ifndef QSVGDIAL_H
#define QSVGDIAL_H
#include <QWidget>
#include <QDial>
#include <QtSvg/QSvgRenderer>
#include <QtSvg/QGraphicsSvgItem>
#include <QGraphicsView>
#include <QGraphicsScene>
class qSVGDial : public QDial
{
Q_OBJECT
public:
explicit qSVGDial(QWidget *parent = 0);
explicit qSVGDial(QString knobFile = "defaultKnob.svg", QString needleFile = "defaultNeedle.svg", QWidget *parent = 0);
~qSVGDial();
private:
void paintEvent(QPaintEvent *pe);
void resizeEvent(QResizeEvent *re);
float degPerPos;
float middle;
float mysize;
QGraphicsView view;
QGraphicsScene scene;
QGraphicsSvgItem *knob;
QGraphicsSvgItem *needle;
QSize k,n;
};
#endif // QSVGDIAL_H
the cpp:
#include "qsvgdial.h"
#include "math.h"
qSVGDial::qSVGDial(QWidget *parent) :
QDial(parent)
{
knob = new QGraphicsSvgItem("defaultKnob.svg");
needle = new QGraphicsSvgItem("defaultNeedle.svg");
view.setStyleSheet("background: transparent; border: none");
k = knob->renderer()->defaultSize();
n = needle->renderer()->defaultSize();
needle->setTransformOriginPoint(n.width()/2,n.height()/2);
knob->setTransformOriginPoint(k.width()/2,k.height()/2);
degPerPos = 340/(this->maximum() - this->minimum());
middle = (this->maximum() - this->minimum())/2;
mysize = k.width();
if (mysize<n.width()) mysize = n.width();
if (mysize<k.height()) mysize = k.height();
if (mysize<n.height()) mysize = n.height();
mysize = sqrt(2)*mysize;
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(knob);
scene.addItem(needle);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
qSVGDial::qSVGDial(QString knobFile, QString needleFile, QWidget *parent) :
QDial(parent)
{
knob = new QGraphicsSvgItem(knobFile);
needle = new QGraphicsSvgItem(needleFile);
view.setStyleSheet("background: transparent; border: none");
k = knob->renderer()->defaultSize();
n = needle->renderer()->defaultSize();
needle->setTransformOriginPoint(n.width()/2,n.height()/2);
knob->setTransformOriginPoint(k.width()/2,k.height()/2);
if (k!=n)
needle->setPos((k.width()-n.width())/2,(k.height()-n.height())/2);
degPerPos = 340/(this->maximum() - this->minimum());
middle = (this->maximum() - this->minimum())/2;
mysize = k.width();
if (mysize<n.width()) mysize = n.width();
if (mysize<k.height()) mysize = k.height();
if (mysize<n.height()) mysize = n.height();
mysize = sqrt(2)*mysize;
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(knob);
scene.addItem(needle);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
qSVGDial::~qSVGDial()
{
//delete ui;
}
void qSVGDial::paintEvent(QPaintEvent *pe)
{
needle->setRotation((this->sliderPosition() - middle)*degPerPos);
}
void qSVGDial::resizeEvent(QResizeEvent *re)
{
if (this->width()>this->height())
{
view.setFixedSize(this->height(),this->height());
view.move((this->width()-this->height())/2,0);
knob->setScale(this->height()/mysize);
needle->setScale(this->height()/mysize);
view.centerOn(knob);
}
else
{
view.setFixedSize(this->width(),this->width());
view.move(0,(this->height()-this->width())/2);
knob->setScale(this->width()/mysize);
needle->setScale(this->width()/mysize);
view.centerOn(knob);
}
QDial::resizeEvent(re);
}
The parent header:
#ifndef PEDAL_H
#define PEDAL_H
#include <QtGui/QWidget>
#include <QString>
#include <QtSvg/QSvgRenderer>
#include <QtSvg/QGraphicsSvgItem>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <skin.h>
#include <qsvgdial.h>
#include <qsvgbutton.h>
#include <qsvgled.h>
#include <qsvgslider.h>
class Pedal : public QWidget
{
Q_OBJECT
public:
explicit Pedal(QWidget *parent = 0);
explicit Pedal(QString boxFile, QWidget *parent = 0);
~Pedal();
int LoadSkin(skin skinfiles);
QWidget* AddControl(QString type, QString param, int x, int y, int w, int h, QString file1, QString file2, QString file3, QString file4);
private:
void resizeEvent(QResizeEvent *re);
QRect PedalPosition();
float myheight;
float mywidth;
float scale;
int effectNumber;
QGraphicsView view;
QGraphicsScene scene;
QGraphicsSvgItem *box;
QSize p;
QWidget* controls[20];
QRect ctrlPos[20];
int numControls;
};
#endif // PEDAL_H
parent cpp
#include "pedal.h"
#include "math.h"
Pedal::Pedal(QWidget *parent)
: QWidget(parent)
{
numControls = 0;
box = new QGraphicsSvgItem("stompbox.svg");
view.setStyleSheet("background: transparent; border: none");
p = box->renderer()->defaultSize();
box->setTransformOriginPoint(p.width()/2,p.height()/2);
myheight = p.height();
mywidth = p.width();
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(box);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
Pedal::Pedal(QString boxFile, QWidget *parent) :
QWidget(parent)
{
numControls = 0;
box = new QGraphicsSvgItem(boxFile);
view.setStyleSheet("background: transparent; border: none");
p = box->renderer()->defaultSize();
box->setTransformOriginPoint(p.width()/2,p.height()/2);
myheight = p.height();
mywidth = p.width();
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(box);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
Pedal::~Pedal()
{
}
void Pedal::resizeEvent(QResizeEvent *re)
{
view.setFixedSize(this->width(),this->height());
//view.move((this->width()-this->height())/2,(this->width()-this->height())/2);
if (this->width()/mywidth>this->height()/myheight)
{
scale = this->height()/myheight;
}
else
{
scale = this->width()/mywidth;
}
box->setScale(scale);
view.centerOn(box);
//QWidget::resizeEvent(re);
QRect v = PedalPosition();
QRect cpos;
for(int i = 0; i<numControls; i++)
{
cpos = ctrlPos[i];
controls[i]->setGeometry(v.x()+cpos.x()*scale,v.y()+cpos.y()*scale,cpos.width()*scale,cpos.height()*scale);
}
}
QWidget* Pedal::AddControl(QString type, QString param, int x, int y, int w, int h, QString file1, Q开发者_C百科String file2, QString file3, QString file4)
{
QWidget* control;
if (type.toLower() == "dial")
{
if (!file2.isEmpty())
control = new qSVGDial(file1,file2,this);
else
control = new qSVGDial(this);
}
else if (type.toLower() == "button")
{
if (!file2.isEmpty())
control = new qSVGButton(file1,file2,this);
else if (!file1.isEmpty())
control = new qSVGButton(file1,this);
else
control = new qSVGButton(this);
}
else if (type.toLower() == "slider")
{
if (!file2.isEmpty())
control = new qSVGSlider(file1,file2,this);
else if (!file1.isEmpty())
control = new qSVGSlider(file1,this);
else
control = new qSVGSlider(this);
}
else if (type.toLower() == "led")
{
if (!file2.isEmpty())
control = new qSVGLED(file1,file2,this);
else
control = new qSVGLED(this);
}
control->setToolTip(param);
ctrlPos[numControls] = QRect(x,360-y-h,w,h);
controls[numControls] = control;
numControls++;
return control;
}
QRect Pedal::PedalPosition()
{
QRect mypos;
mypos.setWidth(mywidth*scale);
mypos.setHeight(myheight*scale);
mypos.setX((this->width()-mypos.width())/2);
mypos.setY((this->height()-mypos.height())/2);
return mypos;
}
finally the test main
#include <QtGui/QApplication>
#include "pedal.h"
#include <string.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Pedal w("skins/default/stompbox.svg",0);
w.AddControl("Dial" , "Level", 133, 295, 47, 47, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Button", "On", 20, 21, 182, 111, "skins/default/blackButton.svg","","","");
w.AddControl("LED", "On", 106, 328, 11, 11, "skins/default/redLEDOff.svg", "skins/default/redLEDOn.svg","","");
w.AddControl("Dial", "Gain", 44, 295, 47, 47, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "Low", 36, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "Mid", 98, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "High", 160, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.show();
return a.exec();
}
hopefully this helps. As you can see I have layers of QGraphicsViews each with one widget. I suspect now this might be the worst way to do it so I wonder if anyone has more experience before I move forward in a bad direction. Also after playing some more with it, the problem seems to occur when I have 2 instances of the qSVGDial. If I load other combinations of widgets it works alright. Thanks again everyone!
void qSVGDial::paintEvent(QPaintEvent *pe)
{
needle->setRotation((this->sliderPosition() - middle)*degPerPos);
}
This looks very suspicious to me. Doing anything that could result in a redraw from the paintEvent
method is dangerous, could lead to infinite update loops.
You should connect the dial's sliderMoved
signal to a slot in your class and rotate the needle in that slot, and remove the paintEvent
handler altogether. If that doesn't trigger an update, call update()
in that slot too, but I don't think this should be necessary.
(To check if this is the problem in the first place, try printing something to the console inside the paintEvent
handler. If it prints like crazy, that's your problem.)
精彩评论