Глава 15 Пользовательские интерфейсы

15.12. Управление экраном

Проблема

Вы хотите выделять символы повышенной интенсивностью, перехватывать нажатия специальных клавиш или выводить полноэкранные меню, но не желаете беспокоиться о том, на каком устройстве вывода работает пользователь.

Решение

Воспользуйтесь модулем Curses с СРАМ, который использует библиотеку curses(3) вашей системы.

Комментарий

Библиотека curses обеспечивает простое, эффективное и аппаратно-независимос выполнение полноэкранных операций. С его помощью можно писать высокоуровневый код вывода данных на логическом экране по символам или по строкам. Чтобы результаты вывода появились
Работа с модулем Curses демонстрируется программой rep из примера 15.5. Вызовите ее с аргументами, описывающими командную строку запускаемой программы:
% rep ps aux % rep netstat % rep -2.5 Ipq

Сценарий rep в цикле вызывает команду и выводит ее данные на экран, обновляя лишь ту часть, которая изменилась с момента предыдущего запуска. Такой вариант наиболее эффективен при малых изменениях между запусками. В правом нижнем углу экрана выводится
По умолчанию rep ожидает 10 секунд перед повторным запуском команды. Чтобы изменить период задержки, передайте нужное количество секунд (допускается дробное число) в качестве аргумента, как это было сделано выше при вызове Ipq. Кроме того, нажатие люб Пример 15.5. rep
#!/usr/bin/perl -w
# rep - циклическое выполнение команды
use strict;
use Curses;
my $timeout = 10;
if (@ARGV && $ARGV[0] =- /"-(\d+\.?\d*)$/) {
$timeout = $1;
shift;
}
die "usage: $0 [ -timeout ] cmd args\n" unless @ARGV;
initscr(); # Инициализировать экран noechoO;
cbreak();
nodelay(1); # Чтобы функция getch() выполнялась без блокировки
$SIG{INT} = sub { done("0uch!") };
sub done { endwin(); print "O.Vn"; exit; }
while (1) {
while ((my $key = getch()) ne ERR) { # Возможен ввод
done("See ya") if $key eq 'q' # нескольких символов }
my @data = '(@ARGV) 2>&1'; # Вывод+ошибки
for (my $i =0; $i < $lines; $i++) {
addstr($i, 0, $data[$i] |] o o x $COLS);
}
standout();
addstr($LINES-1, $COLS - 24, scalar localtime);
standend();
move(0,0);
refresh(); # Обновить экран
my ($in, $out) = ('', '');
vec($in, fileno(STDIN), 1) =1; # Искать символ в stdin
select($out = $in,undef,undef,$timeout);# Ожидание
}

С помощью Curses можно узнать, когда пользователь нажал клавишу со стрелкой или служебную клавишу (например, Ноте или Insert). Обычно это вызывает затруднения, поскольку эти клавиши кодируются несколькими байтами. С Curses все просто:
keypad(1); # Включить режим ввода
$key = getch(); # с цифровой клавиатуры
if ($key eq 'k' || # Режим vi
$key eq "\cP" || # Режим emacs
$key eq KEY_UP) # Стрелка
{
# Обработать клавишу
}

Другие функции Curses позволяют читать текст в определенной позиции экрана, управлять выделением символов и даже работать в нескольких окнах.
Модуль perlmenu, также хранящийся на CPAN, построен на базе низкоуровневого модуля Curses. Он обеспечивает высокоуровневые операции с меню и экранными формами. Приведем пример экранной формы из поставки perlmenu:
Template Entry Demonstration
Address Data Example Record #___
Name: [ _________________________________________________________ ]
Addr: [ _________________________________________________________ ]
City: [__________________] VState: [__] Zip: [\\\\\]
Phone: (\\\) \\\-\\\\ Password: [^^^^^^^^]
Enter all information available.
Edit fields with left/right arrow heys or "delete".
Switch fields with "Tab" or up/down arrow keys.
Indicate completion by pressing "Return".
Refresh screen with "Control-L".
Abort this demo here with "Control-X".
Пользователь вводит текст в соответствующих полях. Обычный текст обозначается символами подчеркивания, числовые данные - символами \, а неотображаемые данные - символами ". Такие обозначения напоминают форматы Perl, за исключением того, что формы пред

> Смотри также -------------------------------
Man-страница curses(3) вашей системы (если есть); документация по модулям Curses и perlmenu с СРАМ; раздел "Formats" perlform(1); рецепт 3.10.

15.13. Управление другой программой с помощью Expect

Проблема

Вы хотите автоматизировать процесс взаимодействия с полноэкранной программой, которая работает с терминалом, не ограничиваясь STDIN и STDOUT.

Решение

Воспользуйтесь модулем Expect с CPAN:
use Expect;
$command = Expect->spawn("program to run")
or die "Couldn't start program: $!\n";
# Запретить вывод программы в STDOUT
$command->log_stdout(0);
# 10 секунд подождать появления "Password:"
unless ($command->expect(10, "Password")) {
# Тайм-аут
}
# 20 секунд подождать вывода текста, совпадающего с /[IL]login: ?/
unless ($command->expect(20, -re => '[ILJogin: ?')) {
} # Таймаут
# Бесконечно долго ждать появления "invalid"
unless ($command->expect(undef, "invalid")) {
# Произошла ошибка: вероятно, работа программы нарушена
}
# Послать программе "Hello, world" и перевод строки
print $command "Hello, world\n";
# Если программа завершается сама, предоставить ей такую возможность
$command->soft_close();
# Если программа должна быть закрыта извне, завершить ее
$command->hard_close();

Комментарий

Для работы модуля Expect необходимы два других модуля с CPAN: I,0::Pty и IO:Stty. Expect создает псевдотерминал для взаимодействия с программами, которые непременно должны общаться с драйвером терминального устройства. Такая возможность часто используется
Запустите нужную программу с помощью Expect->spawn, передайте ей имя программы и .аргументы - либо в виде одной строки, либо в виде списка. Expect запускает программу и возвращает либо представляющий ее объект, либо undef, если запустить программу не Для ожидания вывода программой конкретной строки применяется метод expect. Его первый аргумент равен либо числу секунд, в течение которых ожидается вывод строки, либо undef для бесконечного ожидания. Ожидаемая строка является вторым аргументом expect. Что
$which = $command->expect(30, "invalid", "succes", "error", "boom");
if ($which) {
# Найдена одна из указанных строк

В скалярном контексте expect возвращает номер аргумента, для которого произошло совпадение. В предыдущем примере expect вернет 1 при выдаче программой строки "invalid", 2 - при выводе "succes" и т. д. Если ни одна строка или шаблон не совпали, expect
В списковом контексте expect возвращает список из пяти элементов. Первый элемент определяет номер совпавшей строки или шаблона (идентично возвращаемому значению в скалярном контексте). Второй элемент - строка с описанием причины возврата из expect. Пр
Передача данных программе, находящейся под управлением Expect, сводится к простейшему вызову print. Единственная трудность состоит в том, что терминалы, устройства и сокеты отличаются по тем последовательностям, которые они передают и принимают в каче После завершения работы с запущенной программой у вас есть три возможности. Во-первых, можно продолжить работу с главной программой; вероятно, запущенная программа будет принудительно завершена по завершении главной программы. Однако в этом случае плодятс

> Смотри также --------------------------------
Документация по модулям Expect, IO:Pty и IO:Stty от CPAN.

15.14. Создание меню с помощью Tk

Проблема

Требуется создать окно, в верхней части которого находится меню.

Решение

Воспользуйтесь элементами Tk Menubutton и Frame:
use Tk;
$main = MainWindow->new();
# Создать для меню горизонтальную область
# в верхней части окна.
$menubar = $niain->Frame(-relief => "raised",
-borderwidth => 2) ->pack (-anchor => "nw",
-fill => "x"):
# Создать кнопку с надписью "File" для вызова меню,
$file_menu = $menubar->Menubutton(-text => "File",
-underline => 1)
->pack (-side => "left" );
# Создать команды меню "File"
$file_menu->command(-label => "Print",-command => \&Print);
To же самое можно сделать намного проще, если воспользоваться сокращенной записью
-menuitems:
$file_menu = $menubar->Menubutton(-text => "File",-underlined 1,
-menuitems=> [ [ Button => "Print",-command => \&Print ],
[ Button => "Save",-command => \&Save ] ])
->pack(-side => "left");

Комментарий

Меню приложения можно рассматривать как совокупность четырех компонентов: области (Frame), кнопок меню (Menubutton), меню (Menus) и команд меню (Menu Entries). Область представляет собой горизонтальную полосу в верхней части окна, в котором находится меню
В меню могут включаться разделители - горизонтальные линии, отделяющие один набор команд от другого.
С командами (например, Print в меню File) ассоциируются фрагменты кода. При выборе команды меню вызывается соответствующая функция. Обычно это делается так:
$file_menu->command(-label => "Quit Immediately",
-command => sub { exit } );

С разделителями действия не связываются:
$file_menu->separator();
Команда-флажок может находиться в установленном (on) или сброшенном (off) состоянии, и с ней ассоциируется некоторая переменная. Если переменная находится в установленном состоянии, рядом с текстом команды-флажка стоит специальная пометка (маркер). Если п
$options_menu->checkbutton(-label => "Create Debugging File",
-variable => \$debug,
-onvalue => 1,
-off value => 0);

Группа команд-переключателей ассоциируется с одной переменной. В любой момент времени установленной может быть лишь одна команда-переключатель, ассоциированная с переменной. При выборе команды-переключателя переменное присваивается ассоциированное значение:
$debug_menu->radiobutton(-label => "Level 1",
-variable => \$log_level,
-value => 1):
$debug_menu->radiobutton(-label => "Level 2",
-variable => \$log_level,
-value => 2);
$debug_menu->radiobuttbn(-label => "Level 3",
-variable => \$log_level,
-value ==> 3);

Вложенные меню создаются с помощью каскадных команд. Например, в Netscape Navigator кнопка меню File содержит каскадную команду New, которая открывает подменю с несколькими вариантами. Создать каскадную команду сложнее чем любую другую: вы должны созд
# Шаг 1: создать каскадную команду меню
$format_menu->cascade
(-label => "Font");
# Шаг 2: получить только что созданное меню
$font_menu = $format_menu->cget("-menu");
# Шаг 3: заполнить это меню
$font_menu->radiobutton (-label => "Courier",
-variable => \$font_name,
-value => "courier");
$font_menu=>radiobutton
(-label => "Times Roman",
-variable => \$font_name,
-value => "times");

Отсоединяемый разделитель позволяет перемещать меню, в котором он iici.xu-дится. По умолчанию все кнопки меню и каскадные команды открывают меню, ь верхней части которого находится отсоединяемый разделитель. Чтобы создать меню без него, воспользуйтесь
$format_menu = $menubar->Menubutton(-text => "Format",
-underline => 1
-tearoff => 0)
->Dack;
$ font_menu = $format_menu->Cascade(-label => "Font ,
-tearoff => 0);

Параметр -menuitems метода Menubutton представляет собой сокращенную [шр-му для создания команд меню. В нем передается ссылка на массив с описаниями команд Menubutton. В свою очередь, каждая команда описывается анонимным массивом. Первые два элемента
my $f = $menubar->Menubutton(-text => "File", -underline => 0,
-menuitems =>[ [Button => 'Copy', -command => \&edit_copy ],
[Button => 'Cut', -command => \&edit_cut ],
[Button => 'Paste', -command => \&edit_paste ],
[Button => 'Delete', -command => \&edit_delete ], v[Separator => '' ], [Cascade => 'Object ...', -tearoff => 0,
-menuitems => [
[ Button => "Circle", -command => \&edit_circle ],
[ Button => "Square", -command => \&edit_square ],
[ Button => "Point", -command => \&edit_point ] ]
], ])->grid(-row => 0, -column => 0, -sticky => 'w');


[> Смотри также -------------------------------
Документация по модулю Tk с С PAN.

15.15. Создание диалоговых окон с помощью Tk

Проблема

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

Решение

В простых случаях воспользуйтесь элементом Tk::DialogBox:
use Tk::DialogBox;
$dialog = $main->DialogBox( -title => "Register This Program",
-buttons => [ "Register", "Cancel" ] );
# Добавьте элементы в диалоговое окно методом
$dialog->Add()
# Позднее, когда понадобится отобразить диалоговое окно
$button = $dialog->Show():
if ($button eq "Register") {
# . . .
} elsif ($button eq "Cancel") {
# ... } else {
# Такого быть не должно
}

Комментарий

Диалоговое окно состоит из набора кнопок (в нижней части) и произвольных элементов (в верхней части). Вызов Show выводит диалоговое окно на экран и возвращает кнопку, выбранную пользователем.
Пример 15.6 содержит полную программу, демонстрирующую принципы работы с диалоговыми окнами. Пример 15.6. tksample3
#!/usr/bin/perl -w
# tksample3 - работа с диалоговыми окнами
use Tk;
use Tk::DialogBox;
$main = MainWindow->new();
$dialog = $main->DialogBox( -title => "Register",
-buttons => [ "Register", "Cancel" ], );
# В верхней части окна пользователь вводит имя, при этом
# надпись (Label) действует как подсказка.
$dialog->add("Label", -text => "Name")->pack();
Sentry = $dialog->add("Entry", -width => 35)->pack();
# Диалоговое окно вызывается кнопкой
$main->Button( -text => "Click Here For Registration Form",
-command => \®ister) ->pack(-side => "left");
$main->Button( -text => "Quit",
-command => sub { exit } ) ->pack(-side => "left"); MainLoop;
#
# register
#
# Вызывает диалоговое окно регистрации.
#
sub register { my $button;
my $done = 0;
do {
# Отобразит диалоговое окно.
$button = $dialog->Show;
# Действовать в зависимости от того, какая кнопка была нажата.
if ($button eq "Register") {
my $name = $entry->get;
if (defined($name) && length($name)) { print "Welcome to the fold, $name\n";
$done = 1;
} else {
print "You didn't give me your name!\n";
} } else {
print "Sorry you decided not to register.\n";
$done = 1;
} } until $done;
}

В верхней части диалогового окна расположены два элемента: надпись и текстовое поле. Для ввода дополнительной информации понадобятся другие надписи и текстовые поля. Диалоговые окна часто применяются для вывода предупреждений или сообщений об ошибках. Пример 15.7 показывает, как вывести в диалоговом окне результаты вызова функции warn. Пример 15.7. tksample4
#!/usr/bin/perl -w
# tksample4 - диалоговые окна для предупреждений
use Tk;
use Tk::DialogBox:
my $main;
# Создать обработчик предупреждений, который отображает
# предупреждение в диалоговом окне Tk
BEGIN {
$SIG{__WARN__} = sub { if (defined $main) {
my $dialog = $main->DialogBox( -title => "Warning",
-buttons => [ "Acknowledge" ]);
$dialog->add("Label", -text => $_[0])->pack;
$dialog->Show;
} else { vprint STDOUT joln("\n", о \ "n"'
}
};
}
# Команды вашей программы
$main = MainWindow->new();
$main->Button( -text => "Make A Warning", -command => \&make_warning) ->pack(-side => "left");
$main->Button( -text => "Quit",
-command => sub { exit } ) ->pack(-side => "left'"":
MainLoop:
# Фиктивная подпрограмма для выдачи предупреждения
sub make_warning { my $a;
my $b = 2 * $a;


> Смотри также -----------------------
Страница руководства Tk::DialogBox в документации по модулю Tk с CPAN; страница руководства тепи(п) (если она есть).

15.16. Обработка событий масштабирования в Tk

Проблема

Вы написали программу на базе Tk, но при изменении размеров окна пользователем нарушается порядок элементов.

Решение

Перехватывая событие Configure, можно запретить пользователю измен чь размеры окна:
use Tk:
$main = MainWindow->new();
$main->bind('' => sub {
$xe = $main->XEvent;
$main->maxsize($xe->w, $xe->h);
$main->minsize($xe->w, $xe->h);
});

Кроме того, можно определить особенности масштабирования элементов при изменении размеров контейнера с помощью метода pack:
$widget->pack( -fill => "both", -expand => 1 );
$widget->pack( -fill => "x", -expand => 1 );

Комментарий

По умолчанию упакованные элементы изменяют размеры вместе с контейнером - они не масштабируют себя или свое содержимое в соответствии с новым размером. В результате между элементами возникают пустые места, а их содержимое обрезается или искажается. Первое решение - вообще запретить изменение размеров. Мы перехватываем событие , которое возникает при изменении размера или положения элемента, и регистрируем косвенно-вызываемую функцию (callback) для восстановления прежнего размера окна. Име Иногда запрещать изменение размеров окна нежелательно; в этом случае необходимо определить, как каждый элемент должен реагировать на изменения. Для этого используются аргументы метода pack: -fill управляет той областью, внутри которой должен находиться эл Для правильной работы необходимы оба параметра: -expand без -fill не узнает, в какой области должен увеличиваться элемент, -fill без -expand захватит область нужного размера, но сохранит прежние размеры.
Разные части вашего приложения ведут себя по-разному. Например, главная область Web-броузера при изменении размера окна, вероятно, должна изменить свои размеры в обоих направлениях. Метод pack для такого элемента выглядит так:
$mainarea->pack( -fill => "both", -expand => 1);

Однако меню, расположенное над главной областью, может расшириться по горизонтали, но не по вертикали:
$menubar->pack( -fill => "x", -expand => 1 );

С изменением размеров связана и другая задача - закрепление элементов в определенной точке контейнера. Например, полоса меню закрепляется в левом верхнем углу контейнера следующим образом:
$menubar->pack (-fill => "x",

-expand => 1,
-anchor => "nw" );

Теперь при изменении размеров окна меню останется на своем месте и не будет выровнено по центру пустой области.

> Смотри также -------------------------------
Страницы руководства pack(n), XEvent(3) и XConfigureEvent(1) (если есть).

15.17. Удаление окна сеанса DOS в Perl/Tk для Windows

Проблема

Вы написали программу для Windows-версии Perl и Tk, однако при каждом за пуске программы открывается окно DOS-сеанса.

Решение

Запускайте программу из другого сценария Perl. В примере 15.8 содержите пример загрузчика, который запускает программу realprogram без окна DOS. Пример 15.8. loader
#!/usr/bin/perl -w
! loader - запуск сценариев Perl без раздражающего окна DOS
use strict;
use Win32;
use Win32::Process;
# Создать объект процесса.
Win32::Process::Create($Win32::Process::Create::ProcessObj,
'C:/perl5/bin/perl.exe', # Местонахождение Perl
'perl realprogram', #,
# He наследовать
DETACHED_PROCESS, #
".") or # Текущий каталог die print_error();
sub print_error() {
return Win32::FormatMessage( Win32::GetLastError() );
}

Комментарий

Программа проще, чем кажется. Окно DOS появляется потому, что интерпре татор Perl был откомпилирован как консольное приложение. Для чтения и STDIN и записи в STDOUT ему нужно окно DOS. Это нормально в приложени ях, работающих в режиме командной строки, но Загрузчик использует модуль Win32::Process для запуска программы в качеств^ нового процесса. Этот процесс отделяется от текущего, поэтому при завершенш загрузчика окно DOS пропадет вместе с ним. Ваша программа будет прекрасш работать, не отягощенная переж Если произойдет какая-нибудь беда и программа не запустится, загрузчик ум рет с выдачей сообщения об ошибке Windows.

> Смотри также --------------------------------
Документация по модулю Win32::Process, входящая в поставки Perl для систем Microsoft Windows.

15.18. Программа: tcapdemo

Описание

Следующая программа очищает экран и рисует на нем до тех пор, пока не будет прервана. Она показывает, как использовать модуль Term::Cap для очистки экрана, перемещения курсора и записи в любую позицию экрана. В ней также используется рецепт 16.6. Пример 15.9. tcapdemo
#!/usr/bin/perl -w
# tcapdemo - прямое позиционирование курсора
use POSIX;
use Term::Cap;
init();
zip();
finish();
exit();
# Инициализация Term::Cap.
# Рисование линий на экране.
# Последующая очистка,
# Две вспомогательные функции. Смысл clear_screen очевиден, а
# clear_end очищает до конца экрана.
sub clear_screen { $tcap->Tputs('cl', 1, *STDOUT) }
sub clear_end { $tcap->Tputs('cd', 1, *STDOUT) }
# Переместить курсор в конкретную позицию.
sub gotoxy {
my($x, $y) = @_;
$tcap->Tgoto('cm', $x, $y, *STDOUT);
}
# Определить скорость терминала через модуль POSIX и использовать
# для инициализации Term::Cap.
sub init {
$| = 1;
$delay = (shift() || 0) * 0.005;
my $termios = POSIX::Termios->new();
$termios->getattr;
my $ospeed = $termios->getospeed;
$tcap = Term::Cap->Tgetent ({ TERM => undef, OSPEED => $ospeed });
$tcap->Trequire(qw(cl cm cd));
}
# Рисовать линии на экране, пока пользователь
# не нажмет Ctrl-C.
sub zip {
clear_screen();
($maxrow, $maxcol) = ($tcap->{_li} - 1, $tcap->{_co} - 1)
@chars = qw(* - / I \ _ );
sub circle { push(@chars, shift @chars); }
$interrupted = 0;
$SIG{INT} = sub { ++$interrupted };
$col = $row = 0;
($row_sign, $col_sign) = (1,1);
do {
gotoxy($col, $row);
print $chars[0]; vselect(undef, undef, undef, $delay);
$row += $row_sign;

$col += $col_sign;
if ($row == $maxrow) { $row_sign = -1; circle; } elsif ($row == 0 )
{ $row_sign = +1; circle; }
if ($col == $maxcol) { $col_sign = -1; circle; ^ elsif ($col == 0 )
{ $col_sign = +1; circle; }
} until $interrupted;
}
# Очистить экран.
sub finish {
gotoxy(0, $maxrow);
clear_end();
}


[> Смотри также ------------------------------
Документация по стандартному модулю Term::Cap; termcap(5) (если есть).

15.19. Программа: tkshufflepod

Эта короткая программа с помощью Tk выводит список всех заголовков =head' в файле и позволяет изменить порядок их следования перетаскиванием. Клавиша "s" сохраняет изменения, а "g" завершает программу. Двойной щелчок на элемеите списка выводит его содержи
При запуске программе передается имя просматриваемого pod-файла:
% tkshufflepod chap15.pod

Мы часто использовали эту программу при работе над книгой. Исходный текст программы приведен в примере 15.10. Пример 15.10. tkshufflepod
#!/usr/bin/perl -w
# tkshufflepod - изменение порядка разделов =head1 в pod-файле
use Tk;
use strict;
# declare variables
my $podfile; # Имя открываемого файла
my $m; # Главное окно
my $1; # Элемент Listbox
my ($up, $down); # Перемещаемые позиции
my ©sections; # Список разделов pod
my $all_pod; # Текст pod-файла (используется при чтении)
# Прочитать pod-файл в память и разбить его на разделы,
$podfile = shift || "-";
undef $/;
open(F, "< $podfile") or die "can't open $podfile : $!\n";
$all_pod = ;
close(F);
@sections = split(/(?==head1)/, $all_pod);
# Превратить #sections в массив анонимных массивов. Первый элемент
# каждого массива содержит исходный текст сообщения, а второй -
# текст, следующий за =head1 (заголовок раздела).
foreach (#sections) { /(.*)/;
$_ = [ $_, $1 ];
}
# Запустить Tk и вывести список разделов.
$m = MainWindow->new();
$l = $m->Listbox('-width' => 60)->pack('-expand' => 1, '-fill' => 'both');
foreach my $section ((Bisections) { $l->insert("end", $section->[1]);
}
# Разрешить перетаскивание для элемента Listbox.
$l->bind( '' => \&down );
$l->bind( '' => \&up );
# Разрешить просмотр при двойном щелчке
$l->bind( '' => \&view );
# 'q' завершает программу, a 's' сохраняет изменения,
$m->bind( '' => sub { exit } );
$m->bind( 's' => \&save );
MainLoop;
# down(widget): вызывается, когда пользователь щелкает в Listbox.
sub down {
my $self = shift;
$down = $self->curselection;; ' }
$ up(widget): вызывается, когда пользователь отпускает
# кнопку мыши в Listbox.
sub up {
my $self = shift;
my $elt;
$up = $self->curselection;;
return if $down == $up;
# change selection list
$elt = $sections[$down];
splice(@sections, $down, 1):
splice(@sections, $up, 0, $elt);
$self->delete($down);
$self->insert($up, $sections[$up]->[1]);
}
# save(widget): сохранение списка разделов.
sub save {
my $self = shift;
open(F, "> $podfile")
or die "Can't open $podfile for writing: $! print F map { $_->[0] } @>sections;
close F;
exit;
}
# view(widget): вывод раздела. Использует элемент Pod.
sub view {
my $self = shift;
my $temporary = "/tmp/$$-section.pod";
my $popup;
open(F, "> $temporary")
or warn ("Can't open $temporary : $!\n"), return;
print F $sections[$down]->[0];
close(F);
$popup = $m->Pod('-file' => $temporary);
$popup->bind('' => sub { unlink $temporary } );
}

© copyright 2000 Soft group

Используются технологии uCoz