вторник, 19 января 2016 г.

Строим графики используя gnuplot

Gnuplot - знакомая многим пользователям линукса свободная кросс-платформенная программа, позволяющая строить двухмерные и трехмерные графики. К сожалению, в Qt нет встроенного виджета для отрисовки графиков, но есть сторонние библиотеки позволяющие это сделать. Так есть QwtPlot3D (последний раз обновлялся в 2007 году) для отрисовки трехмерных графиков, для двухмерных графиков есть, например, Qwt. Но сегодня пойдет речь о Gnuplot-iostream interface, позволяющему использовать всю мощь программы gnuplot с минимумом затрат. Эта библиотека представляет собой iostream pipe, независимый от используемой платформы. И так, приступим.
Для начала нам понадобятся сама библиотека, представляющая собой один-единственный заголовочный файл, установленное приложение gnuplot и установленная библиотека Boost (как устанавливать описано тут).
В Windows необходимо также добавить путь к приложению gnuplot в системную переменную Path (например, этот путь может быть следующим C:\Program Files (x86)\gnuplot\bin). Для работы с gnuplot-iostream понадобится также подключить три библиотеки из Boost: filesystem, system и iostreams. Необходимо соблюдать именно такой порядок, иначе получим ошибку. Пример файла проекта Qt:
#-------------------------------------------------
#
# Project created by QtCreator 2016-01-11T16:59:58
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = qgnuplot
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h \
gnuplot-iostream.h
win32 {
BOOSTPATH = D:/Qt/boost_1_60_0
BOOSTVER = 1_60
MINGWVER = 48
INCLUDEPATH += $$BOOSTPATH
LIBS += -L$$BOOSTPATH/stage/lib \
-llibboost_filesystem-mgw$$MINGWVER-mt-$$BOOSTVER \
-llibboost_system-mgw$$MINGWVER-mt-$$BOOSTVER \
-llibboost_iostreams-mgw$$MINGWVER-mt-$$BOOSTVER
}
unix {
LIBS += -lboost_filesystem \
-lboost_system \
-lboost_iostreams
}
view raw qgnuplot.pro hosted with ❤ by GitHub
Не забываем подключить заголовочный файл:
#include "gnuplot-iostream.h"
Использование gnuplot-iostream выглядит следующим образом:
#ifdef _WIN32
Gnuplot gp("gnuplot.exe -persist");
#else
Gnuplot gp;
#endif
std::string expr = "x**2 - y**2";
gp << "f(x,y)=" << expr << "\n";
gp << "splot f(x,y) with pm3d palette title 'f(x,y)=" << expr << "'\n";
view raw simple.cpp hosted with ❤ by GitHub
Для Windows указываем "gnuplot.exe -persist", это не дает сразу же закрыться окну. В линуксе и без этого все хорошо работает.
Рассмотрим простой пример. На главном окне (MainWindow) будет располагаться поле для ввода уравнения f(x,y) и две кнопки. По клику на одну кнопку мы строим через приложение gnuplot график уравнения f(x,y), по клику по другой кнопке мы строим выводим график заданного массива точек (x,y,z).
 
Файл проекта приведен выше. Заголовочный файл главного окна mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
#include <gnuplot-iostream.h> //библиотека для работы с gnuplot
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_plot_expr(); //отрисовка графика по заданному выражению
void on_plot_array(); //отрисовка графика из заданного массива
private:
QLineEdit *expr; //выражение для f(x,y)
QPushButton *plot_expr; //кнопка для отрисовки графика по заданному выражению
QPushButton *plot_array; //кнопка для отрисовки графика из заданного массива
};
#endif // MAINWINDOW_H
view raw mainwindow.h hosted with ❤ by GitHub
Файл mainwindow.cpp:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//Создаем главный виджет
QWidget *main = new QWidget;
setCentralWidget(main);
//На главном виджете размещаем вертикальный layout
QVBoxLayout *main_layout = new QVBoxLayout;
main->setLayout(main_layout);
QLabel *lbl = new QLabel;
lbl->setText("f(x,y)=");
main_layout->addWidget(lbl);
//Поле для ввода выражения f(x,y)
expr = new QLineEdit;
expr->setText("x**2 + y**2");
main_layout->addWidget(expr);
//Кнопка отрисовки графика для выражения f(x,y)
plot_expr = new QPushButton;
plot_expr->setText("Plot expression");
main_layout->addWidget(plot_expr);
connect(plot_expr, SIGNAL(clicked()), this, SLOT(on_plot_expr()));
//Кнопка отрисовки заданного массива
plot_array = new QPushButton;
plot_array->setText("Plot array");
main_layout->addWidget(plot_array);
connect(plot_array, SIGNAL(clicked()), this, SLOT(on_plot_array()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::on_plot_expr()
{
#ifdef _WIN32
Gnuplot gp("gnuplot.exe -persist");
#else
Gnuplot gp;
#endif
gp << "f(x,y)=" << expr->text().toStdString() << "\n";
gp << "splot f(x,y) with pm3d palette title 'f(x,y)=" << expr->text().toStdString() << "'\n";
}
void MainWindow::on_plot_array()
{
#ifdef _WIN32
Gnuplot gp("gnuplot.exe -persist");
#else
Gnuplot gp;
#endif
int size = 100;
QVector<QVector<double> > arr(size*size);
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
{
arr[j + size*i].append(i);
arr[j + size*i].append(j);
arr[j + size*i].append((i*i - j*j)/size);
}
gp << "f(x,y)=" << expr->text().toStdString() << "\n";
gp << "splot '-' with dots palette title 'Plotting the array'\n";
gp.send1d(arr);
}
view raw mainwindow.cpp hosted with ❤ by GitHub
В итоге, при нажатии кнопки "Plot expression" получаем:
Весь проект доступен для скачивания на github: https://github.com/qtneko/qt-cat/tree/master/gnuplot

Комментариев нет:

Отправить комментарий