piotrekg2
piotrekg2

Reputation: 1257

Qt adding child widget in resizeEvent

I have a widget W deriving from QFrame with layout set to an instance of QVBoxLayout. I wonder if the following resizeEvent implementation is correct or is it going to cause an infinite loop:

void W::resizeEvent(QResizeEvent *event) {
    for (/* some condition based on the new size of this widget */) {
        // Infinite loop or not?
        qobject_cast<QVBoxLayout *>(layout())->addWidget(new QWidget());
    }
}

So far it worked for me, is this by pure luck?

Upvotes: 2

Views: 2166

Answers (3)

jpo38
jpo38

Reputation: 21514

Adding widgets to the layout, using addWidget, within the resizeEvent function is not a problem as it does not instantly trigger a drawing.

You can easily verify this by compiling and executing this simple project:

dialog.h:

#pragma once
#include <QDialog>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

    void resizeEvent(QResizeEvent *event);
    void paintEvent(QPaintEvent *event);

private:
    bool resizing;
};

dialog.cpp:

#include "dialog.h"

#include <QResizeEvent>
#include <QVBoxLayout>
#include <QPushButton>
#include <QDebug>

Dialog::Dialog(QWidget *parent)
    : QDialog(parent),
      resizing(false)
{
    new QVBoxLayout(this);
}

Dialog::~Dialog()
{

}

void Dialog::resizeEvent(QResizeEvent *event)
{
    resizing = true;

    if ( event->size().width() == event->size().height() )
    {
        qDebug() << "Adding widget";
        // Infinite loop or not?
        layout()->addWidget(new QPushButton());
    }

    resizing = false;
}

void Dialog::paintEvent(QPaintEvent *event)
{
    if ( resizing )
    {
        qDebug() << "Painting while resizing widget";
    }
}

main.cpp:

#include "dialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();

    return a.exec();
}

When you run the program, resize the dialog to make it be square (width==height), some buttons are inserted ("Adding widget" is printed to the console), but you'll never see "Painting while resizing widget" message. This is most likely because addWidget sets a dirty display flag that is processed later by the framework. It invalidates the display, but does not repaint it right away.

So what you are doing is fine and does not violate the framework requirement ("No drawing need be (or should be) done inside this handler.").

However, if you are not confident (maybe the painting could be operated right away on different OS, or in future Qt versions....you can't be sure), you can also delay the insertion by emitting a signal connected to a slot using Qt::QueuedConnection, this slot would be executed "later" and then do the call to addWidget, guaranteeing that it's done outside the resizeEvent function.

Upvotes: 1

m7913d
m7913d

Reputation: 11064

Painting and constructing a hierarchy of widgets are two different things. So, adding QWidgets is just fine, but using QPainter directly in resizeEvent not.

Hierarchy of QWidgets

A hierarchy of QWidgets derivatives (QLineEdit, QPushButton, ...) is a high level specification of how the graphical user interface should look like and may be ordered using QLayout items.

Painting

Painting (using QPainter) is the process of actually drawing something on the screen and is purely done in the virtual function QWidget::paintEvent. Every derivative of QWidget should provide an implementation of this empty base function. The default derivatives (QLineEdit, ...) provide an implementation of paintEvent based on their current state (size of the widget, current text for a QLineEdit, ...) and the current QStyle object, which is typically automatically set based on your OS, but may be changed programmatically using QWidget::setStyle or QApplication::setStyle. A repaint can be requested using QWidget::update.

"Should not/need not" vs "may not"

The sentence "No drawing need be (or should be) done inside this handler." is meant for people implementing a custom QWidget (with a new implementation of paintEvent) to make it clear that you should not implement your painting here, but that a paintEvent will be automatically triggered.

"Should not/need not" is some advice, they do not write "may not". So, if you for some reason (ex. real-time applications) want an immediate screen refreshment, you may invoke a repaint immediately using repaint, resulting in paintEvent being called during resizeEvent. As long as all the QPainter operations on a QWidget are inside a paintEvent (as required by the warning in the QPainter documentation), everything is just fine.

Upvotes: 1

user2836797
user2836797

Reputation:

This is okay. W owns a QLayout which owns QWidget. Adding the QWidget to the QLayout does not change the size of W. You see this all the time. For example, if you place a child widget in a parent and the parent is too small, the child widget will be clipped. Stately differently, the size of the parent does not stretch to accommodate the size of the child. I believe your code would be a typical way to hide or show widgets based on the size of the parent (for example, when the window size changes).

Upvotes: 1

Related Questions