我在C ++中为8-puzzle游戏编写了一个求解器,现在我正在尝试使用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-&gt; solve)需要一些时间。然后我在solutionMoves中获得了所执行动作的列表,我想要做的是在棋盘上显示这个动作,在动作和下一个动作之间有一些延迟。这个方法由我的main调用,如下所示:
QApplication app(argc, argv);
MainWindow w;
w.show();
w.solveGUI();
return app.exec();
结果是GUI挂起,一段时间后,它只显示解决方案,完全跳过移动。
我错过了什么?谢谢!
P.S。我认为我不需要为解算器使用不同的线程,因为我希望解算器在显示解决方案之前运行。是不是?
答案 0 :(得分:1)
您正在停止主线程(也会执行事件处理)并使其无法响应键盘/鼠标/窗口消息。
您应该使用异步计时器操作而不是休眠功能:使用QTimer来延迟显示下一个解决方案,并避免消息长时间无法应答。
答案 1 :(得分:1)
实际上运行主循环的app.exec()
处理所有事件,包括显示GUI。如果您想在此之前致电solve()
,那就没关系,但如果您想在exec()
之前实际显示和更新GUI,那就错了。我不确定它是否完全不可能,但它绝对不是正确的方法。
围绕它有两种方法。更典型的方法是使用QTimer
重新设计程序。然后一切都会顺利和响应。但有时这可能是单调乏味的。在你的情况下,它应该很容易。只需将结果保存在某个地方,然后每隔1000秒使用一个QTimer
对象调用一个插槽 - 它将与Sleep()
具有相同的效果,但会保持一切响应。
另一种解决方案是在solveGUI()
开始工作后调用exec()
方法。例如,可以使用QTimer::singleShot()
:
QTimer::singleShot(0, &w, SLOT(showGUI()));
return app.exec();
然后,在每个Sleep()
之前,您应该调用QApplication::processEvents()
,这基本上允许您临时控制权,处理所有待处理事件,包括GUI更新。这种方法有点容易,但由于GUI仍在每个Sleep()
冻结,因此效果较差。例如,如果用户想要退出应用程序,或者需要重新绘制窗口,则会导致GUI不舒服。
答案 2 :(得分:0)
在处理循环中有一个不错的article of methods to keep the GUI responsive。如果我认为这不是一个复杂的情况,只需在长的处理循环中插入QCoreApplication::processEvents();
。
答案 3 :(得分:0)
尝试以下操作:
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);
...
}
...
尚未测试-但应该可以使用!