Qt: как заморозить некоторые кнопки на определенное время?

Я пытаюсь создать простую игру памяти с Qt 5.11.1 и C++, где у вас есть несколько плиток на экране, и вам нужно нажать на две и попытаться сопоставить изображения, которые они показывают.

Мои плитки реализованы как QPushButtons. Каждый раз, когда вы нажимаете на один, отображается изображение (вызывая showImage()метод, который меняет фон кнопки). При нажатии второй плитки, если есть совпадение, две кнопки отключены, поэтому вы не можете нажать на них снова (и вы получите более высокий балл). Однако, если вы не получили совпадения, две плитки, на которые вы только что нажали, вернутся в исходное состояние (без изображения) через 1 секунду (это позволяет пользователю "запомнить", какое изображение отображалось на каждой плитке),

Всякий раз, когда вы нажимаете на "плитку" (кнопка), она становится отключенной (button->setEnabled(false)). Если после щелчка по второй плитке не было совпадения, то обе плитки поворачиваются назад, а затем setEnabled(true) снова. Я использую однократный QTimer для вызова метода, который перевернет плитки:

QTimer::singleShot(1000, this, SLOT(turnTilesBack()));
firstTile->setEnabled(true);
secondTile->setEnabled(true);

Все работает, как и ожидалось, за исключением одного: поскольку QTimer работает в своем собственном потоке (или, насколько я понимаю, из того, что я прочитал), все доступные листы остаются включенными в течение 1000 миллисекунд, что позволяет пользователю продолжать нажимать на них. Однако, когда нет совпадения, я бы хотел "заморозить" кнопки, пока не истечет время ожидания QTimer, чтобы пользователь не мог продолжить игру, пока плитки не вернутся назад.

Поэтому вместо использования QTimer я пробовал это решение, которое я видел в этом вопросе ( Как мне создать функцию паузы / ожидания, используя Qt?):

QTime dieTime= QTime::currentTime().addSecs(1);
while (QTime::currentTime() < dieTime)
    turnTilesBack();

хотя я убрал эту строку: QCoreApplication::processEvents(QEventLoop::AllEvents, 100); так как это приведет к тому, что основной поток не будет зависать, а кнопки будут по-прежнему активными.

Но при таком подходе каждый раз, когда пользователь нажимает на вторую плитку, если нет совпадения, изображение даже не отображается, даже когда мой showImage() Метод вызывается перед кодом выше, и я не уверен, почему это так. Таким образом, пользователь знает, что совпадения не было, потому что через 1 секунду плитки возвращаются в свое исходное состояние, но они так и не увидели изображение на второй кнопке.

В качестве другого подхода я также отключил все кнопки, а затем, после истечения времени ожидания QTimer, снова включив только те, которые еще не были сопоставлены. Но это потребует дополнительной логики, чтобы отслеживать, какие плитки были сопоставлены. Так что пока я придерживаюсь

Есть ли более чистое решение? Может быть, есть способ заставить QTimer заморозить основной поток до истечения времени ожидания?

1 ответ

Простой способ включить / отключить всю группу QPushButtons разместить их на промежуточном виджете (в приведенном ниже примере я использовал QFrame)

Если вы хотите отключить все QPushButtons, вы просто отключаете фрейм, и все его дочерние виджеты будут отключены.

Когда вы хотите снова включить их, вы включаете фрейм.

Любые виджеты внутри рамки, которые уже отключены, не будут включены при повторном включении рамки, поэтому вы не потеряете свое состояние включения / выключения на отдельных кнопках.

Вот простой пример. Обратите внимание, что я использовал явные кнопки включения / выключения, которые действуют как прокси для вашего таймера.

#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QFrame>

int main(int argc, char** argv)
{
    QApplication* app = new QApplication(argc, argv);
    QMainWindow*  window = new QMainWindow();
    window->setFixedSize(1024, 200);

    QWidget* widget = new QWidget();
    QHBoxLayout layout(widget);

    QPushButton* enable  = new QPushButton("enable");
    QPushButton* disable = new QPushButton("disable");
    QFrame*      frame   = new QFrame();

    layout.addWidget(enable);
    layout.addWidget(disable);
    layout.addWidget(frame);

    QVBoxLayout frame_layout(frame);
    for (int i = 0; i < 5; ++i)
        frame_layout.addWidget(new QPushButton("click"));

    // this shows that an already disabled button remains disabled
    QPushButton* already_chosen = new QPushButton("click");
    frame_layout.addWidget(already_chosen);
    already_chosen->setEnabled(false);

    QObject::connect(enable,  &QPushButton::clicked, [&]{ frame->setEnabled(true); });
    QObject::connect(disable, &QPushButton::clicked, [&]{ frame->setEnabled(false); });

    window->setCentralWidget(widget);
    window->show();
    return app->exec();
}
Другие вопросы по тегам