Search code examples
c++qtuser-interfaceqwidget

Why does a lazy-loaded QWidget get displayed but a stored one does not?


I have a collection of small popup widgets that appear in various places, but only one of each and one at a time. For simple functionality, new-to-show and delete-to-hide is okay and works like it's supposed to, but as they start to handle their own data, I can see a memory leak coming up.

So because I only need one of each kind, I thought I'd create all of them up front in the parent constructor and just show and hide them as needed. As far as I can tell, that ought to work, but popup->show() doesn't show. The complex app that this example is based on shows that the popup does exist at the correct location and can interact with the user...except that it's invisible.

Here's the lazy version that shows:

#ifndef MAIN_H
#define MAIN_H

#include <QtWidgets>

class Popup : public QLabel
{
    Q_OBJECT
public:
    explicit Popup(int x, int y, int width, int height, QWidget* parent = 0);
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget* parent = 0);
    ~MainWindow() {}
    void mousePressEvent(QMouseEvent* ev);
private:
    Popup* popup;
};

#endif // MAIN_H

/***************
*** main.cpp ***
***************/
#include "main.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}



MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
{
    popup = 0;

    QWidget* cWidget = new QWidget(this);
    cWidget->setStyleSheet("background-color: lightgray");
    setCentralWidget(cWidget);

    showMaximized();
}

void MainWindow::mousePressEvent(QMouseEvent* ev)
{
    if(popup != 0)
    {
        if(!popup->geometry().contains(ev->x(), ev->y()))
        {
            delete popup;
            popup = 0;
        }
    }
    else
    {
        popup = new Popup(ev->x(), ev->y(), 100, 100, this);
        popup->show();
    }
    ev->accept();
}



Popup::Popup(int x, int y, int width, int height, QWidget* parent) :
    QLabel(parent)
{
    setStyleSheet("background-color: black");
    setGeometry(
                x - (width  / 2),   // Left
                y - (height / 2),   // Top
                width ,             // Width
                height              // Height
                );
}

And here's the pre-created version that doesn't show:

#ifndef MAIN_H
#define MAIN_H

#include <QtWidgets>

class Popup : public QLabel
{
    Q_OBJECT
public:
    explicit Popup(QWidget* parent = 0);
    void setup(int x, int y, int width, int height);
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget* parent = 0);
    ~MainWindow() {}
    void mousePressEvent(QMouseEvent* ev);
private:
    Popup* popup;
};

#endif // MAIN_H

/***************
*** main.cpp ***
***************/
#include "main.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}



MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
{
    popup = new Popup(this);

    QWidget* cWidget = new QWidget(this);
    cWidget->setStyleSheet("background-color: lightgray");
    setCentralWidget(cWidget);

    showMaximized();
}

void MainWindow::mousePressEvent(QMouseEvent* ev)
{
    if(popup->isVisible())
    {
        if(!popup->geometry().contains(ev->x(), ev->y()))
        {
            popup->hide();
        }
    }
    else
    {
        popup->setup(ev->x(), ev->y(), 100, 100);
        popup->show();
    }
    ev->accept();
}



Popup::Popup(QWidget* parent) :
    QLabel(parent)
{
    setStyleSheet("background-color: black");
}

void Popup::setup(int x, int y, int width, int height)
{
    setGeometry(
                x - (width  / 2),   // Left
                y - (height / 2),   // Top
                width ,             // Width
                height              // Height
                );
}

What am I missing?


Solution

  • Martin's mention of z-order, which I hadn't made the connection with yet, prompted some more googling that quickly found the QWidget::raise() method. Now it works the way I intended.

    Order of instantiation does work, but not if you create more widgets dynamically. The popup will still appear under the dynamic widgets.

    I'm sure there's a way to make it work (this is my first project with Qt, so I fully expect to call it "ugly" several years from now despite my best efforts), but the window flag broke the coupling between the popup and the way that the app was already written. If I had started with the window flag, I probably could have made it work just fine or maybe even better, but I didn't.

    Here's what I'm doing now, that works:

    #ifndef MAIN_H
    #define MAIN_H
    
    #include <QtWidgets>
    
    class Popup : public QLabel
    {
        Q_OBJECT
    public:
        explicit Popup(QWidget* parent = 0);
        void setup(int x, int y, int width, int height);
    };
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    public:
        MainWindow(QWidget* parent = 0);
        ~MainWindow() {}
        void mousePressEvent(QMouseEvent* ev);
    private:
        Popup* popup;
    };
    
    #endif // MAIN_H
    
    /***************
    *** main.cpp ***
    ***************/
    #include "main.h"
    #include <QApplication>
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }
    
    
    
    MainWindow::MainWindow(QWidget* parent)
        : QMainWindow(parent)
    {
        popup = new Popup(this);
    
        QWidget* cWidget = new QWidget(this);
        cWidget->setStyleSheet("background-color: lightgray");
        setCentralWidget(cWidget);
    
        showMaximized();
    }
    
    void MainWindow::mousePressEvent(QMouseEvent* ev)
    {
        if(popup->isVisible())
        {
            if(!popup->geometry().contains(ev->x(), ev->y()))
            {
                popup->hide();
            }
        }
        else
        {
            popup->setup(ev->x(), ev->y(), 100, 100);
            popup->raise();     /*** THIS IS WHAT I WAS MISSING!!! ***/
            popup->show();
        }
        ev->accept();
    }
    
    
    
    Popup::Popup(QWidget* parent) :
        QLabel(parent)
    {
        setStyleSheet("background-color: black");
    }
    
    void Popup::setup(int x, int y, int width, int height)
    {
        setGeometry(
                    x - (width  / 2),   // Left
                    y - (height / 2),   // Top
                    width ,             // Width
                    height              // Height
                    );
    }