- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF130:applets
Материал из Linuxformat.
Содержание |
Dzen: апплеты на заказ
- Принято считать, что современный рабочий стол немыслим без виджетов. Павел Воронцов научит запрограммировать их самостоятельно – на ваш вкус.
Втой или иной степени, интерес к настройке рабочей среды есть у каждого пользователя. Одних не волнует внешний вид: удобство и быстродействие – их главный приоритет, а остальное – баловство. Другие готовы нарядить свой рабочий стол, как новогоднюю ёлку, и вопрос быстродействия их не волнует. Лично я придерживаюсь точки зрения, что в рабочем окружении, как и в человеке, всё должно быть прекрасно: и быстродействие, и удобство, и внешний вид. Если вы считаете так же, вам непременно следует взглянуть на Dzen, тем более что в последнее время он становится всё более популярным.
Что же такое Dzen? Вообще-то это маленькая программа, весьма ограниченная в возможностях. Она предназначена для создания информационных панелей, всплывающих напоминаний и меню. Основная задача – графический вывод текста, который подаётся на её стандартный ввод. Dzen удобно использовать в скриптах, для передачи результата их работы в графическом виде на рабочий стол. Обычно она используется в паресо сверхлегким оконным менеджером xmonad, но ничто не мешает применить её и в любом другом окружении. То есть, Dzen – это не программа, которая может всё, а лишь инструмент, который поможет вам вывести нужную информацию в нужное время и в нужном месте. Всё остальное придётся делать самому. Этим мы сейчас и займемся.
Что-то с памятью моей...
Для примера, напишем монитор памяти. Он есть во многих программах, но чаще всего в них больше функций, чем реально необходимо, они занимают больше памяти (пардон за каламбур) и системных ресурсов, чем нужно, и кроме того, вывод результата мониторинга осуществляется только в том виде, в котором задумал автор. Количество свободных килобайт памяти меня не интересует – меня вообще не интересует состояние памяти, когда её много. Мне нужен маленький индикатор, который будет мигать, если памяти мало, и постоянно гореть, если её почти не осталось. Найти программу, которая удовлетворит этим требованиям, практически невозможно, поэтому мы напишем её сами, используя Dzen (файл mem.pl на диске):
#!/usr/bin/perl $na = “^fg(#505050)”; $active = “^fg(#4F95DE)”; $critical = “^fg(red)”; open (DZEN, “| dzen2 fn *fixed*r**12******* bg ‘#303030’ x 40 y 0 w 40 ta c”) || die “dzen2ошибка\n”; select (DZEN); $| =1; for (;;) { $mem = `free`; if ($mem =~ /Mem:\s+(\d+).*\n\D+(\d+).*\nSwap:\s+(\d+)\s+(\ d+)/) { $totalm=$1;$usem=$2;$totals=$3;$uses=$4; if ($totalm != 0) {$usemp = int($usem*100/$totalm);} else {$usemp = 0;} if ($totals != 0) {$usesp = int($uses*100/$totals);} else {$usesp = 0;} if ($usemp < 80) {$color = $na;} elsif ($usemp > 95 and $usesp > 90) {$color = $critical;} elsif ($i == 0) {$color = $na; $i = 1;} elsif ($i == 1) {$color = $active; $i = 0;} } print “ $color\MEM\n”; sleep 1; }
Первая строка указывает на используемый интерпретатор. В нашем случае это /usr/bin/perl. Разумеется, Perl должен быть установлен в системе.
Вторая присваивает переменным значения цвета: активный, неактивный и критический. Обратите внимание на формат “^fg(color)”. Это код форматирования Dzen, означающий, что надо поменять цвет всех последующих символов на заданный. (Соответственно, код ^bg(color) меняет цвет фона). Префикс # предваряет задание шестнадцатеричного значения цвета, как в HTML.
Строка 3 запускает dzen2 и связывает его стандартный ввод с дескриптором файла DZEN, который будет использоваться для передачи форматированного текста. Обратите внимание на то, что dzen2 запускается с определёнными параметрами:
- -fn – имя шрифта в формате X Window (для выбора воспользуйтесь xfontsel).
- -bg – стандартный цвет фона.
- -fg – цвет шрифта.
- -x и -y – абсолютные координаты расположения окна в пикселях относительно левого верхнего угла экрана (у нас 40, 0).
- -w – длина строки в пикселях.
- -h – высота, по умолчанию равна высоте шрифта + 2 пикселя.
- -ta – выравнивание заголовка. Если строка одна, как в данном примере, это и есть заголовок; если строк больше, то заголовком является самая верхняя строка, а остальные принадлежат к так называемому ведомому окну. Варианты выравнивания: l – по левому краю, r – по правому и c – по центру.
Выражение $| =1 отключает буферизацию текста, чтобы результат сразу выдавался на стандартный вывод. Это нюанс, который нужно учитывать при обмене данными между программами.
Далее идёт бесконечный цикл (for (;;) {…}) с задержкой в одну секунду, выполняющий код, который мы не будем рассматривать детально. Он анализирует вывод команды free, рассчитывает процент занятой памяти и, в зависимости от результата, меняет цвет по алгоритму: если занято менее 80 % памяти – неактивный, если больше 95 % физической памяти и 90 % подкачки – критический. Во всех остальных случаях на каждой итерации цикла активный и неактивный цвета чередуются – происходит мигание. Размер занятой памяти берётся с учетом кэша – так объективнее.
И, естественно, при каждом проходе цикла результат по сылается на стандартный ввод dzen. Использование free в качестве источника данных упрощает код, но делает его менее эффективным; в качестве упражнения, попробуйте читать напрямую /proc/memstat и избавиться от постоянного вызова внешней программы.
Загрузка процессора
Давайте теперь рассчитаем и выведем на панель текущую загрузку процессора. Для решения этой задачи можно, как и раньше, использовать внешнюю команду (top), но мы пойдем другим путем: вычислим нагрузку самостоятельно по данным файла /proc/stat.
Ниже показана одна из реализаций такого алгоритма (cpu.pl):
#!/usr/bin/perl $na = “^fg(#505050)”; $active = “^fg(#4F95DE)”; $critical = “^fg(red)”; open (DZEN, “| dzen2 -fn -*-fixed-*-r-*-*-12-*-*-*-*-*-*-* -bg '#303030' -x 0 -y 0 -w 40 -ta c”) || die “dzen2 - ошибка\n”; select (DZEN); $| =1; for (;;) { $all=$idle=0; open (CPU,”/proc/stat”) || die “stat-ошибка!\n”; $_ = <CPU>; close(CPU); @cpu = split(/\s+/); shift (@cpu); $idle = @cpu[3]; foreach (@cpu) {$all += $_;} $diffidle = $idle - $preidle; $diffall = $all - $preall; $diffuse = int((1000*($diffall-$diffidle)/$diffall+5)/10); if ($diffuse < 70) {$color = $na} elsif ($diffuse == 100) {$color = $critical} elsif ($i == 0) {$color = $na; $i =1;} elsif ($i == 1) {$color = $active; $i =0;} print “$color\CPU\n”; $preall = $all; $preidle = $idle; sleep 2 }
Если хотите, можете использовать и цифровые значения: например, переменная $diffuse содержит текущую загрузку процессора в процентах. Результат можно вывести так:
print “CPU: $active$diffuse%\n”;
Еще интереснее сделать графический индикатор. Да, Dzen может отображать графические элементы: простейшие фигуры (прямоугольники, окружности) и растровые изображения в формате XBM и XPM. Делается это посредством специальных кодов форматирования. При помощи геометрических фигур можно строить различные индикаторы: в некоторых случаях их использование более наглядно, чем обычный текст. Чтобы облегчить задачу скриптописцам, вместе с Dzen поставляются специальные утилиты (dbar и gdbar), которые, в зависимости от входных данных (цифровое значение), выводят уже отформатированную строку, описывающую индикатор, которую, в свою очередь можно подать на вход Dzen. В некоторых случаях использование этих утилит значительно уменьшает код скрипта. Dbar форматирует индикатор, используя текстовые символы, а gdbar применяет геометрические фигуры. Вот пример вывода (без дополнительных параметров, шкала – от 0 до 100):
$ echo 25 | dbar ; echo 25 | gdbar
25% [====== ] 25% ^fg(white)^r(20x10)^fg(darkgrey)^r(60x10)^fg()
Если говорить о нашем скрипте, то результат можно выводить следующим образом:
$bar = `echo $diffuse | gdbar -w 60 -h 8 -bg '#505050' -fg '#4F95DE' -nonl`; $bar =~ s/^.*%//; print “^i(icons/cpu.xbm)$bar\n”;
Разумеется, самый информативный индикатор – график загрузки по времени. Реализовать его несложно – нужно запоминать текущее значение загрузки в массиве и сдвигать с каждым новым циклом; но правильно отображать его нелегко. Расположение графического элемента определяется только одной горизонтальной координатой, по вертикали он выравнивается по центру, поэтому получить ровный и визуально понятный график в текущей версии Dzen (0.8.5) сложно. Но это вопрос времени – в последних SVN-срезах Dzen стал гибче в плане форматирования. Появилась возможность более точно выводить графические фигуры и создавать более сложные объекты, такие как графики и вертикальные индикаторы загрузок.
Прогноз погоды
Теперь немного расширим зону наших интересов. Пусть нам нужен прогноз погоды. В принципе, данную информацию можно скачивать с weather.com – так делает большинство программ такого типа; но нам интересен другой источник, например, gismeteo.ru. В общем случае, можно брать интересующие нас сведения прямо с web-страницы, разбирая её с помощью регулярных выражений, как мы делали в первом примере. Минус такого способа очевиден: структура страниц может поменяться, и, конечно, они содержат очень много избыточной информации, которую нужно как-то фильтровать. К счастью, российский погодный сервер gismeteo.ru предоставляет пользователю XML-файл с подробным прогнозом на сутки вперёд (4 прогноза с шагом по 6 часов). Найти ссылку вы сможете на странице http://informer.gismeteo.ru, там же расположено и описание формата. Ниже я приведу пример соответствующего скрипта (meteo.pl). Так как выводимой информации будет много, я буду использовать Dzen в форме раскрывающегося списка, появляющегося при наведении курсора мыши.
#!/usr/bin/perl use LWP::Simple; use XML::Simple; $option=”^fg(white)”;$value=”^fg(#4F95DE)”; open (DZEN, “| dzen2 fn *fixed*r**12******* tw 140 bg '#303030' x 80 y 0 w 160 tw 120 ta c sa c l 22 e 'entertit le=uncollapse;leavetitle=collapse;'”) || die “dzen2ошибка!\n”; select (DZEN); $| = 1; @month = qw/месяц января февраля марта апреля мая июня июля августа сентября октября ноября декабря/; @tod = qw/НОЧЬ УТРО ДЕНЬ ВЕЧЕР/; @cloud = qw/ясно малооблачно облачно пасмурно дождь ливень снег снег гроза N\/A /; @direct = qw/С СВ В ЮВ Ю ЮЗ З СЗ/; for(;;) { $weatherxml=get(“http://informer.gismeteo.ru/xml/27612_1.xml”) || die “xmlошибка\n”; $weather=XMLin($weatherxml); $strall = “$option\ПОГОДА Моск ва\n\n”; @time = localtime(); foreach (@{$weather>{REPORT}{TOWN}{FORECAST}}) { if ($_>{day} == $time[3]) {$userdate = “Сегодня”;} else {$userdate = “Завтра”;} $str=”$option$userdate [$tod[$_>{tod}]]\n$option\Температу ра: $value$_>{TEMPERATURE}{min}..$_>{TEMPERATURE}{max} $option\C\n$option\Осад ки: $value$cloud[$_>{PHENOMENA} {cloudiness}], $cloud[$_>{PHENOMENA}{precipitation}]\n$option\ Ветер: $value$direct[$_>{WIND}{direction}] [$_>{WIND}{min} $_>{WIND}{max} м/c]\n\n”; $strall .= $str; } $strall .= ($option . “gismeteo.ru\n”); print $strall; sleep 60*60; }
В скрипте используются два модуля, которые необходимо установить заранее: LWP::Simple и XML::Simple. Первый применяется для скачивания файла с web-сервера, а второй – для разбора полученного XML. Чтобы установить эти модули, выполните в терминале следующую команду:
# perl -MCPAN -e shell
Запустится оболочка, в которой нужно набрать следующее:
cpan> install LWP::Simple cpan> install XML::Simple
Разумеется, это универсальный способ установки, и если в вашем дистрибутиве Linux некоторые модули можно добавить напрямую через менеджер пакетов, воспользуйтесь этой возможностью.
Теперь рассмотрим параметры Dzen, которые мы использовали в нашей программе:
- -l – включает ведомое окно, в качестве значения задается количество строк. У нас их 22.
- -sa – задает режим выравнивания текста в ведомом окне, значения аналогичны ключу –ta.
- -tw – указывает длину заголовка.
- -e – очень важный ключ, который сопоставляет события и действия. В данном примере использованы пары entertitle=uncollapse и leavetitle=collapse. При наведении ведомое окно разворачивается, при отведении – сворачивается. Полный список событий и действий, так же как и коды форматирования, можно прочесть только в readme-файле Dzen, в man представлены лишь основные ключи. Для просмотра описания наберите:
$ bzcat /usr/share/doc/dzen-[версия]/README.bz2 | less
В первом примере мы использовали Dzen как однострочную панель, в этом – как разворачивающийся список. Давайте немного изменим текущий скрипт. Уберём бесконечный цикл, допишем к параметрам Dzen ключ -p 5 (задержка 5 секунд перед закрытием Dzen), добавим событие -e 'onstart=uncollapse;' (развернуть окно при запуске), уберем параметр длины заголовка, чтобы его ширина соответствовала ширине ведомого окна, и отредактируем координаты. Теперь запустим нашу программу. Появится окно с прогнозом погоды, которое закроется через 5 секунд.
В данном случае Dzen будет работать в режиме всплывающих окон. Такой режим очень удобно использовать для задач, которые не требуют постоянной видимости – например, для вывода календаря; а скрипт можно вызывать из меню вашего оконного менеджера, по нажатию горячей клавиши или по любому другому событию.
Вам пришло SMS!
Мобильный телефон, который всегда должен быть рядом, очень часто оказывается в куртке в прихожей, где его совсем не слышно. Результат – несвоевременно прочитанные сообщения и пропущенные вызовы. В настоящее время редкий сотовый не имеет возможности соединения по Bluetooth. Если настроить Linux на автоматическую связь с телефоном через виртуальное COM-устройство (см., например, LXF93), то можно будет посылать мобильному телефону AT-команды и получать результат, разобрав который, выводить требуемую информацию на панель Dzen на рабочем столе. Конкретизируем задачу: пусть нам нужна информация о состоянии счёта, статус телефона (если это «Звонок», есть шанс успеть добежать и ответить), а также счетчик пропущенных вызовов и текстовых сообщений. Список доступных AT-команд можно найти в Сети; многие из них универсальны и подходят для любой модели телефона, но лучше поискать конкретно для своей. Вот что получится в итоге (mobile.pl):
#!/usr/bin/perl use Device::Modem; $option=”^fg(white)”;$value=”^fg(#4F95DE)”;$critical=”^fg(red)”; $wait = “$value\Ожидание”; $ring =”$critical\Звонок”; $status = $wait;$calls = 0;$sms = 0; $bc = $sc = $cc = $value; open (DZEN, “| dzen2 -fn -*-fixed-*-r-*-*-12-*-*-*-*-*-*-* -bg '#303030' -x 220 -y 0 -w 420 -ta r”) || die “dzen2-ошибка!\n”; select (DZEN); $| = 1; $modem = new Device::Modem( port => “/dev/rfcomm0”, log => 'file,/dev/null',); $modem->connect(baudrate=>9600); foreach (qw/ATZ AT+CNMI=2,3 AT*CPI=2/) { $modem->atsend($_ . Device::Modem::CR); $modem->answer($Device::Modem::STD_RESPONSE,1000); } $modem->atsend('AT+CUSD=1,”#102#”' . Device::Modem::CR); $answer = $modem->answer ($Device::Modem::STD_RESPONSE,10000); if ($answer =~ /CUSD:\ 0.*”\D+?([0-9.-]+)/) { $bc = $value; $money = $1; if ($money <= 0.5) {$bc = $critical;} } for (;;) { $answer = $modem->answer(qr/CPI|CMTI/,10000); if (defined($answer)) { if ($answer =~ /CPI: 1,2/) {$status = $ring;$calls += 1; $cc = $critical;} if ($answer =~ /CPI: 1,1/) {$status = $wait;} if ($answer =~ /CMTI/) {$sms +=1;$sc = $critical;} } print “$option\[ТЕЛЕФОН] Баланс: $bc$money$option | Статус: $status$option | Звонков: $cc$calls$option | SMS: $sc$sms \n”; }
Для связи с COM-устройством мы используем модуль Device::Modem – установите его аналогично двум предыдущим.
Пара пояснений по использованным здесь AT-командам:
- AT+CUSD=1,”#102# – послать USSD-запрос оператору, аналогично набору #102# на мобильном телефоне (у моего оператора – информация о текущем счёте); но в данном случае ответ выводится в терминал и с помощью регулярных выражений из него выбирается информация о балансе счёта.
- AT+CNMI=2,3 – разрешить индикацию прихода SMS; по умолчанию выключено.
- AT*CPI=2 – аналогично предыдущей функции, но только для звонков.
Таким образом, после инициализации и получения сведений о балансе программа переходит в бесконечный цикл и опрашивает мобильный телефон, выявляя новые события. Разумеется, в таком виде она не совсем пригодна для постоянного использования: необходимо проверять активность модема, обрабатывать сообщения об ошибках и т. д. В любом случае можно посмотреть: наверняка кто-нибудь уже делал нечто подобное. Например, есть консольная программа gammu, которая может избавить вас от необходимости искать описание AT-команд в Интернете.
Ура, готово!
Итак, посмотрим, что же у нас получилось. Скомпонованные вместе, все описанные выше скрипты должны сформировать единую информационную панель. Сделаем каждый сценарий исполняемым и запустим:
$ chmod a+x cpu.pl mem.pl meteo.pl mobile.pl $ ./cpu.pl & ./mem.pl & ./meteo.pl & mobile.pl &
Если результат вас удовлетворит, можно прописать их запуск в автозагрузку вашей сессии. Но учтите: окна Dzen располагаются поверх всех остальных, их нельзя перемещать и менять размер. Так что если вы решили использовать Dzen в качестве панели, которая постоянно на виду, учтите эти нюансы. В своем оконном менеджере Openbox (LXF124) я просто установил в настройках рабочего стола величину верхнего поля, достаточную для размещения панели. Остальная работа – это уже творчество, ограниченное скромными (пока) возможностями Dzen.
Но, как уже говорилось, проект не стоит на месте и постоянно развивается. Последние новости, документацию и другие полезные ресурсы вы всегда найдете на странице программы, доступной по адресу http://sites.google.com/site/gotmor/dzen.
Изображенные здесь дополнительные апплеты (индикатор съемных устройств, часы и календарь) вы теперь сможете написать самостоятельно.