- 1. Будуємо Графік
- 2. Підсумок
- 3. Відеоурок
Бібліотека QCustomPlot має можливість побудови графіків за шкалою часу, що зручно під час аналізу даних, що змінюються у часі. Для цього необхідно встановити тип підпису до Осі Часу як QCPAxis::ltDateTime. І після цього виставити формат дати та часу. Тобто є можливість відображати на Осі або дату, або час, або одночасно і те й інше, залежно від того, яке форматування Ви задасте. Правила форматування для QCustomPlot використовуються ті самі, що й для класів QDateTime, QDate, QTime.
Координата часу передається у вигляді числа типу double , яке починає відлік у секундах від часу 1970-01-01T00:00:00 . Що необхідно враховувати під час побудови графіка.
Пропоную написати додаток, який будуватиме псевдовипадковий графік доходу та рублях залежно від поточної координати часу. При цьому з графіком можна буде взаємодіяти, тобто наближати і видаляти його, а також пересувати, але тільки горизонтальною Осі. Тобто за висотою відображення графіка не змінюватиметься. Також зробимо можливість зміни формату координат часу залежно видимої області графіка по Осі Часу. Тобто, якщо видно частину графіка менше, ніж за один день, то по осі часу формат підписів буде наступним: hh:mm . В іншому випадку формат буде "dd MMM yy" .
Будуємо Графік
Створюємо проект і підключаємо до нього бібліотеку QCustomPlot. Модифікації будуть піддаватися лише файли mainwindow.h та mainwindow.cpp. Нові файли не додаватимуться.
mainwindow.h
Оголошуємо екземпляр класу QCustomPlot, , а також екземпляр самого графіка. І оголосимо СЛОТ, в який передаватиметься сигнал про зміну області відображення по Осі Часу.
#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"); }
Підсумок
В результаті у Вас повинен вийти графік подібний до того, що показаний на нижче наступному малюнку. Зазначу, що при прокручуванні коліщатка миші та пересуванні графіка мишею, сам Графік змінюватиметься тільки по Осі X, як і було задумано в даному прикладі.
Демонстрацію роботи програми Ви можете побачити у відеоуроці.
Попробовал собрать этот проект, но у меня ошибка 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
Как правильно вытащить данные из БД, если есть два поля с int и timestamp и вставить в graphic->setData(time, income); // Устанавливаем данные ?
В Дебаг режиме собирали? Кажется он в дебаге выкидывает ассерт из-за вот этой строки:
Попробуйте переписать немного иначе:
Вытаскивать нужно обычным QSqlQuery
Получите список записей и методами first(), next(), previous(), last() уже пройтись по этому списку, устанавливая данный в график.
Спасибо. Подскажите еще пожалуйста. У меня есть таблица с доходами.
Как мне вывести сумму по дням в график?total_cash - это сумма за определённую дату, так?
Тогда запрос должен выглядеть примерно так:
Этот запрос используете с QSqlQuery и в цикле while добавляете данные в график. что-то вроде такого должно получиться:
При построении графика используются два параметра, время и доход. Как сопоставить дату с доходом за день?
Ну а подумать? ))
У меня тип данных в поле data_time стоял timestamp, поставил тип data и все ок. Спасибо.
Проблема с value("date_cash").toDate(); не могу передать в graphic->setData().
Прочитал. Вроде со все разобрался. Спасибо.
не могли бы вы выложить архив с рабочей версией скрипта?
После работы поищу, должен где-то быть на винте.
Добавил архив с проектом
При компиляции выдает ошибку https://cloud.mail.ru/public/FUxM/b4NFJCb9w
Мне эта ошибка ни о чём не говорит. Вообще ошибка пишет про RunTime, а ен при компиляцию. При запуске была ошибка.
Там для получения рандомных значений использует выход в неиницализированную область памяти, может из-за этот. Запустите в Release сборке.
В режиме выпуск работает
Хорошо. Там в дебаг режиме просто отслеживаются выходы за пределы массива, за счёт чего получается рандомное первоначальное значение. Для примера этого достаточно.
Скажите пожалуйста, как сделать так, что бы при перетаскивании графика, график достраивался? Пока Строиться только то, что попало в окно.
Я давно уже не работал с QCustomPlot, может создадите тему на форуме и приложите свой архив с проектом ? Надо смотреть по факту, что вы сделали.
Скажите пожалуйста, что такое gridLayout ? В аналогичном проекте выдает ошибку Ui::MainWindow' has no member named 'gridLayout'
Это объект компоновщика QGridLayout. Данная ошибка говорит о том, что данный объект не был добавлен через графический дизайнер в окно MainWindow
Здравствуйте, у меня есть БД. В нее записываются данные пришедшие с COM-порта и время, когда эти самые данные пришли (QDataTime::currentMSecsSinceEpoch()), пихаю данные DataTime в вектор, но вместо нормальной даты и времени у меня на графике показывается не тот месяц, день и даже год (50 380-год вместо 2018-го). Причем если я меняю QDataTime::currentMSecsSinceEpoch() на currentDateTime().toTime_t() все отображается нормально, но мне нужно время с миллисекундами.
Ну так здесь же указывается установка формата в статье
Куски кода:
Дальше делаем запрос на построение таблицы. Для построения графика достаем из таблицы необходимые данные
Я думаю, что там несколько иной формат объявления милисекуд. Поробуйте так
Попробовал, но результат тот же. Спасибо за Ваши ответы и за оперативное реагирование. У Вас очень хороший сайт и канал на Utube. Если у меня получится решить проблему, я отпишусь в чем было дело.
Хорошо. Спасибо за отзыв.
Но могу дать совет посмотреть в эту часть документации на второй QCustomPlot , там есть информация по установке шаблона отображения времени.
Еще вопрос. На сайте есть такая строчка
Проблема решена путём добавления в код следующей строки:
Ага... ясно... хотя если честно не фига не ясно )) У вас видимо своя какая-то хитрая логика была по отображению миллисекунд.
Впрочем главное, что Вы разобрались ))
Думаю, что по какой-то причине полученное число миллисекунд QCustomPlot воспринимал как секунды
Здравствайте! Подскажите, пожалуйста:
customPlot->xAxis2->setTickLabels(true); //Здесь включается отображение данных на оси xAxis2.
а можно как-то продублировать информацию customPlot->xAxis на ось customPlot->xAxis2.
Хотелось бы вверху отображать только даты. А на нижней оси только часы:мин:сек.
Добрый день.
Ну точно также добавляете ту же самую информацию на ось xAxis2, только добавляете другое форматирование
если я правильно понял ваш вопрос, конечно
Только по осям xAxis2, уAxis2 значения начинаются с 0. Почему-то xAxis2 и xAxis не синхронизированы по данным. Ну и QCustomPlot последний.