Рубрики Обзоры

Древняя, новая, будущая

Опубликовал ITC.UA

Прежде чем мы перейдем непосредственно к Tk, уделим немного внимания "немодным
новостям" из "тикль-мира". За время, отделяющее эту часть статьи
от ее предшественницы, произошло несколько существенных изменений. Так, версия
системы Tcl/Tk на момент написания статьи достигла 8.4.7, и, если вы пользуетесь
более старой системой, желательно обновить ее (или с основного ресурса www.tcl.tk
— для пользователей Unix-совместимых ОС, или с сайта компании ActiveState).
Также обновилась версия замечательной интерактивной среды отладки Tcl/Tk-программ
RamDebugger, однозначно заслуживающей
установки на компьютер "тиклолюба".

Ну а теперь все готово к тому, чтобы запустить руки в еще один ящик с "тикль-инструментами"…


Две строки кода — завершенное
GUI-приложение. Характерная особенность Tcl/Tk — освобождение программиста
от массы деталей, неизбежных при программировании GUI на компилируемых языках
высокого уровня

Количество строк в "приложении"
не изменилось, но теперь можно использовать штатный для данной платформы
селектор цвета

Одна-единственная "кнопка"
Tk способна "рассказать о себе" практически все. Рефлексивность
Tk — одно из серьезных достоинств этой библиотеки виджетов

Никаких команд перерисовки
окон, ничего лишнего. Все те же две строки кода… Достаточно только изменить
значение ресурса Tk — и цвет кнопки меняется

Итак, Tk, он же — Tool Kit, по-русски — "набор инструментов". Лаконично,
но непонятно. По сути, Tk представляет собой комплект из мощной библиотеки виджетов
(сейчас мы уточним этот термин), имеющей самостоятельную ценность и допускающей
применение при разработке программ на компилируемых языках (например, C, C++,
Ada), и команд-инструментов Tcl, позволяющих создавать на этом языке интерактивные
программы с графическим пользовательским интерфейсом. Термин "виджет"
ничего таинственного не обозначает — это окно, обладающее собственным характерным
видом на экране и поведением. Хорошо знакомые по GUI разных операционных систем
и прикладных программ элементы интерфейса — кнопки, меню, диалоги, полосы прокрутки
и т. п. — все это виджеты, т. е. самостоятельные окна. Естественно, для создания
завершенного приложения только виджетов недостаточно — им необходимо придать
некоторый порядок. За упорядочение виджетов в Tk отвечают два фундаментальных
для практически всех GUI-подсистем механизма — иерархии виджетов и геометрического
менеджера. Первый механизм обеспечивает формирование композитных виджетов из виджетов-примитивов
(это исключительно важная функция, и для ее поддержки в Tk присутствует специальный
виджет — контейнер-фрейм, предназначенный исключительно для удобства образования
композитных элементов пользовательского интерфейса), второй отвечает за их взаимное
расположение на экране. Последнее важное на этом уровне ознакомления сведение
о Tk касается, естественно, архитектуры данной подсистемы — она относится к классу
"управляемых событиями". Это означает, что в исполнении Tcl-приложения
с Tk GUI инициатива принадлежит пользователю — именно он "генерирует"
события (изменяет координаты мыши, нажимает на кнопки и т. д.), на которые реагирует
программа. Tcl-инструменты Tk устроены таким образом, чтобы решать типовые задачи
можно было буквально за считаные минуты — так, для большинства событий в них
предусмотрены обработчики по умолчанию, а в случае надобности программист может
одной командой "связать" (bind) виджет, событие и "реагирующий" на него код. Более того, причинно-следственный механизм "связывания" (причина — событие, инициированное пользователем, следствие — соответствующие действия программы) допускает формирование собственных иерархий. Это так называемые "классы связываний" (в терминах Tk — bindtags), отвечающие за поведение целой иерархии виджетов. И наконец, последнее необходимое высокоуровневое архитектурное понятие — "фокус". В Tk в любой момент времени "в фокусе" системы находится один виджет — это означает, что сообщения о событиях (например, о нажатиях клавиш) направляются системой этому виджету, т. е. инициируют его реакции на события. Tk располагает двумя основными моделями "фокусировки" виджетов — инициируемой пользователем с помощью указателя мыши и при-нудительной, инициируемой программистом (что означает: программист может задавать находящийся в "фокусе" виджет, изменять "фокусировку" и даже перехватывать "фокусную информацию").

Теперь, без промежуточных отступлений, перейдем к конкретике связки Tcl—Tk. Вопросы использования библиотеки виджетов Tk из компилируемых программ оставляются читателю для самостоятельного ознакомления (естественно, в случае необходимости, так как связка Tcl—Tk обеспечивает все, что нужно для написания как "программ-однодневок", так и сложных программных комплексов с исключительно развитым GUI). Начнем с фундаментального — с Tcl-синтаксиса описания иерархий виджетов в Tk. Благо, правила эти хорошо вписываются в синтаксические модели описания иерархий во многих языках программирования: корневое окно вашей программы обозначается символом "точка", затем следует имя виджета, являющегося первым непосредственным "потомком" корневого окна, затем, опять через "точку", следует "вложенный" в него "подвиджет" и т. д. Например, если мы хотим написать типовую первую Tk-программу (конечно же, "Здравствуй, мир!") и в ней использовать только один виджет-кнопку (назовем ее hellow), то ее полное иерархическое имя будет «.hellow» (что означает — виджет с именем hellow, являющийся потомком корневого окна). Собственно, время для первой программы наступило — ее текст мы разберем по косточкам:

button .hellow -text itc_drupal_Say
Hello -command itc_drupal_puts stdout
"Здравствуй, мир!"
pack .hellow -padx 50 -pady 10

Первое слово button — это, естественно, команда-инструмент Tcl (маленькое уточнение относительно "первое слово" — пользователи Windows вполне могут ограничиться таким синтаксисом, для работающих в Unix-подобных ОС в начале примера надо добавить "бэнг-строку" с указанием пути к интерпретатору wish). Эта команда (button) более чем детально "разжевана" в сопроводительной документации — так, в chm-файле, поставляемом в комплекте ActiveState Tcl, ее описание находится в подразделе Tk Manual — Widgets. Команда принимает в качестве параметров полное иерархическое имя создаваемого ею виджета "кнопка" и строку дополнительных опций. Каждая опция описывается подстрокой, начинающейся с символа "тире", за которым без пробела следует имя опции. В приведенном выше примере полное иерархическое имя «.hellow» указывает, что мы фактически создаем главное окно нашей "программы" и помещаем в него виджет-кнопку с именем
".hellow". Опция -text itc_drupal_Say Hello не является специфической для виджетов-кнопок (button) — это одна из многих опций, которыми "оснащено" большинство виджетов Tk (последнее замечание не случайно — когда вы будете самостоятельно осваивать виджеты Tk, не забывайте, что в документации такие общие опции вынесены в отдельный раздел). Она специфицирует строку текста, отображаемого внутри виджета (к слову, во многих виджетах расположением отображаемого текста можно дополнительно управлять — например, его выравниванием). В данном случае строка «Say Hello» будет написана в отображаемой кнопке. Вторая опция специфична для виджета-кнопки и позволяет удобно ассоциировать определяемые программистом действия с одним событием — отпусканием кнопки мыши # 1 (в ПК — это по умолчанию левая кнопка) после нажатия ею на виджет-кнопку. Здесь ассоциированное действие настолько примитивно, что для него не требуется написания процедуры или функции — оно помещается в одну строчку и делает именно то, что ожидается от такой канонической программы, — выводит в стандартный поток вывода фразу "Здравствуй, мир!". Итак, это была пока всего одна команда Tcl — button (синтаксические принципы языка и механизм формирования команд описаны в предыдущих статьях данного цикла). Вторая строка — новая команда, ее имя — pack. Эта команда — Tcl-интерфейс к одному из встроенных геометрических менеджеров Tk, который размещает виджеты "потомков" относительно границ их "предков". В данном случае виджет с именем hellow располагается в корневом окне программы, а специфика размещения указывается опциями (правила задания опций мы уже оговорили): -padx 50 специфицирует, что измеряемый по горизонтали экрана отступ кнопки от левой и правой границ корневого окна программы должен быть 50 пикселов, -pady 10 аналогично устанавливает отступы от верхней и нижней границы корневого окна в 10 пикселов. Вот, собственно, и все. Остальное, например реакции на стандартные GUI-события (касающиеся, в первую очередь, поведения корневого окна программы), мы даже и не вспоминаем — Tk здесь полностью берет все на себя, а наша программа готова к исполнению. И, естественно, она выполняется именно так, как и ожидалось.

А теперь давайте немного модифицируем нашу первую программу, изменив в ней только реакцию на событие:

button .hellow -text
itc_drupal_Say Hello -command
itc_drupal_clipboard clear ;
clipboard append
"Здравствуй, мир!"
pack .hellow -padx 50 -pady 10

В данном примере вместо идеологически правильного в неинтерактивных скриптах приема — вывода в стандартный поток — мы применили не менее идеологически правильный для интерактивных программ прием вывода… в буфер копирования. В нем используется команда clipboard (полное описание в документации — в разделе Tk Manual — Inter-Client Communication), причем синтаксис ее настолько прост, что ни в каких дополнительных пояснениях не нуждается — сначала буфер копирования очищается, затем к его содержимому добавляется строка "Здравствуй, мир!". Можно попробовать самостоятельно "ликвидировать" в этом примере очистку буфера копирования и, понажимав несколько раз на кнопку Say Hello, убедиться в том, что команда clipboard с опцией append именно добавляет что-то к содержимому буфера, а не полностью переписывает его. Этот пример не случаен — он показывает один из путей создания простейших, но очень полезных Tcl/Tk-программ. А именно, GUI-утилиток буквально из нескольких десятков строк кода, позволяющих удобно интерактивно задавать… опции для хороших, но перегруженных излишествами программ командной строки (в конце концов, удерживать в голове три-четыре десятка опций командной строки нечасто используемой программы нерационально, а каждый раз "подглядывать" в руководство — неудобно).

Если бы в перечне виджетов Tk были только кнопки, естественно, этот инструментальный набор не -заслуживал бы нашего внимания. Кроме кнопок, Tk располагает уже упомянутыми ранее фреймами (команда frame), мощным примитивом, создающим окно со структурированной графикой (команда canvas), очень функциональными так называемыми радиокнопками (checkbutton, кнопки с фиксацией состояния — в отличие от обычных радиокнопки не только "помнят" свое состояние, но и характеризуются двусторонним ассоциированием со значениями указанных глобальных переменных Tcl — как изменение пользователем состояния радиокнопки автоматически приводит к изменению значения переменной, так и изменение ее значения автоматически отображается изменением состояния кнопки), списками выбора, меню, полосами прокрутки, ключевым компонентом построения редакторов текста (команда text), а также несколькими мощными специфическими примитивами GUI. Кроме базовых виджетов, в состав Tk входят также готовые общеупотребительные "мегавиджеты" — например, стандартный диалог селектора цвета tk_chooseColor (попробуйте в предыдущем примере изменить реакцию кнопки hellow, например так: -command itc_drupal_tk_choose-Color -title "Выберите цвет"). Кроме этого, развивавшаяся десятилетиями и продолжающая развиваться система обросла множеством мощных расширений — библиотек "мегавиджетов" (это принятый в мире тикль термин, обозначающий композитные повторно используемые конструкции для создания GUI-приложений), так что зачастую, прежде чем пытаться написать какой-то высокоуровневый элемент GUI, стоит поискать подходящую, уже отлаженную его реализацию. Перечень заслуживающих внимания библиотек начинают уже ставшие де-факто стандартными разработки BLT, BWidgets, Tix. Что же касается геометрических менеджеров, то уже обсужденная команда pack — далеко не единственная в системе. Кроме нее, можно использовать геометрический менеджер, обеспечивающий "абсолютное расположение на резиновом листе" (команда place), — размещенные ею виджеты-потомки могут автоматически изменять размеры при изменении размеров окна предка. Если требуемое расположение характеризуется регулярностью, можно воспользоваться сеточным геометрическим менеджером (его еще называют "табличным", команда grid). И наконец, можно динамически изменять иерархию виджетов (в терминах GUI-систем часто оперируют понятием "стековый порядок", stacking order) командами lower и rise.

Теперь, когда вы вкратце знакомы с двумя основными принципами построения GUI — образованием иерархий из виджетов и размещением виджетов (пусть это знакомство проводилось на очень простом примере — фундаментальные принципы от этого не меняются, и более сложные конструкции будут отличаться разве что объемом практически типового кода), можно перейти к более сложным вещам. Начнем мы с… объектно-ориентированных свойств виджетов Tk. Несмотря на то что Tcl/Tk реализована на далеком от объектной ориентации языке C, Tk можно назвать образцовой объектно-ориентированной подсистемой. Каждый виджет в ней — независимо от его типа, инкапсулирует массу информации, отве-чающей за его вид и поведение. И для доступа к этой информации ис-пользуется весьма специфическая конструкция вида "полное_иерархическое_имя_виджета config необязательные_опции". Давайте немного модифицируем наш пример и приведем его к такому виду:

button .hellow -text
itc_drupal_Параметры виджета button
-command itc_drupal_clipboard clear ;
clipboard append [.hellow config]
pack .hellow -padx 50 -pady 10

В общем, модификации не так уж и страшны — мы изменили надпись на кнопке (теперь она — "Параметры виджета button") и реакцию на отпускание пользователем нажатой кнопки GUI hellow. Результат мы заносим в предварительно очищенный буфер копирования, а вот этим самым результатом является перечень (точнее — список) всех параметров виджета button. Это один из вариантов применения указанной выше конструкции "…config …", при котором мы можем наблюдать так называемое свойство рефлективности Tk (термин "рефлективность", применяемый к программным и, в общем, к вычислительным системам, означает способность системы хранить полную информацию о самой себе и использовать эту информацию для -изменения поведения). Теперь произведем еще одну модификацию, выбрав из полученного перечня, например, параметр -fg (или -foreground):

button .hellow -text
itc_drupal_Параметры виджета button
-command itc_drupal_clipboard clear ;
clipboard append
[.hellow config -fg]
pack .hellow -padx 50 -pady 10

Здесь изменения минимальны — мы только использовали уточняющий параметр, о котором хотим получить исчерпывающую информацию. Запускаем эту программу, нажимаем на кнопку "Параметры виджета button" и вставляем из буфера копирования результат: -foreground foreground Foreground SystemButtonText SystemButtonText. В этом случае виджет .hellow рассказал нам о себе следующее: у него была запрошена информация о параметре "цвет выводимых объектов" (-foreground); данному параметру соответствует ресурс системы с именем foreground, относящийся к классу Foreground, для которого значение по умолчанию равно значению переменной SystemButtonText и текущее значение также равно SystemButtonText (пользователи Unix-подобных систем в качестве значений могут получить в результате выполнения этой программы принятые в X Window описатели цвета в формате "#шестнадцатеричное_число"). Понятие ресурсов — еще одно фундаментальное в Tk. Оно обозначает иерархическую информационную базу параметров виджетов, допускающую как модификацию одного параметра выбранного виджета, так и одновременную — всех виджетов иерархии или множества параметров одного класса для виджетов разных иерархий. Мы же ограничимся последней модификацией нашего примера для демонстрации возможности интерактивной модификации одного из ресурсов Tk — например цвета фона кнопки hellow:

button .hellow -text
itc_drupal_Параметры виджета button
-command itc_drupal_ .hellow config
-bg [tk_chooseColor
-title "Цвет фона"]
pack .hellow -padx 50
-pady 10

Здесь мы использовали только уже известные по этому краткому обзору конструкции, с единственной оговоркой — диалог tk_chooseColor возвращает в вызывающую Tcl-программу выбранный цвет. Теперь, нажимая на кнопку GUI "Параметры виджета button", мы сначала активируем диалог выбора цвета (почему сначала — в предыдущих статьях цикла), затем результат выбора пользователем с помощью конструкции типа ".hellow config -bg цвет" присваивается значению ресурса background виджета .hellow и… кнопка автоматически меняет цвет своего фона — не нужны ее "перерисовки вручную" и прочие присущие GUI-программам "извращения".