А знали ли вы, что...

Этот сайт написан практически с нуля и до сих пор дописывается.

Прочее

Сайт работает
на Raspberry Pi

Спасибы

FLTK - пишем маленькие кросс-платформенные приложения с олдскульным интерфейсом


Иногда требуется написать кросс-платформенное приложение с небольшим размером. Для управления микроконтроллером, например. Или рисования иконок для дисплеев. Но использовать кросс-платформенные библиотеки вроде Qt, WxWidgets не имеет смысла - весят они ну очень много. Неудобно получается, когда приложение весит 100кб, а графическая библиотека для него – под 30Мб.

На помощь к нам приходит FLTK – Fast, Light Toolkit.

Библиотека распространяется в виде исходного кода и скачивается с сайта fltk.org. Значит, сейчас будем её собирать.

Весь процесс описывается для Windows, собираем компилятором MinGw. Если у вас его нет – вперёд скачивать. Разделим процесс на две части.

 

Часть первая – компиляция библиотеки

Итак, начнём. Скачиваем последнюю версию FLTK с официального сайта. Архив должен называться fltk-версия-source.tar.gz. Распаковываем его в любое удобное место. Я распаковал его прямо на на диск C в папку fltk-src.

В распакованном виде всё должно выглядеть так:

 

Теперь нам нужно запустить MSYS. Переходим туда_куда_установлен_MinGw\msys\1.0 и запускаем там msys.bat.

 

Вводим туда cd /диск/папка_с_fltk

 

Потом вводим ./configure --enable-threads --enable-localjpeg --enable-localzlib --enable-localpng и ждём.

 

Далее следует ввести make и ждать. Можно пойти чай заварить.

 

Теперь копируем библиотеки в компилятор. Для этого вводим make install

 

Всё, что получилось складывается в папку туда_куда_установлен_MinGw\msys\1.0\local. Для удобства я перенёс эти папки в корень MinGw.

Всё, библиотека готова к использованию.

 

Часть вторая – пишем программу

Теперь мы можем написать тестовое приложение, используя эту библиотеку. Писать будем программу, которая будет считать количество нарисованных пикселей.

Для начала нужно нарисовать макет нашей программы (о FLUID я узнал позже). Я нарисовал в GIMP.

Зачем это делать? Всё просто. У каждого элемента в окне есть свои координаты. И чтобы не ставить элементы наугад, можно просто навести мышку на точку в графическом редакторе и узнать координаты. Исходя из этого всего пишем программу.

 

fltk_test.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();
}

 

Теперь нужно сие дело откомпилить. Открываем терминал в папке с программой и пишем там

g++ имя_исходника -o имя_исполняемого_файла -DWIN32 -D__NO_INLINE__ -static -static-libgcc -static-libstdc++ -lfltk -lmingw32 -lole32 -luuid -lcomctl32 -mwindows

 

-DWIN32 – флаг, которые заставят думать FLTK, что мы под Windows

-mwindows – убираем чёрное окошко у приложения и подключаем некоторые системные библиотеки

-D__NO_INLINE__ – флаг, который заставит работать math в нашей программе

-static -static-libgcc -static-libstdc++ – флаги. благодаря которым программа запустится на других компьютерах и не будет ничего требовать

-lfltk -lmingw32 -lole32 -luuid -lcomctl32 – флаги, с помощью которых подключается библиотека и всё, что ей требуется. В некоторых случаях может также понадобиться флаг -lgdi32.

Также можно добавить флаги -O2/-Os и -s для уменьшения размера исполняемого файла.

 

 

Теперь можно запустить наше творение :)

И да, код программы ужасен и написан в исключительно демонстрационных целях.

Источников в этот раз не будет, так как всю информацию черпал из программ-примеров, находящихся в том же архиве, что и исходник библиотеки (папки examples и test).