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

9.2 KiB
Raw Blame History

title categories date lastmod draft featured_image
FLTK - пишем маленькие кросс-платформенные приложения с олдскульным интерфейсом
cpp
archive
2015-08-19T00:00:00+03:00 2023-12-04T20:40:58+03:00 false miniature.jpg

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

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

Сейчас всё можно сделать проще!

Обновлённая статья - FLTK - упрощаем себе жизнь с msys2.

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

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

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

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

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

src

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

mingw1

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

mingw2

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

mingw3

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

mingw4

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

mingw5

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

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

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

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

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

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 убираем чёрное окошко у приложения и подключаем некоторые системные библиотеки

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

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

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

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

output

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

app

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

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