= Использование Boost.Lambda при программировании GUI на Gtk. = Библиотека [[http://www.gtk.org|Gtk]] является одной из двух самых популярных библиотек (или ''тулкитов'') для написания пользовательских интерфейсов под Linux (кроме того, Gtk работает под Windows, под MacOS и др.). И хотя она написана на чистом C, у ней есть привязки/обертки на самых разных языках, включая C++, Python, Perl, Java и др. В данной статье я использую Gtkmm - это C++-обертка над Gtk. Рассмотрим работу с Gtk на простом примере рисования: возьмем ''виджет''<> Gtk::!DrawingArea и нарисуем в нем 2 диагонали. Вот стандартный вариант реализации (скриншот внизу - что получаем в итоге): {{{#!cplusplus #include class ExampleDA: public Gtk::DrawingArea { public: virtual bool on_expose_event(GdkEventExpose* event); }; bool ExampleDA::on_expose_event(GdkEventExpose* )//event) { int wdh = get_width(), hgt = get_height(); Cairo::RefPtr cr = get_window()->create_cairo_context(); cr->move_to(0, 0); cr->line_to(wdh, hgt); cr->stroke(); cr->move_to(wdh, 0); cr->line_to(0, hgt); cr->stroke(); return true; } int main(int argc, char* argv[]) { Gtk::Main kit(argc, argv); // инициализация Gtkmm ExampleDA da; // наш виджет Gtk::Window win; // вставляем его в окно win.add(da); win.show_all(); Gtk::Main::run(win); // запускаем цикл сообщений return 0; } }}} || {{attachment:Diagonals.png}} || Что мы видим? Нам пришлось породить класс ExampleDA от Gtk::!DrawingArea и написать 2 функции: main() и ExampleDA::on_expose_event(). И если без функции main() нам не обойтись, то создание целого класса ради отрисовки двух линий слишком расточительно. Попробуем обойтись без создания класса: * удалим класс ExampleDA; * изменим название функции ExampleDA::on_expose_event() на !DrawDiagonals() * в функции main() ''присоединим к сигналу "expose-event"'' функцию-обработчик !DrawDiagonals() Работает это по следующей схеме: в момент, когда требуется перерисовка, пройдет сигнал<> "expose-event", который и вызовет !DrawDiagonals(). /* Убираем огибание */ {{{#!html
}}} {{{#!cplusplus bool DrawDiagonals(GdkEventExpose* )//event) { // ошибки компилятора - нет функций get_height(), get_width(), get_window()! int wdh = get_width(), hgt = get_height(); Cairo::RefPtr cr = get_window()->create_cairo_context(); ... return true; } int main(int argc, char* argv[]) { ... Gtk::DrawingArea da; //ExampleDA da; da.signal_expose_event().connect( &DrawDiagonals ); ... return 0; } }}} Однако, в этом случае компилятор резонно укажет нам на отсутствие функций get_height(), get_width(), get_window(),- все они являются членами класса Gtk::!DrawingArea, а сам объект da мы в функцию !DrawDiagonals() не передали (раньше он был доступен через this). Более того, проблема гораздо шире,- в реальных условиях возникают потребности в передаче не только самого объекта Gtk, над которым производится обработка сигнала, но и другие, самые различные параметры; и наоборот, так как сигнатура функции !DrawDiagonals() жестко определена сигналом "expose-event" (а именно, она равна bool(!GdkEventExpose*) ), то нам пришлось "тащить" в эту функцию бесполезный аргумент "!GdkEventExpose* event", который мы сразу и закомментировали (в данном случае мы его не используем, а вообще-то он полезен :) ). Вот в таких ситуациях мы и применим библиотеку Boost.Lambda<>. Вкратце, Lambda позволяет создавать ''функторы'' (классы со встроенной операцией '()') с нужной сигнатурой. Итак, изменим несколько строчек и получим работающий вариант программы: {{{#!cplusplus #include bool DrawDiagonals(Gtk::DrawingArea& da) { int wdh = da.get_width(), hgt = da.get_height(); Cairo::RefPtr cr = da.get_window()->create_cairo_context(); ... } int main(int argc, char* argv[]) { ... da.signal_expose_event().connect( boost::lambda::bind(&DrawDiagonals, boost::ref(da)) ); ... } }}} __Замечание__: с заявлением, что последний вариант кода скомпилируется, мы поторопились; дело в том, что функция connect() в библиотеке Gtkmm (а точнее, в sigc++) не может определить возвращаемый тип функтора (см. выше в функции main()), а потому просто считает, что он равен void (а в нашем случае надо возвращать bool). Решение - использовать специальную функцию wrap_return(), описанную в заголовке src/mlib/sigc.h (см. исходники Bombono DVD). Однако, если сигнал не предполагает возвращение никакого значения, то спец. функции не нужны (например, сигналы "show", "map", "clicked"). ---- Итого: цель достигнута - с помощью Boost.Lambda отрисовка виджета занимает всего одну функцию, и никакого вспомогательного кода не потребовалось. Окончательный вариант лежит тут: [[attachment:diagonals.cpp|diagonals.cpp]]. Автор: ''Илья Муравьев'' <
> 2009 ---- <>