Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Qt::Popup window behaviour



  • I have a QPushButton with an associated QWidget window, which pops up temporarily (when the button is clicked) to allow user to interact with some widgets on it. The bare essentials are:

    class PushButtonWithPopup : public QPushButton
    {
        Q_OBJECT
    public:
        PushButtonWithPopup(QWidget *parent = nullptr);
    
    private:
        QWidget _popup;
    
    private slots:
        void doPopup();
    };
    
    
    PushButtonWithPopup::PushButtonWithPopup(QWidget *parent /*= nullptr*/) :
        QPushButton(parent)
    {
        // popup window flags (popup with no frame)
        _popup.setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
    
        // connect button click to execute the popup
        connect(this, &QPushButton::clicked, this, &PushButtonWithPopup::doPopup);
    }
    
    /*slot*/ void PushButtonWithPopup::doPopup()
    {
        // show the popup
        _popup.show();
    }
    
    1. This works splendiferously. When I click anywhere outside the popup it goes away, which is just want I want. But now I'm thinking to myself: https://doc.qt.io/qt-5/qt.html#WindowType-enum for Qt::Popup doesn't actually say it will dismiss on click-outside, so is it supposed to do what I see it doing? Am I unable to see the wood for the trees, what's actually going on?

    2. The click outside which dismisses it --- say, on another widget where its assocaited QPushButton lives --- gets "eaten" to dismiss the popup. I am finding I would actually prefer it if that click caused the popup to dismiss and then proceeded as usual to do its action, instead of being consumed. For example, if I click on some checkbox outside the popup, after the popup disappears I'd like that checkbox to toggle as usual. How would I go about achieving this, where is my click getting eaten?


  • Lifetime Qt Champion

    Hi,

    1. AFAIK, yes that's what is expected
    2. I am currently not sure how you could implement that.


  • @SGaist
    Firstly, thank you for answering a question with an answer, not another question ;-)

    Secondly I have some strange Qt behaviour to report on this. I'd really appreciate if you/someone could comment/clarify what might be going on...

    This relates just to my Question #1, how a popup window gets destroyed or not.

    In my own case, the QWidget with setWindowFlags(Qt::Popup) is a member variable allocated on the stack, the class has QWidget _popup; as shown. Consequently I do not expect to leak, and valgrind reports no leak. So far, so good.

    However, a learned colleague here ( @mrjj) allocates these on the heap, so he would have QWidget *_popup and go _popup = new QWidget. He now wonders whether he is consequently leaking? So I changed my stuff over to investigate, and come up with "inexplicable" results:

    For my existing popup, I change to:

    QWidget *_popup;
    _popup = new QWidget;
    connect(_popup, &QObject::destroyed, []() { qDebug() << "Destroyed"; });
    _popup->setWindowFlags(Qt::Popup);
    ...
    _popup->show();
    
    • valgrind (which works for me, Linux) does not report this as a leak on program exit. Yet I do not ever see the "Destroyed" message on QObject::destroyed.
    • I now create an extra QWidget *_popup2. I new it, connect() it, but do not set Qt::Popup. I test, and sure enough valgrind does report leak.
    • I now add just _popup2->setWindowFlags(Qt::Popup);, so it has popup flag too. I do not popup2->show() it any time. Now valgrind does not report it leaking; yet "Destroyed" message on QObject::destroyed is still not called.

    Summary:

    • Putting Qt::Popup attribute on a newed widget (without parent) causes it not to leak, even if it is never shown. However, QObject::destroyed is never called.

    • If we assume for a moment that it is being deleted/destroyed somehow, behind-the-scenes code seems to know to delete it if it was newed onto the heap but not (at least, doesn't go wrong) if it is on the stack.

    Very, very simple, minimal:

    #include <QApplication>
    #include <QWidget>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QWidget w;
    
        // valgrind: next line *DOES* leak
        QWidget *w1 = new QWidget;
        // valgrind: next line does *NOT* leak, because of the line below it
        // and yet if you `connect(w2, &QObject::destroyed)` that does not get called
        QWidget *w2 = new QWidget;
        w2->setWindowFlags(Qt::Popup);
    
        w.show();
        return a.exec();
    }
    

    Explanation, please?


  • Lifetime Qt Champion

    AFAIK, the Popup window flag has no influence with regard to any memory management (and it would not make sense as well).

    Do you have suppression files for Valgrind concerning Qt ?



  • @SGaist said in Qt::Popup window behaviour:

    AFAIK, the Popup window flag has no influence with regard to any memory management (and it would not make sense as well).

    Hence my question. Nonetheless, the code above behaves as I have documented. Qt 5.12.2, Ubuntu 19.04.

    I have an absolutely vanilla install of Ubuntu valgrind, 3.14.0. I assume there is nothing Qt about it..

    Do you have the opportunity to test that 10 line code yourself? I would also summon @mrjj and request he test under Linux/Windows, since he is the one who is dynamically allocating (my own code the popup widget is on the stack) and would be affected.



  • @JonB , regarding question 2, A time consuming way to do it would be to track the position of widgets and cursor clicks, but would work I think?



  • @MEMekaniske
    I can't afford to do that, I would need a generic solution. I am guessing I would need to know where Qt code "eats" this and do something about it, perhaps in some eventFilter?

    At the moment I want to resolve what is going on in #1, before I even think of addressing #2!



  • @JonB I understand.

    Eventfilter would probably do :) HoverMove might be a friend!



  • @MEMekaniske
    In trying to investigate #1 yesterday, I had a look through woboq. I believe I came across code for Qt::Popup flag (list of popped up widgets created from it), which did something like accept a mouse event when a popup was visible, and dismiss the popup. I would need to have a look for that again as the best way forward, I think.

    Meanwhile, however, I am going on strike! Until I have an explanation for the valgrind non-leak-on-newed-Qt::Popup-flag behaviour, I am judging this area as "black magic" and hence too scary to tamper with...



  • STOP PRESS
    [Including for @SGaist, @mrjj's attention.]
    I revisited my 10 line program apparently showing leak on plain new QWidget but not on new QWidget; setWindowFlags(Qt::Popup);.

    • If I remove the "test" non-Popup widget and only have the Popup widget then valgrind does report the Popup as a leak.
    • If I reverse the order so that the Popup widget is new-ed first and the non-Popup is new-ed next, then valgrind does report the Popup widget as leaking but the non-Popup widget is not reported as leaking.

    For whatever reason, this reveals that valgrind sometimes only reports certain leaks but not others [has anyone else encountered this behaviour?]. This is "disappointing", but at least makes sense, showing nothing special about Popup.

    So going back to @mrjj's situation. I do not leak because my Popup widget is on the stack. In his situation he news on the heap, and I conclude that he does need to delete the Popup window.

    Problem #1 at least apparently resolved!


Log in to reply