Журнал LinuxFormat - перейти на главную

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):

LXF130 37 1.jpg Dzen отображает загрузку процессора тремя разными способами.

 #!/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 стал гибче в плане форматирования. Появилась возможность более точно выводить графические фигуры и создавать более сложные объекты, такие как графики и вертикальные индикаторы загрузок.

Прогноз погоды

LXF130 38 1.jpg Прогноз погоды исчезнет ровно через пять секунд (или раньше, если вы перелистнете страницу).

Теперь немного расширим зону наших интересов. Пусть нам нужен прогноз погоды. В принципе, данную информацию можно скачивать с 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 {$user­date = “Завтра”;}
  $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!

LXF130 39 1.jpg Dzen-апплет для телефона: 1 и 2 — в режиме ожидания и при входящем сообщении, 3 — альтернативный компактный вид.

Мобильный телефон, который всегда должен быть рядом, очень часто оказывается в куртке в прихожей, где его совсем не слышно. Результат – несвоевременно прочитанные сообщения и пропущенные вызовы. В настоящее время редкий сотовый не имеет возможности соединения по 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-команд в Интернете.

Ура, готово!

LXF130 39 2.jpg Панель выглядит как единое целое: сразу и не скажешь, что это результат работы четырех независимых скриптов.

Итак, посмотрим, что же у нас получилось. Скомпонованные вместе, все описанные выше скрипты должны сформировать единую информационную панель. Сделаем каждый сценарий исполняемым и запустим:

$ 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.

LXF130 39 3.jpg

Изображенные здесь дополнительные апплеты (индикатор съемных устройств, часы и календарь) вы теперь сможете написать самостоятельно.

Персональные инструменты
Яндекс.Метрика