Сегодня хочу рассказать вам о том, как писать приложения с использованием GTK+ и языка программирования D.
О том, что такое 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
И вуаля!
Не будем на этом останавливаться и создадим предельно простой ui в программе glade, а затем загрузим в нашу программу.
Обратите внимание на дерево добавленных компонентов (сверху справа), для главного окна, конпки и поля отрисовки присвоены идентификаторы отличные от тех, что даются по умолчанию (mwindow, btn, plot соответственно), мы их будем использовать для удобного получения из билдера.
Сохраним файл в папке проекта под именем ui.glade и отредактируем 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();
});
}
...
На этом всё. Программа для тренировки с таблицами Шульте (первая картинка) лежит здесь. В ней Вы сможете найти больше примеров использования gtkd и отрисовки через cairo (хоть и не самых лучших, с точки зрения качества кода).
Так же в пакете gtk-d есть обёртки для:
- gtkgl — использование OpenGL в приложениях GTK+
- sv — SourceView — расширение GTK+ для редактирования текста с разными плюшками вроде подсветки синтаксиса, undo/redo и тд
- vte — виджет терминала
- gstreamer — мультимедийный фреймворк (удивило и порадовало, что биндинг создан и включён в gtkd)
Документация по проекту лежит здесь.
Ещё много примеров лежит здесь.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий