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

Popup and pushButton - how to solve it



  • Hello,

    I have a QPushButton. When I click on it I would like to create QDialog, which has set flags:
    Qt::Window
    Qt::Popup
    Qt::FramelessWindowHin

    I set geometry of this QDialog under QPushButton ( it looks like QComboBox when I click QPushButton ).

    And this works perfect.

    But...

    I can't write good solution to hide this QDialog.

    There are possibilities:

    1. click ESCAPE - this works
    2. click somewhere outside the QPushButton and QDialog - this works
    3. click on the QPushButton - this works, but often with errors

    My goal:
    The first click QPushButton to show QDialog, next click to hide it, next click to show it. It looks very easy - I can have a static variable and do something like:

    if( variable % 2 == 0  )
    {
    
    }
    else
    {
    
    }
    

    But the problem is when I click outside the QPushButton. Then my variable don't change.

    I check and I see that when I set focus on QDialog and when I click outside the QPushButton it will be run only QFocusOutEvent. When I click on QPushButton it will be run both QFocusOutEvent and next mousePressEvent.

    I try using other variables, using exec and I don't have idea...


  • Lifetime Qt Champion

    Hi
    You can set an event filter on the dialog and look for the focus out event and then call
    reject() to close it. This allows you to capture "clicking outside the dialog"

    https://doc.qt.io/qt-5/eventsandfilters.html
    section Event Filters

    Make sure to read
    "it can accept or reject the event, and allow or deny further processing of the event. "
    and understand this part as else you will eats its event and it will do odd stuff.



  • @mrjj Thank you.

    I tried get the position of mouse cursor where it is when I clicked and I compare position with button's position. I know where the user clicked. But when I go to focus out event the widget is already hide ( popup -> focus out ), so I don't have to use reject.

    Sometimes when I click double times in a row I have situation in the end of queue events ( after the first click )

    focus out -> mouse press event

    Sometimes I have only:

    focus out

    So there is no the second click. And this is the problem.


  • Lifetime Qt Champion

    @TomNow99
    Hi
    QComboxBox can do it with a local event filter so i wonder if it does use grabmouse.

    other also had this idea
    https://forum.qt.io/topic/97345/how-to-detect-mouse-click-outside-of-widget/2

    Here they set the event filter on QApp to allow to get all events.

    You could also use GrabMouse but be careful with that.



  • @mrjj
    I find that my problem is the bug:
    https://bugreports.qt.io/browse/QTBUG-50051

    There is a solution, but I don't have source code to qpushButton. How can I solve that?


  • Lifetime Qt Champion

    @TomNow99
    Hi
    The source code of can be installed with the maintenance tool.



  • @mrjj I find it here:
    https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qpushbutton.cpp.html

    I find that function which I have to replace

    void QPushButtonPrivate::_q_popupPressed()

    But the problem is that I need class QPushButtonPrivate. Of course I find that too. But when I check QPushButtonPrivate I saw that I need QMenu and QMenuPrivate. I think there is better way to replace that QPushButtonPrivate function.

    EDIT:
    Of course I can have new class like QPushButton2. I don't need change QPushButton


  • Lifetime Qt Champion

    @TomNow99
    Hi
    well in other cases you would apply the patch to Qt source and rebuild
    the Widget Module. But for such small thing, im not sure its worth it.

    Did you try with event filer on Application instead?
    That should work.
    Or use grabmouse to be sure to get it.



  • @mrjj But I don't understand your idea.

    Filter on application. Do you think about eventFilter and installEventFilter? But in application? Do you think about that variable which I create in main.cpp?


  • Lifetime Qt Champion

    @TomNow99
    Hi
    Like
    qApp->installEventFilter(filter)
    qApp is macro to access the one from main.

    That should allow you to see the click out side.

    BUT

    But Before testing that, could you test with grabMouse on dialog ?
    it should allow you to get the final click with the code you have.

    https://doc.qt.io/qt-5/qwidget.html#grabMouse

    Make SURE to call releaseMouse()
    when you close dialog. always.



  • @mrjj But what I have to add in installEventFilter function? I always add "this", but here?

    And this grabWindow... I add this in click where I show dialog. But when I add this I can't click button to close dialog.


  • Lifetime Qt Champion

    @TomNow99
    hi
    The "this" is the pointer to the QObject that has the filter func
    see here
    https://forum.qt.io/topic/83995/eventfilter-anywhere-in-the-program/3
    its sets on the QApp. So I guess this for you would be where you have the filterfunction, maybe the dialog or the button.

    Its grapmouse :) Did you read about it ?
    What it does ?
    If you tell dialog to grab mouse you cannot click button while dialog is open. but it will work with click outside but that click is used to close dialog and it wont go to button that is outside.



  • @mrjj I understand filter. Thank you :)

    GrabMouse - I understand fifty - fifty :D

    True - I can't click on button. But when I click outside the button ( MainWindow, where I add button ) ) I can't close dialog. When I clicked outside app ( for example click on desktop ) I close dialog.


  • Lifetime Qt Champion

    @TomNow99
    Ok good :)
    well grabmouse simply make all events go to one widget instead of the normal way where they go to widget under mouse
    so we say "give me all"

    Should also work clicking in MainWindow. you should also get that event.



  • @mrjj Now my code looks like:

    MainWindow ( where I have button which shows / hides dialog ):

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        button = new QPushButton(this);
        button->setGeometry(100,100,100,100);
        connect(button, SIGNAL(clicked()), this, SLOT(clickedSlot()));
        widget = new tenWidget; // tenWidget is a dialog
        widget->setFixedSize(300,300);
        widget->setFocus();
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::clickedSlot()
    {
        static int click=1;
    
        if(click%2==1)
        {
            widget->show();
            grabMouse();
        }
        else
        {
            //widget->hide();
        }
        click++;
    }
    
    

    and the dialog ( tenWidget ):

    #include "tenwidget.h"
    
    tenWidget::tenWidget(QWidget *parent)
    {
        setWindowFlag( Qt::FramelessWindowHint);
        setStyleSheet("QDialog {background-color:red}");
    }
    
    void tenWidget::focusOutEvent(QFocusEvent *event)
    {
        hide();
        releaseMouse();
    }
    

    Do you think about something like that? Or somewhere I have error?


  • Lifetime Qt Champion

    @TomNow99
    Hi
    I was thinking tenWidget both had grab and release mouse.
    As if you let button grab mouse then im not sure you can click on anything in tenWidget.

    So i would let it grabmouse so it can get the click out side event.
    then it can close it. but you cant click on button while its up but clicking over it will close anyway.
    its not just really the button :)



  • @mrjj I read your post the fourth time and I don't know what I should do next :D

    Should I change the place in the code where I grab or release mouse?

    EDIT:
    Of course I change:

    grabWindow();
    

    to

    widget->grabMouse();
    

    in mainWindow.

    But the effect is the same.


  • Lifetime Qt Champion

    @TomNow99
    Hi
    well widget->grabMouse(); should be fine
    I dont see the event filter ?
    But you have on on the tenWidget (widget) correct ?


Log in to reply