Сохранение данных в файл

Данные из программы, например,

#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

gp01.png

Подробнее визуализация данных из файла с помощью gnuplot описана здесь.

Использование каналов

Каналы в Unix/Linux позволяют перенаправить вывод команд и программ на вход gnuplot. В простейшем случае, с помощью команды echo можно передать в gnuplot ее же собственную команду. Например:

echo "plot sin(x)" | gnuplot -persist

выводит график синусоиды

gp02.png

Опция 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;
}

Результат (наконец-то не синусоида!):

gp03.png

Класс-обертка для работы с 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();
}

gp04.png

gp05.png

Библиотеки



Комментарии

comments powered by Disqus