Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Программирование с использованием gtkmm 3. Контейнерные виджеты

Оригинал: Programming with gtkmm 3
Авторы: Murray Cumming, Bernhard Rieder, Jonathon Jongsma, Ole Laursen, Marko Anastasov, Daniel Elstner, Chris Vine, David King, Pedro Ferreira, Kjell Ahlstedt
Перевод: А.Панин

8.2. Контейнеры для множества виджетов

Класс контейнеров для множества виджетов наследуется от класса Gtk::Container; как и при работе с классом Gtk::Bin, вы можете использовать методы add() и remove() для добавления и удаления хранимых виджетов. Однако, в отличие от метода Gtk::Bin::remove(), метод remove() класса Gtk::Container принимает аргумент, указывающий на то, какой виджет следует удалить.

8.2.1. Упаковка

Вы наверняка заметили, что окна, созданные с помощью gtkmm, являются "эластичными" - обычно их размер может изменяться различными способами. Такое поведение обусловлено применением системы упаковки виджетов (widget packing system).

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

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

gtkmm производит иерархическое распределение виджетов с помощью контейнеров (containers). Контейнерный виджет может содержать другие виджеты. Большинство предоставляемых gtkmm виджетов являются контейнерами. Окна, вкладки наборов страниц и кнопки являются контейнерными виджетами. Существует два типа контейнеров: контейнеры для хранения одного дочернего виджета, представленные унаследованными от класса Gtk::Bin классами и контейнеры для хранения множества виджетов, представленные унаследованными от класса Gtk::Container классами. Большинство классов предоставляемых gtkmm виджетов, включая класс Gtk::Window, унаследовано от класса Gtk:Bin.

Да, утверждение о том, что окно может содержать только один виджет, является верным. Как же тогда мы можем использовать окно для выполнения какой-либо полезной работы? Мы можем использовать его, разместив в нем контейнер для хранения множества виджетов. Наиболее используемыми контейнерными виджетами являются виджеты, представленные классами Gtk::Grid и Gtk::Box.
  • Виджет, представленный классом Gtk::Grid, распределяет дочерние виджеты в строках и столбцах. Следует использовать методы attach(), attach_next() и add() для добавления дочерних виджетов.
  • Виджет, представленный классом Gtk::Box, распределяет виджеты в вертикальной и горизонтальной плоскостях. Следует использовать методы pack_start() и pack_end() для добавления дочерних виджетов.

Существует несколько других контейнеров, которые мы также обсудим.

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

8.2.2. Улучшенная программа Hello World

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

Рисунок 8-6: Второй вариант программы Hello World
Второй вариант программы Hello World

Исходный код

Файл helloworld.h (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл helloworld.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл main.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

После сборки и запуска программы попробуйте изменить размер окна для ознакомления с его поведением. Также попробуйте изменить параметры метода pack_start() в процессе чтения раздела "Контейнеры общего назначения".

8.2.3. Контейнеры общего назначения (Boxes)

Для упаковки большинства виджетов, как и в описанном выше примере, используются контейнеры общего назначения. Это невидимые контейнеры, в которые мы можем помещать наши виджеты. При упаковке виджетов с помощью горизонтального контейнера общего назначения объекты размещаются в горизонтальной плоскости слева направо или справа налево в зависимости от того, используется ли метод pack_start() или pack_end(). В вертикальном контейнере общего назначения виджеты размещаются сверху вниз или наоборот. Вы можете использовать любую комбинацию контейнеров общего назначения внутри или рядом с другими контейнерами общего назначения для создания желаемого интерфейса.

8.2.3.1. Добавление виджетов

8.2.3.1.1. Параметры упаковки для дочерних виджетов

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

Существует несколько параметров, влияющих на способ упаковки виджетов, которые могут смущать неопытных разработчиков. Если вы сталкиваетесь с проблемами при работе с контейнерами общего назначения, иногда хорошей идеей является использование инструмента проектирования графических пользовательских интерфейсов glade для ознакомления со всеми возможными вариантами упаковки. Вы можете даже принять решение о необходимости использования API Gtk::Builder для загрузки файла вашего графического пользовательского интерфейса в процессе работы приложения.

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

Рисунок 8-7: Упаковка виджетов с использованием контейнеров общего назначения 1
Упаковка виджетов с использованием контейнеров общего назначения 1

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

Это декларация метода pack_start():
void pack_start(Gtk::Widget& child,
                Gtk::PackOptions options = Gtk::PACK_EXPAND_WIDGET,
                guint padding = 0);

В качестве первого аргумента выступает виджет, который вы упаковываете. В нашем примере это все кнопки из строки.

В качестве аргумента options может передаваться одна из следующих трех констант:
  • Gtk::PACK_SHRINK: Пространство выделяется в зависимости от размера дочернего виджета. Виджет займет достаточное пространство и никогда не будет расширяться.
  • Gtk::PACK_EXPAND_PADDING: Неиспользуемое пространство не используется виджетами. Виджеты будут равномерно растянуты, но их размеры не изменятся - вместо этого между виджетами будет свободное пространство.
  • Gtk::PACK_EXPAND_WIDGET: Дополнительное пространство будет занято виджетами с увеличенным размером, причем пространство между виджетами не будет затронуто.

Аргумент padding указывает ширину дополнительной рамки вокруг упакованного виджета.

Справочная информация

8.2.3.1.2. Параметры упаковки для контейнеров

Ниже приведен конструктор виджета контейнера общего назначения, а также методы, используемые для установки параметров упаковки виджетов в контейнер:
Gtk::Box(Gtk::Orientation orientation = Gtk::ORIENTATION_HORIZONTAL, int spacing = 0);
void set_spacing(int spacing);
void set_homogeneous(bool homogeneous = true);

Передача логического значения true методу set_homogeneous() приведет к тому, что все дочерние виджеты будут иметь одинаковый размер. Аргумент spacing устанавливает (минимальное) количество пикселей для использования в промежутках между виджетами.

Какая разница между параметром spacing (устанавливаемым в момент создания контейнера общего назначения) и padding (устанавливаемым тогда, когда элементы упаковываются)? С помощью параметра spacing устанавливается размер промежутков между всеми объектами, а с помощью параметра padding дополнительное пространство может быть добавлено с любой стороны виджета. Следующий рисунок должен дополнительно прояснить ситуацию:

Рисунок 8-8: Упаковка виджетов с использованием контейнеров общего назначения 2
Упаковка виджетов с использованием контейнеров общего назначения 2

8.2.3.2. Класс Gtk::Application и параметры командной строки

Программа, показанная в следующем примере, требует использования параметра командной строки. В исходном коде показаны два способа обработки параметров командной строки с помощью методов класса Gtk::Application.
  • Обработка параметров в рамках функции main() и сокрытие их от механизма класса Gtk::Application путем установки значения argc = 1 при вызове Gtk::Application::create().
  • Передача всех аргументов командной строки методу Gtk::Application::create() с добавлением флага Gio::APPLICATION_HANDLES_COMMAND_LINE. Соединение обработчика сигнала с сигналом "command_line" и последующая обработка параметров командной строки на уровне обработчика сигнала. Вы должны установить дополнительный параметр after = false в вызове signal_command_line().connect(), так как ваш обработчик сигнала должен вызываться до стандартного обработчика сигнала. Вы также должны вызвать Gio::Application::activate() в обработчике сигнала, если вы не хотите, чтобы ваше приложение завершило свою работу без показа главного окна. (Класс Gio::Application является базовым классом для класса Gtk::Application).

8.2.3.3. Пример

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

Исходный код

Файл examplewindow.h (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл packbox.h (для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл packbox.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл examplewindow.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)

Файл main.cc (Для использования совместно с gtkmm 3, а не с gtkmm 2)


Следующий раздел : 8.2.4. Контейнеры для кнопок (ButtonBoxes).