Skocz do zawartości
szeryf

Odmierzanie czasu w QT

Pomocna odpowiedź

Witam,

mam pytanie dotyczące odmierzania czasu w QT. Wykonałem timer według tego tutoriala, później postanowiłem zmodyfikować kod, aby odmierzanie czasu było o zera z dokładnością do milisekundy (jak w stoperze), napisałem sobie poniższy kod:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(myfunction()));

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::myfunction()
{

   QString milisekundy_text = QString::number(milisekundy);
   ui->label_milisekundy->setText(milisekundy_text);
   milisekundy++;

   if (milisekundy == 999){
        milisekundy = 0;
        sekundy++;
        QString sekundy_text = QString::number(sekundy);
        if (sekundy<10)
        ui->label_sekundy->setText("00"+sekundy_text);
        else if ((sekundy>=10) && (sekundy<100))
        ui->label_sekundy->setText("0"+sekundy_text);
        else
        ui->label_sekundy->setText(sekundy_text);

    }
   }


void MainWindow::on_pushButtonStartTime_clicked()
{
      timer->start();
      timer->setInterval(1);
}

void MainWindow::on_pushButtonStopTime_clicked()
{
    timer->stop();
}

void MainWindow::on_pushButtonTimeReset_clicked()
{
    timer->stop();
    milisekundy = 0;
    QString milisekundy_text = QString::number(milisekundy);
    ui->label_milisekundy->setText("00"+milisekundy_text);
    sekundy = 0;
    QString sekundy_text = QString::number(sekundy);
    ui->label_sekundy->setText("00"+sekundy_text);
}

i sama aplikacja działa, tylko że czas jest odmierzany znacznie wolniej,  niż na normalnym stoperze np. w telefonie (po 20 sekundach na telefonie aplikacja w Qt pokazuje nieco ponad 12 "sek"). Czy ktoś wyjaśni mi, jak powinienem to zrobić właściwie?

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Cześć @szeryf, jak zobaczysz sobie tutaj: https://doc.qt.io/qt-5/qtimer.html#accuracy-and-timer-resolution:

  1. "The accuracy of timers depends on the underlying operating system and hardware. "
  2. "All timer types may time out later than expected if the system is busy or unable to provide the requested accuracy. In such a case of timeout overrun, Qt will emit timeout() only once, even if multiple timeouts have expired, and then will resume the original interval"

To dowiesz się, że wskazania mogą oscylować, ale generalnie powinny być akceptowalne w dużej mierze aplikacji używających QTimer, natomiast myślę, że problem u Ciebie nie występuje w dokładności QTimer, tylko podejściu, które zastosowałeś: stosujesz interwał = 1ms, czyli praktycznie wykorzystujesz QTimer do granicy możliwości, co może skutkować tym, że będziesz tracił wystąpienia sygnalizowane przez timeout() - czyli timer pokaże mniejszą niż zmierzoną długość czasu niż ta którą zmierzyłeś telefonem. 

Wydaje mi się że do tego zadania zdecydowanie lepiej nadaje się QElapsedTimer, który działa dokładnie tak jak chcesz - czyli stoper. QElapsedTimer możesz użyć do zmierzenia czasu, a QTimer do cyklicznego (np. co 60Hz) odświeżania interfejsu. Wtedy powinieneś uzyskać dokładnie to co chcesz.

Edytowano przez Matthew11
język polski...
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dzięki za podpowiedź, właśnie próbuje wykorzystać QElapsedTimer, nie mogę tylko rozszyfrować co oznacza zwracana liczba tej wielkości (8583652368141)? 

Udostępnij ten post


Link to post
Share on other sites
9 minut temu, szeryf napisał:

Dzięki za podpowiedź, właśnie próbuje wykorzystać QElapsedTimer, nie mogę tylko rozszyfrować co oznacza zwracana liczba tej wielkości (8583652368141)? 

Strzelam, że nie uruchomiłeś timera: pewnie brakuje wywołania tej metody: start(), możesz to sprawdzić - czy timer działa prawidłowo za pomocą isValid(),.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Najprostszy przykład (projekt z szablonu Qt Console Application):

#include <QCoreApplication>
#include <QDebug>
#include <QElapsedTimer>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QElapsedTimer timer;

    qDebug() << timer.elapsed();
    timer.start();

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

    qDebug() << timer.elapsed();

    return a.exec();
}

Wynik:

-9223366942238
235

Bez widocznego kodu @szeryf bardziej nie pomogę.

Edytowano przez Matthew11
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Ok. Poradziłem sobie z tą liczbą, poprzez dzielenia całkowite i obliczanie reszt udało mi się oddzielić milisekundy i sekundy w timerze. Mam jeszcze ostatni chyba problem, podpiąłem uruchomienie timera po przycisk,

void MainWindow::on_pushButtonLedOn_clicked()
{
    timer->start(17); // tu zrobiłem te 60 Hz oświeżania dla interfejsu
    QElapsedTimer etimer;
    etimer.start();
}

void MainWindow::on_pushButtonLedOff_clicked()
{
    timer->stop();
    etimer.restart();
}

ale jak kliknę ten przycisk, to upływ czasu jest liczony we właściwym tempie, ale nie od zera tylko tak jakby timer się załączał już w momencie uruchomienia aplikacji. Jak kliknę stop i za chwilę start, to restart timera restartuje go, ale nie zatrzymuje, czyli po ponownym kliknięciu start, już mam jakiś bieżący upływ czasu. Na razie nie wiem, jak sobie z tym poradzić.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Coś mi się tutaj nie podoba, masz w slocie on_pushButtonLedOn_clicked() lokalny obiekt QElapsedTimer i chyba także taki sam obiekt z taką samą nazwą jako członka MainWindow. Z tego pierwszego nie korzystasz bo zaraz bo wyjściu z tego slotu zostaje on niszczony. A ten który jest w drugim slocie (jako członek MW) jest tylko restartowany. Czy to tłumaczy zachowanie które opisujesz?

Edytowano przez Matthew11
  • Lubię! 1
  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

W klasie MainWindow mam obiekt (timer) klasy QTimer, bo wcześniej na nim próbowałem robić odmierzanie czasu, ale wtedy miałem te opóźnienia, które opisałem wcześniej, teraz próbuję z obiektem (etimer) klasy QElapsedTimer, obiekt timer zostawiłem do odświeżania wizualnego interfejsu,  może wkleję cały kod, zerknij na voida myfunction, może tam coś namieszałem, w załączniku wrzuciłem screena z interfejsem

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QList>
#include <QSerialPortInfo>
#include <QDateTime>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->device = new QSerialPort(this);
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(myfunction()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::myfunction()
{
    milisekundy = etimer.elapsed()%1000;
   QString milisekundy_text = QString::number(milisekundy);
   if (milisekundy<10)
   ui->label_milisekundy->setText("00"+milisekundy_text);
   else if ((milisekundy>=10) && (milisekundy<100))
   ui->label_milisekundy->setText("0"+milisekundy_text);
   else
   ui->label_milisekundy->setText(milisekundy_text);

        sekundy = (int((etimer.elapsed()%60000)/1000));
        QString sekundy_text = QString::number(sekundy);
        if (sekundy<10)
        ui->label_sekundy->setText("00"+sekundy_text);
        else if ((sekundy>=10) && (sekundy<100))
        ui->label_sekundy->setText("0"+sekundy_text);
        else
        ui->label_sekundy->setText(sekundy_text);


   }


void MainWindow::on_pushButtonSearch_clicked()
{
    ui->comboBoxDevices->clear();

    this->addToLogs("Szukam urządzeń...");

    QList<QSerialPortInfo> devices;
    devices = QSerialPortInfo::availablePorts();

    for(int i = 0; i < devices.count(); i++)
    {
        this->addToLogs("Znalazłem urządzenie: " + devices.at(i).portName() + " " + devices.at(i).description());
        ui->comboBoxDevices->addItem(devices.at(i).portName() + "\t" + devices.at(i).description());
    }
}

void MainWindow::addToLogs(QString message)
{
    QString currentDateTime = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss");
    ui->textEditLogs->append(currentDateTime + "\t" + message);
}

void MainWindow::sendMessageToDevice(QString message)
{
    if(this->device->isOpen() && this->device->isWritable())
    {
        this->addToLogs("Wysyłam informacje do urządzenia " + message);
        this->device->write(message.toStdString().c_str());
    }
    else
    {
        this->addToLogs("Nie mogę wysłać wiadomości. Port nie jest otwarty!");
    }
}

void MainWindow::on_pushButtonConnect_clicked()
{
    if(ui->comboBoxDevices->count() == 0)
    {
        this->addToLogs("Nie wykryto żadnych urządzeń!");
        return;
    }

    QString comboBoxQString = ui->comboBoxDevices->currentText();
    QStringList portList = comboBoxQString.split("\t");
    QString portName = portList.first();

    this->device->setPortName(portName);

    // OTWÓRZ I SKONFIGURUJ PORT:
    if(!device->isOpen())
    {
        if(device->open(QSerialPort::ReadWrite))
        {
            this->device->setBaudRate(QSerialPort::Baud9600);
            this->device->setDataBits(QSerialPort::Data8);
            this->device->setParity(QSerialPort::NoParity);
            this->device->setStopBits(QSerialPort::OneStop);
            this->device->setFlowControl(QSerialPort::NoFlowControl);

            // CONNECT:
            connect(this->device, SIGNAL(readyRead()),
                    this, SLOT(readFromPort()));

            this->addToLogs("Otwarto port szeregowy.");
        }
        else
        {
            this->addToLogs("Otwarcie porty szeregowego się nie powiodło!");
        }
    }
    else
    {
        this->addToLogs("Port już jest otwarty!");
        return;
    }
}

void MainWindow::readFromPort()
{
    while(this->device->canReadLine())
    {
        QString line = this->device->readLine();
        //qDebug() << line;

        QString terminator = "\r";
        int pos = line.lastIndexOf(terminator);


        if(line.left(pos) == "START"){
            this->on_pushButtonLedOn_clicked();
            this->addToLogs("START");
        }
        if(line.left(pos) == "STOP"){
            this->on_pushButtonLedOff_clicked();
            this->addToLogs("STOP");
        }
    }
}

void MainWindow::on_pushButtonCloseConnection_clicked()
{
    if(this->device->isOpen())
    {
        this->device->close();
        this->addToLogs("Zamknięto połączenie.");
    }
    else
    {
        this->addToLogs("Port nie jest otwarty!");
        return;
    }
}

void MainWindow::on_pushButtonLedOn_clicked()
{
    timer->start(17); // tu zrobiłem te 60 Hz oświeżania dla interfejsu
    QElapsedTimer etimer;
    etimer.start();
}

void MainWindow::on_pushButtonLedOff_clicked()
{
    timer->stop();
    etimer.restart();
}

void MainWindow::on_pushButtonClear_clicked()
{
    timer->stop();
    etimer.restart();
    milisekundy = 0;
    QString milisekundy_text = QString::number(milisekundy);
    ui->label_milisekundy->setText("00"+milisekundy_text);
    sekundy = 0;
    QString sekundy_text = QString::number(sekundy);
    ui->label_sekundy->setText("00"+sekundy_text);
}

 

timer.jpg

Udostępnij ten post


Link to post
Share on other sites

Dzięki cierpliwej pomocy i wskazówkom kolegi Matthew11 udało się rozwiązać ostatni problem również. Obiekt etimer klasy QElapsedTimer, był zadeklarowany przeze mnie już w pliku mainwindow.h w sekcji public i niepotrzebnie tworzyłem go jeszcze raz lokalnie w procedurze void MainWindow::on_pushButtonLedOn_clicked() , co powodowało te niechciane zachowania timera. Usunięcie lokalnej deklaracji pomogło i teraz wszystko śmiga. Dzięki jeszcze raz za pomoc.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...