为什么打开 QMessageBox 时 Irrlicht window 变黑?

Why does the Irrlicht window turn black when opening a QMessageBox?

我在 Qt 上下文中使用 Irrlicht。为此,我有一个 IrrlichtWidget class,它在 QWidget 中显示了一个 Irrlicht 设备。这是 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;
}

在我使用 QMessageBox 之前,它工作得很好。当我用这个 class 编程并想使用 QMessageBox 时,我注意到每当消息框打开时,Irrlicht 小部件就会变黑。为了弄清楚发生了什么,我在 Irrlicht 主循环中插入了一个 QMessageBox,它看起来像这样:

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

当我这样做时,背景是黑色的:

为了比较,这里是完全相同的程序,唯一的区别是它没有 QMessageBox

我试着把 QMessageBox 放在 Irrlicht 主循环的任何地方,在开头、结尾、中间,它总是做同样的事情。它为每种 Qt 对话框执行此操作:QMessageBoxQFileDialog

我还尝试删除构造函数中的 setAttribute(Qt::WA_OpaquePaintEvent) 行,然后背景变成了米色而不是黑色。不一样的颜色,还是一样的问题

QMessageBox 似乎以某种方式删除了 Irrlicht 小部件的内容。为什么要这样做?我该如何解决?

问题是 QMessageBox 的静态函数(如 QMessageBox::information)阻塞了主循环,使 Irrlicht 小部件变黑。避免这种情况的一种方法是创建一个 QMessageBox 对象并使用 open() 方法打开它。静态函数使用的 exec() 方法会阻塞主循环并使 Irrlicht 小部件变黑,但是 open() 方法不会阻塞主循环,因此可以正常工作。

我使用 open() 创建了一个函数,它的作用与 QMessageBox::information 相同,但适用于 Irrlicht。由于 open() 方法不会阻塞主循环,我创建的函数使用 lambda 和 finished 信号来处理消息框关闭后发生的事情。这是函数以及如何使用它的示例:

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

下面的代码询问用户是否要打开一个新对话框。如果用户单击是,它会打开一个对话框,显示 "Hello World!",否则什么也不会发生。

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