Evgenii Legotckoi
Evgenii Legotckoi30 листопада 2015 р. 08:49

Qt/C++ - Урок 030. QCustomPlot – Введення в роботу з діаграмами

Постійно чую про QCustomPlot щодо роботи з графіками в Qt , але спочатку зробив в одному своєму додатку свої велосипеди, а зараз у вільну хвилинку вирішив познайомитись із цією бібліотекою. Здалося, що все-таки в ній є ще над чим працювати розробникам, але на даний момент вона має дуже потужний функціонал. Попрацював із нею із задоволенням і накидав свій код для пустощів та вивчення.

А тепер ближчий до справи. Програма у прикладі має виконувати наступний функціонал:

  1. Намалювати один графік по кількох точках;
  2. Мати вертикальну лінію, що пересувається;
  3. За вертикальною лінією слідуватиме трасувальник, який вставатиме в найближчі до точки графіка від вертикальної лінії.

Структура проекту для QCustomPlot

До структури проекту входять такі файли:

  • QCustomPlotExample.pro - профайл проекту;
  • mainwindow.h - заголовний файл основного вікна програми;
  • mainwindow.cpp - файл вихідних кодів основного вікна програми;
  • main.cpp - основний файл вихідних кодів програми;
  • qcustomplot.h - заголовний файл бібліотеки QCustomPlot;
  • qcustomplot.cpp - файл вихідних кодів QCustomPlot.

Лістинги файлів бібліотеки не буду приводити, вони дуже великі і Ми в них не ліземо, просто додаємо файли в проект і підключаємо заголовний файл у класі MainWindow.

mainwindow.h

У заголовному файлі класу MainWindow підключаємо бібліотеку QCustomPlot, а також оголошуємо саме полотно, вертикальну лінії як об'єкт класу QCPCurve та трасувальник QCPItemTracer.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    QCustomPlot *wGraphic;      // Объявляем объект QCustomPlot
    QCPCurve *verticalLine;     // Объявляем объект для вертикальной линии
    QCPItemTracer *tracer;      // Трасировщик по точкам графика

private slots:
    void slotMousePress(QMouseEvent * event);
    void slotMouseMove(QMouseEvent * event);
};

#endif // MAINWINDOW_H

mainwindow.cpp

QCustomPlot може віддавати сигнали кліка та пересування миші, і в звичайних умовах він завжди викликає сигнал пересування миші над собою, тому необхідно перевіряти, а натиснута кнопка миші, щоб робити перемальовку вмісту полотна. Якщо кнопка миші натиснута, то Ми будемо пересувати вертикальну лінію, а за нею буде перескакувати трасувальник, видаючи в полі lineEdit координати свого розташування. Тобто в яку точку графіка він зараз встановлений. Зазначу, що трасувальник проходить не за всіма точками графіка, а лише за тими, що були завантажені за допомогою QVector .

Встановлення об'єкта QCustomPlot у вікно програми проводиться не через графічний дизайнер IDE QtCreator , а у вихідному коді програми.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
  //  this->setGeometry(300,100,640,480);

    // Инициализируем объект полотна для графика ...
    wGraphic = new QCustomPlot();
    ui->gridLayout->addWidget(wGraphic,1,0,1,1); // ... и устанавливаем

    // Инициализируем вертикальную линию
    verticalLine = new QCPCurve(wGraphic->xAxis, wGraphic->yAxis);

    // Подключаем сигналы событий мыши от полотна графика к слотам для их обработки
    connect(wGraphic, &QCustomPlot::mousePress, this, &MainWindow::slotMousePress);
    connect(wGraphic, &QCustomPlot::mouseMove, this, &MainWindow::slotMouseMove);

    // создаём вектора для вертикальной линии
    QVector<double> x(2) , y(2);
        x[0] = 0;
        y[0] = -50;
        x[1] = 0;
        y[1] = 50;

    wGraphic->addPlottable(verticalLine);   // Добавляем линию на полотно
    verticalLine->setName("Vertical");      // Устанавливаем ей наименование
    verticalLine->setData(x, y);            // И устанавливаем координаты

    // создаём вектора для графика
    QVector<double> x1(5) , y1(5);
        x1[0] = -45;
        y1[0] = -43;
        x1[1] = 46;
        y1[1] = 42;
        x1[2] = -25;
        y1[2] = -24;
        x1[3] = -12;
        y1[3] = 10;
        x1[4] = 25;
        y1[4] = 26;

    // Добавляем график на полотно
    wGraphic->addGraph(wGraphic->xAxis, wGraphic->yAxis);
    wGraphic->graph(0)->setData(x1,y1);     // Устанавливаем координаты точек графика

    // Инициализируем трассировщик
    tracer = new QCPItemTracer(wGraphic);
    tracer->setGraph(wGraphic->graph(0));   // Трассировщик будет работать с графиком

    // Подписываем оси координат
    wGraphic->xAxis->setLabel("x");
    wGraphic->yAxis->setLabel("y");

    // Устанавливаем максимальные и минимальные значения координат
    wGraphic->xAxis->setRange(-50,50);
    wGraphic->yAxis->setRange(-50,50);

    // Отрисовываем содержимое полотна
    wGraphic->replot();
}

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

void MainWindow::slotMousePress(QMouseEvent *event)
{
    // Определяем координату X на графике, где был произведён клик мышью
    double coordX = wGraphic->xAxis->pixelToCoord(event->pos().x());

    // Подготавливаем координаты по оси X для переноса вертикальной линии
    QVector<double> x(2), y(2);
    x[0] = coordX;
    y[0] = -50;
    x[1] = coordX;
    y[1] = 50;

    // Устанавливаем новые координаты
    verticalLine->setData(x, y);

    // По координате X клика мыши определим ближайшие координаты для трассировщика
    tracer->setGraphKey(coordX);

    // Выводим координаты точки графика, где установился трассировщик, в lineEdit
    ui->lineEdit->setText("x: " + QString::number(tracer->position->key()) +
                          " y: " + QString::number(tracer->position->value()));
    wGraphic->replot(); // Перерисовываем содержимое полотна графика
}

void MainWindow::slotMouseMove(QMouseEvent *event)
{
    /* Если при передвижении мыши, ей кнопки нажаты,
     * то вызываем отработку координат мыши
     * через слот клика
     * */
    if(QApplication::mouseButtons()) slotMousePress(event);
}

Підсумок

В результаті виконаної роботи Ви отримаєте програму, яка буде виглядати так, як показано на наступному рисунку. Також Ви можете побачити демонстрацію роботи програми у відеоуроці.

Посилання на завантаження проекту в zip-архіві: QCustomPlot Example

Відеоурок

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

Вам це подобається? Поділіться в соціальних мережах!

z
  • 23 січня 2017 р. 06:30

А почему именно QCustomPlot а не QCharts?

Evgenii Legotckoi
  • 23 січня 2017 р. 10:29

Потому, что на момент написания статьи QCharts был только в коммерческой платной версии.

М
  • 20 жовтня 2017 р. 09:30

Здравствуйте. Скачал архив, работает. Создал проект и вставил код в 2 файла, ка вы указали - не работает.  В mainwindow.cpp выдает 3 ошибки:

C:\Qt\project\3\mainwindow.cpp:14: ошибка: 'class Ui::MainWindow' has no member named 'gridLayout'
ui->gridLayout->addWidget(wGraphic,1,0,1,1); // ... и устанавливаем
^
C:\Qt\project\3\mainwindow.cpp:30: ошибка: 'class QCustomPlot' has no member named 'addPlottable'
wGraphic->addPlottable(verticalLine);   // Добавляем линию РЅР° полотно
^
C:\Qt\project\3\mainwindow.cpp:91: ошибка: 'class Ui::MainWindow' has no member named 'lineEdit'
ui->lineEdit->setText("x: " + QString::number(tracer->position->key()) +
^
Скажите пожалуйста, как это исправить!
Evgenii Legotckoi
  • 20 жовтня 2017 р. 09:40

ui->lineEdit и ui->gridLayout - это объекты, которые должны были быть созданы в графическом дизайнере. ui - это объект графического интерфейса. Их у вас просто нет, поработайте с графическим дизайнером, чтобы создать его.


что касается addPlotable, так это отсутсвует инклюд библиотеки, заголовочного файла в mainwindow.h
М
  • 20 жовтня 2017 р. 09:43

Но почему вы это не описали? Не могли бы вы описать.


Evgenii Legotckoi
  • 20 жовтня 2017 р. 10:03

Использование дизайнера в Qt Creator и использование ui файлов является распространённой практикой в Qt фреймворке.
Написать отдельную статью про то, что это такое? - может быть.
Описывать в каждой статье основы? - нет. Не о том статья.

A
  • 23 жовтня 2017 р. 05:25

такая же ошибка с методом addPlottable. Инклуд проблему не решил.

Evgenii Legotckoi
  • 23 жовтня 2017 р. 05:55

Куда инклюд писали?

A
  • 23 жовтня 2017 р. 05:57

Прописал в заголовочном файле mainwindow.h

Может версия другая? У меня в файле библиотеки вообще нет такого метода.
Evgenii Legotckoi
  • 23 жовтня 2017 р. 06:03

То есть вы использовали последнюю версию библиотеки с сайта разработчика QCustomPlot?
Да, вы правы.. я посмотрел документацию у разработчика библиотеки. Он выпустил новую версию. И там отсутсвует данный метод.
Там сейчас делатся через QCPGraph, через установку данных через его метод data()

customPlot->graph()->data()->set(timeData);
A
  • 23 жовтня 2017 р. 06:09

Спасибо за информацию, а как же теперь пользоваться QCPCurve и другими типами? Краб то заполнить могу информацией, а как добавить её на customPlot?

Evgenii Legotckoi
  • 23 жовтня 2017 р. 06:11
Краб то заполнить могу информацией
В смысле QCPGraph ?

QCustomPlot имеет метод addGraph
A
  • 23 жовтня 2017 р. 06:15

Меня интересует именно параметрические кривые, я могу их строить с помощью QCPTGraph, но их отображение имеет отклонения(разрывы в функции, неточности). Я полагал QCPCurve сможет решить эту проблему, но как видите из за отсутствия метода addPlottable не могу сообразить как кривую добавить на график.

A
  • 23 жовтня 2017 р. 06:16

Или может скачать старую версию библиотеки? Вы не могли бы скинуть файлы старой библиотеки мне на почту?

Evgenii Legotckoi
  • 23 жовтня 2017 р. 06:20

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

А что касается бибилотеки, то скачайте проект из этой статьи . Там в составе проетк есть старая библиотека. Ссылка перед видео.
A
  • 23 жовтня 2017 р. 06:25

Спасибо, разберёмся)

h
  • 30 травня 2018 р. 21:14

Here the tracer is moving by Mouse Press And Mouse Move.How to move the Tracer by using the QTimer.The tracer has to be move from origin to end point automatically/programically by using QTimer.

Docent
  • 21 січня 2020 р. 14:12

Полезная статья. Как всегда - то что надо.
Добавлю ещё маленькую полезность - после установки tracer (88 строка) и перед выводом значений в lineEdit (91 строка) стоит добавить updatePosition иначе будут отображаться значения из предыдущей позиции.

Docent
  • 25 грудня 2020 р. 02:36

Доброго времени, это снова я) Имеется ли возможность подключения одного QCPItemTracer ко многим QCPGraph ?
Например у нас на одном QCustomPlot создано множество QCPGraph, делать для каждого их них по одному QCPItemTracer совсем неудобно и громоздко (фильтр для проверки к какому QCPGraph ближе оказался tracer и отключение видимости всех остальных... ну так себе), создание +1 невидимого QCPGraph содержащего в себе данные всех предыдущих - накладно (сотни тысяч точек в каждом). Отсюда и возник вопрос - как используя один QCPItemTracer перемещаться по всем QCPGraph.


(на изображении небольшой пример, tracer остался где-то далеко на 1м graph())

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
1
  • 12333
  • 18 липня 2024 р. 05:34

Qt - Тест 001. Сигналы и слоты

  • Результат:63бали,
  • Рейтинг балів-1
1
  • 12333
  • 18 липня 2024 р. 05:25

C++ - Тест 005. Структуры и Классы

  • Результат:50бали,
  • Рейтинг балів-4
AM

C++ - Тест 005. Структуры и Классы

  • Результат:33бали,
  • Рейтинг балів-10
Останні коментарі
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
Тепер обговоріть на форумі
F
Fynjy22 липня 2024 р. 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCT25 червня 2024 р. 01:00
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
BlinCT
BlinCT05 травня 2024 р. 05:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 14:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Слідкуйте за нами в соціальних мережах