Это появилось, это не могло не появиться
mppg (Modeling Physical Phenomena Graphical User Interface) — мультиплатформенная графическая С++ библиотека для создания пользовательского интерфейса при численном моделировании физических процессов (окно для рисования примитивов, редактируемые поля ввода/вывода, ...). Библиотека проста в освоении и использовании, достаточно функциональна, позволяет сосредоточиться на численных вычислениях и не отвлекаться на разработку интерфейса. Является хорошим решением для школьников, студентов и научных работников, для всех кто занимается отладкой расчетной программы или тестированием численного метода и кому необходимо следить и управлять проводимым расчетом.
На 11.11.06 доступна версия 2.9.10. Ее можно найти в сети интернет: ftp://ancient.hydro.nsc.ru:/local/home_page/mppg/mppg_ru.htm.
mppg основана на FLTK toolkit и для работы требует установленной FLTK.
Библиотека не предоставляет никаких средств для самих численных методов, это исключительно средство для создания интерфейса.
Исходный код mppg библиотеки полностью свободен, любой человек в праве использовать его произвольным образом в открытых и закрытых проектах. При распространении приложений, статически слинкованных с FLTK, а это происходит по умолчанию, ознакомьтесь с лицензионным соглашением по использованию FLTK. Ограничения FLTK лицензии незначительны, и в первом приближении никак вас не ограничивают.
Для сборки mppg библиотеки необходима FLTK, к сожалению, в современности, требуется установка неофициальной ветки FLTK-1.2. Исходные файлы этой библиотеки можно найти на оригинальном сайте www.fltk.org или ftp://ancient.hydro.nsc.ru/local/home_page/fltk. Рекомендуется пользоваться последним вариантом.
Все традиционно:
cd ./mppg-2.9.6
./configure make make install |
После успешного выполнения в директории lib появится файл libmpp.a, а в директории tests будут собраны тесты.
Для облегчения компилирования собственной программы рекомендуется использовать гну утилиты. В директории user лежит заготовка проекта (makefile, mppg_hello.cpp) для создания простого mppg приложения. При обновлении используемой версии mppg рекомендуется и обновить makefile в своем проекте.
Запустите проект visualc/mpp_lib.dsw, соберите библиотеку и тесты, самостоятельно скопируйте заголовочные файлы и библиотеку в директории компилятора.
Все открытые части mppg находятся в пространстве имен mpp.
Приведем пример простейшего приложения, использующего mppg.
// Simple mpp application
#include <mpp/mpp_window.h> using namespace mpp; int main(){ Mpp_window win; // конструируется окно return run(); // запускается обработчик событий } |
Его внешний вид изображен на рис. 1, а.
Приведем пример более полноценного приложения.
#include <cmath>
#include <mpp/mpp_window.h> #include <mpp/draw.h> using namespace mpp; Scale scale(-1, 1, -1, 1); double phi=0, dphi=1e-6; void ud(){ // что будем рисовать set_scale(scale); // устанавливаем масштаб set_color(FL_WHITE); // устанавливаем цвет для рисования line(0,0,cos(phi),sin(phi)); // рисуем линию } void f(){ // расчетная функция while (may_i_repeat())){ // пока позволяют продолжать phi+=dphi; // выполняем расчет ask_update(); // просим при случае перерисовать экран } // и обновить таблицу } int main(){ Mpp_window win; // создаем основное окно с таблицей и областью для рисования win.canvas.add_shape(new User_draw(ud)); // добавляем функцию для рисования win.table.add_item("phi = ", phi); // поле для редактирования переменной set_work(f); // запускать f в отдельном потоке start(); // запускаем расчетную функцию return run(); // запускаем обработчик событий } |
Mppg приложение может состоять из неограниченное количество окон (объектов типа Mpp_window, Table и Canvas).
У mppg приложения возможны только два состояния: "расчет" и "редактирование полей ввода/вывода". Никаких других состояний быть не может. Переключение осуществляется посредством кнопок "Start" и "Stop" или из программы вызовом функциями start() и stop().
В состоянии "расчет" запускается расчетная функция, в которой и происходят основные вычислительные действия приложения. По таймеру обновляются все объекты (таблица, канвас ...), помеченные как требующие обновления. Какой либо из объектов можно пометить, как требующий обновления вызвав его метод ask_update(). Можно пометить все объекты в программе вызвав функцию ask_update().
При перерисовке canvas вызывает метод redraw(), для каждой из фигур (Shape) в своем списке. При этом, каждая из фигур перерисовывается в соответстви с новыми данными. В mppg есть набор часто используемых графических объектов. Если этот набор не удовлетворяет, можно использовать User_draw. Этот класс позволяет рисовать непосредственно с помощью графических примитивов mppg и FLTK.
Поддерживается два способа помещения расчетной функции в приложение. Расчетная функция находится в отдельном потоке исполнения (set_work(f)), расчетная функция находится в одном потоке с интерфейсом (set_work2(f)), периодически вызывается, исполняя небольшую часть расчета. Каждый из способов имеет свои особенности, и в зависимости от конкретных обстоятельств, можно воспользоваться тем или другим, или обоими.
Если расчетная функция выполняется в основном потоке, это снимает проблему синхронизации, но накладывает ограничение на время ее выполнения; пока она выполняется приложение не реагирует на сообщения. Предполагается, что все используемые расчетной функцией ресурсы имеют глобальную область видимости. Это не самое удобное решение, его преимущество — простота взаимодействия расчетной функции с приложением. Расчетная функция может вызывать любые функции, в том числе Table::ask_update(), Сanvas::ask_update(), ask_update().
Более удобным является случай, когда расчетная функция находится в отдельном потоке. Часть расчетного кода обвернута в цикл повторяющийся пока можно (while(may_i_repeat(){...}). После его завершения необходимо оставить все используемые глобальные данные в валидном состоянии и освободить все занимаемые ресурсы. Вся ответственность за синхронизацию лежит на пользователе библиотеки. may_i_repeat() вернет false после нажатия кнопки "stop".
Иерархия классов
∙Canvas (Fl_Window)
∙Table (Fl_Window)
∙Mpp_Window (Fl_Window)
∙ Input (Fl_Input)
∙Manager (Fl_Window)
∙Grid
∙Hist
∙Plot
∙Roll
Widget #include <mpp/mpp.h> Базовый класс для всех виджетов mppg. При конструировании всех унаследованных классов вызывается его конструктор, в котором происходит регистрация, это позволяет централизованно управлять объектами.
Widget()
|
virtual void ask_update()
Метит виджет, как требующий обновления.
virtual void update()
Обновляет виджет в соответствии с текущими значениями данных в программе.
virtual void set_value()
Считывает значение из поля ввода и обновляет состояние соответствующей переменной в программе.
Group#include <mpp/mpp.h> Группа виджетов, которыми можно манипулировать как одним целым. При вызове методов ask_update(), update() и set_value() рекурсивно действие переносится на всех членов группы.
Group()
|
Window#include <mpp/mpp.h> Предок всех окошек mppg, при его конструировании указатель на него отдается менеджеру приложения, что позволяет в дальнейшем манипулировать ими через него.
Canvas #include <mpp/canvas.h> Виджет обеспечивающий пространство для рисования.
Canvas
|
Canvas(const char * name)
Canvas(int w=500, int h=500, const char * name="Canvas")
Canvas(int x, int y, int w=500, int h=500, const char * name="Сanvas")
Конструируется Canvas в виде отдельного окошка с именем name, именно через это имя происходит идентификация окна в менеджере; конструктор по умолчанию; конструируется Canvas в виде виджета в текущем окне.
~Canvas()
void add_shape(Shape *sh)
void add_shape(Shape &sh)
Добавляет форму в список отображаемых объектов. Ничего не перерисовывает. Это единственный метод, который влияет на то что будет рисоваться.
Table#include <MPP/table.h> Меню с редактируемыми полями ввода (таблица).
Table
|
Table(const char * name)
Table(int w=250, int h=300, const char * name="Table")
Table(int x, int y, int w, int h, const char * name=0)
Конструирует пустую таблицу.
template<class T> void add_item(const char * name, T* var, int deact=0)
template<class T> void add_item(const char * name, T& var, int deact=0)
Добавляет в таблицу редактируемую строку со значением переменной по адресу var. Флаг deact указывает на создание нередактируемого поля. Шаблон инстанцируется для всех типов имеющих операторы чтения и записи в поток.
void update()
Обновляет строки в таблице, считывая значения из переменных.
void set_value()
Обновляет глобальные переменные, считывая их значения из таблицы.
Mpp_window#include <mpp/mpp_window.h> Основное окошко приложения. Содержит область для рисования и таблицу.
Mpp_window
|
Mpp_window(int w=600, int h=400, const char *name="Mpp Window")
Mpp_window(const char *name)
Mpp_window::~Mpp_window()
Manager#include <MPP/manager.h> Окно для управления приложением. Позволяет отображать другие окошки, запускать и останавливать счет. Автоматически создается при выполнении функции mpp::run().
Manager(int w=300, int h=200, const char * name="MPP manager")
Библиотека предоставляет набор часто используемых фигур для отображения расчетных данных. Все они являются потомками абстрактного класса Shape, при перерисовке канвас пробегает по списку фигур и вызывает у каждой метод draw, который и производит необходимое рисование. Новый графические объекта добавляются в список вызовом одного из методов канваса: add_shape(Shape *), add_shape(Shape &).
Shape#include <MPP/draw.h> Абстрактный класс для рисования относительно сложных графических объектов (hist, orbit, ...). Предполагается использовать их посредством canvas.add_shape.
Bitmap#include <MPP/draw.h> Рисуется двумерный массив оттенками серого. Bitmap(const double *, int w, int h, double C1, double C2, const Scale *)
Circle#include <MPP/draw.h> Рисуется окружность с заданными параметрами. Circle(Float x, Float y, Float R, Fl_Color, const Scale *)
Grid#include <MPP/draw.h> Рисуется прямоугольная сетка от xmin с шагом dx пока внутри области ограниченной масштабом. Аналогично по y. Grid(double xmin, double dx, double ymin, double dy, const Scale *)
Hist#include <MPP/draw.h> Гистограмма. Hist(unsigned int nc, double xmin, double xmax, Fl_Color color1, const Scale *, int legs=0)
Строит гистограмму из nc каналов на промежутке от xmin до xmax.
void set(const double *x, unsigned int sz)
Распределяет значения из массива x длины sz по каналам гистограммы.
void add(const double *x, unsigned int sz)
Аналогично set, но добавляет новые события к старым значениям в каналах.
Orbit#include <MPP/draw.h> Рисует траекторию точками. Orbit(int sz_all, int sz_head, Fl_Color color_head, Fl_Color color_tail, const Scale*)
Рисует траекторию из sz_all точек, первые sz_head цветом color_head, остальные цветом color_tail.
void add(double x, double y)
Добавляет новую точку к траектории, если количество точек становится больше sz_all, отбрасывает старые.
void reset()
Очищает траекторию.
Plot#include <MPP/draw.h> Рисует один итерратор относительно другого. template <class T> class Plot(T x1, T x2, T y1, Fl_Color color, const Scale *)
Plot_a#include <MPP/draw.h> Рисует один массив относительно другого. Plot_a(const double *x, const double *y, int sz, Fl_Color color, const Scale *)
Plot_v#include <MPP/draw.h> Рисует один вектор относительно другого. Plot_v(const std::vector<double> &x, const std::vector<double> &y, Fl_Color color, const Scale)
Plot_l#include <MPP/draw.h> Рисует список пар точек. Plot_l(const std::list<std::pair<mpp::Float, mpp::Float> > & pointl, Fl_Color color, const Scale & )
Roll#include <MPP/draw.h> Множество точек сдвигающееся влево. Roll(Fl_Color color1, const Scale *)
void add(double x, double y)
Добавляется новая точка.
void reset()
Очищает массив точек.
User_draw#include <MPP/draw.h> Пользователь сам, с помощью графических примитивов, определяет в функции ff что будет рисоваться. В этой функции следует не забывать устанавливать необходимые атрибуты рисования (set_scale(scale), set_color(color), ...). User_draw(void (*ff)())
Scale #include <MPP/scale.h> Класс позволяющий рисовать в canvas"е" в вещественных координатах. Масштаб не принадлежит никакому окну, но адаптирует работу графических примитивов для любого окна с помощью функций void set_scale(const Scale *), void set_scale(const Scale &).
Для преобразования координат и промежутков из физических единиц в оконные используются функции: inline int transx(double x), inline int transy(double y), inline int scalex(double x), inline int scaley(double y).
Scale(Float x1, Float x2, Float y1, Float y2, bool xy=false)
Конструирует масштаб с соответствующими вещественными координатами в окне. Аргумент xy устанавливает одинаковое разрешение по горизонтали и вертикали.
void set(Float x1, Float x2, Float y1, Float y2, bool xy=false)
Устанавливает новые параметры для масштаба.
file_diaolog
|
Набор графических примитивов для рисования можно использовать только в User_draw. Прежде чем использовать примитивы, следует выставить атрибуты рисования: set_color(Fl_Color), set_scale(const Mpp_Scale *). Без последнего масштабы могут не работать.
plot#include <MPP/draw.h> void point(Float x, Float y)
void point(Float x, Float y, Fl_Color color)
void pointb(Float x, Float y)
void pointb(Float x, Float y, Fl_Color color)
void line(Float x, Float y, Float x1, Float y1)
void line(Float x, Float y, Float x1, Float y1, Fl_Color color)
void circle(Float x, Float y, Float r)
void circle(Float x, Float y, Float r, Fl_Color color)
Функция point ставит точку размером 1+2 point_size, pointb ставит точку размером 1+2 point_size_b.
extern int point_size=0
extern int point_size_b=1
Размеры точек по умолчанию. При необходимости их можно изменить.
template <class T> void plot(T x1, T x2, T y1)
x1, x2 — итераторы на начало и конец контейнера с x координатами, y1 — итератор на начало контейнера со значениям y.
template <class T> void plot(const T& f, double xl, double xr, int n)
Рисуется объект функция (f(double)) на промежутке (xl, xr) по n точкам.
void plot(double(*)(double), double xl, double xr, int n)
Рисуется функция (f(double)) на промежутке (xl, xr) по n точкам.
Набор функций для преобразования координат и промежутков из физических единиц в экранные. Правильно работают только после установления атрибута set_scale.
scalex#include <MPP/scale.h> int scalex(Float x)
int scaley(Float y)
transx#include <MPP/scale.h> int transx(Float x)
int transy(Float y)
set_scale#include <MPP/scale.h> void set_scale(const Scale *)
void set_scale(const Scale &s)
set_work#include <MPP/mpp.h> void set_work(void (*f)(void))
void set_work2(void* (*f)(void))
Устанавливает функцию f для запуска в отдельном потоке. Устанавливает функцию f для периодического запуска в главном потоке.
Запускается основной цикл с обработкой событий. Функция завершается когда закрывается последнее окно приложения.
Переводит приложение в режим "счет". Если был режим "редактирование", забирает значения из таблицы в соответствующие переменные. Запускает обновление таблицы по таймеру. Запускает расчетные функции. Если был режим "счет", предварительно вызывается stop.
Переводит приложение в режим "редактирование". Если окно было в режиме "счет" больше не запускает расчетную функцию, если запущена функция в отдельном потоке выставляет флаг askedit=true и дожидается завершения потока. После этого обновляет таблицу и перестает обновлять ее по таймеру. Если окно в режиме "редактирование" — ничего не делает.
Метит все виджеты как требующие обновления (например перерисовка для канваса).
Обновляет все виджеты требующие обновления. Обычно вызывается автоматически по таймеру.
void set_update_time(double time)
Устанавливает период проверки всех объектов на необходимость обновления в секундах. По умолчанию величина периода составляет 0.25 с.
unsigned char * grabbing(Fl_Window *, int x, int y, int dx, int dy)
unsigned char * grabbing(Fl_Window *);
void grabbing(Fl_Window *, int x, int y, int dx, int dy, const char * fname, int compr=COMPRESSION_DEFLATE)
inline void grabbing(Fl_Window *, const char * fname, int compr=COMPRESSION_NONE)
inline void grabbing(Fl_Window &win, const char * fname, int compr=COMPRESSION_NONE)
Копирует окно в буфер или файл. Для копирования в tiff файл поддерживается несколько компрессий COMPRESSION_NONE – без сжатия, COMPRESSION_DEFLATE – zip компрессия без потери качества (дает наилучший результат по сжатию и качеству изображения), COMPRESSION_JPEG – jpg компрессия с потерей качества (рекомендуется применять для сжатия плотно заполненных изображений).
char * file_dialog(const char * dialog_title=0, const char *pattern="All Fies (*.*)\0*.*\0", const char *fname=””)
char * file_dialog_f(const char * message=0, const char *pattern=0, const char *fname=0)
Функции вызывают диалог выбора файла и возвращают строку с именем выбранного файла, если пользователь отказался от выбора файла – возвращается 0. file_dialog – найтивный win32 диалог, file_dialog_f – FLTK диалог.
Приглашаются волонтеры.
Благодарности людям участвовавшим в проекте. Голубенко Д.Ю. за ценные обсуждения, разработчикам библиотеки FLTK за удобный инструмент, Mедведеву Д.А. за несколько "Shapes" и полезные обсуждения.
Интересно получить мнения об использовании mppg, пожелания о расширении возможностей, сообщения об ошибках (в коде и документации).