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
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 11:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 11:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 22:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi01 листопада 2024 р. 00:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 18:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 17:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 21:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi25 червня 2024 р. 01:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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