Постійно чую про QCustomPlot щодо роботи з графіками в Qt , але спочатку зробив в одному своєму додатку свої велосипеди, а зараз у вільну хвилинку вирішив познайомитись із цією бібліотекою. Здалося, що все-таки в ній є ще над чим працювати розробникам, але на даний момент вона має дуже потужний функціонал. Попрацював із нею із задоволенням і накидав свій код для пустощів та вивчення.
А тепер ближчий до справи. Програма у прикладі має виконувати наступний функціонал:
- Намалювати один графік по кількох точках;
- Мати вертикальну лінію, що пересувається;
- За вертикальною лінією слідуватиме трасувальник, який вставатиме в найближчі до точки графіка від вертикальної лінії.
Структура проекту для 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
А почему именно QCustomPlot а не QCharts?
Потому, что на момент написания статьи QCharts был только в коммерческой платной версии.
Здравствуйте. Скачал архив, работает. Создал проект и вставил код в 2 файла, ка вы указали - не работает. В mainwindow.cpp выдает 3 ошибки:
ui->lineEdit и ui->gridLayout - это объекты, которые должны были быть созданы в графическом дизайнере. ui - это объект графического интерфейса. Их у вас просто нет, поработайте с графическим дизайнером, чтобы создать его.
Но почему вы это не описали? Не могли бы вы описать.
Использование дизайнера в Qt Creator и использование ui файлов является распространённой практикой в Qt фреймворке.
Написать отдельную статью про то, что это такое? - может быть.
Описывать в каждой статье основы? - нет. Не о том статья.
такая же ошибка с методом addPlottable. Инклуд проблему не решил.
Куда инклюд писали?
Прописал в заголовочном файле mainwindow.h
То есть вы использовали последнюю версию библиотеки с сайта разработчика QCustomPlot?
Да, вы правы.. я посмотрел документацию у разработчика библиотеки. Он выпустил новую версию. И там отсутсвует данный метод.
Там сейчас делатся через QCPGraph, через установку данных через его метод data()
Спасибо за информацию, а как же теперь пользоваться QCPCurve и другими типами? Краб то заполнить могу информацией, а как добавить её на customPlot?
Меня интересует именно параметрические кривые, я могу их строить с помощью QCPTGraph, но их отображение имеет отклонения(разрывы в функции, неточности). Я полагал QCPCurve сможет решить эту проблему, но как видите из за отсутствия метода addPlottable не могу сообразить как кривую добавить на график.
Или может скачать старую версию библиотеки? Вы не могли бы скинуть файлы старой библиотеки мне на почту?
Ну параметрические кривые могу быть вполне отображены и новой библиотекой, на оф. сайте есть пара примеров параметрических кривых.
Спасибо, разберёмся)
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.
Полезная статья. Как всегда - то что надо.
Добавлю ещё маленькую полезность - после установки tracer (88 строка) и перед выводом значений в lineEdit (91 строка) стоит добавить updatePosition иначе будут отображаться значения из предыдущей позиции.
Доброго времени, это снова я) Имеется ли возможность подключения одного QCPItemTracer ко многим QCPGraph ?
Например у нас на одном QCustomPlot создано множество QCPGraph, делать для каждого их них по одному QCPItemTracer совсем неудобно и громоздко (фильтр для проверки к какому QCPGraph ближе оказался tracer и отключение видимости всех остальных... ну так себе), создание +1 невидимого QCPGraph содержащего в себе данные всех предыдущих - накладно (сотни тысяч точек в каждом). Отсюда и возник вопрос - как используя один QCPItemTracer перемещаться по всем QCPGraph.
(на изображении небольшой пример, tracer остался где-то далеко на 1м graph())