Начинаем использовать GTKD

Доброго времени суток, хабр! Сегодня хочу рассказать вам о том, как писать приложения с использованием GTK+ и языка программирования D.

6f948d1964b34ce1a0f8df4754ea07b4.png

О том, что такое GTK рассказывать не буду. Приступим сразу к делу.

Создадим новый проект, то есть просто папку =)Добавим туда dub.json с таким содержанием:

{ «name»: «hellogtkd», «targetPath»: «bin», «targetType»: «executable», «dependencies»: { «gtk-d»:»~>3.1.3» } } Система сборки dub по умолчанию ищет исходники в папке source.Создадим её и добавим туда файл main.d:

import gtk.Main; import gtk.MainWindow; import gtk.Label;

class HelloWorld: MainWindow { this () { super («wintitle»); setBorderWidth (20); // чтобы не совсем маленькое было add (new Label («hello habr!»)); showAll (); } }

void main (string[] args) { Main.init (args); new HelloWorld (); Main.run (); } Запустим сборку, а затем приложение dub build && bin/hellogtkd И вуаля! 2f1a17510c1b481f964aa7f6d8e5262e.png

Не будем на этом останавливаться и создадим предельно простой ui в программе glade, а затем загрузим в нашу программу.

96924368b6a74216885fc984b27e9c5c.png

Обратите внимание на дерево добавленных компонентов (сверху справа), для главного окна, конпки и поля отрисовки присвоены идентификаторы отличные от тех, что даются по умолчанию (mwindow, btn, plot соответственно), мы их будем использовать для удобного получения из билдера.Сохраним файл в папке проекта под именем ui.glade и отредактируем main.d:

новый main.d import std.string: format;

import gtk.Main; import gtk.Builder; import gtk.Window; import gtk.Widget;

import std.format;

final class UI { string glade_file;

Builder builder; // используется для загрузки интерфейса и его хранения

this (string file) { builder = new Builder;

if (! builder.addFromFile (file)) except («could no load glade object from file '%s'», file);

glade_file = file;

prepare (); }

void prepare () { prepareMainWindow (); }

void prepareMainWindow () { auto w = obj! Window («mwindow»); // получаем экземпляр Window по идентификатору w.setTitle («glade ui»); w.addOnHide ((Widget aux){ Main.quit (); }); // при закрытии окна нужно закрывать всю программу w.showAll (); // показываем главное окно }

// так мы из загруженного файла получаем ссылку на экземпляр необходимого объекта auto obj (T)(string name) { // метод getObject возвращает ссылку на объект класса ObjectG, являющийся родителем для всех обёрнутых классов auto ret = cast (T)builder.getObject (name); // поэтому мы кастуем его к необходимому классу if (ret is null) except («no '%s' element in file '%s'», name, glade_file); return ret; }

void except (string file=__FILE__, size_t line=__LINE__, Args…)(Args args) { throw new Exception (format (args), file, line); } }

void main (string[] args) { Main.init (args); new UI («ui.glade»); // стоит обратить внимание на то, что файл будет искаться в той дирректории, в которой была запущена программа Main.run (); } Думаю все догадались, для чего был добавлен элемент GtkDrawingArea — мы будем рисовать.Добавим действия, выполняемые при нажатии кнопки:

… void prepare () { prepareMainWindow (); prepareButtonAction (); } … void prepareButtonAction () { // мы просто передаём делегат, который будет вызывается при сигнале Clicked obj! Button («btn»).addOnClicked ((Button aux) { obj! DrawingArea («plot»).queueDraw (); // заставить переотрисоваться наш plot }); } Подготовим пока пустой класс для отрисовки, создадим файл draw.d в папке source:

module draw;

import std.stdio; import std.datetime: Clock;

import gtk.Widget; import cairo.Context;

class Figure { bool draw (Scoped! Context cr, Widget aux) { writefln («draw figure %012d», Clock.currAppTick ().length);

// необходимо вернуть true, если мы хотим остановить обработку события // другими обработчиками, иначе возвращаем false, чтобы остальные // обработчики были вызваны при вызове сигнала // такое поведение касается всех сигналов, принимающих bool delegate (…) return false; } } Привяжем фигуру:

… void prepare () { prepareMainWindow (); prepareButtonAction (); prepareDrawing (); } … Figure fig;

void prepareDrawing () { fig = new Figure; // для подключения к сигналу мы можем использовать метод класса, это тоже делегат obj! DrawingArea («plot»).addOnDraw (&fig.draw); } Теперь при запуске приложения вы увидите в консоли примерно такой вывод:

… draw figure 014855276247 draw figure 014872180248 draw figure 014889286316 … Можно заметить, что переотрисовка вызывается не только при нажатии кнопки, но и при других событиях: перемещение и изменение размеров окна, анимация цвета кнопки и тд.За подробностями по поводу перерисовки виджетов обратитесь к документации GTK+.Вызов переотрисовки при нажатии кнопки я поставил для того, чтобы показать как это делается.По сути просто совпало, что нажатие кнопки само по себе ведёт к переотрисовке (из-за изменения цвета фона кнопки), но могут быть и другие события, не связанные с изменением GUI, тогда такой вызов необходим.

Добавим немного кода рисования:

… float angle = 0; bool draw (Scoped! Context cr, Widget aux) { writefln («draw figure %012d», Clock.currAppTick ().length);

import std.math;

auto w = aux.getAllocatedWidth (); auto h = aux.getAllocatedHeight ();

auto xc = w / 2.0; auto yc = h / 2.0;

auto radius = fmin (w, h) / 2.0;

auto x1 = cos (angle) * radius + xc; auto y1 = sin (angle) * radius + yc;

auto x2 = cos (angle + PI / 3×2) * radius + xc; auto y2 = sin (angle + PI / 3×2) * radius + yc;

auto x3 = cos (angle + PI / 3×4) * radius + xc; auto y3 = sin (angle + PI / 3×4) * radius + yc;

cr.setSourceRgb (1.0, 0.0, 0.0); cr.moveTo (x1, y1); cr.lineTo (x2, y2); cr.lineTo (x3, y3); cr.closePath (); cr.fill ();

// необходимо вернуть true, если мы хотим остановить обработку события // другими обработчиками, иначе возвращаем false, чтобы остальные // обработчики были вызваны при вызове сигнала // такое поведение касается всех сигналов, принимающих bool delegate (…) return false; } … И заставим кнопку делать хоть что-нибудь, что не делается автоматически =)

… void prepareButtonAction () { obj! Button («btn»).addOnClicked ((Button aux) { fig.angle += 0.1; // немного поворачиваем фигуру obj! DrawingArea («plot»).queueDraw (); }); } … 32c4fb0bb94c484a91f752ef8d58b226.png

На этом всё. Программа для тренировки с таблицами Шульте (первая картинка) лежит здесь. В ней Вы сможете найти больше примеров использования gtkd и отрисовки через cairo (хоть и не самых лучших, с точки зрения качества кода).

Так же в пакете gtk-d есть обёртки для:

gtkgl — использование OpenGL в приложениях GTK+ sv — SourceView — расширение GTK+ для редактирования текста с разными плюшками вроде подсветки синтаксиса, undo/redo и тд vte — виджет терминала gstreamer — мультимедийный фреймворк (удивило и порадовало, что биндинг создан и включён в gtkd) Документация по проекту лежит здесь.Ещё много примеров лежит здесь.

© Habrahabr.ru