Why does the Irrlicht window turn black when opening a QMessageBox?
-
I'm using Irrlicht in a Qt context. To do this, I have an IrrlichtWidget class which shows an Irrlicht device in a QWidget. Here is the relevant part of that class:
class IrrlichtWidget : public QWidget{ Q_OBJECT public: IrrlichtWidget(){ irr::SIrrlichtCreationParameters params; params.DriverType = irr::video::EDT_OPENGL; params.WindowId = (void*)winId(); m_device = irr::createDeviceEx(params); setAttribute(Qt::WA_OpaquePaintEvent); m_timer = new QTimer; m_timer->setInterval(0); QObject::connect(m_timer, &QTimer::timeout, [this](){ m_device->getVideoDriver()->beginScene(true, true, irr::video::SColor(255, 255, 255, 255)); m_device->getSceneManager()->drawAll(); m_device->getVideoDriver()->endScene(); m_device->run(); }); m_timer->start(); } private: irr::IrrlichtDevice *m_device; QTimer *m_timer; }
This works just fine until I use a QMessageBox. When I was programming with this class and wanted to use a QMessageBox, I noticed that whenever the message box opened, the Irrlicht widget turned black. To try to find out what was going on, I inserted a QMessageBox in the Irrlicht main loop so that it looked like this:
QObject::connect(m_timer, &QTimer::timeout, [this](){ m_device->getVideoDriver()->beginScene(true, true, irr::video::SColor(255, 255, 255, 255)); m_device->getSceneManager()->drawAll(); m_device->getVideoDriver()->endScene(); m_device->run(); QMessageBox::information(this, "Foo", "Bar"); });
When I did this, the background was black:
To compare, here is exactly the same program with the only difference that it doesn't have a QMessageBox in it:
I tried putting the QMessageBox everywhere in Irrlicht's main loop, at the beginning, at the end, in the middle, and it always does the same thing. It does this for every kind of Qt dialog boxes: QMessageBox, QFileDialog, etc.
I also tried removing the setAttribute(Qt::WA_OpaquePaintEvent) line in the constructor, and then the background was beige instead of black. Not the same color, but still the same problem.
It seems like the QMessageBox is somehow erasing the content of the Irrlicht widget. Why is it doing this? How can I fix it?
-
@SGaist said in Why does the Irrlicht window turn black when opening a QMessageBox?:
@Chris-Kawa's suggestion is the right one. In your case avoid using the static methods, create your own message box and call open rather than exec.
Thanks, @Chris-Kawa and @SGaist. I did as you suggested and I've written my own version of QMessageBox::information that works with Irrlicht:
#include <QMessageBox> #include <functional> void showMessageBox(std::function<void(QMessageBox::StandardButton)> lambda, QWidget *parent, const QString &title, const QString &text, QMessageBox::Icon icon = QMessageBox::Information, QFlags<QMessageBox::StandardButton> buttons = QMessageBox::Ok){ QMessageBox *messageBox = new QMessageBox(icon, title, text, buttons, parent); messageBox->open(NULL, NULL); QObject::connect(messageBox, &QMessageBox::finished, [messageBox, lambda](){ if(messageBox->clickedButton() == messageBox->button(QMessageBox::Ok)){ lambda(QMessageBox::Ok); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Open)){ lambda(QMessageBox::Open); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Save)){ lambda(QMessageBox::Save); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Cancel)){ lambda(QMessageBox::Cancel); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Close)){ lambda(QMessageBox::Close); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Discard)){ lambda(QMessageBox::Discard); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Apply)){ lambda(QMessageBox::Apply); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Reset)){ lambda(QMessageBox::Reset); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::RestoreDefaults)){ lambda(QMessageBox::RestoreDefaults); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Help)){ lambda(QMessageBox::Help); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::SaveAll)){ lambda(QMessageBox::SaveAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Yes)){ lambda(QMessageBox::Yes); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::YesToAll)){ lambda(QMessageBox::YesToAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::No)){ lambda(QMessageBox::No); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::NoToAll)){ lambda(QMessageBox::NoToAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Abort)){ lambda(QMessageBox::Abort); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Retry)){ lambda(QMessageBox::Retry); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Ignore)){ lambda(QMessageBox::Ignore); } else{ lambda(QMessageBox::NoButton); } }); }
How to use it:
-
The first parameter is a lambda which is executed when the dialog box gets closed. It takes one parameter, the button on which the user clicked.
-
The second parameter is the parent window, it works like the first parameter of QMessageBox::information.
-
The third parameter is the title of the window.
-
The fourth parameter is the message.
-
The fifth parameter is the icon of the message box. Possible values: QMessageBox::NoIcon, QMessageBox::Question, QMessageBox::Information, QMessageBox::Warning, QMessageBox::Critical.
-
The sixth parameter is the buttons of the dialog box, it works like the fourth parameter of QMessageBox::information.
Example:
showMessageBox([](QMessageBox::StandardButton result){ if(result == QMessageBox::Yes){ showMessageBox([](QMessageBox::StandardButton){}, myWidget, "Hello", "Hello world!"); } }, myWidget, "Title", "Do you want to open a new dialog box?", QMessageBox::Question, QMessageBox::Yes | QMessageBox::No);
The above code asks the user if he wants to open a new dialog box. If the user clicks on Yes, it opens a dialog box saying "Hello World!", otherwise, nothing happens. myWidget is the program's main window.
-
-
Try putting this:
QEventLoop wait; QTimer::singleShot(10, [&wait] { wait.quit(); }); wait.exec();
And see how that works, can you still see things rendered?
My suspicion's that the internal driving of the Qt's event loop is conflicting with the one in your engine (have experienced that one with OGRE). -
@Donald-Duck said in Why does the Irrlicht window turn black when opening a QMessageBox?:
It seems like the QMessageBox is somehow erasing the content of the Irrlicht widget. Why is it doing this? How can I fix it?
That's not what happens.
QMessageBox::information()
and friends are blocking functions that create local event loop.
If you call it from anywhere within your timer callback it will block until the message box is closed. Normal event processing is still happening in the inner loop but no next timeout event will occur until you exit current handler (timer signals don't stack). -
@kshegunov said in Why does the Irrlicht window turn black when opening a QMessageBox?:
Try putting this:
QEventLoop wait; QTimer::singleShot(10, [&wait] { wait.quit(); }); wait.exec();
And see how that works, can you still see things rendered?
My suspicion's that the internal driving of the Qt's event loop is conflicting with the one in your engine (have experienced that one with OGRE).Where should I put that exactly? I tried putting the QMessageBox before and after it and it and inside the lambda and it gave the same result. I also tried putting the QMessageBox right before wait.exec() and it turned black and stayed black after closing the message box.
-
Hi,
@Chris-Kawa's suggestion is the right one. In your case avoid using the static methods, create your own message box and call open rather than exec.
-
@SGaist said in Why does the Irrlicht window turn black when opening a QMessageBox?:
@Chris-Kawa's suggestion is the right one. In your case avoid using the static methods, create your own message box and call open rather than exec.
Thanks, @Chris-Kawa and @SGaist. I did as you suggested and I've written my own version of QMessageBox::information that works with Irrlicht:
#include <QMessageBox> #include <functional> void showMessageBox(std::function<void(QMessageBox::StandardButton)> lambda, QWidget *parent, const QString &title, const QString &text, QMessageBox::Icon icon = QMessageBox::Information, QFlags<QMessageBox::StandardButton> buttons = QMessageBox::Ok){ QMessageBox *messageBox = new QMessageBox(icon, title, text, buttons, parent); messageBox->open(NULL, NULL); QObject::connect(messageBox, &QMessageBox::finished, [messageBox, lambda](){ if(messageBox->clickedButton() == messageBox->button(QMessageBox::Ok)){ lambda(QMessageBox::Ok); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Open)){ lambda(QMessageBox::Open); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Save)){ lambda(QMessageBox::Save); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Cancel)){ lambda(QMessageBox::Cancel); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Close)){ lambda(QMessageBox::Close); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Discard)){ lambda(QMessageBox::Discard); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Apply)){ lambda(QMessageBox::Apply); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Reset)){ lambda(QMessageBox::Reset); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::RestoreDefaults)){ lambda(QMessageBox::RestoreDefaults); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Help)){ lambda(QMessageBox::Help); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::SaveAll)){ lambda(QMessageBox::SaveAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Yes)){ lambda(QMessageBox::Yes); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::YesToAll)){ lambda(QMessageBox::YesToAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::No)){ lambda(QMessageBox::No); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::NoToAll)){ lambda(QMessageBox::NoToAll); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Abort)){ lambda(QMessageBox::Abort); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Retry)){ lambda(QMessageBox::Retry); } else if(messageBox->clickedButton() == messageBox->button(QMessageBox::Ignore)){ lambda(QMessageBox::Ignore); } else{ lambda(QMessageBox::NoButton); } }); }
How to use it:
-
The first parameter is a lambda which is executed when the dialog box gets closed. It takes one parameter, the button on which the user clicked.
-
The second parameter is the parent window, it works like the first parameter of QMessageBox::information.
-
The third parameter is the title of the window.
-
The fourth parameter is the message.
-
The fifth parameter is the icon of the message box. Possible values: QMessageBox::NoIcon, QMessageBox::Question, QMessageBox::Information, QMessageBox::Warning, QMessageBox::Critical.
-
The sixth parameter is the buttons of the dialog box, it works like the fourth parameter of QMessageBox::information.
Example:
showMessageBox([](QMessageBox::StandardButton result){ if(result == QMessageBox::Yes){ showMessageBox([](QMessageBox::StandardButton){}, myWidget, "Hello", "Hello world!"); } }, myWidget, "Title", "Do you want to open a new dialog box?", QMessageBox::Question, QMessageBox::Yes | QMessageBox::No);
The above code asks the user if he wants to open a new dialog box. If the user clicks on Yes, it opens a dialog box saying "Hello World!", otherwise, nothing happens. myWidget is the program's main window.
-