PDA

View Full Version : QT4 layout of complex dialog is very slow



cboles
16th March 2006, 04:22
I'm having trouble with QT4 taking several seconds to initially layout and display a dialog box I have created which has a hierarchy of widgets / layouts. I have attached pictures of a couple of tabs from this dialog for clarity here:

http://tactrix.com/help/wrxbase.gif
http://tactrix.com/help/child.gif

The structure is like this



QDialog
QVBoxLayout
QTabWidget
QWidget (there is more than one of these tabs, as shown in the pictures)
QVBoxLayout
QWidget
QVBoxLayout (the parts inside here vary)
QLabel
QHBoxLayout
QLabel
QComboBox
QLabel
QLineEdit
QHBoxLayout
QLabel
QLineEdit
QLabel
QComboBox
QHBoxLayout
QCheckBox
QCheckBox
QCheckBox
QGroupBox
QVBoxLayout
QHBoxLayout
QLabel
QComboBox
QGroupBox
QHBoxLayout
QTextEdit
QWidget (same structure as previous)
QWidget (same structure as previous)
QHBoxLayout
QPushButton
QPushButton
QPushButton


Obviously, this is a little complex, but it allows the underlying hierarchical objects to represent themselves simply and independently. The layout manager must be going though some sort of layout oscillations to take so long. Is this normal? Can anyone suggest a structural change that will avoid this? Would a heirarchy of QGridLayouts be more efficient? Or perhaps some layout constraints? How can I debug this layout problem in a manageable way with so many objects?

Thanks,

Colby

wysota
17th March 2006, 15:41
It's not a very complex layout. Could we see the code of the constructor? Or did you use Designer to make that dialog? Maybe you could modify the layout or defer construction of some of the items.

cboles
17th March 2006, 19:23
I don't use the Qt Designer - all of this layout is done in code. While investigating with this problem, I changed my code so that creation and presentation are broke into 3 steps

1. create all widgets (called once)
2. place all widgets in layout (called once)
3. update widget contents, show/hide widgets based on data changes (called initially + as needed for changes)

Actually, I have done some more investigation this morning with a profiler and have discovered some interesting facts. The layout is slow, but it is the creation of the widgets which is really taking most of the time. For example, doing just the following in a parent widget ctor takes 577 milliseconds!



lbInfoName = new QLabel;
lbInfo = new QLabel;
lbTableName = new QLabel;
lbType = new QLabel("Type");
cbType = new QComboBox;
ckFlipX = new QCheckBox("Flip X");
ckFlipY = new QCheckBox("Flip Y");
ckSwapXY = new QCheckBox("Swap XY");
lbName = new QLabel("Name");
leName = new QLineEdit;
lbAddress = new QLabel("Address");
leAddress = new QLineEdit;
lbCategory = new QLabel("Category");
cbCategory = new QComboBox;
lbElements = new QLabel("Elements");
leElements = new QLineEdit;
cbScaling = new QComboBox;
lbScaling = new QLabel("Scaling");
pbScaling = new QPushButton("New Scaling...");
teStaticValues = new QTextEdit;
teDescription = new QTextEdit;
gbStaticValues = new QGroupBox;
gbDescription = new QGroupBox;


There are 6 such parent widgets in my dialog, and along with about 1 second spent on layout and other Qt operations taking another second or more, the dialog takes closer to 6 seconds to display, which is an eternity. I looked more carefully at the time spent in Qt widget ctors, and there wasn't really one type of widget that was much slower than another. Most of the actual CPU cycles end up being spent in the function

QWidgetPrivate::create_sys

Which accounts for almost 3.5 seconds (!) of the time to show this dialog.

I have tried running Debug .vs. Release builds, Plastique .vs. standard styles, but nothing has an effect on the speed. My system is a XPSP2, 2.8GHz P4, 800MHz FSB, 1GB CL2 RAM, AGP8X nVidia GeForce4 MX440 running dual DVI displays. It seems like some core part of creating the widgets on the native OS is slow, perhaps in Windows itself. I just don't see things being slow anywhere else on my system, or even in other parts of my app (although I don't create so many widgets anywhere else).

Any ideas?

cboles
17th March 2006, 19:27
For completeness, the actual constructor looks like this:



EcuTableEditWidget::EcuTableEditWidget(EcuTable* _table,QWidget* parent)
: QWidget(parent)
{
table = _table;

lbInfoName = new QLabel;
lbInfo = new QLabel;
lbTableName = new QLabel;
lbType = new QLabel("Type");
cbType = new QComboBox;
ckFlipX = new QCheckBox("Flip X");
ckFlipY = new QCheckBox("Flip Y");
ckSwapXY = new QCheckBox("Swap XY");
lbName = new QLabel("Name");
leName = new QLineEdit;
lbAddress = new QLabel("Address");
leAddress = new QLineEdit;
addressValidator = new QIntValidatorEx(0,0x7FFFFFFF,16,this);
leAddress->setValidator(addressValidator);
lbCategory = new QLabel("Category");
cbCategory = new QComboBox;
lbElements = new QLabel("Elements");
leElements = new QLineEdit;
elementsValidator = new QIntValidatorEx(1,256,10,this);
leElements->setValidator(elementsValidator);
cbScaling = new QComboBox;
lbScaling = new QLabel("Scaling");
pbScaling = new QPushButton("New Scaling...");
teStaticValues = new QTextEdit;
teDescription = new QTextEdit;
gbStaticValues = new QGroupBox;
gbDescription = new QGroupBox;

layout();
updateLayout();

connect(ckFlipX,SIGNAL(stateChanged(int)),this,SLO T(changeFlipX(int)));
connect(ckFlipY,SIGNAL(stateChanged(int)),this,SLO T(changeFlipY(int)));
connect(ckSwapXY,SIGNAL(stateChanged(int)),this,SL OT(changeSwapXY(int)));
connect(leAddress,SIGNAL(textChanged(const QString&)),this,SLOT(changeAddress(const QString&)));
connect(leElements,SIGNAL(textChanged(const QString&)),this,SLOT(changeElements(const QString&)));
connect(leName,SIGNAL(textChanged(const QString&)),this,SLOT(changeName(const QString&)));
connect(cbCategory,SIGNAL(editTextChanged(const QString&)),this,SLOT(changeCategory(const QString&)));
connect(teDescription,SIGNAL(textChanged()),this,S LOT(changeDescription()));
connect(cbType,SIGNAL(activated(const QString&)),this,SLOT(changeType(const QString&)));
connect(cbScaling,SIGNAL(activated(const QString&)),this,SLOT(changeScaling(const QString&)));
}

wysota
17th March 2006, 23:38
It would be better if you placed each widget in its place right when you create it -- by setting appropriate parents. This way you'd avoid doubling some of the code which gets executed.

You could try to get the same layout using Designer and compare times you get to see if the difference is substancial. Designer generates optimal (or almost optimal) code, so if time differs noticable, it is probably because of your code.

cboles
18th March 2006, 00:25
It would be better if you placed each widget in its place right when you create it -- by setting appropriate parents. This way you'd avoid doubling some of the code which gets executed.

You could try to get the same layout using Designer and compare times you get to see if the difference is substancial. Designer generates optimal (or almost optimal) code, so if time differs noticable, it is probably because of your code.

Thanks for the advice. I will experiment with creating the widgets with parents, although I assumed this makes things slower as the parents will be switched when they are placed in the layout. The Trolltech Qt examples do things the same way I do. I still find it baffling how the creation of the widgets (no layout, not even being shown) is taking so long. You can see it right in the profiler - they are taking forever (in CPU cycles) to be created. Layout and display times are minor in comparison...

Colby

cboles
24th March 2006, 22:13
Just an update:

switching from QT 4.0.1 to QT 4.1.0 seems to have made a ~2x difference in the creation speed of these QWidgets. Obviously the problem must be something the Trolls know about...

Colby

cboles
6th April 2006, 18:34
I have solved this problem. The slow speed is due to creating widgets without parents before attaching them to layouts, e.g.



QLabel* label = new QLabel; // SLOW!!!

QLabel* label = new QLabel(this); // FAST!!


The speed difference is huge. Also if you do too many constructions without parents, Qt seems to bog down and freeze up due perhaps to some resource issue. The surprising thing here is that many Qt examples create widgets without parents for use in layouts. This is a definite no-no! I'm going back and fixing this everywhere in my code. Things are going much faster now.

dimitri
16th April 2006, 08:40
Also if you do too many constructions without parents, Qt seems to bog down and freeze up due perhaps to some resource issue.
I've never heard of a problem with constructing too many widgets without parents. On the other hand I know it's not possible to create more than 2000 or 3000 widgets (or less) on many Windows platforms for lack of GDI resources.

The surprising thing here is that many Qt examples create widgets without parents for use in layouts. This is a definite no-no!
Which examples? They will be fixed if that's indeed the case.

cboles
17th April 2006, 01:35
Which examples? They will be fixed if that's indeed the case.

Virtually all of them. Take this one for example:

\Qt\4.1.0\examples\dialogs\standarddialogs\dialog. cpp



Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
errorMessageDialog = new QErrorMessage(this);

int frameStyle = QFrame::Sunken | QFrame::Panel;

integerLabel = new QLabel;
integerLabel->setFrameStyle(frameStyle);
QPushButton *integerButton =
new QPushButton(tr("QInputDialog::get&Integer()"));

doubleLabel = new QLabel;
doubleLabel->setFrameStyle(frameStyle);
QPushButton *doubleButton =
new QPushButton(tr("QInputDialog::get&Double()"));

itemLabel = new QLabel;
itemLabel->setFrameStyle(frameStyle);
QPushButton *itemButton = new QPushButton(tr("QInputDialog::getIte&m()"));

textLabel = new QLabel;
textLabel->setFrameStyle(frameStyle);
QPushButton *textButton = new QPushButton(tr("QInputDialog::get&Text()"));

colorLabel = new QLabel;
colorLabel->setFrameStyle(frameStyle);
QPushButton *colorButton = new QPushButton(tr("QColorDialog::get&Color()"));

fontLabel = new QLabel;
fontLabel->setFrameStyle(frameStyle);
QPushButton *fontButton = new QPushButton(tr("QFontDialog::get&Font()"));

directoryLabel = new QLabel;
directoryLabel->setFrameStyle(frameStyle);
QPushButton *directoryButton =
new QPushButton(tr("QFileDialog::getE&xistingDirectory()"));

openFileNameLabel = new QLabel;
openFileNameLabel->setFrameStyle(frameStyle);
QPushButton *openFileNameButton =
new QPushButton(tr("QFileDialog::get&OpenFileName()"));

openFileNamesLabel = new QLabel;
openFileNamesLabel->setFrameStyle(frameStyle);
QPushButton *openFileNamesButton =
new QPushButton(tr("QFileDialog::&getOpenFileNames()"));

saveFileNameLabel = new QLabel;
saveFileNameLabel->setFrameStyle(frameStyle);
QPushButton *saveFileNameButton =
new QPushButton(tr("QFileDialog::get&SaveFileName()"));

criticalLabel = new QLabel;
criticalLabel->setFrameStyle(frameStyle);
QPushButton *criticalButton =
new QPushButton(tr("QMessageBox::critica&l()"));

informationLabel = new QLabel;
informationLabel->setFrameStyle(frameStyle);
QPushButton *informationButton =
new QPushButton(tr("QMessageBox::i&nformation()"));

questionLabel = new QLabel;
questionLabel->setFrameStyle(frameStyle);
QPushButton *questionButton =
new QPushButton(tr("QMessageBox::&question()"));

warningLabel = new QLabel;
warningLabel->setFrameStyle(frameStyle);
QPushButton *warningButton = new QPushButton(tr("QMessageBox::&warning()"));

errorLabel = new QLabel;
errorLabel->setFrameStyle(frameStyle);
QPushButton *errorButton =
new QPushButton(tr("QErrorMessage::show&M&essage()"));

connect(integerButton, SIGNAL(clicked()), this, SLOT(setInteger()));
connect(doubleButton, SIGNAL(clicked()), this, SLOT(setDouble()));
connect(itemButton, SIGNAL(clicked()), this, SLOT(setItem()));
connect(textButton, SIGNAL(clicked()), this, SLOT(setText()));
connect(colorButton, SIGNAL(clicked()), this, SLOT(setColor()));
connect(fontButton, SIGNAL(clicked()), this, SLOT(setFont()));
connect(directoryButton, SIGNAL(clicked()),
this, SLOT(setExistingDirectory()));
connect(openFileNameButton, SIGNAL(clicked()),
this, SLOT(setOpenFileName()));
connect(openFileNamesButton, SIGNAL(clicked()),
this, SLOT(setOpenFileNames()));
connect(saveFileNameButton, SIGNAL(clicked()),
this, SLOT(setSaveFileName()));
connect(criticalButton, SIGNAL(clicked()), this, SLOT(criticalMessage()));
connect(informationButton, SIGNAL(clicked()),
this, SLOT(informationMessage()));
connect(questionButton, SIGNAL(clicked()), this, SLOT(questionMessage()));
connect(warningButton, SIGNAL(clicked()), this, SLOT(warningMessage()));
connect(errorButton, SIGNAL(clicked()), this, SLOT(errorMessage()));

QGridLayout *layout = new QGridLayout;
layout->setColumnStretch(1, 1);
layout->setColumnMinimumWidth(1, 250);
layout->addWidget(integerButton, 0, 0);
layout->addWidget(integerLabel, 0, 1);
layout->addWidget(doubleButton, 1, 0);
layout->addWidget(doubleLabel, 1, 1);
layout->addWidget(itemButton, 2, 0);
layout->addWidget(itemLabel, 2, 1);
layout->addWidget(textButton, 3, 0);
layout->addWidget(textLabel, 3, 1);
layout->addWidget(colorButton, 4, 0);
layout->addWidget(colorLabel, 4, 1);
layout->addWidget(fontButton, 5, 0);
layout->addWidget(fontLabel, 5, 1);
layout->addWidget(directoryButton, 6, 0);
layout->addWidget(directoryLabel, 6, 1);
layout->addWidget(openFileNameButton, 7, 0);
layout->addWidget(openFileNameLabel, 7, 1);
layout->addWidget(openFileNamesButton, 8, 0);
layout->addWidget(openFileNamesLabel, 8, 1);
layout->addWidget(saveFileNameButton, 9, 0);
layout->addWidget(saveFileNameLabel, 9, 1);
layout->addWidget(criticalButton, 10, 0);
layout->addWidget(criticalLabel, 10, 1);
layout->addWidget(informationButton, 11, 0);
layout->addWidget(informationLabel, 11, 1);
layout->addWidget(questionButton, 12, 0);
layout->addWidget(questionLabel, 12, 1);
layout->addWidget(warningButton, 13, 0);
layout->addWidget(warningLabel, 13, 1);
layout->addWidget(errorButton, 14, 0);
layout->addWidget(errorLabel, 14, 1);
setLayout(layout);

setWindowTitle(tr("Standard Dialogs"));
}


If you instrument with timers a test app that constructs 1000 widgets with and without passing a parent widget pointer, you can easliy see the performance difference.

dimitri
17th April 2006, 10:49
As a side-note, creating 1000 widgets at start-up is a bit excessive. It's quite uncommon to build all the application's widgets when starting up. Thinking about it again, I don't think there is any real "no-no" here, and indeed this has never been reported before.

Anyway, it could be that parenting the widgets results in faster start-up times. However the reason needs to be investigated with a profiler, it could be a Qt inefficiency that could be fixed. Eventually either the examples will be fixed (parenting widgets) or the root cause of the slow-down will be fixed in the Qt library. However it probably won't be a high priority task since other users had never reported this annoyance.

About the "virtually all of them" it looks like most examples do parent widgets.

dimitri
17th April 2006, 13:09
I've modified example standarddialogs to reproduce the problem and indeed the Dialog constructor needs ~ 120 ms without parent compared to ~ 24 ms with parent on X11. I don't have Windows to test right now. Is the situation much worse on Windows?

shad
18th April 2006, 14:07
I've modified example standarddialogs to reproduce the problem and indeed the Dialog constructor needs ~ 120 ms without parent compared to ~ 24 ms with parent on X11. I don't have Windows to test right now. Is the situation much worse on Windows?
on windows it takes from 680 to 750 ms without parent object and 60-80 ms with 'this' as parent !
(tested on winxp sp2 P-4 2.4Ghz 1Gb ram (so swapping shouldn't affect the test), VC++ 2003

oh, by the way, these results are from debug binaries.

cboles
22nd April 2006, 07:49
I'm glad someone else is able to repro this problem and see how bad it is on XP... my app was unusable until I made the fix. I'm wondering why more people don't notice this performance problem?


on windows it takes from 680 to 750 ms without parent object and 60-80 ms with 'this' as parent !
(tested on winxp sp2 P-4 2.4Ghz 1Gb ram (so swapping shouldn't affect the test), VC++ 2003

oh, by the way, these results are from debug binaries.

SkripT
26th April 2006, 12:02
cboles, I've been watching your dialog and I've seen that there are some blocks that mantain the same widget structure ("X Axis Throttle Position" and "Y Axis Engine Speed"). My question is: do you use some kind of structure to avoid repeating code creating and placing the widgets? I'm asking it because I have a similar dialog with repeated structures and I want to know what's the common way to fix this.

PD: I will be grateful if any other person suggest something too ;)

cboles
28th April 2006, 20:57
I;m not sure if this answers your question, but yes, I don't have to repeat code. The overal dialog is a class

class EcuTableEditDialog : public QDialog

which contains ok/cancel buttons and a QTabWidget

Each tab has a

class EcuTableMainEditWidget : public QWidget

which itself contains a layout of multiple

class EcuTableEditWidget : public QWidget

which are the "X Axis Throttle Position" and "Y Axis Engine Speed" widgets you are referring to. Each of those widgets has a number of basic control widgets depending on the configuration. The EcuTableMainEditWidget also has different numbers of EcuTableEditWidgets depending if the table is 1,2, or 3D.

Colby


cboles, I've been watching your dialog and I've seen that there are some blocks that mantain the same widget structure ("X Axis Throttle Position" and "Y Axis Engine Speed"). My question is: do you use some kind of structure to avoid repeating code creating and placing the widgets? I'm asking it because I have a similar dialog with repeated structures and I want to know what's the common way to fix this.

PD: I will be grateful if any other person suggest something too ;)