[devel] I: alterator internals - 6
Stanislav Ievlev
=?iso-8859-1?q?inger_=CE=C1_altlinux=2Eorg?=
Пн Июл 11 18:12:21 MSD 2005
Продолжение
----------- следующая часть -----------
Продолжение истории
3.3 Интерфейс
Что ж, backend у нас уже есть. Давайте создадим для него некий простейший интерфейс и сразу же включим всё что получилось в ифраструктуру системного конфигуратора.
Хочу сразу отметить что в этом месте alterator меняется сейчас как никогда часто, поэтому возможны некоторые несостыковки при прочтении этого текста месяца эдак через два.
Всё сказанное относится к сборке 1.99-alt41.
3.3.1 Карта
Как вы ,должно быть, помните каждый диалог имеет свой идентификатор и есть карта которая даёт соотвествие между этими идентификаторами и файлами описаний.
Назовём наш диалог "/simple_i18n".
Вот как выглядит для него карта:
-------------------simple_i18n.map-----------------------
(/acc-hook view /simple_i18n)
(/simple_i18n file "/usr/share/alterator/ui/simple_i18n/i18n.scm"
acc-icon "/usr/share/icons/mini/nvu.png"
description ,(i18n:tr "Simple i18n config" "alterator-simple_i18n"))
---------------------------------------------------------
Помимо понятного уже
(/simple_i18n file "/usr/share/alterator/ui/simple_i18n/i18n.scm")
появилось ещё несколько невразумительных конструкций. Не вдаваясь пока в подробности расскажу для его они:
(/acc-hook view /simple_i18n)
Эта фраза означает что мы вешаем наш диалог по имени "/simple_i18n" на "крючок" к ALT Linux Control Center и он появится в списке предлагаемых модулей.
Дополнительные параметры:
acc-iсon "/usr/share/alterator/ui/simple_i18n/i18n.png"
Иконка которая будет отображаться рядом с шагом в Control Center. Если иконку не указывать, то будет использована некоторая стандартная.
description ,(i18n:tr "Simple i18n config" "alterator-simple_i18n")
Описание которое будет использоваться для имени модуля в Control Center и не только в нём, а вообще где это может потребоваться.
3.3.2 Диалог
Мы сделаем простейшее окно, которое просто выведет нам спискок доступных локалей, когда пользователь будет выбирать ту или иную из них, мы будем её выставлять в системном конфигурационном файле с кодировкой UTF-8.
Вот тут настал момент истины. Нам надо из диалога исполнить и обработать результат woo-команд "/i18n/available action=list" и "/i18n/current action=write lang=локаль.UTF-8".
Делается это следующим образом.
(woo-list "/i18n/available") - вернёт список ответов, но он будет выглядить примерно так:
/i18n/available/Russian locale for Ukraine
/i18n/available/Russian locale for Russia
Поэтому надо пройтись по ответу и "выдернуть" нужные нам имена объектов из полного их описания. Для этого есть готовая функция woo-read-names, которой передаются в качестве параметров, имя "каталога объектов" и список ответов на нашу woo-команду.
Итак, (woo-read-names "/i18n/available" (woo-list "/i18n/available") - даст нам список строк
'("Russian locale for Ukraine" "Russian locale for Russia"), который можно будет уже передать в listbox.
(woo-write "/i18n/current" lang "локаль.UTF8") - соответствует, как можно догадаться, командена модификацию
Приступим, вот первая версия диалога:
(id 'locales
(listbox
(items (woo-read-names "/i18n/available"
(woo-list "/i18n/available")))))
Что-то тут явно не хватает? А конечно же надо бы какую-нибудь кнопку для того чтобы принять изменения.
Но мы не будем сознательно делать эту кнопку, нам её предоставит "рамка" в которую вставляется наш диалог в системном конфигураторе. Достаточно просто сказать какое действие мы хотим сделать при нажатии на кнопку по имени "Принять".
Сначала на русском языке скажем что нам надо сделать:
1. Выяснить как зовут текущий выделенный элемент в listbox
делается это так: (locales text)
2. Далее нам нужно выяснить у выбранной локали, какое её стандартное имя, то есть выполнить woo-команду примерно такого вида:
"/i18n/available/Russian locale for Russia" action="read"
и обработать её результаты.
3. Склеить полученное стандартное имя (ru_RU в нашем примере) и кодировку "UTF-8" и выполнить команду на модификацию
/i18n/current action="write" lang=ru_RU.UTF8
Чтение данных из локали осуществляется следующим образом:
(woo-read (string-append "/i18n/available/" (locales text)))
В ответ приходит список ответов который в случае read состоит из одного, возьмём этот ответ:
(woo-first-command
(woo-read (string-append "/i18n/available/" (locales text))))
Далее в ответе может быть перечисленно множество аттрибутов, нас интересует только один "stdname",попросим именно его:
(woo-get-option
(woo-first-command
(woo-read (string-append "/i18n/available/" (locales text))))
'stdname)
Ну и наконец можно выполнить запрос на запись.
Чтобы окончательно не убить вас разворачивающейся гирляндой, назовём всё что было перечисленно выше функцией get-current-stdname и тогда долгожданный запрос на запись будет выгладеть так:
(woo-write "/i18n/current" 'lang (string-append (get-current-stdname)
".UTF-8"))
Добавим его в обработчик on-apply, который
вызывается при нажатии кнопки "Принять" в окружающей нас "рамке",
предоставляемой Control Center.
Объединим всё это в окончательную версию диалога:
----------
(on-apply
(woo-write "/i18n/current" 'lang (string-append (get-current-stdname)
".UTF-8")))
(define (get-current-stdname)
(woo-get-option
(woo-first-command
(woo-read (string-append "/i18n/available/" (locales text))))
'stdname))
(id 'locales
(listbox
(items (woo-read-names "/i18n/available"
(woo-list "/i18n/available")))))
----------
Вот и всё, разложим получившиеся файлы следующим образом:
/usr/lib/alterator/backend/i18n - наш бакенд
/usr/share/alterator/ui/simple_i18n/i18n.scm - описание диалога
/usr/share/alterator/maps/simple_18n.map - наша карта
Запускаем acc и наблюдаем свой первый модуль конфигуратора.
Хотите получить standalone версию модуля, которая будет работать без acc? Нет ничего проще, запустите: /usr/bin/alterator-standalone /simple_i18n
3.4 Опять про схему: локальные переменные
Продолжаем неустанно совершенствоваться в Scheme - основном языке программирования alterator.
Вы уже видели в предыдущий раз, что локальные переменные можно объявлять в теле функции, пользуясь тем же самым define
(define (func)
(define a 5)
(+ a 3))
Однако есть ещё несколько интересных и полезных приёмов работы. Воспользуемся тем, что параметры функции по сути те же локальные переменные.
Тогда пример выше, можно было бы сделать следующим образом:
(define (func)
((lambda (a)
(+ a 3))
5))
Попробуем понять что же произошло. Мы создали функцию с параметром, который назвали 'a', поместили в неё всё что нам необходимо, и после этого запустили её придав параметру требуемое значение '5'. Всё ,как говорится , гениально и просто.
Попробуем ещё, вместо:
(define x 5)
(define y 6)
(write x)
(+ x (* x y))
Мы можем написать:
((lambda (x y)
(write x)
(+ x (* x y)))
5
6)
Данный приём настолько популярен, что имеет общепринятое сокращение - let.
Приведённые выражения в сокращённом виде записываются так:
------
(define (func)
(let ((a 3))
(+ a 3)))
------
(let ((x 5) (y 6))
(write x)
(+ x (* x y)))
------
Если немного поразмышлять, то мы получили не просто способ объявления локальных переменных, а возможность делать блоки с локальными переменными в произвольном месте кода, например:
(define a 3)
(write a) ; будет напечатано 3
(let ((a 5))
(write a)) ; будет напечатано 5
(write a) ; будет напечатано 3
У этого приёма есть один существенный недостаток, поскольку формальные параметры инициализируются независимо друг от друга и в неопределённом порядке, мы не можем использовать одни из них для инициализации других, например в примере с двумя параметрами нельзя у задать равным x.
Но против лома всегда есть другой лом.
Применим одну маленькую хитрость - будем связывать переменные по очереди:
(let ((x 3))
(let ((y x))
.....
Тогда всё получится, на момент определения 'y', 'x' уже известен и проинициализирован.
Этот приём тоже очень распространён, а потому тоже имеет общепринятое сокращение - let*.
(let* ((x 3) (y x))
.....
------
Продолжение следует ...
Подробная информация о списке рассылки Devel