Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
Как и в большинстве библиотек для построения графических интерфейсов, в GTK+ есть концепции фреймов, окон, диалогов и менеджеров размещения. Она располагает богатым набором виджетов, включающим большинство стандартных, например метки, кнопки и текстовые поля, а также ряд более сложных: деревья и многоколонные списки.
Хотя GTK+ написана на С, спроектирована она в объектно-ориентированной манере. В связи с этим Ruby/GTK2 предоставляет объектно-ориентированный API, не слишком отдаляясь от исходной реализации на С. Кроме того, Ruby/GTK2 написана вручную, а не с помощью таких генераторов кода, как SWIG. Поэтому API выдержан в духе Ruby, с использованием блоков, необязательных аргументов и т.д. Справочное руководство можно найти на сайте http://ruby-gnome2.sourceforge.ip/.
Библиотека GTK+ создана на базе других библиотек: GLib, Pango, ATK, Cairo и GDK. Она поддерживает неграфические функции (GLib), отображение многоязычных текстов в кодировке UTF-8 (Pango), средства облегчения работы (Atk), графический рендеринг (Cairo), низкоуровневые графические объекты (Gdk), а также множество виджетов и высокоуровневых графических объектов (Gtk).
Во время работы над книгой текущей была версия Ruby/GTK2 0.14.1, совместимая с текущими стабильными версиями Ruby и GTK+ (2.0). Помимо Linux, она поддерживает семейство ОС Windows и Mac OS X (с X Window System). Идет работа по переносу на «родную» платформу Mac OS X, хотя пока эта версия еще не стабильна.
GTK+ — объектно-ориентированная библиотека, поддерживающая логически стройную иерархию виджетов. Классы Gtk::Bin и Gtk::Container весьма развиты, а комбинация менеджеров размещения Gtk::Вох и Gtk::Table обеспечивает простые, но в то же время гибкие средства управления геометрией. В Ruby/GTK2 есть удобный механизм установки обработчиков сигналов.
Среди виджетов GTK+ вы найдете меню, панели инструментов, всплывающие подсказки, деревья, индикаторы хода процесса, бегунки и календари. К слабым местам текущей версии GTK+ можно отнести недостаточно богатый набор стандартных диалогов, с которыми к тому же трудно работать в модальном режиме. Есть недостатки и у стандартного многострочного текстового редактора.
Все строки, передаваемые методам Ruby/GTK2, должны быть представлены в кодировке UTF-8. Нельзя употреблять не ASCII-символы из некоторых одно- или многобайтовых кодовых страниц Windows. Поэтому не забывайте при редактировании Ruby-сценариев переключать редактор в режим UTF-8 и помещайте предложение $KCODE="U" в начале сценария.
12.2.2. Простое оконное приложение
Любая программа, в которой используется Ruby/GTK2, должна содержать директиву require "gtk2". Функциональность Ruby/GTK2 предоставляется посредством модулей Gtk и Gdk, поэтому имена классов GTK+ обычно начинаются с префикса Gtk:: или Gdk::.
Как правило, для инициализации Ruby/GTK2 мы вызываем метод Gtk.init, а затем создаем окно верхнего уровня и обработчик сигнала destroy (который поступает, когда пользователь закрывает окно). Метод show_all делает окно видимым, а обращение к Gtk .main запускает цикл обработки событий.
Мы еще вернемся к этой теме, но сначала рассмотрим пример. Следующий код, как и рассмотренная выше программа для Tk, отображает текущую дату:
$KCODE = "U"
require "gtk2"
Gtk.init
window = Gtk::Window.new("Today's Date")
window.signal_connect("destroy") { Gtk.main_quit }
str = Time.now.strftime("Today is n%B %d, %Y")
window.add(Gtk::Label.new(str))
window.set_default_size(200, 100)
window.show_all
Gtk.main
О переменной $KCODE речь шла в главе 4. Метод Gtk.init инициализирует Ruby/GTK2.
Главное окно (типа Gtk::window) создается как окно «верхнего уровня», а указанный текст отображается в полосе заголовка.
Далее создается обработчик сигнала destroy, который посылается при закрытии главного окна. Этот обработчик (в данном случае один блок) просто завершает главный цикл обработки событий. В документации по Ruby/GTK2 перечислены все сигналы, которые могут поступать каждому виджету (не забудьте и о суперклассах). Обычно они генерируются в результате манипуляций с мышью и клавиатурой, срабатывания таймеров, изменений состояния окна и т.д.
В следующей строке мы добавляем метку прямо в главное окно. Размер метки вычисляется автоматически на основе длины текста.
По умолчанию размеры родительских виджетов в GTK+ устанавливаются автоматически, исходя из размеров потомков. В данном случае длина строки, отображаемой выбранным по умолчанию шрифтом, определяет размер метки, а размер главного окна выбирается так, чтобы в нем поместилась метка. Такое окно получилось бы слишком маленьким, поэтому с помощью метода set_default_size мы говорим, что начальный размер главного окна должен составлять 200×100 пикселей.
Затем мы вызываем метод show_all, чтобы сделать главное окно и всех его потомков видимыми. По умолчанию главное окно скрыто, поэтому в большинстве приложений такой вызов необходим.
Метод Gtk.main запускает цикл обработки событий в GTK+. Он не возвращает управления, пока приложение не завершится. В данном случае обработчик события destroy приводит к выходу из Gtk.main, после чего завершается и все приложение.
12.2.3. Кнопки
Для создания кнопки в Ruby/GTK2 предназначен класс Gtk::Button. В простейшем случае мы задаем обработчик события clicked, которое возникает, когда пользователь щелкает по кнопке.
Программа в листинге 12.5 позволяет ввести одну строку в текстовое поле и после нажатия кнопки All Caps! преобразует ее в верхний регистр. На рис. 12.4 показано текстовое поле до нажатия кнопки.
Листинг 12.5. Кнопки в GTK$KCODE = "U"
require "gtk2"
class SampleWindow < Gtk::Window
def initialize
super("Ruby/GTK2 Sample")
signal_connect("destroy") { Gtk.main_quit }
entry = Gtk::Entry.new
button = Gtk::Button.new("All Caps!")
button.signal_connect("clicked") {
entry.text = entry.text.upcase
}
box = Gtk::HBox.new
box.add(Gtk::Label.new("Text:"))
box.add(entry)
box.add(button)
add(box) show_all
end
end
Gtk.init
SampleWindow.new
Gtk.main
Рис. 12.4. Пример простой кнопки в GTK
В листинге 12.5 определен класс SampleWindow; при таком подходе класс может управлять собственным отображением и поведением (не заставляя вызывающую программу конфигурировать окно). Класс главного окна наследует Gtk::window.
Как и в примере «Текущая дата», обработчик сигнала destroy завершает цикл обработки событий после закрытия главного окна.
Этот класс создает однострочное поле ввода (класс Gtk::Entry) и кнопку Gtk::Button с текстом All Caps!. С кнопкой связан обработчик события clicked, которое генерируется, когда пользователь нажимает и отпускает кнопку мыши, в то время как ее указатель находится над кнопкой.
Класс Gtk::Window — производный от Gtk::Bin, поэтому может содержать только один дочерний виджет. Чтобы добавить в окно два виджета, мы сначала помещаем их в контейнер HBox, который, в свою очередь, делаем потомком главного окна. Виджеты, добавляемые в контейнер Gtk::НВох, по умолчанию размещаются начиная с его правой границы. Есть также контейнер Gtk::VBox, который упаковывает своих потомков по вертикали.
Как и раньше, чтобы главное окно (и все его потомки) стало видимым, необходимо вызвать метод show_all.
Обработчик события clicked вызывается при нажатии кнопки. Он получает текст, находящийся в поле ввода, преобразует его в верхний регистр и записывает обратно в поле ввода.
Собственно код приложения находится после определения класса SampleWindow. В нем всего лишь создается главное окно и запускается цикл обработки событий.
12.2.4. Текстовые поля
В библиотеке GTK+ есть класс Gtk::Entry для ввода одной строки текста — мы видели его в предыдущем примере. Существует также класс Gtk::Textview, представляющий собой мощный многострочный редактор; его мы и опишем.
Программа в листинге 12.6 создает многострочное текстовое поле и помещает в него текст. По мере изменения содержимого поля текущая длина текста отображается с помощью метки, расположенной в нижней части окна (рис. 12.5).
Листинг 12.6. Текстовый редактор в GTK$KCODE = "U"
require "gtk2"
class TextWindow < Gtk::Window
def initialize
super("Ruby/GTK2 Text Sample")
signal_connect("destroy") { Gtk.main_quit }
set_default_size(200, 100)
@text = Gtk::TextView.new
@text.wrap_mode = Gtk::TextTag::WRAP_WORD
@buffer = @text.buffer
@buffer.signal_connect("changed") {