суббота, 19 сентября 2009 г.

Создание кросс-платформенных графических интерфейсов на Perl/Tk

Введение

История графического тулкита Tk неразрывно связана с языком программирования Tcl и его создателем Джоном Остераутом (John Ousterhout). Первая версия Tcl вышла в свет в далёком 1987 году. Четыре года спустя Остераут представил и первый релиз графического тулкита Tk.

Поддержка тулкита Tk в языке Perl, пусть и в виде альфа версии, появилась уже в 1994 году - гораздо раньше чем GTK+, Qt или WxWidgets. Автором этого проекта был Малкольм Бетти (Malcolm Beattie) из Оксфорда. Чуть позже к нему присоединился Ник Инг-Симмонс (Nick Ing-Simmons) из Texas Instruments, который в последствии и продолжил разработку Perl/Tk.

На сегодняшний день существует несколько модулей для работы с Tk (да, TIMTOWTDI в Perl ещё никто не отменял). Основное отличие между ними заключается в способе взаимодействия с библиотекой Tk: или через XS-интерфейс, или через язык Tcl. Первый подход более трудоёмок для разработки и поддержки, но обладает большей производительностью, кроме того, он является стандартной идиомой для языка Perl. Второй подход представляет из себя тонкую прослойку, транслирующую Perl-код для работы с интерфейсом в команды Tcl/Tk, которые затем передаются на исполнение интерпретатору Tcl.

Оба подхода имеют как плюсы так и минусы. Модуль, о котором говорилось в начале статьи относится к первой категории. Чуть позже мы также рассмотрим наиболее популярный модуль из второй категории.

Установка в Linux/BSD


В UNIX-системах проблем с установкой Perl/Tk возникнуть не должно. К примеру, в Mandriva Linux соответствующий пакет называется perl-Tk, а в OpenBSD - p5-Tk. Корректность установки можно проверить простым однострочником:
perl -MTk -e "MainWindow->new(); MainLoop();"
Если всё установлено правильно, то на экране должно появиться пустое окошко:


Установка в Windows

Под Windows Perl/Tk также устанавливается без проблем: ActiveState предоставляет PPM пакет Tk для своего дистрибутива ActivePerl. Проверить корректность установки можно тем же самым однострочником. Вот как выглядит результат его запуска под Windows:


Более сложный пример на Perl/Tk


Для того чтобы продемонстрировать некоторые особенности Perl/Tk напишем чуть более сложное приложение с несколькими стандартными элементами пользовательского интерфейса:

#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Демонстрационная программа на Perl/Tk
#

use strict;
use warnings;

use Tk;

sub main() {
# Создаём главное окно
my $mw = MainWindow->new();

# Фрейм для группировки Radiobutton-ов
my $rb_frame = $mw->Frame()->pack( -side => "top" );

# В этой переменной будет сохраняться -value
# выбранного Radiobutton-а. При изменении значения
# $rb_variable извне, интерфейс будет обновлён
# автоматически.
my $rb_variable = "foo";

foreach my $name ( qw{ foo bar baz } ) {
my $rb = $rb_frame->Radiobutton(
-text => "Radiobutton $name",
-value => $name,
-variable => \$rb_variable,
);
$rb->pack( -side => "left" );
}

# Создаём Checkbutton. Его состояние сохраняется
# в переменной $cb_variable: при её изменении
# извне изменится и внешний вид Checkbutton-а.
my $cb_variable = "on";
my $cb = $mw->Checkbutton(
-text => "Checkbutton foobar",
-onvalue => "on",
-offvalue => "off",
-variable => \$cb_variable,
);
$cb->pack( -side => "top" );

# Создаём кнопку с обработчиком. При нажатии
# будет выведен выбранный Radiobutton и текущее
# состояние Checkbutton-а
my $b = $mw->Button(
-text => "Show status",
-command => sub {
print "Selected Radiobutton: '$rb_variable'\n";
print "Checkbutton state: '$cb_variable'\n";
}
);
$b->pack( -side => "top" );

# Запускаем главный цикл обработки событий
MainLoop();
}

main();

Вид в Linux


Вид в Windows

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

Также стоит отметить, что модуль Perl/Tk базируется на библиотеке Tk версии 8.4. Интерфейс, написанный на Tk 8.5, выглядит иначе: как именно - смотрите в следующем разделе.

Модуль Tkx от ActiveState

Модуль Tkx, разрабатываемый при поддержке ActiveState, реализует своеобразный "мост" между Perl и Tcl. В результате, код для работы с интерфейсом транслируется в команды Tcl/Tk. Плюс такого подхода в том, что можно всегда использовать самую последнюю версию Tk без необходимости существенной переработки модуля. Минус - меньшая производительность по сравнению с XS-версией.

Стоит отметить, что последние версии ActivePerl поставляются с предустановленным модулем Tkx. Это объясняется тем, что Tkx активно используется в программах, входящих в состав PDK (ActiveState Perl Dev Kit). В частности, Tkx используется графической версией менеджера пакетов PPM: для того чтобы посмотреть его в действии достаточно набрать команду ppm. Проверить работоспособность Tkx можно и следующим однострочником:
perl -MTkx -e "Tkx::MainLoop();"
Теперь перепишем предыдущий пример с использованием модуля Tkx и сравним результаты:

#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Демонстрационная программа на Perl/Tkx
#

use strict;
use warnings;

use Tkx;

sub main() {
# Создаём главное окно
my $mw = Tkx::widget->new( "." );

# Фрейм для группировки Radiobutton-ов
my $rb_frame = $mw->new_frame();
$rb_frame->g_pack( -side => "top" );

# В этой переменной будет сохраняться -value
# выбранного Radiobutton-а. При изменении значения
# $rb_variable извне, интерфейс будет обновлён
# автоматически.
my $rb_variable = "foo";
foreach my $name ( qw{ foo bar baz } ) {
my $rb = $rb_frame->new_radiobutton(
-text => "Radiobutton $name",
-value => $name,
-variable => \$rb_variable,
);
$rb->g_pack( -side => "left" );
}

# Создаём Checkbutton. Его состояние сохраняется
# в переменной $cb_variable: при её изменении
# извне изменится и внешний вид Checkbutton-а.
my $cb_variable = "on";
my $cb = $mw->new_checkbutton(
-text => "Checkbutton foobar",
-onvalue => "on",
-offvalue => "off",
-variable => \$cb_variable,
);
$cb->g_pack( -side => "top" );

# Создаём кнопку с обработчиком. При нажатии
# будет выведен выбранный Radiobutton и текущее
# состояние Checkbutton-а
my $b = $mw->new_button(
-text => "Show status",
-command => sub {
print "Selected Radiobutton: '$rb_variable'\n";
print "Checkbutton state: '$cb_variable'\n";
}
);
$b->g_pack( -side => "top" );

# Запускаем главный цикл обработки событий
Tkx::MainLoop();
}

main();

Изменения в коде не значительные, чего не скажешь о внешнем виде:

Вид в Linux


Вид в Windows

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

Не смотря на то, что у Tkx открытая лицензиия, широкого распространения в UNIX-системах он пока не получил. Если в вашей UNIX-системе нет соответствующего пакета, потребуется установить по крайней мере два модуля из CPAN: Tkx и Tcl.

В завершение стоит упомянуть про модуль Tcl::Tk, который также работает по принципу "моста".

Выводы

Итак, мы рассмотрели два Perl-модуля для работы с тулкитом Tk: одноименный модуль Tk и Tkx. К плюсам модуля Tk можно отнести его распространенность и хорошую переносимость. К минусам - некоторую архаичность внешнего вида и привязку к устаревшей версии тулкита Tk. В актив Tkx можно записать современный внешний вид и значительно меньшую зависимость от версии тулкита Tk. С другой стороны, "из коробки" Tkx работает только в ActivePerl, хотя возможно в будущем ситуация и изменится. Также следует учитывать, что добавление ещё одного промежуточного слоя - Tcl - негативно сказывается скорости работы и усложняет и без того не тривиальный API.

В целом, тулкит Tk обладает очень гибким API, но в силу своей оригинальности потребуется какое-то время чтобы к нему привыкнуть. В особенности это относится к фреймворку MegaWidgets для создания собственных виджетов на базе существующих. К странностям почти всех модулей для работы с Tk можно отнести невнятный и непоследовательный способ именования виджетов, методов и классов. Также стоит подчеркнуть обилие качественной документации как по Tcl/Tk так и по соответствующим Perl-модулям. К слову, Tk - единственный графический тулкит, чей Perl-интерфейс удостоился отдельной книги: Mastering Perl/Tk.

Ссылки
Статья опубликована на портале CITForum

1 комментарий:

  1. Tk - это действительно полезная вешь. В том же .NET нужно создавать проект, писать всю объектно-ориентированную обвязку. А с сохранением настроек приложения, или с созданием плагинной системы вообще ужас! Сотни строк! Теперь по немногу забываю все это, по тому, что большинство программ у меня теперь в один Tcl файл умещаются. При этом программы имеют GUI на Tk. А если нужно что-то низкоуровневое то помогает SWIG.

    ОтветитьУдалить