Chapter 6. Расположение виджетов в форме

Table of Contents
6.1. Теория Packing Boxes
6.2. Детальное рассмотрение расположения виджетов в форме
6.3. Пример программы

Иерархия наследования

Object +--- Widget +--- Container +--- Box

При создании приложения, вам будет необходимо расположить более одного виджета на формочке, ооветственной за все приложение. В первом примере была всего одна кнопка, и поэтому можно было использовать простую конструкцию $window->add() чтобы поместить виджет в окно. Но когда нужно поместить более одного виджета, необходимо их позиционировать относительно друг друга, с тем чтобы создать некий интерфейс к создаваемому Вами приложению.

6.1. Теория Packing Boxes

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

Для создания нового горизонтального или вертикального бокса, нужно написать следующие строчки:

$hbox = new Gtk::HBox( $homogeneous, $spacing );

$vbox = new Gtk::VBox( $homogeneous, $spacing );

Если переменная $homogeneous , то виждет можно располагать в пределах, определяемых HBox и Vbox. Переменная $spacing определяет границу в пикселах, на которую отстоят виджеты друг от друга. Следующие функции используются для расположения объектов внутри бокса:

$box->pack_start( $child, $expand, $fill, $padding );

$box->pack_end( $child, $expand, $fill, $padding );

Функция pack_start() располагает элементы списка сверху в VBox и соответственно в слева направо в HBox. Действие функции pack_end() противоположно действию pack_start(). Добавленный объект может быть другим контейнером, например много виджетов - те-же самые контейнеры, за исключением что в таких случаях как правло используются либо pixmaps либо кнопки. Расположенный виджет в боксе является дочерним к этому боксу.

Если аргумент $expand истеннен, то виджеты расположены в боксе так, что заполняют все место, если аргумент ложен, то бокс сокращается до размера виджета. Присваивая параметру расширения ложное значение виждет будет выравниваться по левому или правому краю в зависимости от желания. Следует отметить, что присваивание переменной $homogeneous значения true аналогично присваиванию значения ture переменной $expand

Если аргумент $fill истеннен, тогда дополнительное место распределено между объектами. В другом случае дополнительное свободное место распределяется между виджетами. Такое распределение возможно когда $expand выставлен в положение true.

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

6.2. Детальное рассмотрение расположения виджетов в форме

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

Packing Box Styles Screenshot

Каждая линия примера содержит один горизонтальный бокс с несколькими кнопками. Запрос pack записывается для каждого pack отвечающего за кнопку внутри hbox. Каждая из кнопок помещается в hbox различными способами(то есть, те же самые аргументы подходят для функции pack_start()).

Метод является быстрым вызовом из pack_start() и pack_end(), которые устанавливают растягивающую виджет переменную в true, заполняют её и устанавливают переменную в 0:

$box->pack_start_defaults( $widget );

$box->pack_end_defaults( $widget );

Однотипная(homogeneous) переменная бокса, в котором располагаются виджеты, может быть установлена в on или off при помощи следующей функции:

$box->set_homogeneous( $homogeneous );

Переменная устанваливающая отступы вокруг виджета, может быть установлена при помощи функции

$box->set_spacing( $spacing );

Если Вы хотите перестить потомка на новое место, то нужно использовать функцию

$box->reorder_child( $child, $position );

где $child - перемещаемый виджет и $position - позиция для перемещения, начиная с 0. Если Вы хотите рассмотреть текущий порядок, то нежно смотреть на список, возвращаемый функцией children() (унаследованной от Container).

Если необходимо изменить установку потомка, то используется следующая функция:

$box->set_child_packing( $widget, $expand, $fill, $padding, $pack_type );

Аргументы этой функции похожи на аргументы pack_start() и pack_end(), за исключением $pack_type, которая может принимать значения либо 'start' либо 'end'. Каково различие между интервалом и дополнением? Интервал добавлен между объектами, а дополнение добавлено с обоих сторон объекта. Приведенная картинка поясняет сказанное более ясно:

Packing Box Spacing Screenshot

6.3. Пример программы

Приведенная ниже программа показывает, как реализовать некоторые из типов расположения структур в приложении:

Исходный код

#!/usr/bin/perl -w use Gtk; use strict; set_locale Gtk; init Gtk; unless ($ARGV[0]){ print( "Использование: выберите тип расположения 1, 2, или 3.\n" ); Gtk->exit( 1 ); exit( 1 ); } my $false = 0; my $true = 1; my $which = $ARGV[0]; my ($window,$box1,$box2,$label,$separator,$quitbox,$button); # Вы должны всегда определять обработчик сигнала # delete_event для основного окна # Это очень важно для верного поведения програмы в случае сбоев $window = new Gtk::Window( "toplevel" ); $window->signal_connect( "delete_event", sub { Gtk->exit( 0 ); } ); $window->border_width( 10 ); # Мы создаем вертикальный бокс(vbox), и затем в него # ставим горизонтальные боксы. Такое расположение позволяет # расставлять горизонтальные боксы, заполненные кнопками, один над другим $box1 = new Gtk::VBox( $false, 0 ); # какой пример, соответствующий изображениям выше, показывать if ( $which == 1 ) { # создаем новый лейбл. $label = new Gtk::Label( 'new Gtk::HBox( $false, 0 );' ); # Выравниваем его по левой стороне. Описание этой функции ниже. $label->set_alignment( 0, 0 ); # Поместить лейбл в вертикальный бокс(vbox box1). Запомните, что # эти виджеты добавляются в vbox, который будет расположен свеху # остальных $box1->pack_start( $label, $false, $false, 0 ); # показываем лейбл $label->show(); # запрашиваем нашу создаваемую box функцию # homogeneous = FALSE, spacing = 0, # expand = FALSE, fill = FALSE, padding = 0 $box2 = make_box( $false, 0, $false, $false, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # запрашиваем создающую бокс функцию # homogeneous = FALSE, spacing = 0, # expand = TRUE, fill = FALSE, padding = 0 $box2 = make_box( $false, 0, $true, $false, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Аргументы следующие: homogeneous, spacing, expand, fill, padding $box2 = make_box( $false, 0, $true, $true, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Создаем разделитель, описание позднее. $separator = new Gtk::HSeparator(); # Устанавливаем сепаратор в vbox, помните, # что каждый из этих widgets упаковывается в vbox, # так что они будут сложены вертикально $box1->pack_start( $separator, $false, $true, 5 ); $separator->show(); # Создаем другой лейбл и показываем $label = new Gtk::Label( 'new Gtk::HBox( $true, 0 );' ); $label->set_alignment( 0, 0 ); $box1->pack_start( $label, $false, $false, 0 ); $label->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $true, 0, $true, $false, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $true, 0, $true, $true, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Другой новый сепаратор. $separator = new Gtk::HSeparator(); # Последние три аргумента gtk_box_pack_start: $separator = new Gtk::HSeparator(); # Последние три аргумента gtk_box_pack_start: # expand, fill, padding. $box1->pack_start( $separator, $false, $true, 5 ); $separator->show(); } elsif ( $which == 2 ) { # Создаем новый лейбл, запомните, что box1 это vbox $label = new Gtk::Label( 'new Gtk::HBox( $false, 10 );' ); $label->set_alignment( 0, 0 ); $box1->pack_start( $label, $false, $false, 0 ); $label->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $false, 10, $true, $false, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $false, 10, $true, $true, 0 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); $separator = new Gtk::HSeparator(); # Последние три аргумента gtk_box_pack_start: expand, fill, # и padding. $box1->pack_start( $separator, $false, $true, 5 ); $separator->show(); $label = new Gtk::Label( 'new Gtk::HBox( $false, 0 );' ); $label->set_alignment( 0, 0 ); $box1->pack_start( $label, $false, $false, 0 ); $label->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $false, 0, $true, $false, 10 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Аргументы: homogeneous, spacing, expand, fill, padding $box2 = make_box( $false, 0, $true, $true, 10 ); $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); $separator = new Gtk::HSeparator(); # Последние три аргумента: expand, fill, # и padding. $box1->pack_start( $separator, $false, $true, 5); $separator->show(); } elsif ( $which == 3 ) { # этот пример показывает, как pack_end() выравнивает # widgets по правому краю. Создаем новый box. $box2 = make_box( $false, 0, $false, $false, 0); # Создаем новый лейбл, который будет установлен в конец. $label = new Gtk::Label( "end" ); # Установка с применением gtk_box_pack_end(), $box2->pack_end( $label, $false, $false, 0 ); # показываем лейбл. $label->show(); # Помещаем box2 в box1 $box1->pack_start( $box2, $false, $false, 0 ); $box2->show(); # Сепаратор снизу $separator = new Gtk::HSeparator(); # далее подробно устанавливаем параметры разделителя # в 400 пикселов и ширинойц и 5 пикселов высотой # так-же это значит, что hbox быдет создан шириной в 400 пикселов # и ярлык "end" будет отделен от других лейблов в hbox. # В противном случае все лейблы будут прилегать друг к другу вплотную $separator->set_usize( 400, 5 ); # добавить сепаратор в vbox(box1), созданный вначале программы $box1->pack_start( $separator, $false, $true, 5 ); $separator->show(); } # создать другой бокс, $quitbox = new Gtk::HBox( $false, 0 ); # создаем кнопку выход. $button = new Gtk::Button( "Quit" ); # Послать сигнал для завершения программы, когда кнопка будет нажата $button->signal_connect( "clicked", sub { Gtk->exit( 0 ); } ); # Добавить кнопку в quitbox # последние три аргумента gtk_box_pack_start: # expand, fill, padding. $quitbox->pack_start( $button, $true, $false, 0 ); # добавить quitbox в vbox (box1) $box1->pack_start( $quitbox, $false, $false, 0 ); # Добавить vbox(box1) который теперь содержит все наши виджеты в основное окно $window->add( $box1 ); # и показать все слева $button->show(); $quitbox->show(); $box1->show(); # Показ всего окна со всеми лейблами и кнопками. $window->show(); # вызов основной функции. main Gtk; exit(0); # создаем новый hbox, заполненный кнопками и лейбралми. # Мы не показываем сам бокс, но показываем его содержимое sub make_box{ my ($homogeneous,$spacing,$expand,$fill,$padding) = @_; # создаем новый hbox с соответствующими установками типов и отступов my $box = new Gtk::HBox( $homogeneous, $spacing ); $button = new Gtk::Button( '$box->' ); $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); # Create a series of buttons with the appropriate settings # Создаем серию кнопок с соответствующими настройками $button = new Gtk::Button( "pack" ); $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); $button = new Gtk::Button( '( $button,' ); $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); # создаем кнопку с лейблом, зависящим от переменной, отвечающей за растягивание if ($expand){ $button = new Gtk::Button('$true,'); } else { $button = new Gtk::Button('$false,'); } $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); if ( $fill ) { $button = new Gtk::Button( '$true,' ); } else { $button = new Gtk::Button( '$false,' ); } $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); $button = new Gtk::Button( "$padding );" ); $box->pack_start( $button, $expand, $fill, $padding ); $button->show(); return ( $box ); }