Evgenii Legotckoi
Evgenii LegotckoiЖел. 27, 2015, 9:09 Т.Ж.

Qt/C++ – 031-сабақ. QCustomPlot – уақыт бойынша сызба құру

[QCustomPlot] кітапханасының (https://evileg.com/post/94/) уақыт өте келе графиктерді салу мүмкіндігі бар, бұл уақыт өте өзгеретін деректерді талдау кезінде пайдалы. Ол үшін қолтаңба түрін Уақыт осіне QCPAxis::ltDateTime. ретінде орнату керек, содан кейін күн мен уақыт пішімін орнату керек. Яғни, қандай пішімдеуді көрсеткеніңізге байланысты Осьте күнді немесе уақытты немесе екеуін бір уақытта көрсетуге болады. QCustomPlot үшін пішімдеу ережелері QDateTime, QDate, QTime. сыныптарымен бірдей.

Уақыт координаты 1970-01-01T00:00:00 уақыттан бастап секундтармен есептеле бастайтын қос түрінің саны ретінде беріледі. Графикті салғанда нені ескеру керек.

Мен ағымдағы уақыт координатасына байланысты кіріс пен рубльдің жалған кездейсоқ графигін құрайтын қосымшаны жазуды ұсынамын. Сонымен қатар, диаграммамен өзара әрекеттесу, яғни үлкейту және кішірейту, сонымен қатар оны тек көлденең ось бойымен жылжыту мүмкін болады. Яғни, графиктің дисплейі биіктікте өзгермейді. Біз сонымен қатар уақыт осі бойынша диаграмманың көрінетін аймағына байланысты уақыт координатының пішімін өзгертуге мүмкіндік береміз. Яғни, диаграмманың бір бөлігі бір күннен аз уақыт көрінетін болса, онда уақыт осі бойындағы белгі пішімі келесідей болады: сағ:мм . Әйтпесе, пішім "dd MMM yy" болады.

График құру

Біз жоба жасаймыз және оған QCustomPlot кітапханасын қосамыз. Тек mainwindow.h және mainwindow.cpp. файлдары ғана өзгертіледі. Жаңа файлдар қосылмайды.


mainwindow.h

Біз QCustomPlot, класының данасын, сонымен қатар диаграмманың данасын жариялаймыз. Уақыт осі бойынша дисплей аймағын өзгерту туралы сигнал берілетін SLOT жариялайық.

#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 осі бойымен өзгеретінін ескертемін.

Қосымшаның демонстрациясын бейне оқулықтан көре аласыз.

Жоба мұрағаты

Бейне оқулық

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Юрий
  • Сәуір 17, 2017, 3: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, 2017, 4:55 Т.Қ.

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

Evgenii Legotckoi
  • Сәуір 18, 2017, 1: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, 2017, 1:33 Т.Ж.

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

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

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

Юрий
  • Сәуір 20, 2017, 1: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, 2017, 2: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, 2017, 4:02 Т.Ж.

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

Evgenii Legotckoi
  • Сәуір 20, 2017, 4: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, 2017, 12:19 Т.Қ.

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

Юрий
  • Сәуір 20, 2017, 3:30 Т.Қ.

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

Evgenii Legotckoi
  • Сәуір 20, 2017, 11:54 Т.Қ.
QCPGraph принимает значения времени в типе double, посмотрите внимательно статью, как там инициализируются переменные времени, когда они передаются в график. В статье также про это во втором абзаце сказано.
Юрий
  • Сәуір 21, 2017, 12:17 Т.Ж.

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

М
  • Қаз. 20, 2017, 10:04 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 20, 2017, 10:06 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 20, 2017, 5:06 Т.Қ.

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

М
  • Қаз. 23, 2017, 4:44 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 23, 2017, 5 Т.Ж.

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

М
  • Қаз. 23, 2017, 10:02 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 23, 2017, 10:10 Т.Ж.

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

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

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

Evgenii Legotckoi
  • Қаз. 24, 2017, 3:57 Т.Ж.

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

М
  • Қар. 1, 2017, 9 Т.Ж.

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

Evgenii Legotckoi
  • Қар. 1, 2017, 4:29 Т.Қ.

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

pasagir
  • Мамыр 31, 2018, 4:36 Т.Ж.

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

Evgenii Legotckoi
  • Мамыр 31, 2018, 4:50 Т.Ж.

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

customPlot->xAxis->setDateTimeFormat((newRange.size() <= 86400)? "hh:mm" : "dd MMM yy");
Добавьте просто соответсвующую установку формата
customPlot->xAxis->setDateTimeFormat("hh:mm:ss.zzz");
pasagir
  • Мамыр 31, 2018, 9: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, 2018, 9:15 Т.Ж.

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

"hh:mm:ss.zzz\n dd MM yyyy"
pasagir
  • Мамыр 31, 2018, 9:24 Т.Ж.

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

Evgenii Legotckoi
  • Мамыр 31, 2018, 9:30 Т.Ж.

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


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

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

QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
но в моей подключенной библиотеке нет такого класса QCPAxisTickerDateTime где его можно найти?
Evgenii Legotckoi
  • Мамыр 31, 2018, 9:33 Т.Ж.
Скачайте с того сайта вторую версию QCustomPlot и обновите код своего проекта, чтобы он работал уже с QCustomPlot 2
pasagir
  • Маусым 1, 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, 2018, 10:56 Т.Ж.

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

pasagir
  • Маусым 1, 2018, 11:01 Т.Ж.

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

i
  • Маусым 13, 2019, 10:09 Т.Ж.
  • (өңделген)

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

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

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

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

Evgenii Legotckoi
  • Маусым 16, 2019, 4:21 Т.Қ.

Добрый день.

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

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

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

i
  • Маусым 17, 2019, 2:10 Т.Ж.
  • (өңделген)

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
OI
  • Ora Iro
  • Жел. 24, 2024, 6:38 Т.Ж.

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:40ұпай,
  • Бағалау ұпайлары-8
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
Соңғы пікірлер
ИМ
Игорь МаксимовҚар. 22, 2024, 11:51 Т.Ж.
Django - Оқулық 017. Теңшелген Django кіру беті Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiҚаз. 31, 2024, 2:37 Т.Қ.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEҚаз. 19, 2024, 8:19 Т.Ж.
Qt Creator көмегімен fb3 файл оқу құралы Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовҚаз. 5, 2024, 7:51 Т.Ж.
Django - Сабақ 064. Python Markdown кеңейтімін қалай жазуға болады Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Қар. 15, 2024, 6:04 Т.Ж.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectМаусым 4, 2022, 3:49 Т.Ж.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз