Сохранение данных в файл
Данные из программы, например,
#include <stdio.h>
#include <math.h>
const double pi = 3.14;
int main()
{
const double npoints = 200;
const double step = 4 * pi / npoints;
FILE* fp = fopen("test.dat","w");
for (int i = 0; i < npoints+1; i++)
{
double x = -2 * pi + i * step;
double y = sin(x);
fprintf(fp,"%f\t%f\n", x, y);
}
fclose(fp);
}
сохраняются в файле (test.dat
). Затем в gnuplot по ним строится график
gnuplot> plot 'test.dat' using 1:2 with lines
Подробнее визуализация данных из файла с помощью gnuplot описана здесь.
Использование каналов
Каналы в Unix/Linux позволяют перенаправить вывод команд и программ на вход gnuplot. В простейшем случае, с помощью команды echo
можно передать в gnuplot ее же собственную команду. Например:
echo "plot sin(x)" | gnuplot -persist
выводит график синусоиды
Опция persist
нужна, чтобы задержать окно gnuplot на экране после окончания построения графика.
gnuplot позволяет строить графики на основе данных, вводимых в командной строке
plot '-' using ... with ...
x1 y1
x2 y2
x3 y3
...
e
Псевдофайл данных обозначается как '-'
; символ e
обозначает конец файла (EOF).
Теперь сформируем команду gnuplot, строящую график по данным из псевдофайла, и получим сами данные
#include <stdio.h>
#include <math.h>
const double pi = 3.14;
int main()
{
const double npoints = 200;
const double step = 4 * pi / npoints;
printf("%s\n", "plot '-' using 1:2 with lines"); // Рисовать график по данным
// из командной строки
for (int i = 0; i < npoints+1; i++) // Получаем данные
{
double x = -2 * pi + i * step;
double y = sin(x);
printf("%f\t%f\n", x, y);
}
printf("%s\n", "e"); // Закрыть псевдофайл
}
Направим результат работы программы на вход gnuplot
./tttest | gnuplot -persist
и получим уже знакомый по первому рисунку результат.
Запуск процесса gnuplot из программы на C/C++
В предыдущем примере мы использовали неименованный канал, теперь создадим канал именованный. Начнем с примера, работающего в Unix/Linux:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *gp = popen("gnuplot -persist","w"); // gp - дескриптор канала
if (gp == NULL)
{
printf("Error opening pipe to GNU plot.\n");
exit(0);
}
fprintf(gp, "plot sin(x)\n");
pclose(gp);
return 0;
}
Любопытно, что используя при компиляции стандарт -c99
, выдается предупреждение о неявной декларации popen
и pclose
.
Процесс в Unix/Linux открывается функцией popen
. В результате будет создан канал (gp
), запись в который выполняется при помощи fprintf
. Закрывается процесс функцией pclose
.
Рассмотрим кроссплатформенный пример создания процесса gnuplot. Для каждой операционной системы используются свои функции открытия/закрытия процесса:
#include <iostream>
#include <cstdio>
// Для Windows нужно указать путь к исполняемому файлу gnuplot,
// например: "c:Program Files (x86)gnuplotbinpgnuplot.exe"
#ifdef WIN32
#define GNUPLOT_NAME "pgnuplot -persist"
#else
#define GNUPLOT_NAME "gnuplot -persist"
#endif
int main()
{
#ifdef WIN32
FILE *pipe = _popen(GNUPLOT_NAME, "w");
#else
FILE *pipe = popen(GNUPLOT_NAME, "w");
#endif
if (pipe != NULL)
{
fprintf(pipe, "plot '-' with lines\n");
for(int i = 0; i < 10; i++)
fprintf(pipe, "%d\n", i);
fprintf(pipe, "%s\n", "e");
fflush(pipe);
// ожидание нажатия клавиши
std::cin.clear();
std::cin.ignore(std::cin.rdbuf()->in_avail());
std::cin.get();
#ifdef WIN32
_pclose(pipe);
#else
pclose(pipe);
#endif
}
else
std::cout << "Could not open pipe" << std::endl;
return 0;
}
Результат (наконец-то не синусоида!):
Класс-обертка для работы с gnuplot
Поместим подробности работы с gnuplot внутрь класса. Ничего нового мы здесь не делаем, а нюансы отмечены в комментариях.
// gnuplot.h
#ifndef _GNUPLOT_H_
#define _GNUPLOT_H_
#include <cstdio>
#include <string>
#include <iostream>
#ifdef WIN32
#define GNUPLOT_NAME "pgnuplot -persist"
#else
#define GNUPLOT_NAME "gnuplot -persist"
#endif
using std::string;
using std::cerr;
class Gnuplot
{
public:
Gnuplot() ;
~Gnuplot();
void operator ()(const string & command); // отправить команду gnuplot
protected:
FILE *gnuplotpipe;
};
Gnuplot::Gnuplot()
{
#ifdef WIN32
gnuplotpipe = _popen(GNUPLOT_NAME, "w");
#else
gnuplotpipe = popen(GNUPLOT_NAME, "w");
#endif
if (!gnuplotpipe)
{
cerr << ("Gnuplot not found !");
}
}
Gnuplot::~Gnuplot()
{
fprintf(gnuplotpipe,"exit\n");
#ifdef WIN32
_pclose(gnuplotpipe);
#else
pclose(gnuplotpipe);
#endif
}
void Gnuplot::operator()(const string & command)
{
fprintf(gnuplotpipe,"%s\n",command.c_str());
fflush(gnuplotpipe); //без fflush ничего рисоваться не будет
};
#endif // #ifndef _GNUPLOT_H_
Пример использования созданного класса:
#include <iostream>
#include "gnuplot.h"
int main()
{
Gnuplot plot;
plot("plot sin(x)");
std::cin.get();
plot("plot cos(x)");
std::cin.get();
}
Библиотеки
- gnuplot interfaces in ANSI C — послужила основой для двух следующих библиотек.
- gnuplot-cpp: C++ интерфейс к gnuplot через POSIX-каналы.
- C++ to gnuplot library.
Комментарии
comments powered by Disqus