Search code examples
qtqscrollareaqt5.4

Qt: Strange behavior when calling setGeometry on a widget in QScrollArea


My environment is the following:

  • Qt 5.4, build from source with -platform win32-msvc2013 -opengl desktop -no-icu -skip webkit
  • Visual Studio 2013
  • Windows 7 x64

Consider the following setup (A QScrollArea containing a centered widget with fixed size):

QScrollArea scrollArea;
scrollArea.setAlignment(Qt::AlignCenter);
scrollArea.setWidgetResizable(false);
QWidget scrollAreaWidgetContents;
scrollArea.setWidget(scrollAreaWidgetContents);

Now, I want to resize that widget within my program like the following:

int w = 200, h = 200;
scrollAreaWidgetContents.setGeometry(0, 0, w, h);

And here comes the strange behavior:
If w and h are equal to the current width and height (i.e. nothing changes), the widget jumps to the upper left corner.
If w and h are different from the current width and height, the widgets stays at its centered position and changes its size correctly.

I have written a little demo application that demonstrates the issue. If you click the pushButton_Stay button, the widgets jumps to the upper left corner.

    #include <QtWidgets/QApplication>
    #include <QPushButton>
    #include <QVBoxLayout>
    #include <QScrollArea>

    QScrollArea *scrollArea;
    QWidget *scrollAreaWidgetContents;
    QPushButton *pushButton_Up;
    QPushButton *pushButton_Down;
    QPushButton *pushButton_Stay;
    QVBoxLayout *layout;

    int w = 200, h = 200;

    void sizeUp() {
        w += 10;
        h += 10;
        scrollAreaWidgetContents->setGeometry(0, 0, w, h);
    }

    void sizeDown() {
        w -= 10;
        h -= 10;
        scrollAreaWidgetContents->setGeometry(0, 0, w, h);
    }

    void sizeStay() {
        scrollAreaWidgetContents->setGeometry(0, 0, w, h);
    }

    int main(int argv, char **args)
    {
        QApplication app(argv, args);

        // Scroll area
        scrollArea = new QScrollArea;
        scrollArea->setWidgetResizable(false);
        scrollArea->setAlignment(Qt::AlignCenter);
        scrollAreaWidgetContents = new QWidget();
        scrollAreaWidgetContents->setGeometry(QRect(0, 0, w, h));
        scrollAreaWidgetContents->setAutoFillBackground(true);
        QPalette Pal(scrollAreaWidgetContents->palette());
        Pal.setColor(QPalette::Background, Qt::black);
        scrollAreaWidgetContents->setPalette(Pal);
        scrollArea->setWidget(scrollAreaWidgetContents);

        // Buttons
        pushButton_Up = new QPushButton(scrollAreaWidgetContents);
        pushButton_Up->setGeometry(QRect(85, 50, 31, 23));
        pushButton_Up->setText("+");
        pushButton_Down = new QPushButton(scrollAreaWidgetContents);
        pushButton_Down->setGeometry(QRect(85, 110, 31, 23));
        pushButton_Down->setText("-");
        pushButton_Stay = new QPushButton(scrollAreaWidgetContents);
        pushButton_Stay->setGeometry(QRect(85, 80, 31, 23));
        pushButton_Stay->setText("0");
        QObject::connect(pushButton_Up, &QPushButton::clicked, sizeUp);
        QObject::connect(pushButton_Stay, &QPushButton::clicked, sizeStay);
        QObject::connect(pushButton_Down, &QPushButton::clicked, sizeDown);

        // Central layout
        layout = new QVBoxLayout;
        layout->addWidget(scrollArea);

        // Main window
        QWidget window;
        window.setGeometry(200, 200, 400, 400);
        window.setLayout(layout);
        window.show();

        return app.exec();
    }

Clicking the pushButton_Up button:

First picture

Clicking the pushButton_Stay button:

Second picture

Questions:

What's going wrong here?

Is it a bug? An if and yes, can anyone confirm it?

If it's not a bug, how can I make it that the widget always stays centered?


Solution

  • What's going wrong here?

    Is it a bug? An if and yes, can anyone confirm it?

    It seems the second call of setGeometry with the same arguments use different origin, and the coordinates (0, 0) make the widget move to top-left corner. You can use other coordinates such as setGeometry(100, 50, w, h) to see the difference.

    Here is the source code of QWidget::setGeometry:

    if (testAttribute(Qt::WA_WState_Created)) {
        d->setGeometry_sys(r.x(), r.y(), r.width(), r.height(), true);
        d->setDirtyOpaqueRegion();
    } else {
        data->crect.setTopLeft(r.topLeft()); 
        //          ^^^^^^^^^ Could be the reason why (0, 0) move to top-left
        data->crect.setSize(r.size().boundedTo(maximumSize()).expandedTo(minimumSize()));
        setAttribute(Qt::WA_PendingMoveEvent);
        setAttribute(Qt::WA_PendingResizeEvent);
    }
    

    Personally I think it's a bug, but more evidences need to be provided until we can confirm it.


    If it's not a bug, how can I make it that the widget always stays centered?

    Actually you don't have to call setGeometry if you just want to resize the widget. Use resize instead:

    void sizeStay() {
        scrollAreaWidgetContents->resize(w, h);
    }
    

    It solves the problem in your case.