【问题标题】:Qt - Block user interface while computer is making a moveQt - 在计算机移动时阻止用户界面
【发布时间】:2018-09-15 11:38:01
【问题描述】:

为了学习 Qt,我决定构建一个井字游戏程序。用户界面很简单,但我偶然发现了一个我无法解决的问题。我希望人类用户能够与计算机对战,因此,我需要在 cpu 进行计算时阻止 UI。我尝试过使用 setEnabled 但它没有按照我想要的方式工作,我只是在前面放了一个 Qframe 并使用 hide() 和 show() 函数来阻止与用户的交互。问题是它不起作用。当我单击 QPushButton 时,单元格会更新并调用 show 函数。然后我做任何我需要的计算,当它完成时我只调用hide()。下面是一个示例代码。

void TicTacToe_Gui::on_cell11_clicked(){


   ui->cell11->setText("wait...");
   ui->cell11->repaint();
   ui->frontPanel->show();

   for(int i=0; i<=1000000000; i++){
   }

   ui->cell11->setText("done");
   ui->frontPanel->hide();

}

其中单元格是 QPushButton。

好吧,当 for 循环中的代码正在运行并且我单击其他单元格时,其他按钮的动画(我的意思是推送动画)没有呈现,并且似乎 UI 被阻止但只要代码停止其他按钮更新,就像单击一样。检查下面的图片

我知道类似的问题已经被问过很多次,而且我已经阅读了其中的大部分内容,但我似乎无法解决问题。有人可以帮帮我吗?

【问题讨论】:

  • 以下究竟是什么意思:我尝试使用 setEnabled,但它没有按照我想要的方式工作,您遇到了什么问题?你想要什么?
  • 您可以通过 github、gist 或类似方式分享您的代码以更好地理解。

标签: c++ qt qt5


【解决方案1】:

为了不处理多个按钮的多个信号和插槽,建议使用 QButtonGroup 并将 buttonClicked 信号连接到单个插槽,该类可以将按钮与 id 关联,通过该 id 我们可以获得按钮:

group = new QButtonGroup(this);
for(int i=0; i < 3; i++){
    for(int j=0; j < 3; j++){
        board[i][j] = TicTacToe_State::N;
        QToolButton *btn = new QToolButton(this);
        btn->setFixedSize(100, 100);
        btn->setStyleSheet("QToolButton::disabled{color:black}");
        group->addButton(btn, /*id*/ 3*i+j);
        lay->addWidget(btn, i , j);
    }
}
connect(group, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &TicTacToe_Gui::onClicked);

问题是因为你执行的繁重任务阻塞了GUI的主循环,对于繁重的任务,建议使用辅助线程,当获得一些结果时,将其发送到主线程。在 Qt 中有多种选择,但对于这种情况,最简单的方法是将 QtConcurrent::runQFutureWatcherQEventLoop 一起使用,在此示例中,我将创建一个定义重算法结果的结构:

struct TicTacToe_Result
{
    int row;
    int col;
};
enum TicTacToe_State{N, X, O};
TicTacToe_State board[3][3];

结构是算法的输出,所以如果你想在结果中添加更多字段,只需添加一个新成员。枚举描述了每个按钮可能的状态,数组是存储按钮板信息的矩阵,这是类的成员,所以lambda函数可以访问的时候传入&。

void TicTacToe_Gui::onClicked(int id)
{
    QAbstractButton *btnO = group->button(id);
    btnO->setText("O");
    int row = id/3;
    int col = id%3;
    board[row][col] = TicTacToe_State::O;
    QFutureWatcher<TicTacToe_Result> watcher;
    QFuture<TicTacToe_Result> future = QtConcurrent::run([&](){
        // here the heavy algorithm
        for(int i=0; i<=1000000000; i++){
        }
        return TicTacToe_Result{r, c};
    });

    watcher.setFuture(future);
    QEventLoop loop;
    connect(&watcher, &QFutureWatcherBase::finished, &loop, &QEventLoop::quit);
    loop.exec();
    TicTacToe_Result r = watcher.result();
    board[r.row][r.col] = TicTacToe_State::X;
    QAbstractButton *btnX = group->button(3*r.row+r.col);
    btnX->setText("X");
}

QtConcurrent通过QFuture返回结果,这个类可以同步和异步等待,异步我们使用QFutureWatcher,要能够顺序执行就使用QEventLoop

我在下面link中实现了一个例子

【讨论】:

  • 非常感谢您抽出宝贵时间。您的回答和建议的解决方案非常具有教育意义。我将花一天时间研究您的代码。非常感谢
  • @PML 如果我的解决方案为您服务,请不要忘记将其标记为正确。
猜你喜欢
  • 1970-01-01
  • 2012-08-12
  • 1970-01-01
  • 2018-03-17
  • 2023-04-02
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
  • 2013-01-07
相关资源
最近更新 更多