PDA

View Full Version : QDoubleSpinBox setValue() performance issue



Cruz
15th February 2009, 20:47
Hi there,

on my gui I show a 3D model of a skeleton and around the skeleton I have approx. 20 QDoubleSpinBoxes that show the angles of the joints of the skeleton. I animate the 3D model by setting new joint angles every 12 ms. It works great. I can see a smooth animation and my CPU still can breathe.

Now when I try to use the QDoubleSpinBoxes to display the actual numbers of the joint angles, my CPU almost dies and nothing moves. Here is what I do:




void transferAnglesToSpinboxes()
{
disconnect(this, SIGNAL(inputValueChanged()), this, SLOT(jointAnglesChangedBySpinbox()));

headY->setValue(jointAngles["headY"]);
headZ->setValue(jointAngles["headZ"]);
rShouX->setValue(jointAngles["rShouX"]);
rShouY->setValue(jointAngles["rShouY"]);
rShouZ->setValue(jointAngles["rShouZ"]);
rElbow->setValue(jointAngles["rElbow"]);
rHipX->setValue(jointAngles["rHipX"]);
rHipY->setValue(jointAngles["rHipY"]);
rHipZ->setValue(jointAngles["rHipZ"]);
rKnee->setValue(jointAngles["rKnee"]);
rAnkX->setValue(jointAngles["rAnkX"]);
rAnkY->setValue(jointAngles["rAnkY"]);
lShouX->setValue(jointAngles["lShouX"]);
lShouY->setValue(jointAngles["lShouY"]);
lShouZ->setValue(jointAngles["lShouZ"]);
lElbow->setValue(jointAngles["lElbow"]);
lHipX->setValue(jointAngles["lHipX"]);
lHipY->setValue(jointAngles["lHipY"]);
lHipZ->setValue(jointAngles["lHipZ"]);
lKnee->setValue(jointAngles["lKnee"]);
lAnkX->setValue(jointAngles["lAnkX"]);
lAnkY->setValue(jointAngles["lAnkY"]);

connect(this, SIGNAL(inputValueChanged()), this, SLOT(jointAnglesChangedBySpinbox()));
}



Those are lots of setValue() calls to different spin boxes. The valueChanged() signals of all those QDoubleSpinBoxes are connected to a single inputValueChanged() signal, that I disconnect prior to copying the jointAngles hash into the QDoubleSpinBoxes and then I reconnect the signal again. I do this to suppress the valueChanged() signals that I only want to deal with, when the spin boxes were changed manually and not programatically.

I isolated the update of the 3D model and the update of the spin boxes and I'm absolutely positive, that it's the setValue() calls that strain the CPU. The 3D model alone works fine, the spin boxes alone fry my machine.

I don't understand. The calculation of a new pose of the skeleton for a given set of joint angles is actually quite heavy math, but obviously well manageable in 12 ms. But 20 setValue() calls are too much? Why why why?

Lykurg
15th February 2009, 21:10
Hi,

that's really strange. For debug and more information please measure the time for the setValue()'s of all calls at one block. Is jointAngles a QHash? And by the way, why you are using signal and slots to communicate inside a class?


Lykurg

Cruz
15th February 2009, 21:48
I'm measuring it on Windows, so it's probably not much use. Tomorrow I can have a go at it on a Linux machine. Anyways, the result is still strange. Here is how I did the performance measuring:




void transferAnglesToSpinboxes()
{
disconnect(this, SIGNAL(inputValueChanged()), this, SLOT(jointAnglesChangedBySpinbox()));

QTime time;
time.start();

headY->setValue(jointAngles["headY"]);
headZ->setValue(jointAngles["headZ"]);
[...]
lAnkX->setValue(jointAngles["lAnkX"]);
lAnkY->setValue(jointAngles["lAnkY"]);

qDebug() << time.elapsed();

connect(this, SIGNAL(inputValueChanged()), this, SLOT(jointAnglesChangedBySpinbox()));
}



The result is something like:
0,16,15,16,16,42,15,16,0,16,16,15...say 16 ms.

In the meantime I implemented a quick hack to protect the spin boxes from being updated too often like this:



// An ugly quick hack to circumvent performance issues.
if (lastTime.msecsTo(QTime::currentTime()) > 100)
{
lastTime = QTime::currentTime();
transferAnglesToSpinboxes();
}


With this protection is on, the performance measure shows:
0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0...as if it were nothing.


Is jointAngles a QHash?

Yes.


Why you are using signal and slots to communicate inside a class?

I tried to explain in my first post, I guess it was too lame. Of course the spin boxes can be changed manually by the user and each of them emits an individual valueChanged() signal. This also happens when I set the value of the spin box programatically. But in the latter case I want to supress the valueChanged() signals. So what I did is I connected the valueChanged() signals of all the spin boxes to one inputValueChanged() signal of my class, which is then connected to a slot that handles the transfer of the spin box values into the jointAngles QHash. As you can see in my transferAnglesToSpinboxes() methid, this way I only need to disconnect one signal, when I change the spin boxes programatically. Otherwise I would have to disconnect and then reconnect 20 individual spin boxes.

Cruz
15th February 2009, 21:52
But now that you made me think twice about it, I could solve it with a flag instead of signals and slots.

talk2amulya
15th February 2009, 22:11
there is also a function blockSignals(bool) for QObject class..maybe it can be useful to u..u can use it instead of flags

Cruz
15th February 2009, 22:18
there is also a function blockSignals(bool) for QObject class..maybe it can be useful to u..u can use it instead of flags

Doesn't help because then I would have to block and unblock the signals of 20 spin boxes one by one.

Cruz
16th February 2009, 10:31
For completeness I ran the performance measure on Linux.

With the QTime guard I get:

4, 6, 4, 5, 6, 4, 4, 5, 4, 1, 5, 5, 5, 5, 6, 4, 4, 4, 5, 6, 4, 5, 4, 6, 4, 5, 5, 6, 9, 4, 5 ...ms for the update of 20 QDoubleSpinBoxes

and without the QTime guard:

3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 5, 4, 4, 3, 3, 4, 3, 3 ... even a bit faster.

wysota
16th February 2009, 22:31
Correct me if I'm wrong but if you change values to those spinboxes every 20ms, they need to be redrawn 50 times per second. 50 * 20 = 1000. That's how many times per second the paintEvent of the spinbox class is called.

I suggest you do something like this:

QVector<qreal> values(20, 0.0);
QTimer *updateTimer = new QTimer(this);
connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateBoxes()));
updateTimer->start(500); // 2Hz
//...
void XX::updateBoxes(){
for(int i=0;i<20;i++){
qreal val = values[i];
QDoubleSpinBox *sb = boxes[i];
sb->setValue(val);
}
}

That's more or less what you did in your first approach. Just make sure the "transferAxesToSpinboxes()" method is called 1-2 times a second at most. You won't notice more values of 20 boxes per second anyway.