PDA

View Full Version : QMessageBox on Linux fails to gain focus



napajejenunedk0
9th May 2011, 13:11
There are several topics that are duplicates to this forum thread:
qtforum (http://www.qtforum.org/article/27890/qmessagebox-not-obtaining-the-keyboard-focus.html?905e83b1), qtcentre-0 (http://www.qtcentre.org/threads/29796-QMessageBox-Key-focus), qtcentre-1 (http://www.qtcentre.org/threads/24326-QMessageBox-with-no-focus), qtcentre-2 (http://www.qtcentre.org/threads/17068-QMessageBox-and-Focus), but this one differs in one aspect. Here's the problem:
1) You have a simple widget containing a push button.
2) Closing the widget and pushing the button causes a QMessageBox to appear.
3) Run the widget and immediately after that close it. The QMessageBox is displayed and HAS focus.
4) Run the widget again, move it or click the button. Then close it again - again the message box appears but this time it HAS NO focus.

And the difference:
~~~~~~~~~~~~
Note that the QMessageBox that is displayed inactive is the one shown in an overriden closeEvent(QCloseEvent*) member function. The message box that is shown when the button is clicked is always active e.g. always has focus.

This problem is reproduced only on Linux environment. Since it is not reproduced on Windows is it a Qt framework's bug? What is so special about this closeEvent that when it is fired the shown QMessageBox is inactive and has no focus on Linux?
~~~~~~~~~~~~

Probable solution:
~~~~~~~~~~~
Instead of using the static member functions for creating a warning, question or information message boxes, a good beginning to the solution is to create a QMessageBox object, make some adjustments to it, and then display it.

Currently nothing has helped the QMessageBox to gain focus, none of the following:
1) Try not to set parent to the QMessageBox being displayed.
2) Use setFocus.
3) Use setWindowState( Qt::WindowActive ). Some form members say that setting the window state to Qt::WindowFullScreen solves the problem. Doing this way the message box neither becomes enabled nor it is displayed correctly.
4) Use grabKeyboard and releaseKeyboard.

Fully reproduce the problem:
~~~~~~~~~~~~~~~~~~
Create a simple Qt GUI project with QWidget base class without generating .ui file (remove the checkmark in the project creation wizard) and paste the code below in the .cpp and .h files of the QWidget inherited class.

-----------------------------------------------------------------
Widget.cpp

#include "Widget.h"
#include <QCloseEvent>
#include <QMessageBox>
#include <QPushButton>
#include <QGridLayout>

Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QGridLayout* const gl = new QGridLayout( this );

QPushButton* const pb = new QPushButton("Show Message Box...");
gl->addWidget( pb );

connect( pb, SIGNAL(clicked()), this, SLOT(showMessageBox()) );
}

Widget::~Widget()
{
}

void Widget::closeEvent(QCloseEvent* event)
{
const int buttonId = QMessageBox::warning( this, "Warning", "Close Event", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok );

if ( QMessageBox::Cancel == buttonId )
{
event->ignore();
}
}

void Widget::showMessageBox()
{
const int buttonId = QMessageBox::warning( this, "Warning", "Button click", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok );
Q_UNUSED( buttonId )
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = 0);
~Widget();

protected:
virtual void closeEvent(QCloseEvent* event);

private slots:
void showMessageBox();
};

#endif // WIDGET_H
-----------------------------------------------------------------

Added after 20 minutes:

Noticed that when the widget is created if the user tries to close it for the first time - the message box that is shown is active. Try to close the widget for a second time - the displayed message box is inactive. The focus state of the QMessageBox on close is governed by the times you try to close the widget. The focus state has nothing to do with that whether you move the window or whether you click inside of the window before closing the widget.

Added after 41 minutes:

Another version of the code above, this time using an instance of QMessageBox object:

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>

class QMessageBox;

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = 0);
~Widget();

protected:
virtual void closeEvent(QCloseEvent* event);

private slots:
void showMessageBox();

private:
QMessageBox* m_messageBox;
};

#endif // WIDGET_H


Widget.cpp

#include "Widget.h"
#include <QCloseEvent>
#include <QMessageBox>
#include <QPushButton>
#include <QGridLayout>

Widget::Widget(QWidget *parent)
: QWidget(parent)
, m_messageBox(0)
{
QGridLayout* const gl = new QGridLayout( this );

QPushButton* const pb = new QPushButton("Show Message Box...");
gl->addWidget( pb );

connect( pb, SIGNAL(clicked()), this, SLOT(showMessageBox()) );

m_messageBox = new QMessageBox( QMessageBox::Warning, "Warning", "Close event or button click!", QMessageBox::Ok | QMessageBox::Cancel, this );
}

Widget::~Widget()
{
}

void Widget::closeEvent(QCloseEvent* event)
{
const int buttonId = m_messageBox->exec();

if ( QMessageBox::Cancel == buttonId )
{
event->ignore();
}
}

void Widget::showMessageBox()
{
const int buttonId = m_messageBox->exec();
Q_UNUSED( buttonId )
}