c++qtirrlichtqmessagebox

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:

black background with message box

To compare, here is exactly the same program with the only difference that it doesn't have a QMessageBox in it:

what i want

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?


Solution

  • The problem is that the static functions of QMessageBox (like QMessageBox::information) are blocking the main loop, making the Irrlicht widget turn black. A way to avoid this is creating a QMessageBox object and open it using the open() method. The exec() method which is used by the static functions blocks the main loop and makes the Irrlicht widget turn black, but the open() method doesn't block the main loop and therefore it works.

    I've created a function using open() which does the same thing as QMessageBox::information but works with Irrlicht. Since the open() method doesn't block the main loop, the function that I created uses lambdas and the finished signal to handle what happens after the message box closes. Here is the function along with an example of how to use it:

    #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);
            }
        });
    }
    

    The below 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.

    IrrlichtWidget myWidget = new IrrlichtWidget;    //IrrlichtWidget is the class mentioned in the question
    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);