Консалтинг и автоматизация в области управления
эффективностью банковского бизнеса

Журнал ВРМ World

Anygui: ожидается появление универсального графического интерфейса

В стадии разработки находится один очень интересный проект. Этот проект anygui (буквально, любой GUI) задуман как обертка интерфейса API для большого числа графических библиотек. По его завершении, у программистов Python появится уникальная возможность: будет достаточно вызвать общую функцию anygui, например, создания окна - и "наиболее подходящая" библиотека выполнит эту задачу. На платформе Windows можно будет использовать Win32 API (или wxWindows); на MacOs - "родные" вызовы; на BeOS - Bethon; на Linux - TKinter или GTK; для экранов телнета - Ncurses -все зависит от того, что установлено и доступно на конкретной машине. В данной статье рассказывается, что можно сделать с помощью модуля anygui уже сегодня, и том, какие цели преследует этот проект.

Что такое Python?

Python - свободно доступный, интерпретируемый язык программирования высокого уровня, разработанный Гвидо ван Россумом (Guido van Rossum). Он объединяет ясный синтаксис с мощной (но необязательно) объектно-ориентированной семантикой. Python может быть установлен на любой платформе и обеспечивает прекрасную совместимость при переходе с одной платформы на другую.

Введение: напиши один раз и используй, где угодно.

Несколько лет назад, когда язык программирования Java только заявил о себе, казалось, что еще немного и можно будет "написать один раз, и использовать, где угодно". Вначале пользовательский интерфейс на Java в основном воспринимался как набор апплетов, встроенных в Web-браузеры. Однако прошло совсем немного, и их затмили самостоятельные приложения AWT. В свою на очередь, на смену AWT пришел Swing. Потом Swing превратился в Beans (разработка на основе Swing, но с дополнительными требованиями). При этом классы Swing видоизменялись, добавлялись и удалялись из различных версий Java.

Популярная шутка о Java гласит: "Один раз напиши и везде отлаживай". Действительно, написав приложение на Java, нельзя быть особо уверенным, что оно будет работать на любой машине, если, конечно, вы не ставите цель заставить каждого пользователя настроить версии и конфигурации Java исключительно под ваше приложение. Будет ли приложение работать, зависит не только от версии Java, но и от конкретного поставщика виртуальной машины Java (JVM) и платформы, на которой она работает.

Во многих отношениях, скриптовые языки, такие как Python, Perl и TCL, обеспечивают лучшую совместимость чем Java. Например, можете быть уверены, что большинство скриптов, написанных на Python, будут корректно и единообразно работать на всех пользовательских машинах (возможно, придется выполнить минимальные требования по отношению к версии языка - но реализовать их гораздо проще и они гарантируют большую надежность, в отличие от Java). Конечно, у Java есть сильные стороны помимо одной переносимости: статическая типизация (для тех, кому это необходимо), огромные библиотеки классов, великолепная документация, аккуратный выбор дизайна. Но эта статья вовсе не о сравнении языков.

Возвращаясь к вопросу переносимости между платформами, единственное, в чем скрипт на Python существенно менее переносим, чем приложение Java, так это в реализации пользовательского интерфейса. На уровне командной строки нет никаких проблем. Но как только вам захочется создать сложный интерфейс пользователя- в особенности это касается графических интерфейсов - вы обнаружите, что Python практически ничего не предлагает. Java, несмотря на все недостатки, обычно позволяет воспользоваться Swing и AWT на любой платформе, где установлена JVM. Python, напротив, не имеет ни одной "стандартной" библиотеки GUI.

В связи с этим многие разработчики уже выразили свое желание иметь стандартный Python GUI. Библиотека Tkinter приблизилась к решению этой задачи - имеются надежные версии под Windows и Unix/XWindow System и приемлемая версия под MacOs. Но для этого необходимо установить TCL и TK, и, кроме того, "мелкие" платформы, такие как BeOS или OS/2, оказываются за бортом. Мнения разнятся - многие утверждают, что лучше выбрать другие библиотеки/привязки (есть из чего выбирать - см. Ресурсы). Но любая из них поддерживает только ряд желаемых платформ, и что более важно, ни одна не была признана всеми (и, следовательно, ни одна не стандартизована по отношению к различным дистрибутивам Python). Поэтому разработчику остается писать пользовательский интерфейс и уповать на то, что реальные пользователи смогут с ним работать.

Новый подход к проблеме

Философия Java состоит в том, чтобы создать стандартный набор возможностей, который должна реализовать каждая JVM. При этом существование Java GUI не обсуждается. Python мог бы использовать другой подход. Вместо того, чтобы требовать от каждой машины подчиняться определенному API, просто определите, что может конкретная машина, и отталкивайтесь от этого. Тогда API может оказаться всего лишь оберткой того, что делает базовая платформа.

Библиотека anygui выполняет как раз то, что можно от нее ожидать, исходя из философии Python. Позаимствовав имя и подход у модуля anydbm, который находит "наиболее подходящий" обработчик (backend) базы данных во время выполнения, библиотека anygui определяет наиболее подходящий обработчик GUI на системе, на которой выполняется anygui-приложение. anygui делает акцент на предоставлении практичного множества элементов интерфейса, которое будет работать с каждым обработчиком; хотя отдельные обработчики, возможно, предоставляют более развитые возможности, библиотека anygui выбирает то, что является общим для них всех.

В настоящий момент, anygui пока является alpha-проектом. Что же касается набора целевых обработчиков, anygui уже может очень многое. Но поскольку цель - создание (почти) универсальной обертки, просто располагать таким рабочим набором явно недостаточно. Если цель будет достигнута, имеет смысл включать anygui как стандартный пакет в каждый дистрибутив Python (в значительной степени подобно тому как включены anydbm и xml.sax, несмотря на системно-зависимые обработчики). Смысл всего этого - предоставить его каждому пользователю. Стоит заметить, что сам anygui написан на чистом Python и не требует ничего на C/C++ или других низкоуровневых языках (разумеется, чтобы быть полезным, anygui должен найти библиотеку поддержки GUI).

Платформы и картинки

Работая над статьей, я взглянул на большинство рабочих обработчиков. Есть несколько пока нереализованных, или частично функциональных. Среди рабочих - Tkinter, Java Swing, win32all, PyGTK и wxPython. "Родной" BeOS (с Bethon) почти не работает, но ситуация может улучшиться в любой день. "Родные" PyQT и MacOS - в состоянии планирования, у них имеются руководители разработки, однако ни один из них еще не реализован; конечно, это также может измениться со временем. Кроме того, велись разговоры о прямом обработчике xlib, но никто не решился вести этот проект.

Все вышеперечисленные графические библиотеки работают или будут работать довольно одинаково. Должен признаться, что мои знания о большинстве обработчиков довольно поверхностны, но, судя по всему, API anygui в значительной степени схож с Tkinter. Методика следующая: создать группу элементов окна с обратными вызовами, а затем войти в главный цикл обработки сообщений.

Возможно, в будущем появятся и другие обработчики, которые смогут коренным образом изменить ситуацию с "нормальными" библиотеками GUI. В некотором смысле, это крайне интересные или, по крайней мере, новаторские идеи. Работа над одним из них должна вестись вашим покорным слугой, однако, но я не подошел серьезно к разработке первоначальной версии. Надеюсь, положение изменится к моменту, когда вы читаете эту статью. Ниша моего маленького обработчика - библиотека Ncurses. Если она присутствует, он позволяет выполнять anygui-приложение даже на терминале в текстовом режиме, например, в SSH/telnet-сессии (или просто на обыкновенных UNIX-системах без XWindows).

В духе поддежки ncurses, руководитель проекта anygui, Магнус Лай Хетленд (Magnus Lie Hetland) предложил простой строчно-ориентированный "аварийный" интерфейс, возможно, использующий библиотеку readlines. В этом случае, меню будут сведены к приглашениям к вводу команды, после чего последует выбор опций, а затем - обратная связь и результат, и т. д. Для этого гипотетического anygui потребуется, чтобы работали только STDIN и STDOUT, что является занимательным минимумом для программы, которая в других обстоятельствах работала бы - без изменений - в сложном, графическом, событийно-управляемом, WIMP-интерфейсе (WIMP - windows, окна; icons, иконки; mouse pointer, указатель мыши). Разумеется, это пока всего лишь идея.

Еще одна оригинальная идея столь же интересна. (Почти) каждый использует Web-браузер, даже если это 'links' или 'lynx'. Стандартный модуль Python webbrowser, подобно anygui и его родственникам, позволяет запустить "наиболее подходящий" Web-браузер. Если этот браузер связывается с чем-нибудь вроде LOCALHOST-сервера, все основные элементы управления интерфейса, какие только можно пожелать (кнопки, поля ввода, текстовые окна, графика и пр.), становятся доступными в этом браузере. Этот обработчик также находится на стадии планирования.

Как говорится, лучше один раз увидеть, чем сто раз услышать. А поскольку редактор, и не без оснований, вряд ли захочет, чтобы эта статья превратилась в длиннющий рассказ, давайте лучше просто посмотрим на несколько скриншотов (screenshot). В качестве иллюстрации воспользуемся небольшим приложением-игрушкой, которое играет с кнопками - активизирует неактивные кнопки (ее исходник приведен ниже). Кроме того, добавлена пара текстовых меток. Остальные примеры управления окном приведены в каталоге 'test' дистрибутива anygui.

Первое, на что стоит посмотреть - это, как нам кажется, "умалчиваемый умалчиваемый" обработчик (Tkinter). В целом, эта версия выглядит и работает так, как следует. Однако, при вызове функции win.destroy() происходит забавная вещь - окно исчезает не сразу (соответственно, приложения не закрывается), а превращается в окно-призрак, которое исчезает, как только вы начинаете уделять ему слишком много внимания (например, его перемещать). Как я уже говорил, это альфа-версия. Приложение было запущено под Win98:

{Кнопочное приложение под Tkinter (на Win98):

http://gnosis.cx/publish/programming/lyric_tk.gif}

Работая под Windows, можно также использовать "родные" вызовы Windows с помощью модуля win32all. Если вы используете дистрибутив ActivePython от ActiveState, это можно сделать по умолчанию, в противном случае потребуется получить этот модуль отдельно (также от ActiveState). В общем, эта привязка вела себя лучше всех, что я видел, хотя это верно исключительно для той версии, что я тестировал. Метки располагаются несколько отлично от Tkinter, что свидетельствует о том, что внешний вид приложений для разных обработчиков может отличаться:

{Кнопочное приложение под Win32 (на Win98):

http://gnosis.cx/publish/programming/lyric_msw.gif}

Далее я решил сменить платформу. Запустив Jython под OS/2 Warp 4, вот, что я получил: по непонятной причине спереди к текстовым меткам прилипли неуместные '<html>'. Но помимо этой шероховатости, можно только восхищаться, что идентичный код может быть запущен на абсолютно другой платформе:


{Кнопочное приложение под Java Swing (на OS/2 Warp 4):

http://gnosis.cx/publish/programming/lyric_java.gif}

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

{Кнопочное приложение под GTK (на Enlightenment):

http://gnosis.cx/publish/programming/lyric_gtk_e.gif}

Затем WindowMaker:

{Кнопочное приложение под GTK (на WindowMaker):

http://gnosis.cx/publish/programming/lyric_gtk_wm.gif}

Содержание окна практически не изменилось для каждого оконного менеджера. Размер и представление меток несколько отличается от того, которое мы видели, работая под Windows (добавив несколько пикселей, можно предотвратить усечение слов). Я не смог установить wxPython ни на одну из моих систем, но результат должен быть идентичным.

Версия для BeOS пока еще очень сырая. Даже мое приложение отказывается работать. Однако, базовый класс Windows функционирует нормально:

{Тестовое оконное приложение на BeOS r5:

http://gnosis.cx/publish/programming/windows_beos.gif}

Собственно код

Код, который запускался на тестирующих системах, довольно прост. Первая половина моей программы - просто переключатель, позволяющий вручную выбрать из командной строки, какой обработчик использовать. В реальном коде вы вряд ли будете делать подобное; однако для предварительного тестирования проделанная мною работа весьма полезна. Заметьте, однако, что все тесты, чьи скриншоты приведены в статье, запускались без каких-либо опций командной строки - обработчик выбирался автоматически. Ниже приведен код:

# 'button.py' text application for [anygui] #
import sys
if len(sys.argv)==1 or /
   sys.argv[1].upper()=='DEFAULT':
    from anygui import /
                 Window,/
                 Button,/
                 Application,/
                 Label
elif sys.argv[1].upper()=='TK':
    from anygui.backends.tkgui import /
                           Window,/
                           Button,/
                           Application,/
                           Label
elif sys.argv[1].upper()=='MSW':
    from anygui.backends.tkgui import /
                           Window,/
                           Button,/
                           Application,/
                           Label
elif sys.argv[1].upper()=='BEOS':
    from anygui.backends.beosgui import /
                             Window,/
                             Button,/
                             Application,/
                             Label
elif sys.argv[1].upper()=='GTK':
    from anygui.backends.gtkgui import /
                             Window,/
                             Button,/
                             Application,/
                             Label
elif sys.argv[1].upper()=='JAVA':
    from anygui.backends.javagui import /
                             Window,/
                             Button,/
                             Application,/
                             Label
elif sys.argv[1].upper()=='WX':
    from anygui.backends.wxgui import /
                             Window,/
                             Button,/
                             Application,/
                             Label

def say_hello():
    global bye
    print "Hello, world!"
    bye._set_enabled(1)
app = Application()
win = Window(width=150, height=150,
             title="Beatles Lyric")
win.add(Label(x=10, y=10, width=140,
              text = "I don't know
              why you say..."))
bye = Button(x=30, y=40, width=70,
             height=30,
             text="Goodbye",
             action=lambda: win.destroy(), /
                            enabled=0)
win.add(bye)
win.add(Label(x=10, y=70, width=120,
              height=32,
              text = "When I say..."))
hi = Button(x=30, y=100, width=70, height=30,
            text="Hello", action=say_hello)
win.add(hi)
win.show()
app.run()

Скелет программы состоит из четырех шагов: (1) создать приложения; (2) создать одно или несколько окон; (3) добавить элементы оконного интерфейса; (4) вызвать цикл обработки сообщений app.run(). Все характеристики элементов интерфейса передаются как поименованные параметры.

Заключение

Даже альфа-версия anygui позволяет написать приложение, которое "получает некие данные, обрабатывает их и представляет результат". Мы не успели обсудить другие интересные вопросы: например, более тонкую обработку событий, модели представления, и т.д. Кроме того, anygui пока еще официально не документирован. В качестве утешения могу сказать, что anygui вызвал у меня больше восторга, чем какая-либо другая библиотека Python за долгое время. Просто трудно представить, как удобно будет создавать сложный пользовательский интерфейс для любой платформы, где установлен Python, не меняя при этом ни строчки кода для поддержки специфики конкретной платформы.

Ресурсы

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

http://anygui.sourceforge.net/

Камерон Лайрд (Cameron Laird) приготовил довольно обширный список GUI-привязок для Python. Он привел краткий комментарий о текущем состоянии каждой из них и указал ссылки на соответствующие страницы этих проектов:

Обработчик tinter Деймонда Уолкера (Damond Walker) и улучшенный ctinter Лорента Пелека (Laurent Pelecq) можно найти по следующей ссылки. Модуль curses сам по себе является стандартным модулем Python (для этого необходимо иметь рабочую библиотеку Ncurses):

http://home.iximd.com/~dwalker/tinter.htm

Об авторе

(Фотография автора:http://gnosis.cx/cgi-bin/img_dqm.cgi)

Дэйвид Мертц увлекся синкретизмом; поэтому единственное, что может вернуть его к жизни, помимо хорошей интерактивной библиотеки - это реализация его мечты о возвращении перфокарт и систем пакетной обработки данных. Дэйвид доступен по адресу: mertz@gnosis.cx, а жизнь его описана на http://gnosis.cx/publish/. Присылайте свои замечания и предложения касательно этой, прошлых или будущих статей.