- PVSM.RU - https://www.pvsm.ru -
Когда я впервые узнал об утилитах dialog, xdialog, zenity я был поражен тем, что буквально из пары строк можно было создать графический интерфейс для скрипта.
Однако, когда я пошел дальше и захотел создать что-то более сложное, чем просто окошко со списком или диалог «Да/нет» — я был слегка разочарован, т.к. не смог найти требуемого. В итоге, мое знакомство с этой областью приостановилось на несколько лет.
Недавно я вспомнил о своем желании и попытался узнать о прогрессе в этом деле. Увы, и сейчас результат оказался не таким как ожидалось — я по-прежнему ничего не находил. По этой причине я и написал программу papagaj. Она позволяет из гов… и палок из xml-файла и пары скриптов построить несложный графический интерфейс для вашего консольного приложения.
Сперва об инструментах необходимых для сборки papagaj. Программа написана на ObjC с использованием библиотеки GNUStep Foundation, поэтому вам нужно иметь в системе установленные пакеты gnustep-base, gnustep-base-dev, gnustep-base-runtime.
Для сборки используется GNUmakefile, для него нужен будет пакет gnustep-make. На случай, если вы не хотите устанавливать gnustep-make я приведу обычный Makefile.
В качестве графической библиотеки я использовал IUP, расположенной по адресу www.tecgraf.puc-rio.br/iup/ [1]. В README содержатся инструкции по сборке библиотеки. Имейте в виду, что она требует dev-пакетов для Gtk, т.к. является оберткой для этой библиотеки.
Я не буду описывать установку всех пакетов, у каждого из них есть неплохая документация на этот случай.
После удачной установки перечисленных выше пакетов можно начинать собирать papagaj. Скачайте исходники из git-репозитория [2], зайдите в директорию и смело набирайте make. В случае невероятного везения вы найдет готовый исполняемый файл papagaj-bin в директории ./obj/
. Это и есть программа.
При желании можете переместить ее в /usr/*/bin
или создать ссылку оттуда на исполняемый файл.
Makefile:
CC=gcc
CFLAGS=-O2 -W -Wall
IUP=-liup
GNUSTEP=-lgnustep-base
CONFIG= `pkg-config --cflags --libs gtk+-2.0` `gnustep-config --objc-flags`
INCLUD=-I/usr/include/iup
LIBS=$(GNUSTEP) $(IUP)
COMPLETE=$(CONFIG) $(INCLUD) $(IUP) $(GNUSTEP)
all: main.m
$(CC) main.m -o papagaj-bin $(COMPLETE)
.PHONY: clean
clean:
rm papagaj-bin
rm *.d
clear
papagaj-bin interface-file [onstart-script]
, где
papagaj-bin
— имя программы;
interface-file
— файл с описанием интерфейса
onstart-script
— файл с shell-скриптом, выполняемым при запуске программы.
Пример:
<application name="#app-name">
<dialog name="#dlg1" attributes='TITLE="sup, habr"' other="index">
<vbox name="#vbox1" attributes="EXPAND=YES, SIZE=100x100, MARGIN=15x15">
<button name="#btn1" attributes='TITLE="Click Me!",EXPAND=YES' callback="SHOW #dlg2 :"></button>
</vbox>
</dialog>
<dialog name="#dlg2" attributes='TITLE="SURPRISE!"'>
<vbox name="#vbox2" attributes="EXPAND=YES, MARGIN=15x15">
<label name="#lbl1" attributes='EXPAND=YES,TITLE="Sup, Хабр",FONT="Comic Sans MS, 50"' ></label>
</vbox>
</dialog>
</application>
papagaj-bin index.xml # index.xml - файл интерфейса. см выше
Файлы интерфейса представляют собой обычные xml-файлы, содержащие описание вложенности виджетов.
Формат описания:
<widget-type name="#widget-name" attributes="<>" callback="<>" other=""></widget-type>
Список общих атрибутов для большинства виджетов:
Далее в статье будут показаны возможности обратных вызовов.
Атрибуты:
По-умолчанию — ATOP.
По-умолчанию — NO.
Имеет те же атрибуты, что и hbox.
Атрибуты:
Пример использования:
<radio name="#rd1" attributes="" callback="" other="">
<vbox name="#vbox1" attributes="" callback="" other="">
<toggle name="#n1" attributes='TITLE="Один"' callback="" other=""></toggle>
<toggle name="#n2" attributes='TITLE="Два"' callback="" other=""></toggle>
<toggle name="#n3" attributes='TITLE="Три"' callback="" other=""></toggle>
</vbox>
</radio>
Атрибуты:
Атрибуты:
По-умолчанию — ALEFT:ACENTER.
Атрибуты аналогичны атрибутам элемента label.
1="Один"
— заголовок первого элемента — «Один»
2="Два"
— второго — «Два»
3="Три"
— третьего — «Три»
n="Какой-нибудь"
— n-го — «Какой-нибудь»
Атрибуты:
Пример использования:
<vbox name="#vbox0" attributes="GAP=10">
<list name="#lst1" attributes='1="1",2=2,3=3,4=4,EXPAND=YES' callback="" other=""></list>
</vbox>
Атрибуты:
Атрибуты:
Атрибуты:
Для более подробного описания всех атрибутов виджетов обратитесь к документации по библиотеке IUP, расположенной в www.tecgraf.puc-rio.br/iup/ [1]
Формат команд для обратного вызова:
<word1> <word2> ... <wordN> :
Правила:
Команды обратного вызова размещаются в атрибутах callback (и other для элемента list).
#widget-name WIDGET-ATTRIBUTE
_some-variableName
#dlg1 TITLE = "Hello world" :
_some-variable = "hello world" :
Пример.
<application name="#app">
<dialog name="#dlg" attributes='SIZE=150x150, TITLE="Неизмененное значение"' other="index">
<vbox name="#vbox1" attributes="EXPAND=YES,MARGIN=15x15">
<button name="#b1" attributes='TITLE="Нажми меня",EXPAND=YES' callback='#dlg TITLE = "Измененное значение" :'/>
</vbox>
</dialog>
</application>
При нажатии на кнопку, заголовок окна будет изменен.
Рассмотрим на примере:
<application name="#app">
<dialog name="#dlg" attributes='SIZE=150x150, TITLE=""' other="index">
<vbox name="#vbox1" attributes="EXPAND=YES,MARGIN=15x15">
<label name="#lbl1" attributes='TITLE="Привет",EXPAND=YES'/>
<label name="#lbl2" attributes='TITLE="Мир",EXPAND=YES'/>
<button name="#b1" attributes='TITLE="Swap!",EXPAND=YES' callback='_swap = #lbl1 TITLE : #lbl1 TITLE = #lbl2 TITLE : #lbl2 TITLE = _swap :'/>
</vbox>
</dialog>
</application>
Создан файл интерфейса с двумя ярлыками и одной кнопкой, при нажатии на кнопку подписи в ярлыках меняются местами. Разберем обратный вызов кнопки подробнее:
_swap = #lbl1 TITLE :
— создается переменная _swap, с содержимым подписи первого ярлыка.
#lbl1 TITLE = #lbl2 TITLE :
— задается значение #lbl1 TITLE равным #lbl2 TITLE.
#lbl2 TITLE = _swap :
— присваивает значение переменной _swap атрибуту TITLE виджета #lbl2.
DETACH #widget-name :
<application name="#app">
<dialog name="#dlg" attributes='SIZE=150x150, TITLE=""' other="index">
<vbox name="#vbox1" attributes="EXPAND=YES,MARGIN=15x15">
<label name="#lbl1" attributes='TITLE="Привет",EXPAND=YES'/>
<button name="#b1" attributes='TITLE="detach",EXPAND=YES' callback='DETACH #lbl1 :'/>
</vbox>
</dialog>
</application>
При нажатии на кнопку удаляется виджет #lbl1.
APPEND #widget-parent #widget-child
, <application name="#app">
<dialog name="#dlg" attributes='SIZE=150x150, TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES">
<vbox name="#vbox1" attributes="EXPAND=YES,MARGIN=15x15">
<label name="#lbl1" attributes='TITLE="Привет",EXPAND=YES'/>
<button name="#b1" attributes='TITLE="detach",EXPAND=YES' callback='DETACH #lbl1 :'/>
</vbox>
<vbox name="#vbox2" attributes="EXPAND=YES,MARGIN=15x15">
<button name="#b2" attributes='TITLE="append",EXPAND=YES' callback='APPEND #vbox2 #lbl1 : '/>
</vbox>
</vbox>
</dialog>
</application>
При нажатии на кнопку detach, виджет #lbl1 будет удален из контейнера #vbox1. При нажатии на кнопку append, #lbl1 будет присоединен к контейнеру #vbox2. Обратите внимание на то, что добавляемый виджет сперва должен быть удален из предыдущего контейнера.
INSERT #new-parent-container #ref-widget #new-widget
, <application name="#app">
<dialog name="#dlg" attributes='SIZE=150x150, TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES">
<vbox name="#vbox1" attributes="EXPAND=YES,MARGIN=15x15">
<label name="#lbl1" attributes='TITLE="Привет",EXPAND=YES'/>
<button name="#b1" attributes='TITLE="detach",EXPAND=YES' callback=''/>
</vbox>
<vbox name="#vbox2" attributes="EXPAND=YES,MARGIN=15x15">
<button name="#b2" attributes='TITLE="insert",EXPAND=YES' callback='DETACH #lbl1 : INSERT #vbox2 #b2 #lbl1 : '/>
</vbox>
</vbox>
</dialog>
</application>
SHOW #some-dialog :
, Пример:
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<button name="#b1" attributes='TITLE="SHOW",EXPAND=YES' callback='SHOW #dlg2 :'/>
</vbox>
</dialog>
<dialog name="#dlg2" attributes='TITLE="привет",SIZE=50x50'>
<vbox name="#vbox" attributes="EXPAND=YES, MARGIN=15x15">
<label name="#lbl1" attributes='TITLE="Привет",EXPAND=YES'/>
</vbox>
</dialog>
</application>
При нажатии на кнопку SHOW отобразится диалог #dlg2
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<button name="#b1" attributes='TITLE="APPEND",EXPAND=YES' callback='APPEND #main #vbox2 :'/>
</vbox>
</dialog>
<vbox name="#vbox2" attributes='EXPAND=YES, MARGIN=15x15, FGCOLOR="100 50 250"'>
<label name="#some-label" attributes='TITLE="SOME LABEL"' />
</vbox>
</application>
Обратным вызовом может быть выполнение и внешних прорамм, таких как shell-скрипты, команды оболочки и т.п.
Если программа не может разобрать обратный вызов виджета, она предполагает, что он является внешней командой и обращается к оболочке для исполнения команды.
Пример:
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<multiline name="#content" attributes="EXPAND=YES" />
<button name="#b1" attributes='TITLE="List",EXPAND=YES' callback='ls -l' />
</vbox>
</dialog>
</application>
При нажатии на кнопку List будет выполнена команда оболочки ls -l.
Однако, какой смысл в выполнении команды, если нет возможности увидеть результат? Так как программы зачастую выводят множество информации, которая нам не нужна есть ее следует отфильтровать и только затем вернуть в основную программу.
Пример:
ls -a | awk '{printf(" #content APPEND = "%s" : ", $0);}'
Но даже сейчас вывод команды никак не воздействует на основную программу. Чтобы вернуть информацию в вызывающую программу сперва должен идти маркер о начале приема информации — !BEGIN. Маркер !END сообщает об окончании приема. Т.е. весь вывод находящийся между этими двумя маркерами будет считаться валидными командами для программы и она будет стремиться их выполнить.
Поместим ls -a | awk '{printf(" #content APPEND = "%s" : ", $0);}'
в файл ls.sh и добавим маркеры для получения результата.
ls.sh:
echo "!BEGIN "
ls -a | awk '{printf(" #content APPEND = "%s" : ", $0);}'
echo " !END"
Файл с описанием интерфейса:
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<multiline name="#content" attributes="EXPAND=YES" />
<button name="#b1" attributes='TITLE="List",EXPAND=YES' callback='bash ./ls.sh :'/>
</vbox>
</dialog>
</application>
Теперь при нажатии на кнопку List в текстовый виджет #content будет добавлен результат выполнения скрипта ls.sh.
Стоит заметить, что подстановка значений атрибутов или переменных происходит и в командах для выполнения в оболочке.
Следующая программа возвращает список файлов в директории указанной в #addr.
echo "!BEGIN "
ls -a $1 | awk '{printf(" #content APPEND = "%s" : ", $0);}'
echo " !END"
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<oneline name="#addr" attributes="EXPAND=HORIZONTAL" />
<multiline name="#content" attributes="EXPAND=YES" />
<button name="#b1" attributes='TITLE="APPEND",EXPAND=YES' callback='#content VALUE = "" : bash ./ls.sh #addr VALUE :'/>
</vbox>
</dialog>
</application>
Скрипты, выполняемые при запуске.
Кроме файла интерфейса, параметром для papagaj-bin может быть еще файл скрипта, выполняемый при запуске программы. Это должен быть просто shell-скрипт, похожий на используемый нами ранее ls.sh
. Дальше, в примерах, показывается как использовать скрипты при запуске и зачем они вообще нужны.
В завершение несколько простых примеров:
Ползунок и индикатор прогресса:
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<progressbar name="#bar" attributes='EXPAND=YES,MIN=0,MAX=100,VALUE=50' />
<hscale name="#scale" attributes='EXPAND=YES,MIN=0,MAX=100,VALUE=-50' callback="#bar VALUE = #scale VALUE :"/>
</vbox>
</dialog>
</application>
Список и поле ввода
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<oneline name="#addr" attributes="EXPAND=HORIZONTAL" callback="#lst APPENDITEM = #addr VALUE :"/>
<list name="#lst" attributes="EXPAND=YES" />
</vbox>
</dialog>
</application>
Небольшая графическая обертка для команды ls -a
Файл интерфейса:
<application name="#app">
<dialog name="#dlg1" attributes='EXPAND=YES,TITLE="YAFM",SIZE=500x200' other="index">
<vbox name="#main" attributes="NGAP=10,EXPAND=YES">
<hbox name="#top-fill" attributes="MARGIN=5x5"></hbox>
<hbox name="#list-panel" attributes="EXPAND=YES,MARGIN=5x5" >
<vbox name="#lst-cont" attributes="EXPAND=YES,NGAP=5" >
<hbox name="#up-cont" attributes="MARGIN=0x0" >
<oneline name="#curr-pos" attributes='EXPAND=HORIZONTAL'></oneline>
<button name="#btn-go-lst" attributes='TITLE="Go/Refresh",EXPAND=NO' callback='bash ./change_dir.sh #curr-pos VALUE :'></button>
</hbox>
<list name="#lst" attributes='EXPAND=YES,MULTIPLE=NO,EDITBOX=YES' callback='_active = "#lst1" : ' />
</vbox>
</hbox>
</vbox>
</dialog>
</application>
Скрипт исполняемый при загрузке:
onstart.sh
pwd_now=$PWD
echo " !BEGIN "
echo " #curr-pos VALUE = "$pwd_now" : "
bash get_filelist.sh $pwd_now
echo " !END"
Пара вспомогательных скриптов:
change_dir.sh
echo " !BEGIN "
echo " #lst REMOVEITEM = ALL : "
bash ./get_filelist.sh $1
echo " !END "
get_filelist.sh
# взял из http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
SAVEIFS=$IFS
IFS=$(echo -en "nb")
cd $1
some_files=$(ls -a)
for x in $some_files;
do
if [ -d $x ];
then
echo " #lst APPENDITEM = "[$x]" : ";
else
echo " #lst APPENDITEM = "$x" : ";
fi;
done
IFS=$SAVEIFS
Использование papagaj совместно с zenity
<application name="#app">
<dialog name="#dlg" attributes='TITLE=""' other="index">
<vbox name="#main" attributes="EXPAND=YES,MARGIN=15x15">
<oneline name="#content" attributes="EXPAND=YES,READONLY=YES" />
<button name="#b1" attributes='TITLE="My name is ...",EXPAND=YES' callback='echo "!BEGIN #content VALUE = ""; zenity --entry ; echo "" : !END " :' />
</vbox>
</dialog>
</application>
git-репозиторий papagaj [2]
GNUStep.org [3]
Страница проекта IUP на сайте Tecgraf/PUC-Rio [1]
С радостью отвечу на вопросы.
p.s. если заметите опечатку или что-то подобное — сообщите в личку, буду очень благодарен.
Автор: rikardoac
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/gui-2/17854
Ссылки в тексте:
[1] www.tecgraf.puc-rio.br/iup/: http://www.tecgraf.puc-rio.br/iup/
[2] git-репозитория: https://github.com/rikardoac/papagaj
[3] GNUStep.org: http://gnustep.org
Нажмите здесь для печати.