Постоянно слышу про 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())