GtkAda User's Guide

Version 1.2.12

Document revision level 1.102.2.1

Date: 2001/04/20 07:14:33

E. Briot, J. Brobecker, A. Charlet


Содержание


Copyright (C) 1998-2000, Emmanuel Briot, Joel Brobecker, Arnaud Charlet

Copyright (C) 2000-2001, ACT-Europe

This document may be copied, in whole or in part, in any form or by any means, as is or with alterations, provided that (1) alterations are clearly marked as alterations and (2) this copyright notice is included unmodified in any copy.

Перевод Copyright (C) 2003 А.Гавва.

Коммерческое распространение перевода, без разрешения автора перевода, запрещено.

Введение: Что такое GtkAda ?

GtkAda - это высокоуровневая переносимая графическая библиотека, которая основана на графической библиотеке gtk+, одной из официальных библиотек GNU. Она позволяет легко создавать интерфейсы пользователя, которые бутут переносимы для множества платформ, включая основные платформы, которые используют сервер X11 и платформу Win32.

Хотя библиотека GtkAda основана библиотеке языка C, для облегчения использования и создания пользовательских интерфейсов она использует некоторые развитые средства Ada95, которыми являюся тэговые типы, настраиваемые пакеты, ссылочные типы для подпрограмм, исключения и т.д... В целях повышения эффективности, GtkAda не использует контролируемых типов, используя для управление памятью другие способы.

В результате, эта библиотека предусматривает безопасные, легкие в использовании и расширяемые средства.

В сравнении с библиотекой C, GtkAda предусматривает безопасность типов (в частности, для подпрограмм обратного вызовов (callbacks)), и средства объектно-ориентированного программирования. В противоположность общеизвестной информации GtkAda требует менее частого использования приведения типов чем в случае использования библиотеки C. Произвводительность средств библиотеки GtkAda подобна производительности библиотеки C благодаря использованию средств встроенной подстановки (inline) кода подпрограмм.

Библиотека GtkAda обладает полной интеграцией со средством построения графических интерфейсов пользователя Glade (1). Это максимально упрощает процесс разработки интетфейсов пользователя, поскольку создание окон и диалогов приложения с последующей генерацией кода Ады можно выполнить с помощью мышки.

Кроме того, GtkAda предоставляет полную поддержку OpenGL, и она поставляется обеспечением очень тесной связи с библиотекой OpenGL (Mesa). Это позволяет создавать графические приложения которые отображают 3D графику, и выполняют ее отображение в окне GtkAda, также как и в случае любой 2D графики. Это руководство не документирует OpenGL. За более подробной информацией понадобится обратиться к любой книге по OpenGL или к спецификациям, которые поставляются с вашей библиотекой OpenGL.

Следующие сайты Internet всегда содержат последние публично доступные пакеты GtkAda, gtk+ и Glade:

Для нумерации версий библиотеки GtkAda используется следующая схема: старший номер версии соответствует старшему номеру версии используемой библиотеки gtk+ (например, 1.2 или 1.3). Таким образом, стабильная версия GtkAda имеет четный старший номер версии, а разрабатываемая версия (потенциально менее стабильная, хотя мы пытаемся убедиться в том, что в этой версии также отсутствуют грубые ошибки) имеет нечетный старший номер версии. Младший номер версии зависит от номера реализации GtkAda.

Эта документация поставляется с библиотекой GtkAda версии 1.2.12. В настоящий момент, реализованы все виджеты, которые присутствуют в gtk+ 1.2, и тестовая программа, которая присутствует в реализации gtk, теперь полностью переписана на Аде (ее можно обнаружить в каталоге testgtk/ дистрибутива GtkAda).

Эта библиотека была оттестирована на следующих системах:

с помощью последней версии компилятора GNAT, который разрабатывается и поддерживается компанией Ada Core Technologies (http://www.gnat.com).

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

Эта версия GtkAda совместима с версиями библиотеки gtk+ от 1.2.2 до 1.2.8. Эта реализация более не совместима с более старыми версиями gtk+ (вплоть до 1.0).

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

Этот документ не описывает все виджеты, которые доступны в GtkAda, и не пытается объяснить назначение всех подпрограмм. Эта информация содержится в справочном руководстве GtkAda Reference Manual и в соответствующих исходных текстах файлов спецификаций GtkAda, которые имеют расширение .ads.

Данный документ не содержит полного примера. Вместо этого, примеры предусматриваются в каталогах testgtk/ и examples/ дистрибутива GtkAda, и эти каталоги предоставляют более новые и более полные версии примеров программ. Примеры хорошо комментированы и содержат много информации, которая может оказаться интересной.

Если вы заинтересованы в получении поддержки, при использовании GtkAda (включая: приоритетное исправление ошибок и быстрое получение новых реализаций, получение помощи в использовании библиотеки и разработке пользовательского интерфейса, получение консультаций...), вы можете обратиться в ACT Europe () или в Ada Core Technologies ().

Основы GtkAda

Эта глава описывает то, как начать новое приложение GtkAda. Она рассматривает базовые средства библиотеки и показывает как откомпилировать и запустить ваше приложение.

Кроме того, эта глава предоставляет краткий обзор расширяемой иерархии виджетов, которые доступны в GtkAda.

Как построить и установить GtkAda

Эта секция описывает то, как построить и установить GtkAda на вашей системе. Эти сведения Unix-ориентированые, поскольку для Windows-систем GtkAda поставляется в прекомпилированном двоичном формате, обеспечивая установку всех зависимых пакетов, включая библиотеки gtk+ и Glade. Если вы пользователь Windows-системы, то вы можете пропустить чтение этой секции.

На системе Unix, первоначально необходимо установить библиотеки glib и gtk+. Полные пакеты этих библиотек можно загрузить с WEB-сайта gtk+ (http://www.gtk.org), после чего выполнить их компиляцию и установку. При компиляции glib, следует убедиться, что активирована поддержка многопоточности (многонитиевости). Как правило, эта поддержка активируется по умолчанию, но она также может быть активирована принудительно, путем использования опций командной строки --enable-threads и --with-threads.

Следует соответствующим образом изменить содержимое переменной окружения PATH, чтобы для GtkAda был автоматически доступен скрипт gtk-config, который указывает где установлена библиотека gtk+ и другие необходимые библиотеки. После установки GtkAda этот скрипт вам больше не понадобится, за исключением случаев, когда необходимо разрабатывать часть приложения с помощью языка C.

Если на вашей системе не установлены библиотеки OpenGL, то поддержка OpenGL не будет активирована в GtkAda. В качестве примера, вы можете использовать реализацию Mesa, которая является свободно распространяемой.

Опционально, вы также можете установить средство построения интерфейсов Glade. Для этого следует взять совместимый пакет с WEB-сайта Glade, откомпилировать его и установить. В настоящее время, официальная версия осуществляет поддержку разработки интерфейсов с помощью Ады (этого как минимум достаточно для вызова собственных подпрограмм GtkAda). Таким образом официальная версия Glade не требует дополнительных патчей.

В заключение вы можете загрузить с WEB-сайта последнюю версию GtkAda. Затем, разархивируйте дистрибутивный пакет исходных текстов GtkAda, и выполните следующие команды:

    
    
    > ./configure
    > make install
    

Как и при обычном использовании скрипта configure, вы можете определить куда вы хотите установить библиотеки GtkAda путем использования опции --prefix.

Если на вашей системе установлены какие-либо библиотеки OpenGL, то вы можете убедиться в том, что скрипт configure правильно их обнаруживает путем использования опции --with-GL-prefix. Скрипт configure, однако, способен выполнить автоматическое детектирование библиотек OpenGL (прим. но это не всегда работает).

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

Организация пакета GtkAda

В дополнение к комплекту полных исходных текстов, дистрибутив GtkAda содержит множество примеров, которые хорошо комментированы. Если вы еще не ознакомились с этими примерами, мы настоятельно рекомендуем вам просмотреть их и попытаться понять как они работают, поскольку в них содержатся некоторые примеры кода, которые могут быть интересны и полезны в ваших собственных приложениях.

Как откомпилировать приложение GtkAda

Эта секция объясняет как вы можете компилировать ваши собственные приложения. Такая процедура - системно зависима, и, таким образом, материал секции разделен на две логически самостоятельные части.

Системы Unix

На системах Unix, при построении GtkAda осуществляется автоматическое создание скрипта gtkada-config. Этот скрипт копируется в подкаталог bin/, который расположен в каталоге установки GtkAda.

Самый простой и рекомендуемый способ построения приложений GtkAda заключается в использовании утилиты gnatmake, которая поставляется с GNAT и осуществляет автоматическую обработку всех зависимостей. Скрипт gtkada-config используется для указания места установки библиотек GtkAda и gtk+ следующим образом:

    
    
    > gnatmake <main-file> `gtkada-config`
    

Следует обратить внимание на использование обратных кавычек вокруг gtkada-config, что вынуждает командный интерпретатор shell осуществить выполнение скрипта и поместить его вывод в командную строку.

Однако, при построении сложных приложений, простое использование утилиты gnatmake может оказаться не достаточным. В подобных случаях пользователи часто создают файлы управления сборкой проекта Makefile. Скрипт gtkada-config остается полезным и в этих случаях, поскольку вы можете вызвать его выполнение из вашего файла Makefile (используя показанный выше синтаксис с обратными кавычками) для создания переменных подобных FLAGS и LIBS.

Скрипт gtkada-config воспринимает следующие опции командной строки (выбор опций определяется совместимостью с опциями для gtk-config):

Системы Windows

Сборка приложения под Windows осуществляется несколько проще. В этом случае не требуется использовать скрипт gtkada-config . Однако, еще одной приятной новостью является то, что нет нужды указывать какие библиотеки использовать и где они расположены.

Единственное, что вам необходимо указать в командной строке запуска утилиты gnatmake - это то, где расположены исходные тексты файлов спецификаций GtkAda:

    
    
    > gnatmake <main-file> -Ic:\GtkAda\lib
    

Если библиотека GtkAda была установлена в c:\GtkAda.

В текущий момент не предусматривается использование статических библиотек. Таким образом, вы не можете создать статические версии ваших приложений и должны использовать динамические библиотеки.

Архитектура инструментальной библиотеки

Инструментальная библиотека gtk+ с самого начала разрабатывалась с учетом требований перенрсимости. Фактически gtk+ состоит из трех библиотек: gtk, gdk и glib.

Библиотека glib не является графической библиотекой. Она хорошо оптимизирована, является платформенно независимой и обеспечивает поддержку списков, хэш-таблиц, нитей и т.д. Поскольку в настоящий момент большая часть ее содержимого непосредственно написано Аде (или содержится в иерархии пакетов GNAT.* дистрибутива компилятора GNAT), GtkAda не содержит связывания с этой библиотекой, за исключением нескольких пакетов. Эти пакеты расположены в иерархии пакетов Glib.* дистрибутива GtkAda.

Библиотека gdk является платформенно зависимой частью gtk+. Ее внутренняя реализация для систем win32 и X11 полностью отличается, хотя интерфейс, естественно, одинаковый. Эта библиотека предусматривает набор функций для отрисовки линий, прямоугольников и пикселей на экране, осуществления манипуляций с цветами и т.д... GtkAda содержит полноценный эквивалент этой библиотеки, распологающийся в иерархии пакетов Gdk.*.

Библиотека gtk является библиотекой верхнего уровня. Она является платформенно независимой, и выполняет все отрисовки посредством вызовов gdk. Эта библиотека является местом определения высокоуровневых виджетов. Она также включает поддержку для обратных вызовов (callback). GtkAda содержит эквивалент библиотеки gtk, который располагается в иерархии пакетов Gtk.*. Библиотека выполнена в виде полностью объектно-ориентированной иерархии виджетов (см. Иерархия виджетов).

Поскольку ваше приложение только обращается к GtkAda, то оно является полностью переносимым, и может быть, без каких-либо изменений, перекомпилировано на другой платформе.

    
    
    +---------------------------------------------+
    |             Ваше  Приложение                |
    +---------------------------------------------+
    |                 GtkAda                      |
    |               +-----------------------------+
    |               |            GTK              |
    |       +-------+-----------------------------+
    |       |           GDK                       |
    +-------+--------------+--+-------------------+
    |          GLIB        |  | X-Window / Win32  |
    +----------------------+  +-------------------+
    

Хотя по сравнению с первыми версиями GtkAda содержимое пакетов претерпело множество изменений, в настоящий момент спецификации стабилизировались. Мы стараемся также обеспечить обратную совместимость настолько, насколько это возможно.

Поскольку инструментальная библиотека GtkAda основана на gtk+, мы попытались максимально сохранить близость к gtk+ при использовании высокоуровневых средств языка Ada95. Таким образом, можно относительно легко перенести внешние примеры из C в Аду.

Мы попытались адаптировать согласованную схему наименований для Ада-идентификаторов следующим образом:

ВНИМАНИЕ: все настраиваемые пакеты осуществляют динамическое размещение памяти для внутренних структур, и выполняют вызовы внутренних функций. Gtk самостоятельно освобождает занятую динамическую память путем вызова некоторых функций Ады. Следовательно настраиваемые пакеты должны конкретизироваться на уровне библиотеки, а не внутри подпрограмм. Таким образом, функции остаются определенными когда gtk нуждается в освобождении памяти.

ВНИМАНИЕ: Перед выполнением вызова любой подпрограммы из библиотеки GtkAda, необходимо вызвать Gtk.Main.Init. В большинстве случаев эта процедура вызывается из главной процедуры приложения, причем, в такой ситуации, обращения к GtkAda не могут быть использованы в процессе элаборации приложения.

Иерархия виджетов

Все виджеты GtkAda реализованы как тэговые типы, и они имеют одного общего предка Gtk.Object.Gtk_Object. Общим предком всех видимых объектов является Gtk.Widget.Gtk_Widget.

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

Хотя инструментальная библиотека gtk+ написана на языке C, ее дизайн полностью объектно-ориентированный, и, таким образом, GtkAda имеет такую же самую структуру. Для преобразования имен C в имена Ады, приняты следующие правила: какой-либо виджет Gtk_XXX описывается в пакете Ады Gtk.XXX, который находится в файле gtk-xxx.ads. Это соответствует соглашениям понаименованию файлов, которые приняты в системе компилятора GNAT. Например, виджет Gtk_Text определяется в пакете Gtk.Text, который находится в файле gtk-text.ads.

Примечательно, что значительная часть документации GtkAda непосрественно располагается в исходных текстах файлов спецификаций.

Важное значение имеет знакомство с иерархией виджетов, поскольку это облегчает понимание того, как построить и организовать окна вашего приложения. Большинство виджетов демонстрируются в каталоге testgtk/ дистрибутива GtkAda.

hierarchy

            Иерархия виджетов GtkAda

Иерархическая композиция окна

В GtkAda, также как и в Motif, интерфейсы строятся слоями. Например, обычное диалоговое окно основывается на Gtk_Window, которое содержит Gtk_Box, который, в свою очередь, разделен на два Gtk_Box и содержит Gtk_Separator, и т.д...

boxes

Хотя такой способ выглядит несколько сложнее чем установка абсолютных позиций для потомков, он позволяет упростить автоматическую обработку при изменении размеров окон. Для каждого контейнера, который создает какой-либо слой, известно как он должен выглядеть при изменении своих размеров и как он должен перемещать своих потомков. Таким образом, практически все это обрабатывается автоматически, и вам не потребуется что-либо делать для поддержки изменения размеров.

Если вам реально понадобится установка потомков в специфических позициях, то обратитесь к примеру виджета Gtk_Fixed и его демонстрации в testgtk/. Однако, в действительности вам не следует использовать этот контейнер, поскольку в таком случае вам придется всю обработку выполнять вручную.

Все контейнеры демонстрируются в каталоге testgtk/ дистривутива GtkAda. Это должно помочь вам понять все параметры ассоциируемые с контейнерами. Понимание принципов работы и использования этих контейнеров имеет важное значение, поскольку выбор подходящего контейнера облегчает общее построение интерфейса.

Если вы посмотрите на иерархию виджетов (см. Иерархия виджетов), то вы увидите, что Gtk_Window является наследником Gtk_Bin, и, таким образом, Gtk_Window может иметь только одного потомка. В большинстве случаев потомком Gtk_Window будет Gtk_Box, который способен иметь любое число потомков.

Некоторые виджеты GtkAda, сами построены с помощью использования этой стратегии, начиная с элементарного виджета кнопки Gtk_Button и заканчивая более развитым Gtk_File_Selection.

Например, по умолчанию кнопка Gtk_Button может содержать метку Gtk_Label, которая отображает текст кнопки (подобно "OK" или "Cancel").

Однако, достаточно легко поместить на кнопку вместо текста, изображение какой-либо картинки. При создании кнопки не следует определять какую-либо метку. Таким образом, не будет осуществлено добавление какого-либо потомка, и вы можете добавить к кнопке своего собственного потомка. Обратитесь к примеру testgtk/create_pixmap.adb, который демонстрирует как это можно выполнить.

Обработка сигналов

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

Сигнал является сообщением, которое объект желает передать. Сигналы идентифицируются по их именам, и каждый сигнал ассоциирован с конкретными событиями, которые происходят в период "жизни" виджета. Например, когда пользователь щелкает кнопками мышки на кнопке Gtk_Button, происходит эмиссия этой кнопкой сигнала "clicked". Множество примеров сигналов можно обнаружить в справочном руководстве GtkAda.

Существует возможность заставить приложение реагировать на подобные события, путем подключения (connecting) к сигналу специальной процедуры, которую называют обработчиком (handler) или процедурой обратного вызова (callback). Такой обработчик будет вызываться каждый раз, в случае эмиссии сигнала, предоставляя приложению возможность выполнить необходимую обработку. К одному и тому же сигналу, одного и того же объекта можно подключить более одного обработчика. В таком случае, обработчики будут вызываться в порядке их подключения.

В зависимости от типа, виджет не определяет (как Gtk_Label) или определяет один и более различных сигналов. Естественно, что сигналы, как и все остальное, наследуются от предков. Таким образом, каждый виджет отвечает на большое количество сигналов.

Самый простой способ определить эмиссию каких сигналов может осуществить виджет - это обратиться к справочному руководству GtkAda. Будем надеяться, что там будет документирован каждый виджет. Этот документ объясняет когда происходит эмиссия сигнала и какую основную форму должен иметь обработчик (хотя вы всегда можете по желанию добавить пользовательские данные User_Data).

Кроме того, вы можете также непосредственно обратиться в заголовочные файлы C, которые поставляются с инструментальной библиотекой gtk+. Каждый виджет описан в своем собственном C-файле, и содержит две C-структуры, ассоциируемые с ним. Одна из этих структур - это структура "класса", которая содержит набор указателей на функции. Каждая из этих функций имеет такое же имя, как и сигнал.

Например, рассмотрим следующий фрагмент из заголовочного файла gtkbutton.h:

    
    
    struct _GtkButtonClass
    {
      GtkBinClass        parent_class;
      
      void (* pressed)  (GtkButton *button);
      void (* released) (GtkButton *button);
      void (* clicked)  (GtkButton *button);
      void (* enter)    (GtkButton *button);
      void (* leave)    (GtkButton *button);
    };
    

Это подразумевает, что виджет Gtk_Button переопределяет пять новых сигналов, соответственно называемых "pressed", "release", ...

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

Таким образом, профиль обработчика должен иметь подобный вид:

    
    
    procedure Pressed_Handler
      (Button    : access Gtk_Button_Record'Class;
       User_Data : ...);
    

Подпрограмма обратного вызова не обязана использовать все аргументы. Допускается использование процедуры, которая "отбрасывает" несколько последних аргументов. Аргумент User_Data является специальным случаем, поскольку во время подключения сигнала вы сами определяете его необходимость. Если вы решили использовать User_Data, то ваша процедура обратного вызова должна его принимать, и это будет проверено компилятором.

Любое количество аргументов может быть отброшено, пока эти аргументы являются последними в списке аргументов, и вы сохраняете первый аргумент. Например, сигнал "button_press_event" может быть нормальным образом подключен к обработчику, который имеет любой показанный ниже профиль:

    
    
    --  с User_Data
    procedure Handler
      (Widget    : access Gtk_Widget_Record'Class;
       Event     : Gdk.Event.Gdk_Event;
       User_Data : ...);
    procedure Handler
      (Widget    : access Gtk_Widget_Record'Class;
       User_Data : ...);
    
    --  без User_Data
    procedure Handler
      (Widget : access Gtk_Widget_Record'Class;
       Event  : Gdk.Event.Gdk_Event);
    procedure Handler (Widget : access Gtk_Widget_Record'Class);
    

Следует учитывать, что добавление новых аргументов не возможно, поскольку для них не будут предусмотрены какие-либо значения. Следует также обратить внимание на то, что при подключении обработчика GtkAda не всегда проверяет, что ваш обработчик использует больше аргументов чем ожидается (кроме случаев использования пакета Gtk.Marshallers, рассматриваемого далее).

Вся работа по обработке сигналов осуществляется с помощью сервисов пакета Gtk.Handlers. Код этого пакета полностью документирован, поэтому, для получения подробной информации об этом пакете, следует обратиться или к справочному руководству GtkAda, или непосредственно к исходному тексту спецификации этого пакета.

Существует откомментированный пример использования этого пакета. Этот пример находится в файле create_file_selection.adb (внутри подкаталога testgtk/). Представте себе, что приложение открыло селектор файлов, чтобы позволить пользователю выбрать какой-либо файл. Для этого GtkAda предусматривает высокоуровневый виджет Gtk_File_Selection, который может быть использован в этом случае:

    
    
    declare
       Window : Gtk_File_Selection;
    begin
       Gtk.File_Selection.Gtk_New (Window, Title => "Select a file");
    end;
    

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

    
    
    procedure OK (Files : access Gtk_File_Selection_Record'Class) is
    begin
       Ada.Text_IO.Put_Line ("Selected " & Get_Filename (Files));
       --  печатает имя выбранного файла
       Destroy (Files);
       --  разрушает диалог селектора файлов
    end Ok;
    

Теперь нам необходимо подключить только что описанную процедуру обратного вызова к объекту, который описан в первой части. Как указывается в справочном руководстве, пакет Gtk.Handlers определяет четыре типа настраиваемых модулей, в зависимости от количества ожидаемых аргументов и от того, что подпрограмма обратного вызова возвращает какое-либо значение или нет. Примечательно, что вы не можете произвольно выбирать возвращаемое значение, поскольку это зависит от сигнала.

В нашем случае, поскольку подпрограмма обратного вызова не возвращает какое-либо значение и не имеет параметра User_Data (то есть, нам не нужно передавать какие-нибудь дополнительные значения, определяемые во время подключения обработчика), то пожходящим настраиваемым пакетом будет Gtk.Handlers.Callback.

Следовательно, мы осуществляем конкретизацию этого пакета. Еще раз напомним, что настраиваемые пакеты GtkAda должны присутствовать в памяти постоянно, поскольку они заботятся об освобождении памяти, которую они использовали. Эти пакеты должны быть конкретизированы на уровне библиотеки, но не внутри какой-либо подпрограммы.

    
    
    package Files_Cb is new 
      Handlers.Callback (Gtk_File_Selection_Record);
    

Вы можете увидеть в Gtk.Handlers, что этот пакет содержит набор подпрограмм Connect, которые могут быть использованы для установки перманентной связи между виджетом и обработчиком. Пакет также содержит набор других подпрограмм, которые можно использовать для ручной эмиссии сигналов, хотя в большинстве случаев эмиссия сигналов осуществляется внутренними средствами GtkAda. Здесь мы опускаем обсуждение возможностей подпрограмм Emit_By_Name.

Общая форма обработчика, которая используется в Gtk.Handlers, предполагает, что некоторые обработчики, которые принимают два или три аргумента, получают в качестве аргументов: виджет, на котором произошел прием сигнала, массив всех дополнительных аргументов, переданных внутри GtkAda, и, возможно, некоторые данные пользователя, указываемые при подключении обработчика.

Такая форма обработчика является наиболее общей и она заключает в себе все возможные частные случаи. Однако, такая форма также ожидает, что пользователь может самостоятельно распаковать требуемые значения из массива аргументов. Подобное решение не всегда является самым удобным. Поэтому GtkAda предусматривает еще один пакет, который относится к обработке сигналов. Этим пакетом является Gtk.Marshallers.

Пакет Gtk.Marshallers предусматривает набор функций, которые могут быть использованы как подпрограммы обратного вызова непосредственно для GtkAda, и они будут вызывать ваши собственные обработчики после распаковки требуемых значений из массива аргументов. Хотя это выглядит несколько сложно, фактически, такой механизм - достаточно мощный, и он подобен тому, что реально выполняется инструментальной библиотекой gtk+ в C. Этот механизм не требует дополнительной производительности по сравнению с программами написанными на C, поскольку мы выполняем те же базовые действия, но только на уровне Ады.

Набор функций To_Marshaller содержится в каждом настраиваемом пакете Gtk.Handlers. Такие функции принимают единственный аргумент - имя функции, которую вам необходимо вызвать, и они возвращают обработчик, который может быть непосредственно использован в Connect.

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

Следует обратить внимание на две вещи: мы используем To_Marshaller, поскольку наш обработчик не принимает массив аргументов как параметр, и мы используем специальную процедуру Object_Connect. Это подразумевает, что параметром нашей подпрограммы обратного вызова (Files) будет Slot_Object, указанный в Object_Connect вместо самой кнопки.

    
    
    Files_Cb.Object_Connect
      (Get_Ok_Button (Window),  --  Объект подключаемый к обработчику
       "clicked",               --  Имя сигнала
       Files_Cb.To_Marshaller (Ok'Access),  --  Обработчик сигнала
       Slot_Object => Window);
    

Старт приложения GtkAda

Вам необходимо выполнить некоторую инициализацию перед стартом приложения GtkAda:

    
    
    --  предопределенные модули библиотеки
    with Gtk.Rc;
    with Gtk.Main;
    with Gtk.Enums;
    with Gtk.Window;
    ...
    --  мои модули
    with Callbacks;
    ...
    procedure Application is
       procedure Create_Window is ...
    
    begin
       --  Установить данные "locale", определяемые национальными особенностями,
       --  (например форматы даты и времени)
       Gtk.Main.Set_Locale;
    
       --  Инициализировать GtkAda
       Gtk.Main.Init;
    
       --  Загрузить ресурсы. Следует заметить, что это опционально.
       Gtk.Rc.Parse ("application.rc");
    
       --  Создать главное окно приложения
       Create_Window;
    
       --  Цикл обработки сигналов
       Gtk.Main.Main;
    end Application;
    

Процедура создания главного окна приложения, Create_Window, может иметь следующий вид:

    
    
       procedure Create_Window is
          Main_Window : Gtk.Window.Gtk_Window;
          ...
       begin
          Gtk.Window.Gtk_New
            (Window   => Main_Window,
             The_Type => Gtk.Enums.Window_Toplevel);
    
          --  Из Gtk.Widget:
          Gtk.Window.Set_Title (Window => Main_Window, Title  => "Editor");
    
          --  Построение окна и подключение различных обработчиков
    
          ...
          Gtk.Window.Show_All (Main_Window);
       end Create_Window;
    

Файлы ресурсов

Файлы ресурсов позволяют параметризировать аспекты отображения виджетов приложения GtkAda без необходимости перекомпиляции.

Файл ресурсов должен быть загружен (Gtk.Rc.Parse) перед установкой соответствующего окна.

В файле ресурсов можно указать визуальные характеристики виджетов (цвета, шрифты, ...). Под управлением системы X, команда xfontsel позволяет вам легко выбрать шрифт. Кроме того, простым средством для выбора шрифтов является виджет FontSelection.

Ниже демонстрируется пример файла ресурсов:

    
    
    # application.rc
    #
    # файл ресурсов для "Application"
    
    # Стиль отображения кнопок "button"
    style "button"
    {
    # Цвета фона (BackGround Colors)
    #                  Red  Green  Blue
      bg[PRELIGHT] = { 0.0,  0.75, 0.0 } # Зеленый (Green), когда указатель
                                         # мышки расположен над кнопкой
      bg[ACTIVE]   = { 0.75, 0.0,  0.0 } # Красный (Red), при щелчке
    # Цвета переднего плана (ForeGround Colors)
    #                  Red  Green  Blue
      fg[PRELIGHT] = { 1.0,  1.0,  1.0 } # Белый (White), когда указатель
                                         # мышки расположен над кнопкой
      fg[ACTIVE]   = { 1.0,  1.0,  1.0 } # Белый (White), при щелчке
    }
    
    # Все кнопки будут иметь стиль "button"
    widget_class "*GtkButton*" style "button"
    
    # Стиль отображения текста "text"
    style "text"
    {
      font = "-adobe-courier-medium-r-normal-*-15-*-*-*-*-*-*-*"
      text[NORMAL] = { 0.0, 0.0, 0.0 } # черный (black)
      fg[NORMAL]   = { 0.0, 0.0, 0.0 } # черный (black)
      base[NORMAL] = { 1.0, 1.0, 1.0 } # белый (white) : цвет фона
    }
    
    # Все виджеты Gtk_Text будут иметь стиль "text"
    widget_class "*GtkText" style "text"
    

Управление памятью

В большинстве случаев GtkAda самостоятельно заботится об управлении динамической памятью. Здесь представлен краткий обзор того как эта работа организована, а при необходимости получения более подробных сведений вам потребуется обратиться к исходным текстам. Gtk+ (библиотека C) осуществляет самостоятельное управление памятью посредством счетчиков ссылок. Это подразумевает, что любой виджет разрушается когда в приложении на него нигде нет ссылок.

В GtkAda, пользовательские данные, "user_data", ассоциируются с каждым объектом, который распределяется с помощью процедуры Gtk_New. Также осуществляется ассоциирование подпрограммы обратного вызова (callback) для разрушения объекта, "destroy" (см. Gtk.Initialize_User_Data). Таким образом, каждый раз когда C-объект разрушается, осуществляется разрушение эквивалентной Ада-структуры (см. Gtk.Free_User_Data). Существуют также виджеты, которые имеют потомков. В этом случае, каждый контейнер содержит ссылку на своих потомков, чьи счетчики ссылок, таким образом, отличаются от 0 (в общем случае равны 1). При разрушении контейнера, счеткики ссылок потомков уменьшаются на единицу, и они разрушаются по очереди, если это необходимо. Таким образом, деаллокация иерархии виджетов осуществляется автоматически.

Многозадачность и GtkAda

Библиотека Glib может использоваться в безопасном многозадачном режиме с помощью выполнения вызова Gdk.Thread.Init перед любым другим обращением к сервисам библиотеки Glib. В этом режиме, в случае необходимости, Glib осуществляет автоматическую блокировку всех внутренних структур данных. Это не подразумевает, что две задачи могут одновременно обращаться, например, к одной и той же хэш-таблице, однако они могут одновременно обращаться к двум разным хэш-таблицам. Если две различные задачи нуждаются в доступе к одной и той же хэш-таблице, то ответственность за организацию блокировок возлагается на приложение (например, путем использования защищенных объектов).

Когда Glib инициализирована для работы в безопасном многозадачном режиме, GtkAda знает о наличии многозадачности. Существует единственная глобальная блокировка, которую вы можете использовать с помощью Gdk.Threads.Enter, перед тем как выполнять какой-либо вызов Gdk/Gtk, и которую вы затем должны освободить с помощью Gdk.Threads.Leave.

Следовательно, процедура основного цикла Gtk.Main.Main должна быть вызвана после установки блокировки с помощью Gdk.Threads.Enter (см. пример далее), гарантируя, что все функции, которые выполняются в задаче стартовавшей главный цикл, не нуждаются в защите от самих себя.

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

Примечательно, что Gdk.Threads подразумевает, что вы используете среду исполнения задач во время выполнения программы, которая отображает задачи Ады на родные нити управления (native threads). Например, при использовании GNAT под управлением Linux, вам понадобится изменить среду исполнения задач во время выполнения программы, принятую по умолчанию.

Минимальная программа, использующая многозадачность с GtkAda, имеет следующий вид:

    
    
    with Gdk.Threads;
    with Gtk.Main;
    with Gtk.Enums; use Gtk.Enums;
    with Gtk.Window; use Gtk.Window;
    
    procedure GtkAda_With_Tasks is
       Window : Gtk_Window;
    begin
       Gdk.Threads.Init;
       Gtk.Main.Init;
    
       Gtk_New (Window, Window_Toplevel);
       Show (Window);
    
       Gdk.Threads.Enter;
       Gtk.Main.Main;
       Gdk.Threads.Leave;
    end GtkAda_With_Tasks;
    

Подпрограммы обратного вызова (callbacks) требуют несколько большего внимания. Подпрограммы обратного вызова из GtkAda (обработчики сигналов) сделаны внутри блокировки GtkAda. Однако, Подпрограммы обратного вызова из Glib (таймауты, обратные вызовы ввода/вывода и функции простоя (idle)) сделаны вне блокировки GtkAda. Следовательно, внутри обработчиков сигналов вам не надо вызывать Gdk.Threads.Enter, но внутри других типов подпрограмм обратного вызова вы должны использовать вызовы Gdk.Threads.Enter и Gdk.Threads.Leave.

Объектно-ориентированные свойства

GtkAda с самого начала разрабатывалась с учетом построения объектно-ориенированных уровней поверх gtk+. Это подразумевает, что свойства, подобные расширению типа, динамической диспетчеризации,... сделаны доступными с помощью стандартного языка Ада.

Данная секция описывает как это все работает и как вы можете расширить возможности существующих виджетов, и даже то, как вы можете создать свои собственные виджеты.

Общее описание тэговых типов

Зачем использовать объектно-ориентированное программирование ?

Каждый виджет GtkAda является тэговым типом, который имеет несколько примитивных подпрограмм, известных всем его потомкам.

Это подразумевает, что в отличие от кода C, вам не надо (в большинстве известных случаев) выполнять явное приведение типов, а когда вам это реально понадобится, то Ада всегда убедится в правильности подобных действий.

Таким образом, ваши программы намного безопаснее и большинство ошибок обнаруживается еще на этапе компиляции, что является обычным для Ады.

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

Это делает инструментальную библиотеку GtkAda очень мощным средством разработки графических интерфейсов.

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

Возможно вам захочется создать свой собственный, полностью новый виджет, который знает как себя отрисовывать, как реагировать на события,... и вы хотите, в случае необходимости, иметь возможность повторно его использовать. Опять же, используя стандартные средства Ады, вы можете просто создать новый тэговый тип и научить его взаимодействию с пользователем (см. Создание виджетов на Аде далее).

Конверсия типов виджетов из C в Аду

Существуют три базовых вида виджетов, которые вы можете использовать в GtkAda:

В первом случае, GtkAda всегда содержит или предоставляет возможность создать соответствующий тэговый тип. Не имеет значения, создаете-ли вы виджет явно или он автоматически создается с помощью gtk+. Например, если вы создали виджет в Аде, поместили его в таблицу, а позднее извлекли его из таблицы, то у вас будет тот же самый виджет.

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

Если виджет создан неявно (например каждый раз при создании кнопки Gtk_Button, для отображения текста кнопки создается Gtk_Label), то GtkAda будет способна только создать соответствующий тип, который задан по умолчанию, для следующих виджетов: Gtk_Label, Gtk_Button, Gtk_Item, Gtk_List_Item, Gtk_Menu_Item, Gtk_Check_Menu_Item, Gtk_Radio_Menu_Item, Gtk_Tearoff_Menu_Item и Gtk_Tree_Item. Для других виджетов, GtkAda будет создавать Gtk_Widget, и вам потребуется или вызвать Gtk.Unchecked_Cast, для приведения типа к типу, который вам необходим, или использовать Gtk.Type_Conversion, что будет показано далее. Ниже демонстрируется пример использования Gtk.Unchecked_Cast:

    
    
    declare
       Stub : Gtk_Window_Record;
    begin
       Window := Gtk.Unchecked_Cast (Widget, Stub);
    end;
    

В третьем случае (импорт виджетов из C), по умолчанию, GtkAda не может создать соответствующий тип Ады.

Предлагаемое нами решение, для рассмотренных двух случаев, заключается в указании в спецификаторе with модуля Gtk.Type_Conversion. В этой стуации, каждый стандартный виджет, вне зависимости от того кто его создал, будет всегда коттектно конвертироваться в соответствующий тип Ады. Таким образом, в основном, если вы поместите следующее в ваш головной модуль:

    
    
    with Gtk.Type_Conversion;
    
    begin
       Gtk.Main.Init;
       Gtk.Type_Conversion.Init;
       ...
    end
    

то вы сможете безопасно получить потомка каждого виджета (таблица, бокс,...) и можете быть уверены в правильности типа Ады. Вам нет необходимости явно конвертировать ваш виджет в что-либо еще.

Однако, указание этого модуля в спецификаторе with подразумевает, что ваше приложение будет зависеть от каждого пакета GtkAda, а это "утяжеляет" ваше приложение, объясняя, таким образом, почему такой подход не принят по умолчанию. Мы настоятельно рекомендуем вам использовать этот пакет если отсутствует экстремальная важность зависимости вашего приложения от всех пакетов GtkAda.

Случай импорта виджетов, написанных на C, несколько более хитрый. Поскольку GtkAda ничего не знает о построении таких виджетов, она не может магически конвертировать C-виджеты в Ада-виджеты. В этом случае, задача научить GtkAda выполнять правильную конверсию полностью возлагается на вас.

Мы предусмотрели "hook"-функцию, которую вам понадобиться модифицировать. Эта функция определена в пакете Gtk.Type_Conversion. Она принимает строку с именем C-виджета (например, "GtkButton"), и ожидает вновь распределенный (аллоцированый) указатель в возвращаемом значении. Если вы не знаете этот тип, просто верните null.

Использование тэговых типов для расширения виджетов Gtk

Начиная с версии 0.6 этой инструментальной библиотеки существует возможность ассоциирования ваших собственных данных с существующими виджетами путем создания новых типов. Эта секция демонстрирует простой пример, но вам лучше обратиться к исходным текстам в подкаталоге testgtk/, чтобы увидеть как мы используем это свойство вместо традиционно используемого user_data, в версии на языке C.

    
    
    type My_Button_Record is new Gtk_Button_Record with record
        -- какие-либо данные которые вы хотите ассоциировать с вашей кнопкой
    end record;
    type My_Button is access all My_Button_Record'Class;
    

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

    
    
    procedure My_Primitive_Func (Myb : access My_Button_Record);
    

Для определения экземпляра объекта типа My_Button в вашем приложении, выполните следующее:

    
    
    declare
       Myb : My_Button;
    begin
       Myb := new My_Button_Record;
       Initialize (Myb);   --  из Gtk.Button
    end;
    

Первая строка создает тип Ады, а вызов Initialize создает фактический виджет C и ассоциирует его с типом Ады.

Создание виджетов на Аде

В настоящий момент GtkAda позволяет создавать новые виджеты непосредственно на языке Ада. Такие виджеты могут быть использованы, как будто они являются непосредственной частью gtk.

Создание новых виджетов позволяет создавать повторно используемые компоненты. Вы можете применять к новым виджетам те же функции, которые используются для любых других виджетов (например, Show, Hide, ...).

Эта секция объясняет как создавать два типа виджетов: композитные виджеты и вновь создоваемые виджеты (иначе, виджеты, создаваемые с нуля). GtkAda предусматривает два примера, которые располагаются в каталогах examples/composite_widget и examples/base_widget. Пожалуйста, обратитесь также к учебнику по gtk+ описывающему базовые механизмы, которые вам необходимо знать при создании виджетов (даже если Ада-еод реально отличается от C-кода).

Создание композитных виджетов

Композитный виджет - это виджет, который большую часть обработки делает не самостоятельно. Такой виджет является коллекцией подчиненных виджетов, которые сгруппированы в более общую сущность. Например, среди стандартных виджетов к этой категории принадлежат Gtk_File_Selection и Gtk_Font_Selection.

Преимуществом является то, что для создания композитных виджетов не нужно знать ничего специального. Необходимо просто создать тэговый тип, расширив один из существующих виджетов (или даже какой-либо ваш собственный виджет) предусмотрев функцию Gtk_New, которая аллоцирует память для этого виджета и вызывает функцию Initialize, выполняющую фактическое создание виджета и подчиненных виджетов. При этом необходимо не забыть только одну вещь: функция Initialize должна вызывать функцию Initialize класса предка, для создания лежащего в основе C-виджета.

Пример в каталоге examples/composite_widget демонстрирует перепостроение виджета Gtk_Dialog, который фактически написан на C создателями gtk+.

Создание виджетов с нуля

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

Создание виджета с нуля необходимо в случаях когда ваш виджет должен отрисовываться каким-либо специальным способом, должен создавать и осуществлять эмиссию новых сигналов, ... Пример, который мы предоставляем в examples/base_widget, является небольшой целью на которой пользователь может щелкать кнопками мышки, и которая посылает один из двух сигналов "bullseye" или "missed", в зависимости от того где был выполнен щелчек.

Посмотрите также пример более сложного виджета в каталоге examples/tutorial/gtkdial, реализующий индикатор, в котором пользователь может перемещать стрелку, для выбора нового значения.

Вы должны создать две функции Gtk_New и Initialize. В настоящий момент, Initialize должна выполнить два действия:

    
    
    Parent_Package.Initialize (Widget);
    
    --  Строка выше, вызывает функцию Initialize предка.
    --  Это создает C-виджет, лежащий в основе, и который мы будем
    --  модифицировать следующим вызовом:
    
    Gtk.Object.Initialize_Class_Record
      (Widget, Signals, Class_Record);
    --  Этот вызов инициализирует "запись класса" виджета
    --  и создает сигналы.
    

В показанном выше примере, новой частью является второй вызов. Он принимает три или четыре аргумента:

  1. Widget
    Это виджет, который вы хотите проинициализировать.
  2. Signals
    Это массив ссылок на строки содержащих имена сигналов, которые вы хотите создать. Например, вы можете создать Signals с помощью:

      
      
      Signals : Gtkada.Types.Chars_Ptr_Array := "bullseye" + "missed";
      
    Это создаст два сигнала, с именами "bullseye" и "missed", чьи аргументы подпрограмм обратного вызова могут быть специфицированы четвертым параметром.
  3. Class_Record
    Каждый C-виджет ассоциирован с двумя записями. Первая, которая существует в единственном экземпляре, для каждого типа виджета является "записью класса". Она содержит список сигналов, которые известны этому типу виджета, список подпрограмм обратного вызова (для обработки сигналов) по умолчанию,... Вторая запись является "записью экземпляра", и она содержит данные, которые характерны для конкретного экземпляра виджета. В GtkAda, "запись экземпляра" просто является вашим тэговым типом и его полями. Вызов Initialize_Class_Record предусмотрен для инициализации "записи класса". Как мы уже говорили, "запись класса" может быть только одна для типа виджета. Параметр "Class_Record" будет указывать на эту запись. Как только она создана, она будет использоваться повторно для каждого экземпляра виджета этого типа.
  4. Parameters
    Этот четвертый аргумент, фактически, опционален, и используется для указания разновидностей (типов) параметров, которые ожидает каждый новый сигнал. По умолчанию (то есть, когда вы не передаете никакого значения этому параметру), все сигналы не ожидают ни одного аргумента, исключая, естественно, возможный параметр user_data. Однако, вы, например, можете решить, что первый сигнал ("bullseye") должен фактически принимать второй аргумент (скажем Gint), и что "missed" будет принимать два параметра (два Gint). Таким образом, Parameters будет содержать значения:

      
      
      (1 => (1 => Gtk_Type_Int, 2 => Gtk_Type_None),
       2 => (1 => Gtk_Type_Int, 2 => Gtk_Type_Int));
      
    Согласно правил обработки массивов принятых в Аде, каждый компонент должен иметь одно и то же число сигналов. Однако, если вы укажите тип Gtk_Type_None, то это будет фактически подразумевать отсутствие аргументов. Следовательно, первый сигнал, показанный выше, имеет один параметр. Примечательно, что для того чтобы иметь возможность осуществить эмиссию сигнала подобного второму сигналу, то есть с множеством аргументов, вы должны расширить пакеты определенные в Gtk.Handlers. По умолчанию, предусмотренные пакеты могут осуществлять эмиссию не более одного аргумента (и только для некоторых определенных типов). После просмотра исходного текста gtk-marshallers.adb, создание ваших собственных подпрограмм Emit_By_Name не должно вызвать трудностей. По существу, что-либо подобное:

      
      
      procedure Emit_With_Two_Ints
        (Object : access Widget_Type'Class;
         Name   : String;
         Arg1   : Gint;
         Arg2   : Gint);
      pragma Import (C, Emit_With_Two_Ints,
          "gtk_signal_emit_by_name");
      
      Emit_With_Two_Ints (Gtk.Get_Object (Your_Widget),
          "missed" & ASCII.NUL, 1, 2);
      
    будет осуществлять эмиссию сигнала "missed" с двумя параметрами 1 и 2.

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

  1. "size_request"
    Эта подпрограмма обратного вызова принимает один параметр:

      
      
      procedure Size_Request
         (Widget      : access My_Widget_Record;
          Requisition : in out Gtk.Widget.Gtk_Requisition);
      
    Эта функция должна модифицировать Requisition для указания идеального размера виджета. Это может отличаться от того размера виджета, который установлен, поскольку некоторые контейнеры могут увеличить или уменьшить виджет.
  2. "size_allocate"
    Эта подпрограмма обратного вызова вызывается каждый раз, когда виджет передвигается в его окне-предке, или при изменении размера. Она принимает один параметр:

      
      
      procedure Size_Allocate
         (Widget     : access My_Widget_Record;
          Allocation : in out Gtk.Widget.Gtk_Allocation)
      
    На эту функцию возлагается ответственность за перемещение виджета, например, с помощью использования Gdk.Window.Move_Resize.
  3. "expose_event"
    Эта подпрограмма обратного вызова вызывается каждый раз, когда виджет нуждается в перерисовке. Она принимает один параметр - участок, который нуждается в перерисовке (для повышения скорости, вам нет нужды перерисовывать весь виджет, только этот участок).

Поддержка для Glade, средство построения Gtk-интерфейсов

Введение

В настоящее время GtkAda поставляется с поддержкой средства построения Gtk-интерфейсов Glade (не следует это путать с реализацией GNAT glade, которая предназначена для распределенных вычислений). Использование Glade - не является сложным: это интуитивное "покажи и щелкни " ("point and click") средство построения графических интерфейсов (GUI). Главное отличие от других подобных средств построения интерфейсов заключается в том, что поскольку GtkAda строит интерфейс пользователя из блоков по умолчанию, Glade не запрашивает у вас установки по умолчанию для размеров и позиций ваших окон. Если вы нуждаетесь в подобном взаимодействии, то вам необходимо рассмотреть использование контейнера Gtk_Fixed. Однако, перед тем как рассматривать использование Gtk_Fixed, следует предварительно почитать справочное руководство по GtkAda.

Использование Glade

Следует заметить, что мы рекомендуем использовать только версию 0.5.9 Glade, поско некоторые предыдущие версии не обеспечивают совместимость. Используя эту версию вы можете создавать файлы Ады непосредственно из Glade, путем указания языка Ada95 в опциях проекта. Делая это, вы указываете Glade на необходимость осуществления внутреннего вызова Gate, когда используете команду "Write Source Code" (записать исходный текст). Glade сохраняет ваш интерфейс в файле проекта, синтаксис которого основан на XML. В последующих секциях, мы попеременно будем ссылаться на этот файл как на "XML-файл" или "файл проекта".

Использование преимуществ опций Glade

В XML-файле Glade сохраняет различные опции, преимуществами которых может воспользоваться Gate. В частности, для окна установки опций проекта, некоторые Главные Опции (General Options) в секции "C Options" также будут использоваться Gate. Опция "Поддержка Gettext" (Gettext support), если она установлена, будет генерировать дополнительный пакет <project>_intl, который будет использовать Gtkada.Intl. Опция "Установка Имен Виджетов" (Set Widget Names) также распознается и будет генерировать дополнительный вызов Set_Name для каждого виджета.

Ассоциирование иконок с кнопками

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

Примечания для пользователей Linux

Некоторые дистрибутивы Linux поставляются с прекомпилированной версией Glade, которая обычно содержит поддержку для Gnome. В настоящее время, GtkAda и Gate не предусматривают поддержку для Gnome (вместо этого GtkAda предусматривает вместо виджетов Gnome альтернативные переносимые виджеты, например, см. Gtkada.Canvas, Gtkada.Dialogs, Gtkada.Pixmaps), которые подразумевают, что Glade не содержит какие-либо свойства, характерные для Gnome. В частности, вы должны заблокировать поддержку Gnome в окне установки опций проекта, и убедиться в том, что вы не используете свойство "Stock Button" или список предопределенных иконок Gnome, предусматриваемый различными виджетами, подобными кнопкам панели управления (toolbar buttons) или пунктам меню (menu items).

Gate

Запуск Gate

Gate является статической версией нашей поддержки. Этот инструмент принимает XML-файл Glade как аргумент и генерирует набор Ада-файлов, которые после компиляции воссоздают только что разработанный с помощью Glade интерфейс. Для запуска Gate необходимо использовать команду gate, которая требует наличия XML-файла Glade. В результате, это вызовет генерацию Ада-файлов в каталоге, который определен установкой опции проекта "Source Directory", или в текущем каталоге, если эта опция не установлена.

Структура генерируемых файлов

Главный файл

Главный файл является именем программы, определенным в XML-файле Glade: <program name>.adb. Он содержит код инициализации и создания каждого виджета верхнего уровня, расположенного в XML-файле. Это подразумевается как соглашение по умолчанию для визуализации вашего графического интерфейса, но вы можете модифицировать такую инициализацию.

Файлы виджетов верхнего уровня

Для каждого виджета верхнего уровня Gate сгенерирует пакет <widget>_Pkg.Callbacks в файлах <widget>_pkg-callbacks.ads и <widget>_pkg-callbacks.adb. Эти пакеты содержат все вызовы GtkAda, которые необходимы для создания виджетов, разработанных вами спомощью Glade. Как правило, вам нет необходимости модифицировать эти файлы самостоятельно.

Главный файл обработчиков

Этот файл, называемый callbacks_<program name>.ads, содержит все конкретизированные пакеты обработчиков (Handler), которые необходимы вашему приложению. Как правило, вам нет необходимости модифицировать этот файл самостоятельно.

Файлы сигналов виджетов верхнего уровня

Эти файлы являются наиболее важными из всех файлов, созданных с помощью Gate. Каждый такой файл имеет название вида <widget_name>_pkg-callbacks.adb. Они содержат подпрограммы-заглушки для всех подпрограмм обратного вызова, относящихся к виджетам верхнего уровня, которые вы создали с помощью Glade. Также как и главный файл, эти файлы вы модифицируете самостоятельно. Следует заметить, что вам рекомендуется, на сколько это возможно, структурировать ваше приложение в соответствии с тем как реальный код помещается в самостоятельные пакеты (то есть не генерируемые файлы), для того, чтобы избежать слияния проблем в случае значительных изменений вашего интерфейса при работе с Glade. Более детально это рассматривается в следующей секции.

В текущий момент, Gate будет генерировать заглушки подпрограмм обратного вызова, которые способны обрабатывать ожидаемое число аргументов для каждого сигнала. Это осуществляется путем использования относительно низкоуровневого механизма, объясняемого в справочном руководстве (см. также пакет Gtk.Handlers). В основном, подпрограммы обратного вызова, ожидающие аргументы, принимают как свой параметр только Gtk_Argument, и Gate будет генерировать соответствующие описания переменных и вызовы преобразования, чтобы обеспечить ожидаемые аргументы GtkAda. Вы не должны модифицировать этот код самостоятельно, за исключением случаев, когда вы точно знаете, что вы делаете.

Например, для сигнала delete_event, Gate сгенерирует следующее тело процедуры, предоставляя доступ к аргументам этой подпрограммы обратного вызова: Gtk_Window_Record (Object) и Gdk_Event (Arg1)

    
    
    function On_Main_Window_Delete_Event
      (Object : access Gtk_Window_Record'Class;
       Params : Gtk.Arguments.Gtk_Args) return Boolean
    is
       Arg1 : Gdk_Event := To_Event (Params, 1);
    begin
       return False;
    end On_Main_Window_Delete_Event;
    

Модификация сгенерированных файлов

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

Необходимо также заметить, что при отслеживании изменений Gate полагается на наличие в вашей системе таких средств как merge, или patch и diff Если ваша система не содержит работающего комплекта diff/patch, то скрипт configure просто заменит их пустыми операциями, которые подразумевают, что вновь генерируемые файлы будут переписывать предыдущие версии.

На системах, основаных на Win32, GtkAda теперь поставляется с набором Unix-подобных инструментов (включая patch и diff), что предусматривает такую же функциональность. Примечательно, что эти средства предусматриваются в виде "как есть" (as is), и мы рекомендуем вам использовать комплект утилит, который вы самостоятельно поддерживаете. В частности, такие инструменты обладают известными сложностями под управлением Windows 95 и Windows 98. Будущие версии GtkAda будут предусматривать какую-либо альтернативную реализацию скрипта gate командного интерпретатора shell на Аде, чтобы решить эту проблему переносимости чистым способом. Здесь находится полный пакет утилит GNU: http://www.weihenstephan.de/~syring/win32/UnxUtils.html.

В некоторых случаях, ввиду значительных изменений файла проекта, Gate не сможет объединить все изменения. В такой ситуации, Gate проинформирует вас об этом и сохранит в файлах как старые, так и новые секции (если вы используете merge), или сгенерирует файлы с расширением .rej, чтобы помочь вам выполнить объединение изменений вручную.

Добавление поддержки новых виджетов в Gate

Эта секция предназначена для разработчиков, которые хотят добавить поддержку новых виджетов GtkAda в Gate. Следует заметить, что эта секция не полная.

Реализация процедуры "Generate"

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

Динамическая загрузка XML-файлов

Введение в Libglade

Библиотека Libglade - это внешняя библиотека, предусматриваемая по умолчанию на некоторых системах (например, Linux, Gnome), которая поддерживает динамическую загрузку XML-файлов и создание виджетов во время выполнения. Преимуществом использования библиотеки libglade является то, что вам не надо перекомпилировать ваше приложение для изменения разработанного вами интерфейса пользователя. С другой стороны, все эти действия осуществляются во время выполнения, что подразумевает меньший объем проверок во время компиляции, а также невозможность использования преимуществ объектно-ориентированных свойств GtkAda и Ады, подобных наследованию от виджета, который создан с помощью средства построения графического интерфейса, и добавлению новых полей и свойств. За более полной информацией обратитесь к пакетам Glade и Glade.XML.

Ограничения

В настоящее время Gate поддерживает все виджеты Gtk+ и свойства, доступные в Glade. Но, чтобы помочь вам идентифицировать виджеты, которые могут не поддерживаться (например, виджеты Gnome), Gate будет генерировать предупреждающие сообщения на стандартное устройство вывода сообщений об ошибках:

    
    
    $ gate warning.glade
    Generating Ada files...
    
    GtkAda-WARNING **: Unsupported widget GnomeCanvas (canvas1)
    The following files have been created/updated in src:
    [...]
    

и будет добавлять комментарии в файл <widget>_pkg.adb, которые имеют следующий вид:

    
    
    --  WARNING: Unsupported widget GnomeCanvas (canvas1)
    

Это подразумевает, что в процессе генерации файла Gate детектирует не поддерживаемые виджеты (в данном случае GnomeCanvas с именем canvas1). Если вы получаете подобные предупреждающие сообщения, то ваш файл может откомпилироваться, а может и не откомпилироваться, а во время выполнения (если он все-таки откомпилировался) вы не сможете получить полную иерархию виджетов.

Вы можете прислать нам XML-файл (см. Как сообщить об ошибках), который вызвал подобную проблему. Мы не гарантируем быстрое исправление каждой отдельно взятой проблемы, но получение реальных примеров отсутствующей функциональности может оказать помощь в ее более быстрой реализации.

Связывание с новыми C-виджетами

С GtkAda поставляется Perl-скрипт, который может помочь вам создать связывание с C-виджетами (этот скрипт мы используем сами). Он не позволяет автоматизировать процесс полностью, хотя значительно ускоряет работу. Возможно, что вам понадобится менее 15 минут для создания нового связывания с C-виджетами, как только вы привыкните к принципам работы GtkAda. Примечательно, что ваши C-файлы должны иметь такой же формат, как и файлы Gtk+.

Здесь описываются шаги создания нового связывания:

Как сообщить об ошибках

Пр все возрастающем использовании, GtkAda становится все более стабильной инструментальной библиотекой. Тем не менее, при использовании GtkAda вы можетеобнаружить какие-либо ошибки. Мы стараемся тестировать GtkAda настолько, насколько это возможно, в основном при конвертировании файла testgtk.c, расположенного в дистрибутиве gtk+, а также при генерации большого количества интерфейсов, используя средство построения графических интерфейсов Glade и Gate. Мы настоятельно советуем, чтобы вы обратили внимание на testgtk, который демонстрирует множество примеров использования этой инструментальной библиотеки.

Существует два вида проблем, которые вы можете обнаружить:

Если вы являетесь поддерживаемым пользователем GNAT, то пошлите e-mail по адресу для сообщения об ошибках, в противном случае пошлите e-mail авторам () подробно объяснив суть проблемы (что вы сделали, что вы ожидали получить, и что вы реально получили). Пожалуйста, включите требуемые исходные тексты, которые позволяют воспроизвести проблему, в формате, воспринимаемом утилитой gnatchop (в основном, вставте все требуемые исходные тексты в конец сообщения e-mail). Пожалуйста, попытайтесь предоставить как можно меньшее подмножество исходных текстов.

Естественно, мы будем рады получить любые предусмотренные вами заплатки (patch), что возможно сделает эту инструментальную библиотеку еще более полезной.

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

Мы рекомендуем следующие документы. Большинство из них написано в рассчете на использование языка C, но может быть легко адаптировано после того как вы прочитали этот документ.


Сноски

(1)

Glade была написана Damon Chaplin