Skocz do zawartości

Odmierzanie czasu w QT


szeryf

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?

Link do komentarza
Share on other sites

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
Link do komentarza
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
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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
Link do komentarza
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ć.

Link do komentarza
Share on other sites

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
Link do komentarza
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

Link do komentarza
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
Link do komentarza
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!

Anonim
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...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.