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

UnixForum





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

Программирование с использованием gtkmm 3. Виджет области рисования (DrawingArea)

Оригинал: 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 г.
Перевод: А.Панин
Дата перевода: 28 марта 2014 г.

17. Виджет области рисования (DrawingArea)

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

GTK+ использует API рисования Cairo. При работе с gtkmm вы можете использовать API cairo для языка программирования C++ под названием cairomm.

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

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

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

17.1. Модель рисования Cairo

Базовая концепция рисования с использованием Cairo заключается в создании "невидимых" контуров с их последующей обводкой или заливкой для того, чтобы сделать их видимыми.

Для выполнения любых операций рисования с использованием Cairo в первую очередь вы должны создать объект на основе класса Cairo::Context. Этот класс хранит все параметры состояния графического представления, описывающие то, как рисование должно быть осуществлено. Эти параметры включают такую информацию, как жирность линий, цвет, поверхность для рисования, а также другие данные. Такой подход позволяет сократить количество аргументов функций для непосредственного рисования с целью упрощения интерфейса. В gtkmm экземпляр класса Cairo::Context создается путем вызова функции Gdk::Window::create_cairo_context(). Так как контексты Cairo являются объектами с подсчетом количества ссылок, эта функция возвращает объект Cairo::RefPtr<Cairo::Context>.

В следующем примере показана методика создания контекста Cairo с красным цветом для рисования и шириной линий, равной 2. Любые функции рисования, использующие данный контекст, будут использовать эти настройки.
Gtk::DrawingArea myArea;
Cairo::RefPtr<Cairo::Context> myContext = myArea.get_window()->create_cairo_context();
myContext->set_source_rgb(1.0, 0.0, 0.0);
myContext->set_line_width(2.0);

Каждый контекст на основе класса Cairo::Context ассоциируется с определенным объектом окна на основе класса Gdk::Window, поэтому в первой строке приведенного выше примера создается виджет области рисования на основе класса Gtk::DrawingArea, а во второй - используется ассоциированный объект окна на основе класса Gdk::Window для создания объекта контекста Cairo на основе класса Cairo::Context. В последних двух строках изменяются параметры состояния контекста.

Существует множество переменных состояния графического представления, значение которых может быть установлено для контекста Cairo. Наиболее часто используемыми атрибутами контекста являются: цвет (изменяемый с помощью метода set_source_rgb() или set_source_rgba() для полупрозрачных цветов), жирность линии (изменяемая с помощью метода set_line_width()), шаблон пунктирной линии (изменяемый с помощью метода set_dash()), стиль концов линий (изменяемый с помощью метода set_line_cap()) и стиль соединений линий (изменяемый с помощью метода set_line_join()), а также стили шрифта (изменяемые с помощью методов set_font_size(), set_font_face() и других). Наряду с описанными существует множество других настроек, таких, как матрицы преобразования, правила заливки, параметры сглаживания и других. Для получения дополнительной информации обратитесь к документации API cairomm.

Текущие параметры состояния контекста на основе класса Cairo::Context могут быть сохранены во внутреннем стеке сохраненных состояний и в последствии восстановлены в том же виде, в котором они находились до сохранения. Для осуществления этой операции следует использовать метод save() и метод restore(). Описанная функция может оказаться полезной в том случае, если вы хотите временно изменить жирность линии и ее цвет (или какие-либо другие параметры графического представления) с целью рисования чего-либо и последующего возвращения к предыдущим настройкам. В этой ситуации вы должны вызвать метод Cairo::Context::save(), изменить параметры состояния графического представления, нарисовать линии, после чего осуществить вызов метода Cairo::Context::restore() для восстановления оригинальных параметров состояния графического представления. Вызовы методов save() и resore() могут быть вложенными; каждый вызов метода restore() восстанавливает параметры состояния, сохраненные при соответствующем вызове метода save().

Хорошей практикой является размещение всех операций модификации параметров состояния графического представления между вызовами функций save()/restore(). Например, если у вас есть функция, которая принимает ссылку на экземпляр класса Cairo::Context в качестве аргумента, вы можете реализовать ее следующим образом:
void doSomething(const Cairo::RefPtr<Cairo::Context>& context, int x)
{
    context->save();
    // изменение параметров состояния графического представления
    // выполнение операций рисования
    context->restore();
}

Виртуальный метод on_draw() передает контекст Cairo, который вы должны использовать для рисования на поверхности виджета области рисования, представленного классом Gtk::DrawingArea. Не обязательно сохранять и восстанавливать параметры состояния этого контекста Cairo в рамках функции on_draw().

17.2. Рисование прямых линий

После того, как мы разобрались с основными подходами к работе с графической библиотекой Cairo, мы готовы приступить к рисованию. Давайте начнем с самого простого из графических примитивов: прямой линии. Но для начала вам придется узнать немного больше о системе координат библиотеки Cairo. Начало системы координат в Cairo расположено в верхнем левом углу окна, причем увеличение координат по оси x происходит по направлению вправо, а по оси y - по направлению вниз.

Ввиду того, что при разработке графической библиотеки Cairo была реализована поддержка множества систем вывода (оконной системы X, изображений формата PNG, текстур OpenGL и других), проводится разделение между координатами пространства пользователя и координатами устройства. Соотношение между этими двумя системами координат по умолчанию устанавливается как один к одному, поэтому целочисленные координаты точно соответствуют пикселям на экране, но эта настройка может быть изменена при необходимости. Иногда может оказаться полезным масштабирование системы координат, в результате которого полная ширина и высота окна будет находиться бы в диапазоне значений от 0 до 1 ("единичный квадрат") или какое-либо другое масштабирование, связанное с механизмом работы вашего приложения. Описанное масштабирование может быть выполнено с помощью функции Cairo::Context::scale().

17.2.1. Пример

В данном примере мы создадим полнофункциональную программу на основе gtkmm и нарисуем несколько линий в окне. Процесс рисования линий будет заключаться в формировании контуров и их последующей обводке. Контур создается с помощью функций Cairo::Context::move_to() и Cairo::Context::line_to(). Функция move_to() аналогична отрыву вашего карандаша от бумаги и переносу его в какую-либо другую точку - между начальной точкой и точкой, в которую был перенесен карандаш, линии нарисовано не будет. Для рисования линии между двумя точками следует использовать функцию line_to().

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

Многие функции рисования библиотеки Cairo имеют вариант с суффиксом _preserve(). Обычные варианты таких функций, как clip(), fill() или stroke() удаляют ваш текущий контур. В том же случае, если вы используете вариант функции с суффиксом _preserve(), текущий контур будет сохранен, поэтому вы сможете использовать этот же конткр совместно со следующей функцией рисования.

Рисунок 17-1: Область рисования - линии
Область рисования - линии

Исходный код

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

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

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

Данная программа содержит единственный класс MyArea, являющийся подклассом класса Gtk::DrawingArea и содержащий функцию on_draw(). Эта функция вызывается в любой момент, когда изображение в области рисования должно быть перерисовано. В качестве аргумента функции передается указатель типа Cairo::RefPtr на объект контекста типа Cairo::Context, который мы используем для рисования. В самом коде рисования устанавливается цвет, который мы желаем использовать для рисования линий путем вызова функции set_source_rgb(), которая принимает аргументы, устанавливающие красную, синюю и зеленую составляющие желаемого цвета (корректные значения данных аргументов находятся в диапазоне от 0 до 1). После установки цвета мы создаем новый контур с помощью функций move_to() и line_to() и обводим этот контур с помощью функции stroke().

Рисование с использованием относительных координат
В примере выше мы рисовали все линии, используя абсолютные координаты. Вы также можете осуществлять операции рисования, используя относительные координаты. Для рисования прямой линии в этом случае может быть использована функция Cairo::Context::rel_line_to().

17.2.2. Стили линий

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

В том случае, если вы уже нарисовали множество линий, формирующих контур, у вас может появиться желание соединить их определенным образом. Cairo предоставляет три различных способа соединения линий: с острым углом (Miter) со срезанным углом (Bevel) и со скругленным углом (Round). Эти три типа соединения линий показаны ниже:

Рисунок 17-2: Различные типы соединения линий в Cairo
Различные типы соединения линий в Cairo

Стиль соединения линий устанавливается с помощью функции Cairo::Context::set_line_join().

Окончания линий также могут оформляться в соответствии с различными стилями. Используемый по умолчанию стиль предусматривает четкое равенство расстояния между началом и окончанием линии расстоянию между точками соответствующего пути. Этот стиль называется обычным завершением линии (Butt cap). Другими вариантами стилей являются скругление окончаний (Round) (используются скругленные окончания линий с центром круга в конечной точке) или квадратные окончания (используются квадратные окончания линий с центром квадрата в конечной точке). Этот стиль устанавливается с помощью функции Cairo::Context::set_line_cap().

Существуют и другие параметры, которые вы также можете изменить, включая возможность создания пунктирных и других типов линий. Для получения более подробной информации обратитесь к документации API Cairo.

17.2.3. Рисование тонких линий

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

Решением данной проблемы, способным гарантировать получение желаемых результатов, является расположение линии посередине пикселя в том месте, где она должна быть нарисована. Обратитесь к разделу часто задаваемых вопросов по Cairo.

Рисунок 17-3: Область рисования - Тонкие линии
Область рисования - Тонкие линии

Исходный код

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

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

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

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

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


Следующий раздел : 17.3. Рисование изогнутых линий.