HTML page

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

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

Введение

Все, чем мы пользуемся - видеомагнитофоны, компьютеры, телефоны и даже книги, - имеет свой пользовательский интерфейс. Интерфейс есть и у наших программ. Какие аргументы должны передаваться в командной строке? Можно ли перетаскивать мышью файлы? Должны ли мы нажимать Enter после каждого ответа или программа читает входные данные но одному символу? В этой главе мы не будем обсуждать проектирование пользовательского ин-герфейса - на эту тему и так написано множество книг. Вместо этого мы сосредоточим внимание на реализации интерфейсов - передаче аргументов в командной строке, посимвольному чтению с клавиатуры, записи в любое место экрана и программированию графического интерфейса.
Простейшим пользовательским интерфейсом обычно считается так называемый консольный интерфейс. Программы с консольным интерфейсом читают целые строки и выводят данные также в виде целых строк. Примером консольного интерфейса являются фильтры (например, grep) и утилиты (например, mail). В этой | лаве консольные интерфейсы почти не рассматриваются, поскольку им уделено достаточно внимания в остальных частях книги.
Более сложный вариант - так называемый полноэкранный интерфейс. Им обладают такие программы, как elm или lynux. Они читают по одному символу и могут выводить данные в любой позиции экрана. Этот тип интерфейса рассматривается в рецептах 15.4, 15.6, 15.9-15.11. Последнюю категорию интерфейсов составляют графические пользователь-кие интерфейсы (GUI, Graphic User Interface). Программы с графическим интерфейсом работают не только с отдельными символами, но и с отдельными шкселями. В графических интерфейсах часто используется метафора окна - рограмма создает окна, отображаемые на пользовательском устройстве вывода.
Окна заполняются элементами (widgets) - например, полосами прокрутки или кнопками. Netscape Navigator, как и ваш менеджер окон, обладает полноценным графическим интерфейсом. Perl позволяет работать со многими инструментальными пакетами GUI, однако мы ограничимся пакетом Tk, поскольку он является самым распространенным и переносимым. См. рецепты 15.14, 15.15 и 15.19.
Не путайте пользовательский интерфейс программы со средой, в которой она работает. Среда определяет тип запускаемых программ. Скажем, при регистрации па терминале с полноэкранным вводом/выводом вы сможете работать с консо.'; пыми приложениями, но не с графическими программами. Давайте кратко р;г смотрим различные среды.
Некоторые из них позволяют работать лишь с программами, обладающими чисто консольным интерфейсом. Упрощенный интерфейс позволяет объединять их в качестве многократно используемых компонентов больших сценариев; такп" объединение открывает чрезвычайно широкие возможности. Консольные при граммы прекрасно подходят для автоматизации работы, поскольку они не зависят от клавиатуры или экрана. Они используют лишь STDIN и STDOUT, да и то не всегда. Обычно эти программы обладают наилучшей переносимостью, поскольку они ограничиваются базовым вводом/выводом, поддерживаемым практичен ки в любой системе.
Типичный рабочий сеанс, в котором участвует терминал с экраном и клавиатурой, позволяет работать как с консольными, так и полноэкранными интерфейсами. Программа с полноэкранным интерфейсом взаимодействует с драйвером терминала и хорошо знает, как вывести данные в любую позицию экрана. Для автоматизации работы таких программ создается псевдотерминал, с которым взаимодействует программа (см. рецепт 15.13).
Наконец, некоторые оконные системы позволяют выполнять как консольные и полноэкранные, так и графические программы. Например, можно запустит. grep (консольная программа) из vi (полноэкранная программа) в OKHex'term (графическая программа, работающая в оконной среде). Графические программы автоматизируются труднее всего, если только они не обладают альтернативным интерфейсом на основе вызова удаленных процедур (RPC). Существуют специальные инструментальные пакеты для программирования в полноэкранных и графических средах. Такие пакеты (curses для полноэкранных программ; Tk - для графических) улучшают переносимость, поскольку nporpav ма не зависит от особенностей конкретной системы. Например, программа, написанная с применением curses, работает практически на любом терминале. При этом пользователю не приходится думать о том, какие служебные команды использу ются при вводе/выводе. Tk-нрограмма будет работать и в UNIX и в Windows -при условии, что в ней не используются специфические функции операционной системы. Существуют и другие варианты взаимодействия с пользователем, в пс|'чую очередь - через Web. Программирование для Web подробно рассматривается в главах 19 и 20, поэтому в этой главе мы не будем задерживаться на этой теме.

15.1. Лексический анализ аргументов

Проблема

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

Решение

Передача односимвольных параметров командной строки обеспечивается стандартным модулем
Getopt::Std:
use Getopt::Std;
# -v ARG, -D ARG, -o ARG, присваивает
$opt_v, $opt_D, $opt_o getopt("vDo");
# -v ARG, -D ARG, -o ARG, присваивает
$args{v}, $args{D}, $args{o} getopt("vDo", \%args);
getopts("vDo:"); # -v, -D, -o ARG, присваивает
# $opt_v, $opt_D, $opt_o getopts("vDo:", \%args);
# -v, -D, -o ARG, присваивает
# sets $args{v}, $args{D}, $args{o}

Или воспользуйтесь модулем Getopt::Long, чтобы работать с именованными аргументами:
use Getopt::Long;
Get0ptions( "verbose" => \$verbose, # --verbose
"Debug" => \$debug, # --Debug
"output=s" => \$output ); # --output=string

Комментарий

Многие классические программы (такие, как Is и пп) получают односимвольные параметры (также называемые флагами или ключами командной строки) - например, -1 или -г. В командных строках Is -I и гт -г аргумент является логической величиной: он либо присутствует, либо нет. Иначе дело обстоит в командной строке gcc -o compiled/He source.c, где compiled/He - значение, ассоциированное с параметром -о. Логические параметры можно объединять в любом порядке; например, строка:
% rm -r -f /tmp/testdir эквивалентна следующей:
% rm -rf /tmp/testdir

Модуль Getopt::Std, входящий в стандартную поставку Perl, анализирует эти традиционные типы параметров. Его функция getopt получает одну строку, где каждый символ соответствует некоторому параметру, анализирует аргументы командной строки в массиве @ARGV и для каждого параметра присваивает значение глобальной переменной. Например, значение параметра -D будет храниться в переменной $opt_D. Параметры, анализируемые с помощью getopt, не являются логическими (то есть имеют конкретное значение).
Модуль Getopt::Std также содержит функцию getopts, которая позволяет указать, является ли параметр логическим или принимает значение. Параметры со значениями (такие, как параметр -о программы gcc) обозначаются двоеточием, как это сделано в следующем фрагменте:
use Getopt::Std;
getopts("o:");
if ($opt_o) {
print "Writing output to $opt_o";

Обе функции, getopt и getopts, могут получать второй аргумент - ссылку на хэш. При наличии второго аргумента значения вместо переменных $opt_X сохраняются в $hash{X}:
use Getopt::Std;
%option =();
getopts("Do:", \%option):
if ($option{D}) {
print "Debugging mode enabled.\n";
}
# Если параметр -о не задан, направить результаты в "-". " Открытие "-" для записи означает STDOUT $option{o} = "-" unless defined $option{o};
print "Writing output to file $option{o}\n" unless $option{o} eq "-";
open(STDOUT, "> $option{o}")
or die "Can't open $option{o} for output: $!\n";

Некоторые параметры программы могут задаваться целыми словами вместо о т-дельных символов. Обычно они имеют специальный префикс - двойной дефис:
% gnutar --extract --file latest.tar Значение параметра -file также может быть задано с помощью знака равенства:
% gnutar --extract --file=latest.tar

Функция GetOptions модуля Getopt::Long анализирует эту категорию параметров. Она получает хэш, ключи которого определяют параметры, а значения представляют собой ссылки на скалярные переменные:
use Getopt::Long;
Get0ptions( "extract" => \$extract, "filers" => \$file );
if ($extract) {
print "I'm extracting.\n";
}
die "I wish I had a file" unless defined $file;
print "Working on the file $file\n";

Если ключ хэша содержит имя параметра, этот параметр является логическим. Соответствующей переменной присваивается false, если параметр не задан, или 1 в противном случае. Getopt::Long не ограничивается логическими параметрами и значениями Getopt::Std. Возможны следующие описания параметров:
Описание Значение Комментарий
option Нет Задастся в виде "option или не задастся вообще
option! Нет Может задаваться в виде "option или "nooption
option=s Да Обязательный строковый параметр: "option-somestring
option: s Да Необязательный строковый параметр: -option или "option-somcstring
option=i Да Обязательный целый параметр: "option-35
option: i Да Необязательный целый параметр: "option или "oplion 35
option=f Да Обязательный вещественный параметр: --option-3.141
option :f Да Необязательный вещественный параметр: "option __ или--option 3.141

> Смотри также ------------------------------
Документация по стандартным модулям getopt::Long и Getopt::Std; примеры ручного анализа аргументов встречаются в рецептах 1.5, 1.17, 6.22, 7.7, 8.19 и 15.12.

15.2. Проверка интерактивного режима

Проблема

Требуется узнать, была ли ваша программа запущена в интерактивном режиме или нет. Например, запуск пользователем из командного интерпретатора является интерактивным, а запуск из cron - нет.

Решение

Воспользуйтесь оператором -t для проверки STDIN и STDOUT:
sub I_am_interactive {
return -t STDIN && -t STDOUT;
}

В POSIX-совместимых системах проверяются группы процессов:
use POSIX qw/getpgrp tcgetpgrp/;
sub I_am_interactive {
local *TTY; ft local file handle open(TTY, "/dev/tty") o
r die "can't open /dev/tty: $!";
my $tpgrp = tcgetpgrp(fileno(tty));
my $pgrp = getpgrpo;
close TTY;
return ($tpgrp == $pgrp);
}

Комментарий

Оператор -t сообщает, соответствует ли файловый манипулятор или файл терминальному устройству (tty); такие устройства являются признаком интерактивного использования. Проверка сообщит лишь о том, была ли ваша программа перенаправлена. Если программа запущена из командного интерпретатора, при перенаправлении STDIN и STDOUT первая версия I_am_interactive возвращает false. При запуске из cron I_am_interactive также возвращает false. Второй вариант проверки сообщает, находится ли терминал в монопольном распоряжении программы. Программа, чей ввод и вывод был перенаправлен, все равно при желании может управлять своим терминалом, поэтому POSIX-версня I_am_interactive возвращает true. Программа, запущенная из cron, не имеет собственного терминала, поэтому I_am_interactive возвратит false.
Какой бы вариант I_am_interactive вы ни выбрали, он используется следую щим образом:
while (1) {
if (I_am_interactive()) { print "Prompt: ";
}
$line = ;
last unless defined $line;
'' Обработать $line } Или более наглядно:
sub prompt { print "Prompt: " if I_am_interactive() }
for (promptO; $line = ; promptO) {
# Обработать
$line }


> Смотри также --------------------------------
Документация по стандартному модулю POSIX. Оператор проверки файлов -t описан вреr1ор(1).

15.3. Очистка экрана

Проблема

Требуется очистить экран.

Решение

Воспользуйтесь модулем Term::Cap для посылки нужной последовательности символов. Скорость вывода терминала можно определить с помощью модуля' 15.4. Определение размера терминала или окна 527
POSIX::Termios (или можно предположить 9600 бит/с).
Ошибки, возникающие при работе с POSIX::Termios, перехватываются с помощью eval:
use Term::Cap;
$@SPEED = 9600;
eval {
require POSIX;
my $termios = posix::termios->new();
$termios->getattr;
$@SPEED = $termlos->getospeed;
};
@terminal = term::cap->Tgetent({@SPEED=>$@SPEED});
$terminal->Tputs('cl', 1, STDOUT);

Или выполните команду clear:
system("clear");

Комментарий

Если вам приходится часто очищать экран, кэшируйте возвращаемое значение Term::Cap или команды clear:
$clear = $terminal->Tputs('с1'):
$clear = 'clear';

Это позволит очистить экран сто раз подряд без стократного выполнения clear: print $clear;

> Смотри также -------------------------------
Man-страницы clear(1) и termcap(i) (если они есть); документация по стандартному модулю Term::Cap; документация по модулю Term::Lib с CPAN.

15.4. Определение размера терминала или окна

Проблема

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

Решение

Воспользуйтесь функцией iocti (см. рецепт 12.14) или модулем Term::ReadKey с CPAN:
use Term::ReadKey;
($wchar, $hchar, $wpixels, $hpixels) = getterminalsize();

Комментарий

Функция GetTerminalSize возвращает четыре элемента: ширину и высоту в символах, а также ширину и высоту в пикселях. Если операция не поддерживается для устройства вывода (например, если вывод был направлен в файл), возвращается пустой список. Следующий фрагмент строит графическое представление values при условии, что среди элементов нет ни одного отрицательного:
use Term::ReadKey;
($width) = getterminalsizeo;
die "You must have at least 10 characters" unless $width >= 10;
$max =0;
foreach (@values) {
$max = $_ if $max < $_;
}
$ratio = ($width-10)/$max; # Символов на единицу
foreach (@values) {
printf("%8.1f %s\n", $_, "." x ($ratio*$_));
}


> Смотри также ------------------------------
Документация по модулю Term::ReadKey с CPAN; рецепт 12.14.

15.5. Изменение цвета текста

Проблема

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

Решение

Воспользуйтесь модулем Term::ANSIColor с CPAN для передачи терминалу последовательностей изменения цвета ANSI:
use Term::ANSIColor;
print color("red"), "Danger, Will Robinson!\n", color("reset");
print "This is just normal text.\n";
print colored("Do you hurt yet?", "blink");

Или воспользуйтесь вспомогательными функциями модуля Term::ANSIColor:
use Term::ANSIColor qw(:constants);
print RED, "Danger, Will Robinson!\n", RESET:

Комментарий

Модуль Term::ANSIColor готовит служебные последовательности, которые опознаются некоторыми (хотя далеко не всеми) терминалами. Например, в color-xterm этот рецепт работает. В обычной программе xterm или на терминале vt100 он работать не будет.
Существуют два варианта использования модуля: либо с экспортированными функциями соlоr($АТРИБУТ) и colored($TEKCT, $АТРИБУТ), либо с вспомогательными функциями (такими, как BOLD, BLUE и RESET).
Атрибут может представлять собой комбинацию цветов и модификаторов. Цвет символов принимает следующие значения: black, red, green, yellow, blue, magenta (черный, красный, зеленый, желтый, синий, малиновый). Цвет фона принимает значения on_black, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan и on_white (черный, красный, зеленый, желтый, синий, малиновый, голубой и белый). Допускаются следующие модификаторы: clear, reset, bold, underline, underscore, blink, reverse и concealed (очистка, сброс, жирный, подчеркивание, подчеркивание, мерцание, инверсия и скрытый). Clear и reset являются синонимами (как и underline с underscore). При сбросе восстанавливаются цвета, действовавшие при запуске программы, а при выводе скрытого текста цвет символов совпадает с цветом фона.
Атрибуты могут объединяться:
print color("red on.black"), "venom lack\n";
print color("red on.yellow"), "kill that feilow\n";
print color("green on.cyan blink"), "garish!\n";
print color("reset");
Этот фрагмент можно было записать в виде:
print colored("venom lack\n", "red on_black");
print coloredC'kill that fellow\n", "red", "on_yellow");
print colored("garish!\n", "green", "on_cyan", "blink"),;

или:
use Term::ANSIColor qw(:constants)
print BLACK, ON.WHITE, "black on white\n";
, print WHITE, ON.BLACK, "white on J3lack\n";
print GREEN, ON.CYAN, BLINK; "garish !\n;';
print RESET;

где BLACK - функция, экспортированная из Term::ANSIColor. Не забывайте вызвать print RESET или со1ог(" reset") в конце программы, если вызов colored не распространяется на весь текст. Если этого не сделать, ваш терминал будет раскрашен весьма экзотическим образом. Сброс даже можно включить в блок END: END { print color("reset") } чтобы при завершении программы цвета были гарантированно сброшены.
Атрибуты, распространяющиеся на несколько строк текста, могут привести и замешательство некоторые программы или устройства. Если у вас возникнут затруднения, либо вручную установите атрибуты в начале каждой строки, либо используйте colored, предварительно присвоив переменной $Term: :ANSIColor: :EACHLINE разделитель строк:
$Теrm::ANSIColor::EACHLINE = $/;
print colored("EOF, RED, ON_WHITE, BOLD, BLINK);
This way each line has its own attribute set. EOF


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

15.6. Чтение с клавиатуры

Проблема

Требуется прочитать с клавиатуры один символ. Например, на экран выведено меню с клавишами ускоренного вызова, и вы не хотите, чтобы пользователь нажимал клавишу Enter при выборе команды.

Решение

Воспользуйтесь модулем Term::ReadKey с CPAN, чтобы перевести терминал в режим cbreak, прочитать символы из STDIN и затем вернуть терминал в обычный режим:
use Term::ReadKey;
ReadMode 'cbreak';
$key = readkey(o);
ReadMode 'normal';

Комментарий

Модуль Term::ReadKey может переводить терминал в разные режимы, cbreak лишь один из них. В этом режиме каждый символ становится доступным для программы сразу же после ввода (см. пример 15.1). Кроме того, в нем происходит эхо-вывод символов; пример режима без эхо-вывода рассматривается в рецепте 15.10.
Пример 15.1. sasdi
#!/usr/bin/perl -w
# sascii - Вывод АSCII-кодов для нажимаемых клавиш
use Term::ReadKey;
ReadMode('cbreak');
print "Press keys to see their ASCII values. Use Ctrl-C to quit.\n";
while (1) {
$char = readkey(o);
last unless defined $char:
printf(" Decimal: %d\tHex: %x\n", ord($char), ord($char));
}
ReadMode('normal');

Режим cbreak не мешает драйверу терминала интерпретировать символы конца файла и управления. Если вы хотите, чтобы ваша программа могла прочитать комбинации Ctrl+C (обычно посылает процессу SIGINT) или Ctrl+D (признак конца файла в UNIX), используйте режим raw. Вызов Read Key с нулевым аргументом означает, что мы хотим выполнить нормальное чтение функцией getc. При отсутствии входных данных программа ожидает их появления. Кроме того, можно передать аргумент -1 (неблокирующее чтение) или положительное число, которое определяет тайм-аут (продолжительность ожидания в целых секундах; дробные значения секунд не допускаются). Неблокирующее чтение и чтение с тайм-аутом возвращает либо undef при отсутствии входных данных, либо строку нулевой длины при достижении конца файла. Последние версии Term::ReadKey также включают ограниченную поддержку систем, не входящих в семейство UNIX.
> Смотри также -------------------------------- Документация по модулю Term::ReadKey с CPAN; рецепты 15.8-15.9. Функции getc и sysread описаны в perlfunc(1).

15.7. Предупреждающие сигналы

Проблема

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

Решение


Воспользуйтесь символом "\а" для выдачи звукового сигнала:
print "\aWake up!\n";

Другой вариант - воспользуйтесь средством терминала "vb" для выдачи визуального сигнала:
use Term::Cap;
$OSPEED = 9600;
eval {
require POSIX;
my $termios = posix::termios->new();
$termios->getattr;
$OSPEED = $termios->getospeed;
};
$terminal = term::cap->Tgetent({OSPEED=>$OSPEED});
$vb = "";
eval {
$terminal->Trequire("vb");
$vb = $terminal->Tputs('vb', 1);
}
print $vb; # Визуальный сигнал

Комментарий

Служебный символ "\а" - то же самое, что и "\cG", "\007" и "\х07". Все эти обозначения относятся к символу ASCII BEL, который выдает на терминал противный звонок. Вам не приходилось бывать в переполненном терминальном классе в конце семестра, когда десятки новичков одновременно пытаются перевести vi в режим ввода? От этой какофонии можно сойти с ума. Чтобы не злить окружающих, можно использовать визуальные сигналы. Идея проста: терминал должен показывать, а не звучать (по крайней мере, не в многолюдных помещениях). Некоторые терминалы вместо звукового сигнала позволяют на короткое время поменять цвет символов с цветом фона, чтобы мерцание привлекло внимание пользователя.
Визуальные сигналы поддерживаются не всеми терминалами, поэтому мы включили их вызов в eval. Если визуальный сигнал не поддерживается, Т require инициирует die, при этом переменная $vb останется равной "". В противном случае переменной $vb присваивается служебная последовательность для выдачи сигнала. Более разумный подход к выдаче сигналов реализован в графических терминальных системах (таких, как xterm). Многие из них позволяют включить визуальные сигналы на уровне внешнего приложения, чтобы программа, тупо выводящая chr(7), была менее шумной.

> Смотри также -------------------------------
Раздел "Quote и Quote-like Operators" в perlop(\); документация по стандартному модулю Term::Cap.

15.8. Использование termios

Проблема

Вы хотите напрямую работать с характеристиками своего терминала.

Решение

Воспользуйтесь интерфейсом POSIX termios.

Комментарий

Представьте себе богатые возможности команды stty - можно задать все, от служебных символов до управляющих комбинаций и перевода строки. Стандартный модуль POSIX обеспечивает прямой доступ к низкоуровневому терминальному интерфейсу и позволяет реализовать 5й:г/-подобные возможности в вашей программе.
Программа из примера 15.2 показывает, какие управляющие символы используются вашим терминалом для стирания в предыдущей и текущей позиции курсора (вероятно, это клавиши "забой" и Ctrl+U). Затем она присваивает им исторические значения, # и @, и предлагает ввести какой-нибудь текст. В конце своей работы программа восстанавливает исходные значения управляющих символов. Пример 15.2. demo
#!/usr/bin/perl -w
# Демонстрация работы с интерфейсом POSIX termios
use POSIX qw(:termios_h);
$term = posix::termios->new;
$term->getattr(fileno(STDIN));
$erase = $term->getcc(VERASE);
Skill = $term->getcc(VKILL);
printf "Erase is character %d, %s\n", $erase, uncontrol(chr($erase));
printf "Kill is character %d, %s\n", $kill, uncontrol(chr($kill));
$term->setcc(VERASE, ord('ff'));
$term->setcc(VKILL, ord('@'));
$term->setattr(1, TCSANOW);
print "erase is #, kill is @; type something: ");
$line = ;
print "You typed: $line";
$term->setcc(VERASE, $erase);
$term->setcc(VKILL, Skill);
$term->setattr(1, TCSANOW);
sub, uncontrol {
local $_ = shift;
s/([\200-\377])/sprintf("M-%c",ord($1) & 0177)/eg;
s/([\0-\37\177])/sprintf(""%c",ord($1) " 0100)/eg;
return $_:
}

Следующий модуль, HotKey, реализует функцию read key на Perl. Он не обладает никакими преимуществами по сравнению с Term::ReadKey, а всего лишь показывает интерфейс termios в действии:
# HotKey.pm
package HotKey;
@ISA = qw(exporter);
@EXPORT = qw(cbreak cooked readkey):
use strict;
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(stdin);
$term = posix::termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = echo | echok | icanon;
$noecho = $oterm & ~$echo;
sub cbreak {
$term->setlflag($noecho); # Эхо-вывод не нужен
$Term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked {
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW):
}
sub readkey {
my $key = ' ';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
END { cooked ( ) }
1;


> Смотри также
Документация по стандартному модулю POSIX; рецепты 15.6; 15.9.

15.9. Проверка наличия входных данных

Проблема

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

Решение

Воспользуйтесь модулем Term::ReadKey от CPAN и попытайтесь прочитать символ в неблокирующем режиме, для этого используется аргумент -1:
use Term::ReadKey;
ReadMode ('cbreak''):
if (defined ($char = tieadkey(-l)) ) {
# Имеется необработанный ввод $char } else {
# Необработанного ввода нет
}
ReadMode ('normal'); # Восстановить нормальные
# параметры терминала

Комментарий

Аргумент -1 функции ReadKey означает неблокирующее чтение символа. Если символа нет, ReadKey возвращает undef.

> Смотри также --------------------------------
Документация по модулю Term::ReadKey с CPAN; рецепт 15.6.

15.10. Ввод пароля

Проблема

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

Решение

Воспользуйтесь модулем Term::ReadKey с CPAN, установите режим ввода noecho, после чего воспользуйтесь функцией Read Line:
use Term::ReadKey;
ReadMode 'noecho';
$password = readline 0:

Комментарий

Пример 15.3 показывает, как организовать проверку пароля пользователя. Если в вашей системе используются скрытые пароли, getpwuid вернет зашифрованный пароль лишь привилегированному пользователю. Всем остальным в соответствующем поле базы данных возвращается лишь *, что совершенно бесполезно при проверке пароля.
Пример 15.3. checkuser
#!/usr/bin/perl -w
# checkuser - чтение и проверка пароля пользователя
use Term::ReadKey;
print "Enter your password: ";
ReadMode 'noecho';
$password = readline 0;
chomp $password;
ReadMode 'normal':
print "\n";
($username, $encrypted) = ( getpwuid $< )[0,1J;
if (crypt($password, $encrypted) ne $encrypted) {
die "You are not $username\n";
} else {
print "Welcome, $username\n";
}


> Смотри также ----------------
Документация по модулю Term::ReadKey с CPAN; man-страницы crypt(3) n passwd(5) вашей системы (если есть). Функции crypt и getpwuid описаны в perlfunc(\),

15.11. Редактирование входных данных

Проблема

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

Решение

Воспользуйтесь стандартной библиотекой Term::ReadLine в сочетании с модулем
Term::ReadLine::Gnu с CPAN:
use Term::ReadLine;
$term = term::readl_ine->new("APP DESCRIPTION"):
$OUT = $term->OUT || *STDOUT;
$term->addhistory($fake_line);
$line = $term->readline(PROMPT);
print $OUT "Any program output\n";

Комментарий

Программа из примера 15.4 работает как простейший командный интерпретатор. Она читает строку и передает ее для выполнения. Метод read line читает строку с терминала с поддержкой редактирования и вызова истории команд. Вводимая пользователем строка автоматически включается в историю команд.
Пример 15.4. vbsh
#!/usr/bin/perl -w
# vbsh - очень плохой командный интерпретатор
use strict;
use Term::ReadLine;
use POSIX qw(:sys_wait_h);
my $term = term::readline->new("Simple Shell");
my $OUT = $term->OUT() Ц *STDOUT;
my $cmd;
while (defined ($cmd = $term->readline('$ ') )) { my @output = '$cmd';
my $exit_value = $? " 8;
my $signal_num = $? & 127;
my $dumped_core =$? & 128;
printf $OUT "Program terminated with status %d from signal %d%s\n",
$exit_value, $signal_num,
$dumped_core ? " (core dumped)" : "";
print Ooutput;
$term->addhistory($seed_line);
}

Чтобы занести в историю команд свою строку, воспользуйтесь методом
addhistory:
$term->addhistory($seed_line);

В историю нельзя заносить больше одной строки за раз. Удаление строк из истории команд выполняется методом remove_history, которому передается индекс в списке истории: 0 соответствует первому (самому старому) элементу, 1 - второму и т. д. до самых последних строк.
$term->remove_history($line_number);

Для получения списка истории команд используется метод GetHistory:
@history = $term->GetHistory;


Смотри также --------------------------------
Документация по стандартным модулям Term::ReadLine и Term::ReadLine::Gnu с CPAN.
copyright 2000 Soft group