Qt: как заморозить некоторые кнопки на определенное время? - PullRequest
0 голосов
/ 04 января 2019

Я пытаюсь построить простую игру памяти с 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 Ответ

0 голосов
/ 05 января 2019

Простой способ включить / отключить всю группу 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();
}
...