QT: Как зацикливать метод каждую секунду? С++

Я создал проект Qt, который отображает круг на виджете. Затем у меня есть метод, который перерисовывает круг в разных позициях каждый раз, когда я вызываю метод. Я хочу запустить этот метод в цикле for, скажем, десять раз, и показать каждую из 10 позиций, в которых круг перерисовывается каждую секунду.

Что-то вроде:

void method::paintEvent(QPaintEvent * p)
{

//code

    for(int i=0; i<10;i++)//do this every second
    {
       method(circle[i]); //co-ordinates change
       circle[i].pain( & painter); //using QPainter
    }

//code

}

Я читал о QTimer, но не знаю, как его использовать. И функция сна не работает.


person Leo    schedule 15.04.2014    source источник
comment
Функция сна работает, но вы никогда не должны использовать ее в потоке графического интерфейса. Ваши пользователи будут ненавидеть вас за это. Секундный сон в потоке графического интерфейса буквально блокирует всю обработку событий на одну секунду. В это время ваше приложение не перерисовывается, не реагирует на клики мыши, ввод с клавиатуры, запросы на его закрытие и т. д. Оно просто зависает.   -  person Kuba hasn't forgotten Monica    schedule 16.04.2014
comment
Вы имеете в виду, что вам нужна одна итерация в секунду? Или все 10 итераций сразу должны происходить каждую секунду?   -  person tmpearce    schedule 17.04.2014


Ответы (4)


Все, что вам нужно сделать, это вызвать update() из события таймера. Метод update() планирует paintEvent для виджета.

Недопустимо рисовать на виджете за пределами paintEvent - это ошибка, которую делали все другие ответы на момент публикации этого ответа. Простой вызов метода paintEvent не является обходным путем. Вы должны позвонить update(). Вызов repaint() тоже сработает, но делайте это только в том случае, если вы поняли разницу с update() и у вас есть для этого веская причина.

class Circle;

class MyWidget : public QWidget {
  Q_OBJECT
  QBasicTimer m_timer;
  QList<Circle> m_circles;
  void method(Circle &);
  void paintEvent(QPaintEvent * p) {
    QPainter painter(this);
    // WARNING: this method can be called at any time
    // If you're basing animations on passage of time,
    // use a QElapsedTimer to find out how much time has
    // passed since the last repaint, and advance the animation
    // based on that.
    ...
    for(int i=0; i<10;i++)
    {
       method(m_circles[i]); //co-ordinates change
       m_circles[i].paint(&painter);
    }
    ...
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() != m_timer.timerId()) {
      QWidget::timerEvent(ev);
      return;
    }
    update();
  }
public:
  MyWidget(QWidget*parent = 0) : QWidget(parent) {
    ...
    m_timer.start(1000, this);
  }
};
person Kuba hasn't forgotten Monica    schedule 16.04.2014
comment
Я почти уверен, что вы хотите удалить цикл из paintEvent, поскольку поведение цикла - это то, что должно происходить через таймер. - person tmpearce; 17.04.2014
comment
@tmpearce: Я не так прочитал вопрос: насколько я понимаю, комментарий относится ко всему блоку. Спрашивающий может уточнить это. - person Kuba hasn't forgotten Monica; 17.04.2014

Как вы уже догадались, QTimer является правильным механизмом для использования здесь. Как подойти к его настройке?

Вот один из вариантов:

class MyClass : public QObject
{
   public:
   MyClass():i(0)
   {
       QTimer::singleShot(1000,this,SLOT(callback()));//or call callback() directly here
   } //constructor

   protected:
   unsigned int i;
   void paintEvent(QPaintEvent * p)
   {    
     //do your painting here  
   }

   public slots:
   void callback()
   {
      method(circle[i]); //co-ordinates change
      //circle[i].pain( & painter); //don't use QPainter here - call update instead
      update();
      ++i;//increment counter
      if(i<10) QTimer::singleShot(1000,this,SLOT(callback()));
   }

}
person tmpearce    schedule 15.04.2014
comment
Отрисовка виджета не может быть выполнена в слоте, запускаемом по таймеру, за пределами paintEvent. - person Kuba hasn't forgotten Monica; 16.04.2014
comment
@KubaOber Верно ... Я не обращал внимания на эту часть вопроса, а скорее на неспособность правильно настроить QTimer. - person tmpearce; 16.04.2014

Попробуйте что-то вроде этого:

class MyDrawer : public QObject
{
    Q_OBJECT
    int counter;
    QTimer* timer;

public:
    MyDrawer() : QObject(), counter(10)
    {
        timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(redraw()));
        timer->start(1000);
    }

public slots:
    void redraw()
    {
        method(circle[i]); //co-ordinates change
        circle[i].pain( & painter);

        --counter;
        if (counter == 0) timer->stop();
    }

};

Не забудьте запустить moc для этого файла, хотя, если ваш класс является QObject, вы, вероятно, уже это сделали.

person vdavid    schedule 15.04.2014

Try this.

for(int i=0; i<10;i++)//do this every second
{
   method(circle[i]); //co-ordinates change
   circle[i].pain( & painter);
   sleep(1000);// you can also use delay(1000);

}

Использовать sleep() Функция, называемая sleep(int ms), объявленная в которой заставляет программу ждать указанное время в миллисекундах.

person lost_boatman    schedule 15.04.2014
comment
Блокировка цикла событий сделает графический интерфейс невосприимчивым. Такой код - это мгновенный отрицательный голос от меня. - person Kuba hasn't forgotten Monica; 16.04.2014
comment
Я согласен с Кубой, что это не очень хорошая идея. Qt разработан вокруг идей, позволяющих избежать всего этого. - person lpapp; 17.04.2014