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

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
Дата публикации: 15 Октября 2013 г.
Перевод: А.Панин
Дата перевода: 10 Апреля 2014 г.

28. Собственные виджеты

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

28.1. Собственные контейнерные виджеты

При наследовании класса виджета от класса Gtk::Container вы должны перекрыть следующие виртуальные методы:
  • get_request_mode_vfunc(): Возврат указания на предпочтительный для контейнера режим запроса размера типа Gtk::SizeRequestMode.
  • get_preferred_width_vfunc(): Расчет минимальной и обычной ширины контейнера.
  • get_preferred_height_vfunc(): Расчет минимальной и обычной высоты контейнера.
  • get_preferred_width_for_height_vfunc(): Расчет минимальной и обычной ширины контейнера в случае передачи заданного значения высоты.
  • get_preferred_height_for_width_vfunc(): Расчет минимальной и обычной высоты контейнера в случае передачи заданного значения ширины.
  • on_size_allocate(): Позиционирование дочерних виджетов при условии передачи реальных значений высоты и ширины контейнера.
  • forall_vfunc(): Вызов одной и той же функции обратного вызова для каждого из дочерних виджетов.
  • on_add(): Добавление дочернего виджета в контейнер.
  • on_remove(): Удаление дочернего виджета из контейнера.
  • child_type_vfunc(): Возврат типа доступного для вставки дочернего виджета.

Виртуальные методы get_request_mode_vfunc(), get_preferred_width_vfunc(), get_preferred_height_vfunc(), get_preferred_width_for_height_vfunc(), get_preferred_height_for_width_vfunc() и on_size_allocate() контролируют размещение дочерних виджетов. Например, в том случае, если ваш контейнер содержит 2 дочерних виджета, причем один находится под другим, ваш метод get_request_vfunc() может возвращать идентификатор режима изменения высоты в зависимости от ширины. После этого ваш метод get_preferred_width_func() может возвращать максимальное из значений ширины дочерних виджетов, а метод get_preferred_height_for_width_vfunc() - сумму их высот. Если вы захотите добавить границу между дочерними виджетами, вам придется также добавить ее ширину к значениям ширины и высоты. Контейнер вашего виджета будет использовать эти результирующие значения для гарантированного получения вашим виджетом достаточного пространства, а не меньшего пространства, чем ему требуется. Путем получения параметров родительского виджета каждого из виджетов, с последующим получением параметров его родительского виджета, данный алгоритм в конце концов позволяет вычислить размеры окна верхнего уровня.

Нет никакой гарантии, что вы получите тот идентификатор режима запроса размера типа Gtk::SizeRequestMode, который вы предлагали использовать. Следовательно, все четыре метода get_preferred_xxx_vfunc() должны возвращать имеющие смысл значения.

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

В том случае, если ваш контейнерный виджет не является окном верхнего уровня на основе унаследованного от класса Gtk::Window класса, вам, скорее всего, придется осуществлять вызов метода Gtk::Widget::set_has_window(false) в рамках вашего конструктора. Это значит, что ваш контейнерный виджет не будет создавать свой экземпляр класса Gtk::Window, а будет использовать окно родительского виджета. (Обратите внимание на различия классов Gtk::Window и Gdk::Window). В том случае, если вашему контейнерному виджету не требуется свой экземпляр класса Gdk::Window, причем его класс не наследуется от класса Gtk::Window, вы должны также перекрыть метод on_realize() таким же образом, как описано в разделе "Собственные виджеты общего назначения". И в том случае, если ваш контейнерный виджет не осуществляет прорисовку непосредственно в расположенном уровнем ниже окне на основе класса Gdk::Window, вы, скорее всего, должны осуществить вызов метода set_redraw_on_allocate(false) для повышения производительности.

Перекрывая метод forall_vfunc(), вы можете позволить приложениям управлять всеми дочерними виджетами контейнерных виджетов. Например, метод show_all_children() использует этот метод для поиска всех дочерних виджетов и их показа.

Хотя ваш контейнерный виджет и может иметь свой метод для упаковки дочерних виджетов, вы всегда должны предоставлять реализации виртуальных методов on_add() и on_remove() из базового класса, следовательно, методы add() и remove() должны выполнять соответствующие операции при вызове.

Ваша реализация метода child_type_vfunc() должна сообщать тип виджета, который может добавляться в ваш контейнер в том случае, если он еще не заполнен. Обычно должен использоваться метод Gtk::Widget::get_type() для получения идентификатора типа, указывающего на то, что контейнер может содержать виджеты на основе любого класса, унаследованного от класса Gtk::Widget. В том случае, если в контейнер не может быть помещено больше виджетов, этот метод должен возвращать идентификатор типа G_TYPE_NONE.

28.1.1. Пример

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

Рисунок 28-1: Собственный контейнерный виджет
Собственный контейнерный виджет

Исходный код

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

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

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

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

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

28.2. Собственные виджеты общего назначения

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

При наследовании класса виджета от класса Gtk::Widget вы должны перекрыть приведенные ниже виртуальные методы. Методы, отмеченные как (необязательные), не должны перекрываться во всех виджетах общего назначения. При этом методы базового класса могут подходить для реализации функций виджета.
  • get_request_mode_vfunc(): (необязательный) Возврат предпочтительного для виджета режима запроса размеров типа Gtk::SizeRequestMode.
  • get_preferred_width_vfunc(): Расчет минимальной и обычной ширины виджета.
  • get_preferred_height_vfunc(): Расчет минимальной и обычной высоты виджета.
  • get_preferred_width_for_height_vfunc(): Расчет минимальной и обычной ширины виджета в случае передачи заданного значения высоты.
  • get_preferred_height_for_width_vfunc(): расчет минимальной и обычной высоты виджета в случае передачи заданного значения ширины.
  • on_size_allocate(): Позиционирование виджета на основании реальных значений ширины и высоты.
  • on_realize(): Ассоциация окна на основе класса Gdk::Window с виджетом.
  • on_unrealize(): (необязательный) Разрыв ассоциации с окном на основе класса Gdk::Window.
  • on_map(): (необязательный)
  • on_unmap(): (необязательный)
  • on_draw(): Прорисовка виджета с использованием переданного экземпляра класса контекста Cairo Cairo::Context.

Первые 6 методов аналогичны методам из предыдущего списка и перекрываются при работе с собственными контейнерными виджетами. Эти методы кратко описаны в разделе "Собственные контейнерные виджеты".

Большей части собственных виджетов общего назначения требуется собственное окно на основе класса Gdk::Window для рисования на его поверхности. Впоследствии вы можете вызвать метод Gtk::Widget::set_has_window(true) в рамках вашего конструктора. (Это стандартное значение аргумента.) В том случае, если вы не осуществите вызов метода set_has_window(false), вы должны перекрыть метод on_realize() и осуществить вызовы методов Gtk::Widget::set_realized() и Gtk::Widget::set_window() из перекрытого метода.

28.2.1. Пример

В рамках данного примера реализован виджет, который выводит треугольник Пенроуза.

Рисунок 28-2: Собственный виджет общего назначения
Собственный виджет общего назначения

Исходный код

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

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

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

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

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


Следующий раздел : Многопоточные программы.