How to recognize QMouseEvent inside child widgets?

3.3k views Asked by At

EDIT and Some Self Critisicm I tried both given solutions, which both solved my problem, and thus I thank you both! I marked the transparent solution as accepted because I thought it is was the easiest implementation when I only had one child widget, but I wish to share some insight for other beginners:

I first used QLabel, which apperently has enabled Qt::WA_TransparentForMouseEvents by default and thus obviously worked, but I also wanted the text to be selectable, by using QPlainTextEdit instead. Laughably, this is not possible in any way, because if you try to select the text (by clicking) you will close the window! I ended up keeping the transparancy, and neglecting the select-text-feature.


I'm guessing my following question has been answered somewhere before, but after an hour of searching I now post the question myself. I'm grateful if someone can point me to an already answered question that solves my problem.

Anyhow, I'm creating a popup window, using C++ and Qt. I've created the following PopupDialog class which works well and fine for all its purposes. However, I've removed its frame (including the bar containing the close button and window title) to make it look minimalistic, and now I want it to close if the user presses/releases the mouse button anywhere inside the popup window (dialog).

The below code works, however in such a way that I have to click and release the mouse exactly at the QDialog-window itself. It will not close when i click if the mouse hovers over the child widget(s) inside the QDialog, e.g. a QPlainTextEdit, which is displaying text.

Hence, I'm in need of a solution for QDialog to recognize QMouseEvents inside its child widgets. Please, don't hesitate to ask if something is unclear. I have not included my mainwindow.h/.cpp files, or popupdialog.ui file since I believe it would be a little too much to post here, but the .ui extremely simple: Just the QDialog window holding a QBoxLayout, containing a single widget, a QPlainTextEdit. I may posts these on request if it helps.

    // popupdialog.h

    #ifndef POPUPDIALOG_H
    #define POPUPDIALOG_H

    #include <QDialog>
    #include <QString>

    namespace Ui {class PopupDialog;}

    class PopupDialog : public QDialog
    {
            Q_OBJECT

    public:
            explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
            ~PopupDialog();

    private:
            Ui::PopupDialog *ui;
            QString messageText;
            void mouseReleaseEvent(QMouseEvent*);
    };

    #endif //POPUPDIALOG_H

...

    // popupdialog.cpp

    #include "popupdialog.h"
    #include "ui_popupdialog.h"


    PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
            QDialog(parent),
            ui(new Ui::PopupDialog),
            messageText(msgTxt)
    {
            ui->setupUi(this);
            setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
            setModal(true);
            ui->message_text_display->setText(messageText);
            // The message_text_display is an instance of the class,
            // "PlainTextEdit". Using "QLabel" partly solves my
            // problem, but does not allow text selection.
    }

    PopupDialog::~PopupDialog()
    {
            delete ui;
    }


    void PopupDialog::mouseReleaseEvent(QMouseEvent *e)
    {
            this->close();
    }
2

There are 2 answers

0
Dusteh On BEST ANSWER

As you already noticed mouse events are handled from child widgets and propagated to parents if not accepted. You can read more about it here

To close your popup window when the click is done inside a child widget you can do two things. You could try looking into installEventFilter and set it up on each child widget to call close().

Another option would require you to have a kind of centralWidget (like the MainWindow usually has) - just to group all your child widgets. This way you could call setAttribute() on it to set Qt::WA_TransparentForMouseEvents property to simply skip handling mouse events on the widget and all of its children.

groupWidget->setAttribute(Qt::WA_TransparentForMouseEvents);

According to Qt docs:

When enabled, this attribute disables the delivery of mouse events to the widget and its children. Mouse events are delivered to other widgets as if the widget and its children were not present in the widget hierarchy; mouse clicks and other events effectively "pass through" them. This attribute is disabled by default.

Which basically means the event would be passed up the chain to the first widget which can handle the event. In your case it would be the PopupDialog and the already overriden mouseReleaseEvent slot.

0
user1277317 On

in header file

class PopupDialog : public QDialog
{
        Q_OBJECT

public:
        explicit PopupDialog(QWidget *parent = 0, QString msgTxt="");
        ~PopupDialog();

//////////////////////////////////
protected:
bool eventFilter(QObject *obj, QEvent *event);

//////////////////////////////////////

private:
        Ui::PopupDialog *ui;
        QString messageText;
        void mouseReleaseEvent(QMouseEvent*);
};

in cpp

 PopupDialog::PopupDialog(QWidget *parent, QString msgTxt) :
        QDialog(parent),
        ui(new Ui::PopupDialog),
        messageText(msgTxt)
{
        ui->setupUi(this);
        setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
        setModal(true);
        ui->message_text_display->setText(messageText);
        // The message_text_display is an instance of the class,
        // "PlainTextEdit". Using "QLabel" partly solves my
        // problem, but does not allow text selection.
///////////////////////////////////////
  foreach (QObject *child, children())
{
    child->installEventFilter(this);
}
///////////////////////////////////////
}


///////////////////////////////////////
bool PopupDialog::eventFilter(QObject *obj, QEvent *event)
{

if(event->type() == QEvent::MouseButtonRelease)
{
this->close();
}
}