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

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

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

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

Теперь немного расширим зону наших интересов. Пусть нам нужен прогноз погоды. В принципе, данную информацию можно скачивать с 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!

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-команд в Интернете.

Ура, готово!

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

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

$ 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

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

Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию