Clearing a Layout in Qt
I'm creating an application in Qt that allows users to drag around various "modules" in a QGraphicsView. Whenever one of these modules is selected, it emits a signal which is then picked up by a "ConfigurationWidget" which is a side-panel which should display all of the relevant parameters of the selected module:
class ConfigurationWidget : public QWidget
{
Q_OBJECT
public:
ConfigurationWidget(QWidget *parent) : QWidget(parent) {}
public slots:
void moduleSelected(Module* m)
{
if(layout())
{
while (itsLayout->count()>0)
{
delete itsLayout->takeAt(0);
}
}
delete layout();
itsLayout = new QFormLayout(this);
itsLayout->addRow(QString(tr("Type:")), new QLabel(m->name()));
itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
// ... Display a whole bunch of other fields that depends on the module
}
};
The problem is that the ConfigurationWidget never actually gets cleared when a module is selected. The new fields are just drawn over the old ones. I've tried various combinations of hide() and show(), invalidate(), update(), etc. to n开发者_运维百科o avail.
What's the correct way to make a widget that can change its fields like this on the fly?
The code loop I've used before is as follows:
void clearLayout(QLayout *layout) {
if (layout == NULL)
return;
QLayoutItem *item;
while((item = layout->takeAt(0))) {
if (item->layout()) {
clearLayout(item->layout());
delete item->layout();
}
if (item->widget()) {
delete item->widget();
}
delete item;
}
}
Hopefully that will be helpful to you!
If you transfer the layout to another widget allocated on the stack, the widgets in that layout become the children of the new widget. When the temporary object goes out of scope it destroys automatically the layout and all the widgets in it.
void moduleSelected(Module* m)
{
if(layout())
QWidget().setLayout(layout());
itsLayout = new QFormLayout(this);
itsLayout->addRow(QString(tr("Type:")), new QLabel(m->name()));
itsLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
// ... Display a whole bunch of other fields that depends on the module
}
It looks like the best way to do this is to use a QStackedLayout, as hinted at by armonge:
void ConfigurationWidget::moduleSelectedSlot(Module* m)
{
QStackedLayout *stackedLayout = qobject_cast<QStackedLayout*>(layout());
QWidget *layoutWidget = new QWidget(this);
QFormLayout *formLayout = new QFormLayout(layoutWidget);
formLayout->addRow(QString(tr("Type:")), new QLabel(m->name()));
formLayout->addRow(QString(tr("Instance:")), new QLabel(m->instanceID()));
// ... Display a whole bunch of other fields that depends on the module
delete stackedLayout->currentWidget();
stackedLayout->addWidget(layoutWidget);
stackedLayout->setCurrentWidget(layoutWidget);
}
I recently run into the same problem with a QFormLayout. What worked for me (and is also in Qt's documentation: http://qt-project.org/doc/qt-5/layout.html) was the takeAt(int) method.
void clearLayout(QLayout *layout)
{
QLayoutItem *item;
while ((item = layout->takeAt(0)))
delete item;
}
Using while((item = layout->takeAt(0)))
will cause warning message "Invalid index take at 0".
I use count()
instead
void clearLayout(QLayout *layout)
{
if (layout) {
while(layout->count() > 0){
QLayoutItem *item = layout->takeAt(0);
QWidget* widget = item->widget();
if(widget)
delete widget;
delete item;
}
}
}
Reparent the items to NULL when you want them to disappear from a window. I had similar problems recently and reparenting solved them.
I have found none of the other answers so far work. They either crash or don't clear the layout completely. So here is what I have found to work, I hope others find this useful.
void eraseLayout(QLayout * layout)
{
while(layout->count() > 0)
{
QLayoutItem *item = layout->takeAt(0);
QWidget* widget = item->widget();
if(widget)
{
delete widget;
}
else
{
QLayout * layout = item->layout();
if (layout)
{
eraseLayout(layout);
}
else
{
QSpacerItem * si = item->spacerItem();
if (si)
{
delete si;
}
}
}
delete item;
}
}
Thanks to Vaidotas Strazdas's commentary, I found my solution to reset a layout properly. Using the deleteLater()
method works very good, otherwise the delete
keyword causes massive display errors in the refreshed layout.
void resetLayout(QLayout* apLayout)
{
QLayoutItem *vpItem;
while ((vpItem = apLayout->takeAt(0)) != 0) {
if (vpItem->layout()) {
resetLayout(vpItem->layout());
vpItem->layout()->deleteLater();
}
if (vpItem->widget()) {
vpItem->widget()->deleteLater();
}
delete vpItem;
}
}
精彩评论