О едином языке разметки GUI и векторной графики LuaGML

Скребцов Владимир
Октябрь, 2006

Красота есть критерий истины

1. Введение

Создание современных информационных систем, как правило, предполагает создание пользовательского интерфейса (UI), под которым будем понимать как собственно графический интерфейс пользователя (GUI), так и векторную 2D-графику для графиков, диаграмм различного вида и прочих визуализаций данных. Командная строка и полноэкранный алфавитно-цифровой интерфейс также имеют право на существование, но их можно воспринимать как частные случаи UI. Возможность функционирования UI не только в корпоративной среде, но и в среде Internet сейчас воспринимается весьма желательной, а подчас и просто необходимой. Даже в корпоративной среде требуется кроссплатформенность реализации UI, позволяющая использовать не только коммерческое программное обеспечение (например Windows), но и свободно распространяемое (например Linux). Осознание этой реальности приводит к пониманию концепции единого языка разметки как средства описания UI.

До эпохи WWW GUI воспринимался как статичный неизменяемый объект. В то же время такие понятия, как выходной отчет и генератор отчетов, были известны и широко использовались. Эпоха WWW позволила взглянуть на GUI, как на "обычный" выходной отчет, который может быть динамически сгенерирован соответствующим генератором на стороне сервера и передан клиенту, получен и проинтерпретирован на стороне клиента в виде действующей "картинки". Для иллюстрации этого достаточно вспомнить формы HTML с их управляющими элементами. Векторная графика также может рассматриваться как "обычный" выходной отчет. Однако выразительных возможностей HTML для описания элементов UI катастрофически не хватает.

Это привело к возникновению целого ряда языков разметки, ориентированных на описание элементов UI. Обзор и сравнительный анализ языков разметки GUI приведены в статье [3]. Язык разметки SVG (Scalable Vector Graphics) ориентирован исключительно на описание векторной 2D-графики. Как правило, все эти языки созданы на базе стандарта XML. Первое субъективное впечатление автора от существующих языков разметки GUI далеко неоднозначно, что скорее всего определяется их неоправданной сложностью. По крайней мере, желание использовать какой-либо из этих языков для решения практических задач у автора не возникло. Более глубокое исследование достоинств и недостатков существующих языков разметки GUI целью настоящей статьи не является.

Цель настоящей статьи состоит в изложении принципов единого языка разметки GUI и векторной графики, сочетающего в себе функциональность, простоту и логичность, и подтвержденную практической реализацией его прототипа LuaGML. По сути, статья представляет собой неформальное введение в такой язык с акцентом на наиболее существенные его компоненты. Поэтому некоторые подробности сознательно опущены или перенесены в приложения, чтобы "суть не утонула в мелочах".

2. Принципы единого языка разметки

UI по своей природе имеет структурную и процедурную составляющие. Структура определяет иерархию элементов UI. Процедурный код определяет их взаимодействие, динамизм поведения, реакции на события. Другими словами, структура задает скелет, а процедура представляет мышцы и нервы. Взаимодействие этих двух начал и лежит в основе функционирования UI. Следовательно, язык разметки, описывающий UI, также должен иметь декларативную (структурную) и процедурную составляющие.

Структура UI представляется в виде дерева. Для описания дерева используем синтаксис языка GML (Graph Modelling Language), информация по которому содержится в статье [1]. Не путать с языком разметки GML (Generalized Markup Language), который является прародителем семейства языков разметки SGML (Standart Generalized Markup Language), HTML (Hypertext Markup Language) и собственно XML (Extensible Markup Language). Каждая вершина дерева в нотации языка GML выражается в виде элемента. Элемент состоит из имени тега и значения. Значение может быть числом, символьной строкой или списком элементов. Иерархия выражается именно через списки элементов. Под именем элемента будем понимать имя его тега.

Пример 2.1. Hello World!

	a [
	    b 123.45
	    c "Hello World!"
	]

Пример 2.2. Генеалогическое дерево

	man [ name "Сергей"
	    man [ name "Павел"
		man [ name "Иван" ]
		woman [ name "Татьяна"
		    man [ name "Владимир" ]
		    woman [ name "Надежда" ]
		]
	    ]
	    woman [ name "Мария" ]
	]

2.1. Простота, логичность, лаконичность

Язык разметки должен, нет, просто обязан быть максимально простым, логичным и лаконичным. В конечном счете, именно простоту, структурированность и читабельность ожидает разработчик UI от языка разметки. В противном случае, он мог бы воспользоваться кроссплатформенными графическими библиотеками типа Qt(С++) или Swing(Java). Правда, интеграция с Internet-средой в случае Qt(C++) также может стать, и скорее всего станет, проблемой.

Необоснованная сложность выражения простых понятий UI присуща всем языкам разметки GUI, основанным на стандарте XML. XML на уровне своих спецификаций отвергает принцип простоты и лаконичности в угоду универсальности. Стандарт стандартом, но и здравый смысл тоже иметь надо.

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

Пример такого подхода демонстрирует небольшая фирма из Калифорнии (США) Amulet Technologies, которая запатентовала и использует в своих разработках упрощенный язык разметки типа HTML, но с расширенным набором тегов для описания GUI. Более подробная информация об этом содержится в статье [2].

2.2. Вычисляемые значения элементов

Способ объединения декларативной семантики с процедурной, или, другими словами, способ включения процедурного кода в структуру является краеугольным камнем языка разметки. Предлагаемое решение состоит в том, что значение элемента в общем случае является вычисляемым. То есть, в качестве значения элемента можно использовать фрагмент процедурного кода, результат вычисления которого и использовать, собственно, как значение этого элемента. Результат вычисления процедурного кода может быть числом, символьной строкой или списком элементов. Следовательно, структура может динамически изменяться процедурным кодом.

Выбор процедурного языка пал на скриптовый язык Lua. Lua сочетает в себе простоту синтаксиса с удивительной гибкостью, расширяемостью и эффективностью выполнения. Более подробную информацию по языку Lua можно получить на сайте Lua и в "Руководстве по языку Lua" для программистов на русском языке.

Два примера, приведенные ниже, представляют собой одно и то же дерево:

Пример 2.2.1. Без использования Lua

	a [
	    b 123.45
	    c "Hello World!"
	    d [
		e [
		    f 321
		    g "Привет Мир!"
		]
	    ]
	]

Пример 2.2.2. С использованием Lua

	a [
	    b { return( 123.45) }
	    c { return( "Hello World!") }
	    d { return( "[ e [ f 321 g 'Привет Мир!']]") }
	]

Таким образом, декларативная семантика, выражаемая языком GML, органично дополняется процедурной семантикой, выражаемой языком Lua, образуя единый язык разметки, который назовем LuaGML по именам его составляющих.

3. Реализация UI

Выразительные возможности языка разметки LuaGML применим к описанию векторной 2D-графики и GUI. Для этого, конечно, необходимо определить набор тегов, что будет выполнено по мере необходимости.

Все элементы UI располагаются в прямоугольном поле, с которым связана 2-х мерная система координат с началом в левом-верхнем углу, осью абcцисс слева-направо, осью ординат сверху-вниз и единицей измерения пиксель. Поле размером 800x600 пикселей с фоном белого цвета определено ниже.

Пример 3.1. Поле 800x600

	gml [
	    w 800 h 600
	    color "white"
	]

3.1. Векторная графика

Реализация векторной графики на LuaGML по духу очень близка к языку разметки SVG (Scalable Vector Graphics), но имеет и целый ряд серьезных отличий. Однако, сравнение LuaGML c SVG не является темой настоящей статьи.

Пример 3.1.1 Красный прямоугольник

Прямоугольник 150x150 красного цвета с черной границей на белом поле 400x400

	gml [ w 400 h 400 color "white"
	    rect [ x 100 y 50 w 150 h 150
		stroke "black" fill "red"
	    ]
	]

Пример 3.1.2. Два прямоугольника

Второй прямоугольник 200x150 без окраски c синей границей на фоне первого 150x150 (красного) повернут на 45 градусов по часовой стрелке относительно своего левого-верхнего угла, расположенного в точке 150x100.

	gml [ w 400 h 400 color "white"
	    rect [ x 100 y 50 w 150 h 150
		stroke "black" fill "red"
	    ]
	    g [ transform "translate( 150, 100) rotate( 45)"
		rect [ w 200 h 150
		    fill "none" stroke "blue"
		]
	    ]
	]
    где:
    g - "графический контекст"
    • transform - графическое преобразование
    • translate - перенос начала координат
    • rotate - поворот относительно начала координат

Пример 3.1.3. "Продвинутая" векторная графика

"Продвинутая" векторная графика представлена тремя картинками (исходный код вынесен в Приложения 1. Тигр, 2. Вечность, 3. Шары). Две последние картинки используют градиентную закраску.

Тигр Вечность Шары

3.2. Графический интерфейс пользователя GUI

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

Пример 3.2.1. Галерея векторной графики

Создадим небольшую галерею векторных картинок, приведенных выше в примере 3.1.3. Выбор этих картинок будет выполняться через меню. Для этого создадим линейку меню в верхней части поля 600x600, на которой разместим меню "Галерея". Внутри этого меню создадим вложенное меню "Хиты сезона", содержащее три пункта со ссылками на соответствующие векторные картинки: "Тигр", "Вечность" и "Шары". Полный вариант галереи находится на сайте luagml.ucoz.ru. Снимок с экрана GUI полного варианта приведен ниже.

	gml [ w 600 h 600 color "white"
	    menubar [
		start {
		    HTTP = "http://luagml.ucoz.ru/"
		    GALLERY = HTTP.."demo/gallery/"
		    GML = HTTP.."gml/"
		    ICONS = GML.."icons/"
		}
		menu [ str "Галерея" icon { return( ICONS.."penguin.png")}
		    menu [ str "Хиты сезона"
			item [ str "Тигр"
			    src { return( GALLERY.."misc/tiger.gml")}
			]
			item [ str "Вечность"
			    src { return( GALLERY.."misc/pendule.gml")}
			]
			item [ str "Шары"
			    src { return( GALLERY.."misc/quad.gml")}
			]
	    	    ]
		]
	    ]
	]
    где:
    menubar - "линейка меню"
    • start - предварительные установки
    • menu - "меню"
      • str - текст названия меню
      • icon - иконка меню
      • item - "пункт меню"
        • str - текст пункта меню
        • src - источник данных при активизации пункта меню

Пример 3.2.2. Картотека банков

Картотека банков представлена в виде прокручиваемого списка банков, содержащего порядковый номер, БИК и наименование банка. Под этим списком расположено поле адреса текущего банка.

Реализация картотеки банков содержится в трех файлах (исходный код вынесен в Приложение 4. Картотека банков):
  • main.gml - основной экран
  • onl_ban.gml - собственно картотека банков
  • bank.gml - данные для картотеки банков
  • Пример 3.2.3. Медицинская информационная система

    Язык разметки LuaGML был использован в реализации доступа к ресурсам Медицинской Информационной Системы (МИС) через Internet/Intranet. Демонстрационный вариант МИС находится на сайте luagml.ucoz.ru. Снимок с экрана картотеки пациентов и журнала электронных записей пациентов МИС приведен ниже.

    Картотека пациентов Журнал электронных записей пациентов

    4. Реализация LuaGML

    Реализация языка разметки LuaGML выполнена на Java (JDK 5.0) в виде интерпретатора. Интерпретатор LuaGML может быть использован как в качестве апплета для встраивания в HTML-страницы, так и в качестве независимого приложения.

    Интерпретатор LuaGML построен по архитектуре микроядра. Базовые функции сконцентрированы в основном модуле интерпретатора, который представляет собой ядро интерпретатора и загружается первым. Дополнительные функции вынесены в дополнительные модули, которые динамически подгружаются ядром по мере необходимости в них. Такое построение позволяет сохранять размер ядра интерпретатора относительно небольшим и стабильным, что весьма немаловажно при его удаленной загрузке в качестве апплета.

    Поскольку на данный момент времени отсутствует реализация языка Lua непосредственно на Java, но имеется его реализация на C в виде библиотеки под конкретную платформу (Linux, Windows), то интерпретатор LuaGML при необходимости динамически подгружает эту библиотеку, формируя среду своего выполнения.

    Интерпретатор LuaGML состоит из следующих компонентов:

    1. Ядро интерпретатора
    2. Дополнительные модули
    3. Библиотеки реализации языка Lua

    5. Заключение

    Предложен единый язык разметки GUI и векторной графики LuaGML, сочетающий в себе функциональность с простотой и логичностью синтаксиса. Ключевое отличие LuaGML от других языков разметки состоит в способе соединения декларативной и процедурной семантики. А именно, значением элемента может являться результат вычисления фрагмента процедурного кода, встроенного в структуру.

    Реализация LuaGML выполнена на Java (JDK 5.0) в виде интерпретатора и может использоваться как в виде апплета, так и в виде независимого приложения. Приведены различной степени сложности примеры реализации GUI и векторной графики на LuaGML.

    6. Приложения

    1. Тигр - векторная графика
    2. Вечность - векторная графика
    3. Шары - векторная графика
    4. Картотека банков - графический интерфейс пользователя

    7. Библиография

    1. "GML: A portable Graph File Format", Michael Himsolt, Universitet Passau, Germany
    2. "'Железный' GUI", Зубинский А., Компьютерное обозрение, ноябрь 2003г.
    3. "Языки описания пользовательских интерфейсов", Шейко Д., апрель 2005г.