Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Bhted от Февраль 20, 2015, 13:14



Название: QTimer, QThread
Отправлено: Bhted от Февраль 20, 2015, 13:14
Нужен таймер, который будет работать в отдельном потоке приложения и после определённого числа переполнений останавливаться. Почему таймер в отдельном потоке? У меня в приложении есть ещё события, и пока они отрабатывают сигналы от таймер выстраиваются в очередь и потом одной пачкой отрабатывают, что мне не подходит. А не подходит, потому что таймер при переполнении рисует разные положения стрелочного индикатора. Визуально стрелка из крайнего положения сразу дёргается в нуль, без плавного движения.
Вот нашёл в сети код, чтобы таймер работал в отдельном потоке. Тут как-то можно из потока сказать таймеру стоп?

worker.h
Код:

#include <QDebug>
#include <QThread>

class Worker : public QObject
{
    Q_OBJECT

public:

    Worker();
private slots:
    void onTimeout();

private:
    int counter;
};




worker.cpp
Код:
#include "worker.h"

Worker::Worker()
{
    counter = 0;
}

void Worker::onTimeout()
{
    qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
    counter++;
    //if (counter == 3)
    // stopTimer();
}



mainwindow.h
Код:
#include <QMainWindow>
#include <QDebug>
#include <QThread>
#include <QTimer>
#include "worker.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    QThread t;
    QTimer timer;
    Worker worker;

private slots:
    void onInit();
    void onStart();
};



mainwindow.cpp
Код:
...
    connect(ui->pushButton_2, SIGNAL(clicked()), this, SLOT(onStart()));
...
//------------------------------------------------------------------------------
void MainWindow::onStart()
{
    qDebug()<<"From main thread: "<<QThread::currentThreadId();

    QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
    timer.start(1000);

    worker.moveToThread(&t);

    t.start();
}


Название: Re: QTimer, QThread
Отправлено: Fregloin от Февраль 20, 2015, 15:06
что мешает запихнуть таймер в Worker и общаться посредством сигналов и слотов?


Название: Re: QTimer, QThread
Отправлено: Bhted от Февраль 20, 2015, 15:26
Я тогда получу "timers cannot be stopped from another thread"
Вот как я делал:
worker.h
Код:
class Worker : public QObject
{
    Q_OBJECT

public:

    Worker();
    void start();

private slots:
    void onTimeout();
    void stop();

signals:
     void stopTimer();

private:
    int counter;
    QTimer timer;
};

worker.cpp
Код:
Worker::Worker()
{
    counter = 0;
    QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    QObject::connect(this, SIGNAL(stopTimer()), this, SLOT(stop()));
}

void Worker::start()
{
    timer.start(1000);
}

void Worker::stop()
{
    timer.stop();
}

void Worker::onTimeout()
{
    qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
    counter++;
    if (counter == 3)
    {
        emit stopTimer();
        //timer.stop();
    }
}

mainwindow.cpp
Код:
//------------------------------------------------------------------------------
void MainWindow::onStart()
{
    qDebug()<<"From main thread: "<<QThread::currentThreadId();

    worker.moveToThread(&t);
    worker.start();

    t.start();
}


Название: Re: QTimer, QThread
Отправлено: Пантер от Февраль 20, 2015, 15:29
Работай через сигналы/слоты или инвокМетод, а не прямыми вызовами методов.


Название: Re: QTimer, QThread
Отправлено: Bhted от Февраль 20, 2015, 15:50
Работай через сигналы/слоты или инвокМетод, а не прямыми вызовами методов.
А конкретней можете сказать? Я вроде через сигналы/слоты работаю. Когда надо остановить таймер, посылаю сигнал emit stopTimer(), который связан со слотом stop() и вот тогда я и получаю "timers cannot be stopped from another thread".

Зато на прямой вызов worker.start(), который делает timer.start(1000), он почему-то не ругается, что стартовать не хочет.


Название: Re: QTimer, QThread
Отправлено: Пантер от Февраль 20, 2015, 15:52
Код
C++ (Qt)
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::QueuedConnection);


Название: Re: QTimer, QThread
Отправлено: Igors от Февраль 20, 2015, 15:58
Нужен таймер, который будет работать в отдельном потоке приложения и после определённого числа переполнений останавливаться. Почему таймер в отдельном потоке? У меня в приложении есть ещё события, и пока они отрабатывают сигналы от таймер выстраиваются в очередь и потом одной пачкой отрабатывают, что мне не подходит. А не подходит, потому что таймер при переполнении рисует разные положения стрелочного индикатора. Визуально стрелка из крайнего положения сразу дёргается в нуль, без плавного движения.
Насчет "пачки событий от таймера" это Вы загнули, там одно событие. Ну ладно, допустим вынесли как-то таймер в поток и тогда получаете от него события "по расписанию". А толку? Ведь рисовать "стрелочный индикатор" можно только в главной нитке которая забита др событиями. Поэтому лучше использовать вторичный цикл processEvents


Название: Re: QTimer, QThread
Отправлено: Bhted от Февраль 20, 2015, 16:15
Код
C++ (Qt)
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::QueuedConnection);
Ммм... если у меня сигналы и слоты от таймера внутри Worker будут помещаться/извлекаться в/из очереди, то как это повлияет на "timers cannot be stopped from another thread"?... Я Вас, видимо, не так понял.

Цитировать
Насчет "пачки событий от таймера" это Вы загнули, там одно событие. Ну ладно, допустим вынесли как-то таймер в поток и тогда получаете от него события "по расписанию". А толку? Ведь рисовать "стрелочный индикатор" можно только в главной нитке которая забита др событиями. Поэтому лучше использовать вторичный цикл processEvents
Ну, там таймер приконнекчен через Qt::QueuedConnection, поэтому я решил, что из-за этого там будет сразу несколько событий, также по индикатору это видно.

Цитировать
Поэтому лучше использовать вторичный цикл processEvents
Да вот когда я вызывал QApplication::processEvents(), там начинались проблемы... Сейчас ещё раз посмотрю, что там было.


Название: Re: QTimer, QThread
Отправлено: Пантер от Февраль 20, 2015, 16:18
Как я понял, у тебя идут "тяжелые" вычисления, во время которых ты хочешь крутить индикатор. Если так, то ты не то выносишь в поток - нужно выносить вычисления, а не таймер.


Название: Re: QTimer, QThread
Отправлено: Igors от Февраль 20, 2015, 16:39
Да вот когда я вызывал QApplication::processEvents(), там начинались проблемы... Сейчас ещё раз посмотрю, что там было.
Насчет processEvents() это я погорячился :) - если в очереди много событий, они и будут извлекаться. Ну а почему бы самому не отслеживать истекшее время и обновлять индикатор синхронно? Часто это выглядит примерно так (псевдокод)
Код
C++ (Qt)
// расчеты
for (int i = 0; i < data.size(); ++i) {
DoCalc(i);
if ((i % 100) == 0)    // обновляем каждые 100 итераций
  UpdateIndicator(i);
}
 
void UpdateIndicator( int val )
{
 static QTime theTime = QTime::currentTime();
 if (theTime.elapsed() < 300) return;
 theIndicator->setValue(val);
 theIndicator->repaint();
 theTime.restart();
}
 

Как я понял, у тебя идут "тяжелые" вычисления, во время которых ты хочешь крутить индикатор. Если так, то ты не то выносишь в поток - нужно выносить вычисления, а не таймер.
Qt рекомендует так делать потому что до этого делали по-старинке (как выше). Но это не значит что новый способ всегда лучше  :)


Название: Re: QTimer, QThread
Отправлено: Bhted от Февраль 20, 2015, 17:08
У меня много частых событий (каждый 100 мс) от com-порта. С портом работаю через QtSerialPort. Читаю данные через сигнал от порта, сигналы выстраиваются в очередь с помощью Qt::QueuedConnection. Данные мы обрабатываем и строим график и рисуем стрелку. Логика работы стрелки такая: установили заданное значение и за n-секунд стрелка возвращается в исходное положение. Значения, устанавливаемые стрелке, меняются не слишком часто.
График строится отлично при такой интенсивности входных данных. А вот стрелка, повторюсь, мельком устанавливается в заданное положение и сразу в исходное возвращается.

Значит, попробую запихать чтение данных из порта и их обработку в отдельный поток.
Ну и также попробую "отслеживать истекшее время и обновлять индикатор синхронно". Только счётчик i цикла станет членом класс или статической переменной. В общем, попробую. Суть я уловил.


Название: Re: QTimer, QThread
Отправлено: Alexu007 от Февраль 22, 2015, 09:57
Для отсчитывания промежутков времени в потоке необязательно прикручивать таймер, в потоке есть функция паузы. В поток можно передавать макс. значение стрелки, и он через фиксированные промежутки времени будет посылать сигналы, по которым можно возвращать стрелку в 0.

Код
C++ (Qt)
detki::detki(int x)
{
   postrelka = x;
}
 
 
void detki::run()
{
   // приоритет потока
   this->setPriority(QThread::IdlePriority);
 
       for (int i = postrelka; i > 0; i--)
       {
           //пауза в микросекундах
           this->msleep(500);                
 
           emit YourSignal(QString::number(i));
       }
 
   exec();
}