Qt - GUI freezing

Qt - GUI freezing

本文关键字:freezing GUI Qt      更新时间:2023-10-16

我用C++为8拼图游戏编写了一个求解器,现在我正试图使用Qt为它提供一个GUI。

基本上,我有一个类型为"Board"的底层对象,它表示谜题的板,并且我将GUI组织为QPushButton的网格。然后我有一个方法updateUI,它根据Board将正确的文本关联到每个按钮。类似的东西

    for(int i=0; i<Board::MATRIX_DIM * Board::MATRIX_DIM; i++)
    {
        m_buttons[i]->setText(m_values[i]);
    }

在另一种方法(solveGUI)中,我有

void MainWindow::solveGUI()
{
    m_game->solve();
    int solutionDepth = m_game->getSolutionDepth();
    Move *solutionMoves = m_game->getSolutionMoves();
    for(int i=0; i<solutionDepth; i++)
    {
        Move m = solutionMoves[i];
        m_board.performMove(m);     /* perform the move on the Board object */
        updateUI();       /* should update the GUI so that it represents the Board */
        Sleep(1000);
    }
}

其中第一行(m_game->solve)需要一些时间。然后,我获得了解决方案moves中执行的动作列表,我想做的是在棋盘上显示这些动作,在一个动作和下一个动作之间有一些延迟。这个方法是由我的main调用的,看起来像这样:

QApplication app(argc, argv);
MainWindow w;
w.show();
w.solveGUI();
return app.exec();

结果是GUI挂起,一段时间后,它只显示解决方案,完全跳过移动。

我错过了什么?非常感谢。

附言:我认为解算器不需要其他线程,因为我希望解算器在显示解决方案之前运行。对吗?

实际上是app.exec()运行处理所有事件(包括显示GUI)的主循环。如果您想在此之前调用solve(),这是可以的,但如果您想在exec()之前实际显示和更新GUI,这是错误的。我不确定这是否完全不可能,但这绝对不是正确的方法。

有两种方法。更规范的方法是使用QTimer重新设计程序。然后一切都会很顺利,反应灵敏。但这有时会很乏味。不过,在你的情况下,这应该很容易。只需将结果保存在某个地方,并每1000秒使用QTimer对象调用一个插槽——它将具有与Sleep()相同的效果,但会使所有内容都保持响应。

另一种解决方案是在exec()启动其作业后调用solveGUI()方法。例如,可以使用QTimer::singleShot():

QTimer::singleShot(0, &w, SLOT(showGUI()));
return app.exec();

然后,在每次Sleep()之前,您应该调用QApplication::processEvents(),这基本上允许您临时进行屈服控制,处理所有挂起的事件,包括GUI更新。这种方法稍微简单一些,但由于GUI仍然在每个Sleep()处冻结,因此它的性能较差。例如,如果用户想要退出应用程序,或者需要重新绘制窗口,这将导致不舒服的GUI滞后。

您正在暂停主线程(它还负责事件处理),并使其无法响应键盘/鼠标/窗口消息。

您应该使用异步计时器操作而不是睡眠功能:使用QTimer来延迟显示下一个解决方案,并避免消息长时间未得到答复。

有一篇很好的方法文章可以让GUI在处理循环过程中保持响应。如果我认为这不是一个复杂的情况,只需在长处理循环中插入QCoreApplication::processEvents();即可。

尝试以下操作:

void MainWindow::Wait(int interval ) {
    QTime timer = new QTime;
    timer.restart();
    while(timer.elapsed() < interval) {
        QApplication::processEvents();
    }
}
...
 
for(...) { 
//wait 1 second (1000 milliseconds) between each loop run at first
Wait(1000);
...
}
...

尚未测试-但应该可以工作(可能有一些cpu负载)!