mmote.ru/content/posts/fltk-apps/index.md

188 lines
9.0 KiB
Markdown
Raw Normal View History

2023-01-01 18:21:01 +03:00
---
title: "FLTK - пишем маленькие кросс-платформенные приложения с олдскульным интерфейсом"
categories: ["cpp", "archive"]
date: 2015-08-19T00:00:00+03:00
draft: false
featured_image: miniature.jpg
---
Иногда требуется написать кросс-платформенное приложение с небольшим размером. Для управления микроконтроллером, например. Или [рисования иконок для дисплеев](/avr-lcd-images). Но использовать кросс-платформенные библиотеки вроде Qt, WxWidgets не имеет смысла - весят они ну очень много. Неудобно получается, когда приложение весит 100кб, а графическая библиотека для него под 30Мб.
На помощь к нам приходит FLTK Fast, Light Toolkit.
<!--more-->
Библиотека распространяется в виде исходного кода и скачивается с сайта [fltk.org](http://www.fltk.org/software.php"). Значит, сейчас будем её собирать.
Весь процесс описывается для Windows, собираем компилятором **MinGw**. Если у вас его нет [вперёд скачивать](https://sourceforge.net/downloads/mingw). Разделим процесс на две части.
# Часть первая компиляция библиотеки
Итак, начнём. Скачиваем последнюю версию FLTK с официального сайта. Архив должен называться fltk-версия-**source**.tar.gz. Распаковываем его в любое удобное место. Я распаковал его прямо на на диск C в папку fltk-src.
В распакованном виде всё должно выглядеть так:
![src](src.png)
Теперь нам нужно запустить MSYS. Переходим удауда_установлен_MinGw\msys\1.0` и запускаем там msys.bat.
![mingw1](mingw1.png)
Вводим туда `cd /диск/папка_с_fltk`
![mingw2](mingw2.png)
Потом вводим `./configure --enable-threads --enable-localjpeg --enable-localzlib --enable-localpng` и ждём.
![mingw3](mingw3.png)
Далее следует ввести `make` и ждать. Можно пойти чай заварить.
![mingw4](mingw4.png)
Теперь копируем библиотеки в компилятор. Для этого вводим `make install`
![mingw5](mingw5.png)
Всё, что получилось складывается в папку удауда_установлен_MinGw\msys\1.0\local`. Для удобства я перенёс эти папки в корень MinGw.
Всё, библиотека готова к использованию.
# Часть вторая пишем программу
Теперь мы можем написать тестовое приложение, используя эту библиотеку. Писать будем программу, которая будет считать количество нарисованных пикселей.
Для начала нужно нарисовать макет нашей программы (о FLUID я узнал позже). Я нарисовал в GIMP.
![gimp](gimp.png)
Зачем это делать? Всё просто. У каждого элемента в окне есть свои координаты. И чтобы не ставить элементы наугад, можно просто навести мышку на точку в графическом редакторе и узнать координаты. Исходя из этого всего пишем программу.
fltk_test.cpp:
```cpp
#include <FL/Fl.H>
#include <Fl/Fl_Window.H>
#include <Fl/Fl_Box.H>
#include <Fl/fl_draw.H>
#include <stdio.h>
#include <map>
#include <math.h>
#define CANVAS_X 10
#define CANVAS_Y 10
#define CANVAS_WIDTH 180
#define CANVAS_HEIGHT 100
#define GRID_LEN 5 // размер зерна в холсте
Fl_Box *count_label;
char buf[16];
struct point {
int x, y;
};
typedef std::map<std::pair<int, int>, point> PointMap; // карта с координатами пикселей
PointMap pixels;
class PaintWindow : public Fl_Window { //немного модифицируем стандартное окно
public:
PaintWindow(int w, int h, char const *title) : Fl_Window(w, h, title) {
}
void setpixel(int x, int y, bool add) {
std::pair<int, int> p = std::make_pair(x * GRID_LEN, y * GRID_LEN); // с помощью древней магии мы используем два аргумента как ключ к карте пикселей
if (add) {
point pixel;
pixel.x = x * GRID_LEN;
pixel.y = y * GRID_LEN;
pixels[p] = pixel;
} else {
pixels.erase(p);
}
sprintf(buf, "%d px", pixels.size());
count_label->label(buf); // обновляем надпись
redraw();
}
int handle(int event) {
double x = Fl::event_x();
double y = Fl::event_y();
if (x < CANVAS_X || x > CANVAS_X + CANVAS_WIDTH - 1 || y < CANVAS_Y || y > CANVAS_Y + CANVAS_HEIGHT - 1) // если мышка за границей холста, то ничего не делаем
return Fl_Window::handle(event);
if (event == FL_PUSH) {
setpixel(round(x / GRID_LEN), round(y / GRID_LEN), Fl::event_button() == FL_LEFT_MOUSE); //округляем координаты исходя из зерна, добавляем или удаляем пиксель в зависимости от кнопки мыши
}
if (event == FL_DRAG) {
setpixel(round(x / GRID_LEN), round(y / GRID_LEN), Fl::event_button() == FL_LEFT_MOUSE);
}
return Fl_Window::handle(event);
}
void draw(void) {
Fl_Window::draw();
fl_color(FL_WHITE);
fl_rectf(CANVAS_X, CANVAS_Y, CANVAS_WIDTH, CANVAS_HEIGHT); // заливаем белый квадрат
fl_color(FL_BLACK);
typedef PointMap::iterator it_type;
for (it_type iterator = pixels.begin(); iterator != pixels.end(); iterator++) { // перебираем все пиксели
int x = iterator->second.x;
int y = iterator->second.y;
fl_rectf(x, y, 5, 5); // рисуем точки
}
}
};
PaintWindow *window;
int main(void) {
window = new PaintWindow(200, 300, "Points"); //создаём окно
count_label = new Fl_Box(34, 177, 133, 56, "..."); // создаём подпись
count_label->labelsize(50); //выставляем размер шрифта
window->end(); // закрываем группу окна
window->show(); // показываем окно
return Fl::run();
}
```
Теперь нужно сие дело откомпилить. Открываем терминал в папке с программой и пишем там
```cmd
g++ <имя_исходника> -o <имя_исполняемогоайла> -DWIN32 -D__NO_INLINE__ -static -static-libgcc -static-libstdc++ -lfltk -lmingw32 -lole32 -luuid -lcomctl32 -mwindows
```
**-DWIN32** флаг, которые заставят думать FLTK, что мы под Windows
**-mwindows** убираем чёрное окошко у приложения и подключаем некоторые системные библиотеки
**-DNO_INLINE** флаг, который заставит работать math в нашей программе
**-static -static-libgcc -static-libstdc++** флаги. благодаря которым программа запустится на других компьютерах и не будет ничего требовать
**-lfltk -lmingw32 -lole32 -luuid -lcomctl32** флаги, с помощью которых подключается библиотека и всё, что ей требуется. В некоторых случаях может также понадобиться флаг -lgdi32.
Также можно добавить флаги **-O2** и **-s** для уменьшения размера исполняемого файла.
![output](output.png)
Теперь можно запустить наше творение :)
![app](app.png)
И да, код программы ужасен и написан в исключительно демонстрационных целях.
Источников в этот раз не будет, так как всю информацию черпал из программ-примеров, находящихся в том же архиве, что и исходник библиотеки (папки examples и test).