Как стать автором
Обновить

Управление графическим интерфейсом с помощью echo и cat

Время на прочтение 5 мин
Количество просмотров 15K
Или независимые управляемые формы графического интерфейса.


Предыстория


Однажды я заинтересовался вопросом о том, каким образом можно было бы создавать пользовательский интерфейс программ и познакомился с Qt Designer — утилиты для разработки графических интерфейсов, которая входит в стандартный состав Qt.

Кроме всего прочего Qt Designer впечатлил своей способностью сохранять все формы в файлах формате XML, которые могут уже в последствие подключаться непосредственно в программу.

Мне это показалось очень удобным, так как я бы мог рисовать формочки с кнопочками не отходя от текстового редактора.
И тут мысль было уже не остановить: если весь интерфейс описан в стороннем файле, то программный код может вообще не содержать элементы работы непосредственно с формами, а только использовать какой-то программный интерфейс намертво изолирующий бизнес логику и представление.
Если так, то такая программа с пользовательским интерфейсом может быть написана не только на С++, о, священный GNU, она может быть вообще может без языка программирования.

Пока не отпустило, надо было срочно выдумывать как такое можно провернуть.

OpenForm


И так появился на свет концепт OpenForm — создание и управление формами с помощью командной строки.

Где основная идея базируется на том, что любое состояние интерфейса можно представить в формате XML.

Значит если я хочу отрисовать форму, мне нужно всего лишь какая-то утилита, которая возьмет на себя непосредственно задачу отображения форм, а вся логика будет на стороне.
Другими словами, требуется такой мини Qt Designer, который прочитает описание форм и виджетов, и это дело уже отобразит. А чтобы поменять состояние интерфейса, нужно просто как-то передать программе новое описание, например, в виде XML.

Вот и получается, что OpenForm сперва читает файл описания форм, отрисовывает их, и вешает на заданные события определенные триггеры, т. е. действия.
Если событие случается, например кто-то куда-то кликнул, то выполняется какое-то действие. А действие — это ничто иное как исполнение какой-то внешней программы, которая должна возвратить новое состояние пользовательского интерфейса.

А теперь по пунктам.
1. Qt Designer сохраняет дизайн в виде *.ui файлов формата XML.
2. OpenForm читает эти файлы и отображает.
3. На определенные события вешаются триггеры.
4. Если событие произошло, то выполняется сторонняя команда или программа, которая должна вернуть новое состояние интерфейса, т. е. вывести XML новой версии дизайна на стандартное устройство вывода.

Мануал


OpenForm работает с *.ui файлами, которые создаются в среде Qt Designer.
<ui version="4.0" >
  <class>MainWindow</class>
  <widget class="QMainWindow" name="MainWindow">
  <property name="windowTitle">
    <string>MainWindow</string>
  </property>
  </widget>
</ui>

И кроме дефолтных способностей просто отобразить форму, обладает еще и дополнительными плюшки.

Решетки комментируют

Для тех, кто фигачит, комментарии штука очень нужная, если не сказать необходимая.

<ui version="4.0" >
  <class>MainWindow</class>
  <widget class="QMainWindow" name="MainWindow">
  # This is a comment
  <property name="windowTitle">
    <string>MainWindow</string> # title of window
  # <string>MainWindow changed</string> # unneeded title
  </property>
  </widget>
</ui>

Разделяй и подключай

Полный набор для тех, кого не отпускает: знакомая директива для препроцессора #include.
Теперь можно интерфейс разделять на модули.

По традиции, если *.ui исполняемые файлы, то заголовочные должны быть как *.hui

file: geometry.hui:
<property name="geometry" >
  <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
</property>

file: widget.ui:
<ui version="4.0" >
  <class>MainWindow</class>
  <widget class="QMainWindow" name="MainWindow">
  <property name="windowTitle">
    <string>MainWindow</string>
  </property>
  #include "geometry.hui"
  </widget>
</ui>

События, сигналы и триггера

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

<triggers>
  <trigger object="btnSubmit">
    <event signal="clicked">cat update_widget.uui</event>	
  </trigger>
</triggers>

Это означает, что если пользователь нажмет на кнопку с именем btnSubmit, выполнится команда
cat update_widget.uui, выводящая содержимое файла с новым состоянием интерфейса.

<triggers>
  # Триггер для объекта с именем OBJECT_NAME
  <trigger object="OBJECT_NAME">
    # Список событий над текущим объектом.
    #   signal: стандартный Qt сигнал
    #   action: тип триггера
    #     "execute" - COMMAND должна быть выполнена, и возвратить новое состояние интерфейса в XML
    #     "return"  - COMMAND должна быть выведена на стандартное устройство вывода
    <event signal="SIGNAL" action="execute|return">COMMAND</event>
  </trigger>
</triggers>

action=return необходим, когда требуется спросить что-то у пользователя или узнать состояние интерфейса из внешней программы, которая вызывает OpenForm (смотри пример с диалоговым окном).

Доставай и обрабатывай

OpenForm обладает еще одним необычайной возможностью получения значения свойств виджетов. Т.е. если надо узнать, что там пользователь напечатал в поле ввода, то можно пользоваться конструкцией {WIDGET_NAME.PROPERTY_NAME}

Например
<event signal="clicked">php script.php —user-input={lineEdit.text}</event>

Где {lineEdit.text} — это означает, что из виджета lineEdit возвратить значение свойства с именем text. Если пользователь что-то введет, то это можно передать стороннему скрипту.

То же самое работает и для булевых свойств.
<event signal="clicked">php script.php --checked={checkBox.checked}</event>

Возвратит true или false.

Экранирование

Если надо использовать { в командах, то можно просто написать так \{

Точечное обновление

Если триггер возвратит дизайн в виде
<ui version="4.0">
...
</ui>

то форма заново перерисуется.
Но в случаях, когда не требуется заново пересоздавать форму, а нужно просто обновить состояние одного элемента, можно использовать :
<update>
...
</update>

И, наконец, управление графическим интерфейсом с помощью echo

Вот и подошли к вопросу, каким же таким способом можно управлять графическим интерфейсом с помощью утилиты echo.

Часто не хочется создавать новые файлы дизайна на каждое событие, поэтому можно сразу в триггерах указывать, что надо поменять и как.

<event signal="clicked">echo "[[update]]
[[widget name='label']]
[[property name='text']]
[[string]]{lineEdit.text}[[/string]]
[[/property]]
[[/widget]]
[[/update]]"
</event>

По событию нажатия, выполнится команда echo, где [[ заменится на <, а ]] — на >. Результатом будет копирование текста из поля ввода lineEdit в объект label.

Ну и зачем все это надо?


Ну, например, помощник при установки программного обеспечения:

Рис 1. Шаг лицензионного соглашения.


Рис 2. Пример анимации, прогресс бара.

Можно даже просто о чем-то спрашивать грозного пользователя с помощью графических форм.

Рис 3. Диалоговое окно с ожиданием действия от пользователя.

Работает следующим образом:
#!/bin/bash
an=`./dialog.ui`
if [ $an = 'yes' ]; then
    echo 'There should be real installation of something very cool'
else
    echo 'Ok, we will do nothing...'
fi

Ну или… а может, написать графический калькулятор на баше.

Рис 4. Графический калькулятор с обработкой выражений на баше.

Или даже так:

Рис 5. Графический калькулятор на PHP.
Теги:
Хабы:
+31
Комментарии 14
Комментарии Комментарии 14

Публикации

Истории

Работа

Программист C++
122 вакансии
QT разработчик
13 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн