Evgenii Legotckoi
Evgenii Legotckoi27. Dezember 2015 09:09

Qt/C++ - Lektion 031. QCustomPlot – Der Aufbau von Diagrammen mit der Zeit

Die Bibliothek QCustomPlot bietet die Möglichkeit, Diagramme auf einer Zeitskala zu zeichnen, was bei der Analyse von Daten nützlich ist, die sich im Laufe der Zeit ändern. Dazu müssen Sie den Signaturtyp auf der Zeitachse als QCPAxis :: ltDateTime einstellen und dann das Datums- und Zeitformat einstellen. Das heißt, es ist möglich, auf der Achse entweder das Datum oder die Uhrzeit oder beides gleichzeitig anzuzeigen, je nachdem, welche Formatierung Sie angeben. Formatierungsregeln für QCustomPlot sind dieselben wie für QDateTime, QDate, QTime.

Die Zeitkoordinate wird als Doppelzahl übergeben, die ab der Uhrzeit 1970-01-01T00:00:00 in Sekunden zu zählen beginnt. Dinge, die Sie beim Erstellen eines Diagramms beachten sollten.

Ich schlage vor, eine Anwendung zu schreiben, die abhängig von der aktuellen Zeitkoordinate einen pseudozufälligen Graphen von Einkommen und Rubel erstellt. In diesem Fall ist es möglich, mit dem Graphen zu interagieren, d. h. hinein- und herauszuzoomen sowie ihn zu verschieben, jedoch nur entlang der horizontalen Achse. Das heißt, die Grafikanzeige ändert sich nicht in der Höhe. Wir werden es auch ermöglichen, das Format der Zeitkoordinaten abhängig vom sichtbaren Bereich des Diagramms entlang der Zeitachse zu ändern. Das heißt, wenn ein Teil des Diagramms in weniger als einem Tag sichtbar ist, haben die Beschriftungen entlang der Zeitachse das folgende Format: hh: mm . Andernfalls ist das Format "dd MMM yy" .

Erstellen Sie ein Diagramm

Erstellen Sie ein Projekt und verbinden Sie die QCustomPlot-Bibliothek damit. Nur mainwindow.h und mainwindow.cpp-Dateien werden geändert. Neue Dateien werden nicht hinzugefügt.


mainwindow.h

Wir deklarieren eine Instanz der Klasse QCustomPlot sowie eine Instanz des Diagramms selbst. Und wir werden einen SLOT deklarieren, an den das Signal über die Änderung des Anzeigebereichs entlang der Zeitachse gesendet wird.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qcustomplot.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QCustomPlot *customPlot;    // Объявляем графическое полотно
    QCPGraph *graphic;          // Объявляем график

private slots:
    void slotRangeChanged (const QCPRange &newRange);
};

#endif // MAINWINDOW_H

mainwindow.cpp

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

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    customPlot = new QCustomPlot(); // Инициализируем графическое полотно
    ui->gridLayout->addWidget(customPlot,0,0,1,1);  // Устанавливаем customPlot в окно проложения

    customPlot->setInteraction(QCP::iRangeZoom,true);   // Включаем взаимодействие удаления/приближения
    customPlot->setInteraction(QCP::iRangeDrag, true);  // Включаем взаимодействие перетаскивания графика
    customPlot->axisRect()->setRangeDrag(Qt::Horizontal);   // Включаем перетаскивание только по горизонтальной оси
    customPlot->axisRect()->setRangeZoom(Qt::Horizontal);   // Включаем удаление/приближение только по горизонтальной оси
    customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime);   // Подпись координат по Оси X в качестве Даты и Времени
    customPlot->xAxis->setDateTimeFormat("hh:mm");  // Устанавливаем формат даты и времени

    // Настраиваем шрифт по осям координат
    customPlot->xAxis->setTickLabelFont(QFont(QFont().family(), 8));
    customPlot->yAxis->setTickLabelFont(QFont(QFont().family(), 8));

    // Автоматическое масштабирование тиков по Оси X
    customPlot->xAxis->setAutoTickStep(true);

    /* Делаем видимыми оси X и Y по верхней и правой границам графика,
     * но отключаем на них тики и подписи координат
     * */
    customPlot->xAxis2->setVisible(true);
    customPlot->yAxis2->setVisible(true);
    customPlot->xAxis2->setTicks(false);
    customPlot->yAxis2->setTicks(false);
    customPlot->xAxis2->setTickLabels(false);
    customPlot->yAxis2->setTickLabels(false);

    customPlot->yAxis->setTickLabelColor(QColor(Qt::red)); // Красный цвет подписей тиков по Оси Y
    customPlot->legend->setVisible(true);   //Включаем Легенду графика
    // Устанавливаем Легенду в левый верхний угол графика
    customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop);

    // Инициализируем график и привязываем его к Осям
    graphic = new QCPGraph(customPlot->xAxis, customPlot->yAxis);
    customPlot->addPlottable(graphic);  // Устанавливаем график на полотно
    graphic->setName("Доход, Р");       // Устанавливаем
    graphic->setPen(QPen(QColor(Qt::red))); // Устанавливаем цвет графика
    graphic->setAntialiased(false);         // Отключаем сглаживание, по умолчанию включено
    graphic->setLineStyle(QCPGraph::lsImpulse); // График в виде импульсных тиков

    /* Подключаем сигнал от Оси X об изменении видимого диапазона координат
     * к СЛОТу для переустановки формата времени оси.
     * */
    connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)),
            this, SLOT(slotRangeChanged(QCPRange)));

    // Будем строить график с сегодняшнего дни и текущей секунды в будущее
    double now = QDateTime::currentDateTime().toTime_t();
    // Объявляем вектора времени и доходов
    QVector <double> time(400), income(400);

    srand(15); // Инициализируем генератор псевдослучайных чисел

    // Заполняем график значениями
    for (int i=0; i<400; ++i)
      {
        time[i] = now + 3600*i;
        income[i] = qFabs(income[i-1]) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
      }

    graphic->setData(time, income); // Устанавливаем данные
    customPlot->rescaleAxes();      // Масштабируем график по данным
    customPlot->replot();           // Отрисовываем график
}

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

void MainWindow::slotRangeChanged(const QCPRange &newRange)
{
    /* Если область видимости графика меньше одного дня,
     * то отображаем часы и минуты по Оси X,
     * в противном случае отображаем дату "День Месяц Год"
     * */
    customPlot->xAxis->setDateTimeFormat((newRange.size() <= 86400)? "hh:mm" : "dd MMM yy");
}

Ergebnis

Als Ergebnis sollten Sie ein Diagramm ähnlich dem in der folgenden Abbildung gezeigten haben. Beachten Sie, dass sich beim Scrollen des Mausrads und Bewegen des Graphen mit der Maus der Graph selbst nur entlang der X-Achse ändert, wie es in diesem Beispiel beabsichtigt war.

Eine Demonstration der Anwendung können Sie im Video-Tutorial sehen.

Archiv mit dem Projekt

Videoanleitung

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

Юрий
  • 17. April 2017 15:52

Попробовал собрать этот проект, но у меня ошибка ASSERT failure in QVector ::operator[]: "index out of range", file C:\Qt\Qt5.5.1\5.5\mingw492_32\include/QtCore/qvector.h, line 401

Юрий
  • 17. April 2017 16:55

Как правильно вытащить данные из БД, если есть два поля с int и timestamp и вставить в graphic->setData(time, income); // Устанавливаем данные ?

Evgenii Legotckoi
  • 18. April 2017 01:30

В Дебаг режиме собирали? Кажется он в дебаге выкидывает ассерт из-за вот этой строки:

income[i] = qFabs(income[i-1]) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);

Попробуйте переписать немного иначе:

    for (int i=1; i<400; ++i)
      {
        time[i-1] = now + 3600*i;
        income[i-1] = qFabs(income[i-1]) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
      }
Evgenii Legotckoi
  • 18. April 2017 01:33

Вытаскивать нужно обычным QSqlQuery

QSqlQuery query;
query.exec("SELECT column1, cilumn2 FROM table;");

Получите список записей и методами first(), next(), previous(), last() уже пройтись по этому списку, устанавливая данный в график.

Юрий
  • 20. April 2017 01:52

Спасибо. Подскажите еще пожалуйста. У меня есть таблица с доходами.

`id_cash` int(11) NOT NULL, 
`date_cash` date NOT NULL, 
`appointment_cash` int(11) NOT NULL, 
`operation_cash` int(11) NOT NULL, 
`total_cash` int(11) NOT NULL, 
`note_cash` int(11) NOT NULL, 
`documents_cash` int(11) NOT NULL, 
`non_cash` int(11) NOT NULL
Как мне вывести сумму по дням в график?
Evgenii Legotckoi
  • 20. April 2017 02:54

total_cash - это сумма за определённую дату, так?

Тогда запрос должен выглядеть примерно так:

SELECT SUM(total_cash) FROM cash_table WHERE date_cash BETWEEN "2017-04-01" AND "2017-04-20" GROUP BY  date_cash

Этот запрос используете с QSqlQuery и в цикле while добавляете данные в график. что-то вроде такого должно получиться:

QSqlQuery query;
query.exec("SELECT SUM(total_cash) FROM cash_table WHERE date_cash BETWEEN "2017-04-01" AND "2017-04-20" GROUP BY  date_cash;)");
while (query.next()) {
    double total_for_day = query.value(0).toDouble();
    // Todo Something
}
Юрий
  • 20. April 2017 04:02

При построении графика используются два параметра, время и доход. Как сопоставить дату с доходом за день?

Evgenii Legotckoi
  • 20. April 2017 04:18

Ну а подумать? ))

SELECT date_cash, SUM(total_cash) FROM cash_table WHERE date_cash BETWEEN "2017-04-01" AND "2017-04-20" GROUP BY  date_cash;
Юрий
  • 20. April 2017 12:19

У меня тип данных в поле data_time стоял timestamp, поставил тип data и все ок. Спасибо.

Юрий
  • 20. April 2017 15:30

Проблема с value("date_cash").toDate(); не могу передать в graphic->setData().

Evgenii Legotckoi
  • 20. April 2017 23:54
QCPGraph принимает значения времени в типе double, посмотрите внимательно статью, как там инициализируются переменные времени, когда они передаются в график. В статье также про это во втором абзаце сказано.
Юрий
  • 21. April 2017 00:17

Прочитал. Вроде со все разобрался. Спасибо.

М
  • 20. Oktober 2017 10:04

не могли бы вы выложить архив с рабочей версией скрипта?

Evgenii Legotckoi
  • 20. Oktober 2017 10:06

После работы поищу, должен где-то быть на винте.

Evgenii Legotckoi
  • 20. Oktober 2017 17:06

Добавил архив с проектом

М
  • 23. Oktober 2017 04:44

При компиляции выдает ошибку https://cloud.mail.ru/public/FUxM/b4NFJCb9w

Evgenii Legotckoi
  • 23. Oktober 2017 05:00

Мне эта ошибка ни о чём не говорит. Вообще ошибка пишет про RunTime, а ен при компиляцию. При запуске была ошибка.
Там для получения рандомных значений использует выход в неиницализированную область памяти, может из-за этот. Запустите в Release сборке.

М
  • 23. Oktober 2017 10:02

В режиме выпуск работает

Evgenii Legotckoi
  • 23. Oktober 2017 10:10

Хорошо. Там в дебаг режиме просто отслеживаются выходы за пределы массива, за счёт чего получается рандомное первоначальное значение. Для примера этого достаточно.

Ну а в своём проекте вам этого не понадобится, когда напишите свою логику построения графика. Поэтому будет работать в Debug режиме с вашей логикой, если только сами ошибок не наделаете.
М
  • 24. Oktober 2017 03:53

Скажите пожалуйста, как сделать так, что бы при перетаскивании графика, график достраивался? Пока Строиться только то, что попало в окно.

Evgenii Legotckoi
  • 24. Oktober 2017 03:57

Я давно уже не работал с QCustomPlot, может создадите тему на форуме и приложите свой архив с проектом ? Надо смотреть по факту, что вы сделали.

М
  • 1. November 2017 09:00

Скажите пожалуйста, что такое  gridLayout ? В аналогичном проекте выдает ошибку Ui::MainWindow' has no member named 'gridLayout'

Evgenii Legotckoi
  • 1. November 2017 16:29

Это объект компоновщика QGridLayout. Данная ошибка говорит о том, что данный объект не был добавлен через графический дизайнер в окно MainWindow

pasagir
  • 31. Mai 2018 04:36

Здравствуйте, у меня есть БД. В нее записываются данные пришедшие с COM-порта и время, когда эти самые данные пришли (QDataTime::currentMSecsSinceEpoch()), пихаю данные DataTime в вектор, но вместо нормальной даты и времени у меня на графике показывается не тот месяц, день и даже год (50 380-год вместо 2018-го). Причем если я меняю QDataTime::currentMSecsSinceEpoch() на currentDateTime().toTime_t() все отображается нормально, но мне нужно время с миллисекундами.

Evgenii Legotckoi
  • 31. Mai 2018 04:50

Ну так здесь же указывается установка формата в статье

customPlot->xAxis->setDateTimeFormat((newRange.size() <= 86400)? "hh:mm" : "dd MMM yy");
Добавьте просто соответсвующую установку формата
customPlot->xAxis->setDateTimeFormat("hh:mm:ss.zzz");
pasagir
  • 31. Mai 2018 09:10

Куски кода:

data.append(QDateTime::currentMSecsSinceEpoch());//currentDateTime().toTime_t() //вставляем в БД текущее время в милисекундах с 01.01.1970

Дальше делаем запрос на построение таблицы. Для построения графика достаем из таблицы необходимые данные

for(int i = 0; i < countRow; i++)
    {
       date = ui->tableViewDiagrm->model()->data(//Получаем значения ячеек столбца дата/время
              ui->tableViewDiagrm->model()->index(i, 2)).value<qulonglong>();
        mess = ui->tableViewDiagrm->model()->data(//Получаем значения ячеек столбца данных
               ui->tableViewDiagrm->model()->index(i, 3)).value<QString>();
        //Получение десятичных значений шестнадцатиричной строки
        mesLong = mess.toULongLong(&ok, 16);//Переводим строку в цифры

        vecDateTime.push_back(date);  //Соваем данные в вектор
        vecMessage.push_back(mesLong);//Соваем данные в вектор
    }
    graphic->setData(vecDateTime, vecMessage);//Устанавливаем данные
Настройка оси Х - оси времени
customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime);//Подпись х как дата/время
customPlot->xAxis->setDateTimeFormat("hh:mm:ss.ms\n dd MM yyyy");
Как результат подпись под осью Х: 20:30:00.200
25 02 50383
если использовать
data.append(QDateTime::currentDateTime().toTime_t());
время и дата отображается корректно, но некорректно отображаются миллисекунды. Что я делаю не так?
Evgenii Legotckoi
  • 31. Mai 2018 09:15

Я думаю, что там несколько иной формат объявления милисекуд. Поробуйте так

"hh:mm:ss.zzz\n dd MM yyyy"
pasagir
  • 31. Mai 2018 09:24

Попробовал, но результат тот же. Спасибо за Ваши ответы и за оперативное реагирование. У Вас очень хороший сайт и канал на Utube. Если у меня получится решить проблему, я отпишусь в чем было дело.

Evgenii Legotckoi
  • 31. Mai 2018 09:30

Хорошо. Спасибо за отзыв.


И ещё, как вариант попробуйте новую версию QCustomPlot, поскольку эти статьи уже устарели, а я этой библиотекой не занимаюсь.
Но могу дать совет посмотреть в эту часть документации на второй QCustomPlot , там есть информация по установке шаблона отображения времени.
pasagir
  • 31. Mai 2018 09:31

Еще вопрос. На сайте есть такая строчка

QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
но в моей подключенной библиотеке нет такого класса QCPAxisTickerDateTime где его можно найти?
Evgenii Legotckoi
  • 31. Mai 2018 09:33
Скачайте с того сайта вторую версию QCustomPlot и обновите код своего проекта, чтобы он работал уже с QCustomPlot 2
pasagir
  • 1. Juni 2018 10:05

Проблема решена путём добавления в код следующей строки:

date /= 1000;
т.е.
for(int i = 0; i < countRow; i++)
    {
        date = ui->tableViewDiagrm->model()->data(//Получаем значения ячеек столбца дата/время
               ui->tableViewDiagrm->model()->index(i, 2)).value<qulonglong>();
        date /= 1000;
        mess = ui->tableViewDiagrm->model()->data(//Получаем значения ячеек столбца данных
               ui->tableViewDiagrm->model()->index(i, 3)).value<QString>();
        //Получение десятичных значений шестнадцатиричной строки
        //mess = QString::number(mess.toULongLong(&ok, 16), 10);
        mesLong = mess.toULongLong(&ok, 16);//Переводим строку в цифры

        vecDateTime.push_back(date);  //Соваем данные в вектор
        vecMessage.push_back(mesLong);//Соваем данные в вектор
    }
Evgenii Legotckoi
  • 1. Juni 2018 10:56

Ага... ясно... хотя если честно не фига не ясно )) У вас видимо своя какая-то хитрая логика была по отображению миллисекунд.
Впрочем главное, что Вы разобрались ))

pasagir
  • 1. Juni 2018 11:01

Думаю, что по какой-то причине полученное число миллисекунд QCustomPlot воспринимал как секунды

i
  • 13. Juni 2019 10:09
  • (bearbeitet)

Здравствайте! Подскажите, пожалуйста:

customPlot->xAxis2->setTickLabels(true); //Здесь включается отображение данных на оси xAxis2.

а можно как-то продублировать информацию customPlot->xAxis на ось customPlot->xAxis2.

Хотелось бы вверху отображать только даты. А на нижней оси только часы:мин:сек.

Evgenii Legotckoi
  • 16. Juni 2019 16:21

Добрый день.

Ну точно также добавляете ту же самую информацию на ось xAxis2, только добавляете другое форматирование

customPlot->xAxis2->setDateTimeFormat("hh:mm");

если я правильно понял ваш вопрос, конечно

i
  • 17. Juni 2019 02:10
  • (bearbeitet)

Только по осям xAxis2, уAxis2 значения начинаются с 0. Почему-то xAxis2 и xAxis не синхронизированы по данным. Ну и QCustomPlot последний.

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken