Приложения Qt. Построение графика в реальном времени.

В прошлых статьях мы научились строить график в приложении Qt при помощи библиотеки Qwt, теперь же возьмем и сделаем так, чтобы он строился в реальном времени! Алгоритм будет такой:

  • Определим два массива x[] и y[] для хранения координат точек кривой графика. В качестве примера заранее заполним массивы данными, но, на самом-то деле, можно записывать туда данные, приходящие, например, из com-порта в реальном времени.
  • Настроим таймер, так, чтобы каждые 25 мс он выдавал сигнал timeout() и присоединим к этому сигналу слот – обработчик timerHandle().
  • В этом обработчике будем брать последнюю не отображенную точку, и передавать ее функции appendGraphPoint().
  • Эта функция будет добавлять точку в массив данных, а также на график. Правда для хранения данных будет использоваться не просто массив, а объект класса CurveData, который мы сами и реализуем.

Итак, самое главное сделали – придумали как все должно работать, осталось реализовать 🙂

Создаем новый проект Qt, и сразу добавим в него файлы для реализации класса RealTimePlot. Для этого идем в меню “Файл-Новый файл или проект” и далее выбираем “Класс С++”:

Создание нового проекта

Меняем файл realtimeplot.h:

class RealTimePlot : public QwtPlot
{
	Q_OBJECT
	
public:
	RealTimePlot();
	~RealTimePlot();


public slots:
	void timerHandle();


private:
	QwtPlotCurve *curve;
	QwtPlotDirectPainter *painter;
	void setData();
	void appendGraphPoint(QPointF point);
	int counter;
	double x[1000];
	double y[1000];
};

Ну, тут все вроде понятно, просто объявляем поля класса RealTimePlot:

  • объект curve понадобится непосредственно для рисования кривой.
  • painter – для дорисовки точек в реальном времени.
  • counter – тут будем хранить номер точки в массиве данных, которая должна быть добавлена на график следующей.

Добавим реализацию уже упомянутого класса CurveData в файл realtimeplot.cpp:

class CurveData: public QwtArraySeriesData
{
public:
	CurveData()
	{
	}

	virtual QRectF boundingRect() const
	{
		if (d_boundingRect.width() < 0.0)
			d_boundingRect = qwtBoundingRect(*this);

		return d_boundingRect;
	}

	inline void appendDataPoint(const QPointF &point)
	{
		d_samples += point;
	}

	void clear()
	{
		d_samples.clear();
		d_samples.squeeze();
		d_boundingRect = QRectF(0.0, 0.0, -1.0, -1.0);
	}
};

Класс наследует методы и члены класса QwtArraySeriesData, здесь определяются всего лишь 3 метода. Нам в данном примере понадобится только добавление точки – то есть функция appendDataPoint(). В качестве аргумента она принимает как раз таки точку, то есть объект класса QPointF.

Итак, пришло время реализовать конструктор для класса RealTimePlot:

RealTimePlot::RealTimePlot()
{
	counter = 0;
	painter = new QwtPlotDirectPainter(this);
	this->setAxisScale(QwtPlot::xBottom, -1, 1);
	this->setAxisScale(QwtPlot::yLeft, 0, 1);
	curve = new QwtPlotCurve("y(x)");
	curve->setStyle(QwtPlotCurve::NoCurve);
	curve->setData(new CurveData());
	curve->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, Qt::NoBrush, QPen(Qt::red), QSize(1, 1)));
	curvev->attach(this);
	setAutoReplot(false);
	setData();
}

Давайте смотреть, что же тут делается. Объявляем объект painter, он нам будет дорисовывать график в реальном времени.

painter = new QwtPlotDirectPainter(this);

Затем устанавливаем интервалы на осях x и y, которые будут отображаться на графике:

this->setAxisScale(QwtPlot::xBottom, -1, 1);
this->setAxisScale(QwtPlot::yLeft, 0, 1);

Дальше создаем новую кривую (curve), ну и по минимуму настраиваем ее вид, цвет и т. п.

curve = new QwtPlotCurve("y(x)");
curve->setStyle(QwtPlotCurve::NoCurve);
curve->setData( new CurveData() );
curve->setSymbol( new QwtSymbol(QwtSymbol::Ellipse, Qt::NoBrush, QPen(Qt::red), QSize(1, 1)));

Добавляем кривую на объект RealTimePlot: curve->attach(this);

И, наконец, вызываем функцию setData(), которая заполнит массивы x[] и y[] данными. Кстати, давайте сразу добавим реализацию этой функции:

void RealTimePlot::setData()
{
	const int n = 1000;
	double h = 2.0 / n;
	for(int i = 0; i < n; i++)
	{
		x[i] = -1 + i * h;
		y[i] = qAbs(x[i]);
	}
}

Тут все просто, точно так же мы строили график тут. Не забываем написать деструктор для класса RealTimePlot, который у нас уже объявлен в файле realtimeplot.h:

RealTimePlot::~RealTimePlot()
{
	delete zoomer;
	delete painter;
	delete curve;
}

Теперь поработаем с классом MainWindow. Создадим нужные объекты, а также таймер. Вот полный код файлов mainwindow.h и mainwindow.cpp:

  • Файл mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "realtimeplot.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
	Q_OBJECT

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

private:
	Ui::MainWindow *ui;
	RealTimePlot *plot;
	QTimer *timer;
};

#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);
	plot = new RealTimePlot();
	this->setCentralWidget(plot);
	timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), plot, SLOT(timerHandle()));
	timer->start(25);
}

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

В конструкторе класса к сигналу таймера timeout() подключаем слот timerHandle() и затем запускаем таймер так, чтобы он сигнализировал нам каждые 25 мс. Опять идем в файл realtimeplot.cpp и дописываем туда реализацию слота timerHandle():

void RealTimePlot::timerHandle()
{
	QPointF newPoint = QPointF(this-x[this->counter], this->y[this->counter]);
	this->counter++;
	RealTimePlot::appendGraphPoint(newPoint);
}

Тут мы создаем объект newPoint – то есть точку, которая должны быть следующей добавлена на график. Соответственно, увеличиваем счетчик counter. И вызываем метод appendGraphPoint(). Пришло время реализовать и его 🙂

void RealTimePlot::appendGraphPoint(QPointF point)
{
	CurveData *data = static_cast(curve->data());
	data->appendDataPoint(point);
	painter->drawSeries(curve, 0, data->size() - 1);
}

Здесь мы наконец-то добавляем к данным нашу новую точку и рисуем ее на графике. Вот и все! Запускаем приложение и видим как медленно, но верно прорисовывается наша функция y=|x|:

График в реальном времени в Qt.

Можно еще добавить на график интерфейс для масштабирования – как в прошлой статье про графики.

Итак, в общем, мы получили график, рисующийся в реальном времени. Пусть пример довольно простой, но в принципе на его основе можно легко реализовать что-нибудь вроде электронного осциллографа, например 🙂 Если возникли какие-либо проблемы или вопросы, пишите в комментарии, буду рад помочь!

Поделиться!

Подписаться
Уведомление о
guest
47 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Владимир
Владимир
7 лет назад

Отличные статьи! Есть пара вопросов, подскажите пожалста:
1) как мне организовать прием данных с COM порта?
2) посоветуйте какую-нибудь литературу/ресурсы в интернете, по которым можно поизучать QT

p.s. у вас опечатка здесь: QPointF(this-x[this->counter]
это в обработчике прерывания таймера

Victor
Victor
7 лет назад

День добрый.
Можете выложить проект на какой либо хостинг?

Victor
Victor
7 лет назад

При попытке собрать проект по инструкции появляются три ошибки:
1) untitled/realtimeplot.h:5: ошибка: expected class-name before ‘{‘ token
2) untitled/realtimeplot.h:15: ошибка: ‘QwtPlotCurve’ does not name a type
3) untitled/realtimeplot.h:16: ошибка: ‘QwtPlotDirectPainter’ does not name a type

Можете что подсказать как собрать правильно, просто совсем не давно начал разбираться в Qt

Victor
Victor
Reply to  Aveal
7 лет назад

Спасибо огромное, буду ждать.

Victor
Victor
Reply to  Aveal
7 лет назад

Можешь отправить проект на почту: bazavr@gmail.com

Павел
Павел
Reply to  Aveal
9 месяцев назад

Здравствуйте. Не могли бы Вы выслать проект на почту: gmgfifius@gmail.com
Если он конечно сохранился

Victor
Victor
7 лет назад

P.S. хотя google может и не пропустить…
vit_30@pochta.ru

Иван
Иван
7 лет назад

Видимо что QwtSymbol не нужно в куче объявлять, new ненужен, ну или удалить потом. Также в деструкторе нужно удалить CurveData объект.

Иван
Иван
7 лет назад

А ну да, zoomer удалять тут ненужно, его нет в этой статье (в отличие от предыдущей).

Иван
Иван
7 лет назад

Да и this зачем? Можно ж без него …

Иван
Иван
7 лет назад

painter удалять тоже не нужно, он удалиться самим QT, ведь родительский объект ему задан через QObject.

Артур
Артур
7 лет назад

Прикрепите архив с проектом, а то если копировать из текста и тыкать самому в файлы, то у меня ошибки вылазят. Ну или скиньте на мыло ifuelen@gmail.com

Артур
Артур
Reply to  Aveal
7 лет назад

Спасибо большое 🙂

Александр
Александр
6 лет назад

Здравствуйте. У вас опечатка в “class CurveData: public QwtArraySeriesData
{
public:…”
Правильно “class CurveData: public QwtArraySeriesData
{
public:…”
Из за этого проект не компилируется.
Только после того как посмотрел ваш скачанный проект понял почему. В нем все правильно.

Александр
Александр
6 лет назад

Удаляется QPointF в стрелках в конце QwtArraySeriesData. Видимо косяк самого сайта.

Ирина
Ирина
6 лет назад

Здравствуйте.
Очень понравилась ваша статья. Написано понятно для начинающего. Но у меня все никак не получается собрать проект. Даже полностью скачанный, который тут выложен.
Библиотека по вашей инструкции собралась нормально. А вот после сборки файла появляется следующая ошибка : C:\QtSDK\qwt\qwt-6.0.1\lib\qwtd.dll:-1: ошибка: LNK1107: invalid or corrupt file: cannot read at 0x2D8.
Меняла версию библиотеки – не помогло.

Ирина
Ирина
6 лет назад

QT += core gui

TARGET = realtimegraph
TEMPLATE = app

INCLUDEPATH += C:\QtSDK\qwt\qwt-6.0.1\src

LIBS +=C:\QtSDK\qwt\qwt-6.0.1\lib\qwtd.dll

SOURCES += main.cpp\
mainwindow.cpp \
realtimeplot.cpp \
scrollbar.cpp \
scrollzoomer.cpp

HEADERS += mainwindow.h \
realtimeplot.h \
scrollzoomer.h \
scrollbar.h

FORMS += mainwindow.ui

Ирина
Ирина
6 лет назад

Та ошибка ушла, теперь появилась новая.
moc_realtimeplot.obj:-1: ошибка: LNK2001: unresolved external symbol “public: static struct QMetaObject const QwtPlot::staticMetaObject” (?staticMetaObject@QwtPlot@@2UQMetaObject@@B)

moc_scrollzoomer.obj:-1: ошибка: LNK2001: unresolved external symbol “public: static struct QMetaObject const QwtPlotZoomer::staticMetaObject” (?staticMetaObject@QwtPlotZoomer@@2UQMetaObject@@B)

Ирина
Ирина
6 лет назад

не помогло(

Ирина
Ирина
6 лет назад

Везде, где есть примеры установки библиотеки, она собрана с помощью mingw. А у меня кьют на стидии 2010 стоит, есть ли какие-нибудь особенности. А то я уже и серийным портом забуксовала

Сергей
Сергей
6 лет назад

Здравствуйте. Большое спасибо за примеры, пользуюсь Вашими статьями по программированию Qt и STM32. Возможно, у Вас опечатка в строках
void RealTimePlot::appendGraphPoint(QPointF point)
CurveData *data = static_cast( curve->data() );
Строку
CurveData *data = static_cast (curve->data() );
заменил на
CurveData *data = static_cast (curve->data() );
Иначе компилятор выдавал ошибку.

Сергей
Сергей
6 лет назад

Проблемы с комментариями: пропускается фраза в кавычках. static_cast

Сергей
Сергей
6 лет назад

static_cast кавычкиCurveData *кавычки

Олег
Олег
5 лет назад

Здравствуйте. У вас опечатка в «class CurveData: public QwtArraySeriesData
{
public:…»
Правильно «class CurveData: public QwtArraySeriesData
{
public:…»

и правильный и не правильный вариант абсолютно одинаковые!!)
в чем ошибка то??
а то у меня тоже не компилится

Cherkesova
Cherkesova
4 лет назад

Огромное спасибо за статью!)запустила,все работает,но у меня вопрос: почему-то дорисовывает линию от 1 до 0 после прорисовки графика…никак не пойму что не так…..

byulent
byulent
4 лет назад

Можно сделать так, что новые точки не будут прорисовываться, если счётчик больше n – достаточно обернуть метод appendGraphPoint в if.

byulent
byulent
4 лет назад

Вопрос. Как сделать так, чтобы график не ПРОрисовывался, а ПЕРЕрисовывался с течением времени?

byulent
byulent
4 лет назад

Так я уже пробовал вызывать метод replot(), но он почему-то всё равно прорисовывает стоячий график, да ещё и совершенно неадекватный.

Evgen
4 лет назад

Здравствуйте! а можно ли этот класс использовать в сечение видео ???допустим есть видео на котором происходят изменения…мне нужно построить график в реальном времени, указав сечения на видео.

Алексей
Алексей
3 лет назад

Если несложно, не могли бы вы еще раз ссылку на проект скинуть?
А то со старой ничего скачать уже, видимо, не представляется возможным

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Июль 2020
Пн Вт Ср Чт Пт Сб Вс
« Июн    
 12345
6789101112
13141516171819
20212223242526
2728293031  

© 2013-2020 MicroTechnics.ru