Книга: Программирование на языке Ruby
12.2.5. Прочие виджеты
Разделы на этой странице:
12.2.5. Прочие виджеты
Даже для организации сравнительно простого графического интерфейса текстовых полей и кнопок может оказаться недостаточно. Нужны переключатели, флажки и другие виджеты. В следующем примере демонстрируются некоторые из них.
В листинге 12.7 предполагается, что пользователь хочет заказать билет на самолет. Для выбора города назначения используются классы Gtk::TreeView
, Gtk::ListStore
и Gtk::TreeViewColumn
(многоколонный список). Флажок (класс Gtk::CheckButton
) определяет, нужен ли обратный билет, а переключатель (класс Gtk::RadioButton
) позволяет указать класс салона. Завершает интерфейс кнопка Purchase
(Заказать) — рис. 12.6.
Листинг 12.7. Заказ билета на самолет
$KCODE = "U"
require "gtk2"
class TicketWindow < Gtk::Window
def initialize
super("Purchase Ticket")
signal_connect("destroy") { Gtk.main_quit }
dest_model = Gtk::ListStore.new(String, String)
dest_view = Gtk::TreeView.new(dest_model)
dest_column = Gtk::TreeViewColumn.new("Destination",
Gtk::CellRendererText.new,
:text => 0)
dest_view.append_column(dest_column)
country_column = Gtk::TreeViewColumn.new("Country",
Gtk::CellRendererText.new,
:text => 1)
dest_view.append_cоlumn(country_cоlumn)
dest_view.selection.set_mode(Gtk::SELECTION_SINGLE)
[["Cairo", "Egypt"], ["New York", "USA"],
["Tokyo", "Japan"]].each do |destination, country|
iter = dest_model.append
iter[0] = destination
iter[1] = country
end
dest_view.selection.signal_connect("changed") do
@city = dest_view.selection.selected[0]
end
@round_trip = Gtk::CheckButton.new("Round Trip")
purchase = Gtk::Button.new("Purchase")
purchase.signal_connect("clicked") { cmd_purchase }
@result = Gtk::Label.new
@coach = Gtk::RadioButton.new("Coach class")
@business = Gtk::RadioButton.new(@coach, "Business class")
@first = Gtk::RadioButton.new(@coach, "First class")
flight_box = Gtk::VBox.new
flight_box.add(dest_view).add(@round_trip)
seat_box = Gtk::VBox.new
seat_box.add(@coach).add(@business).add(@first)
top_box = Gtk::HBox.new
top_box.add(flight_box).add(seat_box)
main_box = Gtk::VBox.new
main_box.add(top_box).add(purchase).add(@result)
add(main_box)
show_all
end
def cmd_purchase
text = @city
if @first.active?
text += ": first class"
elsif
@business.active?
text += ": business class"
elsif @coach.active?
text += ": coach"
end
text += ", round trip " if @round_trip.active?
@result.text = text
end
end
Gtk.init
TicketWindow.new
Gtk.main
Рис. 12.6. Различные виджеты GTK
В этом приложении, как и в предыдущих примерах, создается главное окно с обработчиком события. Затем формируется список с двумя колонками, дизайн которого следует паттерну Модель-Вид-Контроллер (Model-View-Controller — MVC); класс Gtk::ListStore
(модель) имеет две колонки типа String
.
Далее создается виджет Gtk::TReeView.
Класс Gtk::treeViewColumn
конфигурирует эту колонку. Первая колонка называется «Destination», а для отображения клеток применяется класс рисовальщика Gtk::CellRendererText
. Первая колонка модели (с номером 0) — Gtk::ListStore
— служит значением текстового свойства. Итак, рисовальщики клеток наполняют древесную модель данными. В GTK+ 2.x есть несколько готовых рисовальщиков клеток, в том числе Gtk::CellRendererText
, Gtk::CellRendererPixbuf
и Gtk::CellRendererToggle
. Далее в список добавляются три строки данных и устанавливается обработчик события "changed"
, который будет вызываться, когда пользователь выберет другую строку. Этот обработчик изменит значение переменной @city
, записав в нее текст из первой колонки только что выбранной строки.
Затем создается простой флажок (Gtk::CheckButton
) и кнопка (Gtk::Button
). Обработчик события нажатия кнопки вызовет метод cmd_purchase
. Метка @result
первоначально пуста, но позже в нее будет записана строка, определяющая вид заказанного билета.
Три переключателя создаются как члены одной группы, то есть в любой момент может быть выбран лишь один из них. Когда пользователь щелкает по любому переключателю, равнее выбранный сбрасывается. Первым параметром конструктору переключателя передается первый переключатель из той же группы. Поэтому у конструктора первого переключателя в группе этого параметра нет, а остальным передается ссылка на первый переключатель.
Виджеты нужно организовать на экране так, чтобы пользователю было удобно. Мы воспользовались комбинацией контейнеров Gtk::НВох
и Gtk::VBox
. Список расположен над флажком. Все три переключателя расположены вертикально справа от списка. А кнопка помещена под всеми остальными виджетами.
Метод cmd_purchase
очень прост: он строит строку, отражающую состояние всех виджетов в момент нажатия кнопки. У переключателей и флажков есть метод active?
, который возвращает true
, если виджет отмечен. Построенная строка записывается в метку @result
и потому появляется на экране.
Во многих приложениях интерфейс содержит меню. В следующем примере показано, как можно организовать меню в Ruby/GTK2. Заодно демонстрируется применение всплывающих подсказок — мелкая деталь, способная украсить любую программу.
В листинге 12.8 создается главное окно с меню, содержащим пункт File
и еще два фиктивных пункта. В меню File
есть команда Exit
, которая завершает приложение. Оба пункта File
и Exit
снабжены всплывающими подсказками.
Листинг 12.8. Пример меню в GTK
$KCODE = "U"
require "gtk2"
class MenuWindow < Gtk::Window
def initialize
super("Ruby/GTK2 Menu Sample")
signal_connect("destroy") { Gtk.main_quit }
file_exit_item = Gtk::MenuItem.new("_Exit")
file_exit_item.signal_connect("activate") { Gtk.main_quit }
file_menu = Gtk::Menu.new
file_menu.add(file_exit_item)
file_menu_item = Gtk::MenuItem.new("_File")
file_menu_item.submenu = file_menu
menubar = Gtk::MenuBar.new
menubar.append(file_menu_item)
menubar.append(Gtk::MenuItem.new("_Nothing"))
menubar.append(Gtk::MenuItem.new("_Useless"))
tooltips = Gtk::Tooltips.new
tooltips.set_tip(file_exit_item, "Exit the app", "")
box = Gtk::VBox.new
box.pack_start(menubar, false, false, 0)
box.add(Gtk::Label.new("Try the menu and tooltips!"))
add(box)
set_default_size(300, 100)
show_all
end
end
Gtk.init
MenuWindow.new
Gtk.main
И здесь базовая структура программы такая же, как в предыдущих примерах. В данном случае мы создаем пункт меню Gtk::MenuItem
с именем Exit
и задаем для него обработчик события, который завершает программу. Событие называется activate
и генерируется, когда пользователь выбирает пункт меню.
Далее создается меню File
и в него добавляется пункт Exit
. Это все, что требуется для создания выпадающего меню. В конце создается пункт меню File
; именно он и появится в полосе меню. Чтобы присоединить пункт File
к меню File
, мы вызываем метод submenu=
.
Затем создается
полоса меню Gtk::MenuBar
, в которую добавляются три меню: File
, Nothing
и Useless. Что-то делает лишь первое меню, остальные приведены только для демонстрации.
Всплывающими подсказками управляет единственный объект Gtk::Tooltips
. Чтобы создать подсказку для любого виджета, например для пункта меню, нужно вызвать метод set_tip
, которому передаются сам виджет, текст подсказки и строка, содержащая дополнительный скрытый текст. Скрытый текст не показывается в составе подсказки, но может, например, использоваться для организации оперативной справки.
Чтобы разместить полосу меню в верхней части главного окна, мы взяли Gtk::VBox
в качестве самого внешнего контейнера. В данном случае мы добавляем в него полосу меню не методом add
, а методом pack_start
, чтобы точнее контролировать внешний вид и положение виджета.
Первым параметром методу pack_start
передается размещаемый виджет. Второй параметр — булевский признак, показывающий, должен ли виджет занимать все доступное пространство. Отметим, что виджет при этом не растет, а обычно просто размещается в центре контейнера. Мы хотим, чтобы полоса меню располагалась сверху, поэтому передаем false
.
Третий параметр — тоже булевская величина, говорящая о том, должны ли размеры виджет быть изменены так, чтобы он занял все отведенное пространство. Нам нужна лишь узкая полоса меню, поэтому мы и тут передаем false
. Последний параметр метода pack_start
задает отступы, то есть пустое место вокруг виджета. Нам это ни к чему, поэтому мы передаем нуль.
Большую часть главного окна занимает метка. Напоследок мы принудительно устанавливаем размер окна 300?100 пикселей.