Привіт усім.
В даній статті ми розглянемо віджет GtkDrawArea, який відноситься до віконної системи GTK, яка в свою чергу використовується в операційних системах на базі ядра Лінукс. Даний віджет можна використовувати для різних цілей: відображення графіків, створення унікальних віджетів і, можливо, написання простих ігор.
Після створення нового примірника віджета GtkDrawArea і додавання його до структури головного вікна, отримується пуста площина, на якій можна виконувати певні функції малювання за допомогою бібліотеки-підсистеми Cairo. Cairo — це векторна бібліотека, за допомогою якої вся віконна система GTK виконує графічні операції.
На даному віджеті можна відмалювати:
- лінії, криві;
- інші геометричні фігури;
- фігури можна малювати як лініями, так разом з заливкою;
- текст;
- зображення.
Хоча написання тексту на віджеті може викликати деякі труднощі, однак їх легко обійти.
Функції
Для того, щоб створити примірник віджета необхідно використати функцію gtk_drawing_area_new яка виглядає наступним чином:
GtkWidget* gtk_drawing_area_new () ;
Повернений вказівник — структура типу GtkDrawingArea.
Після створення примірника, його необхідно додати до вікна функцією gtk_container_add. Попередньо даний віджет можна упакувати в контейнер, якщо, наприклад, Ви бажаєте використовувати і інші віджети (меню, інструменти у вигляді кнопок і інше).
Прекрасно! Віджет створено, залишилось тільки намалювати щось на ньому. Але як це зробити?
Щоб виконати відображення графіки на віджеті GtkDrawArea спочатку необхідно отримати площину малювання, яка має тип cairo_t. Деякі функції автоматично отримують дану площину, як, наприклад, вона передається до обробника події мальовання (тобто обробник події “draw”).
Але в основному її прийдеться отримувати самостійно. Виконати це можна за допомогою функцій gdk_cairo_create і gtk_widget_get_window. Їхнє оголошення виглядає наступним чином:
GdkWindow* gtk_widget_get_window (GtkWidget *widget) ;
cairo_t* gdk_cairo_create (GdkWindow *window) ;
gtk_widget_get_window повертає вікно віджета, якщо це доступно. Передається їй вказівник на віджет.
gdk_cairo_create створює нову площину для даного вікна GdkWidget.
Після того як Ви отримаєте вікно GdkWindow віджета GtkDrawArea і створите для нього площину малювання cairo_t, Ви можете притупити до відображення графіки на віджеті.
Окрім того, якщо необхідно, щоб віджет відкликався на події миші, або клавіатури необхідно встановити маску подій для даного віджета наступним чином:
gtk_widget_add_events (drawarea,
GDK_MOTION_NOTIFY |
GDK_BUTTON_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK) ;
Даний виклик функції вказує віконній системі, що даному віджету необхідно передавати події переміщення миші і натискання/відпускання клавіші клавіатури.
Функції малювання
До функцій малювання можна віднести: cairo_set_line_width, cairo_move_to, cairo_line_to, gdk_cairo_set_source_rgba, cairo_curve_to, cairo_rectangle, cairo_arc. Слід пам'ятати, що після використання попередніх функцій необхідно використати одну з наступних: cairo_fill, cairo_stroke, в іншому випадку на екрані нічого не з'явиться.
cairo_move_to призначена для встановлення поточної позиції на площині (використовуються разом з cairo_line_to). Оголошення:
void cairo_move_to (cairo_t *cr, double x, double y) ;
Аргументи x i y це позиції на координатній системі віджета, а cr — це площина для малювання.
cairo_line_to — створює лінію від поточної позиції (cairo_move_to) до вказаних точок. Оголошення:
void cairo_line_to (cairo_t *cr, double x, double y) ;
cairo_set_line_width — встановлює ширину лінії. Оголошення:
void cairo_set_line_width (cairo_t *cr,
double width) ;
gdk_cairo_set_source_rgba — встановлює поточний колір малювання. Оголошення:
void gdk_cairo_set_source_rgba (cairo_t *cr, const GdkRGBA *rgba) ;
typedef struct {
gdouble red;
gdouble green;
gdouble blue;
gdouble alpha;
} GdkRGBA;
Структуру GdkRGBA слід заповнювати значеннями від 0.0 до 1.0 для червоного, зеленого, синього і компонента прозорості відповідно.
cairo_curve_to — малює плавну криву. Оголошення:
void cairo_curve_to (cairo_t *cr,
double x1,
double y1,
double x2,
double y2,
double x3,
double y3) ;
[x1, y1] і [x3, y3] — координати початку і кінця кривої, а точка [x2, y2] впливає на кривизну лінії. Чим більш віддалена точка [x2, y2] від уявної лінії, прокладеної між точками [x1, y1] і [x3, y3], тим більша кривизна кінцевої лінії.
cairo_rectangle — малює прямокутний чотирикутник. Оголошення:
void cairo_rectangle (cairo_t *cr,
double x,
double y,
double width,
double height) ;
Верхній лівий кут чотирикутника у точці [x, y], а правий нижній у точці [x+width, y+height].
cairo_arc — призначена для малювання круга. Оголошення:
void cairo_arc (cairo_t *cr,
double xc,
double yc,
double radius,
double angle1,
double angle2) ;
Центр круга буде розташовуватись у точці [xc, yc], а радіус круга буде дорівнювати radius пікселів. Аргументи angle1 i angle2 призначені для неповного малювання окружності і вказуються в радіанах.
Текст
З малюванням тексту у cairo, не все так просто, як з графічними примітивами. Складність полягає у тому, що для кожного символу необхідно розраховувати його позицію. Окрім того, функція малювання тексту приймає параметром не звичайний масив const char*, а масив спеціальної структури cairo_glyph_t. Оголошення даної структури виглядає наступним чином:
typedef struct {
unsigned long index ;
double x ;
double y ;
} cairo_glyph_t ;
Поле index — це код символу, який необхідно відобразити, а x і y — це координати місця відображення.
Експерементальним методом було встановлено, що код символу — це ніщо інше, як UTF-16 (або код Unicode) код з зміщенням в 30 символів у сторону нуля. Тобто, якщо код англійського символу 'A' в UTF-16 має значення 65 в десятковій системі (Перші 128 символів UTF-16 і UTF-8 відповідають кодам ASCII) то в нашому випадку це буде 36. Залишилось тільки заповнити координати кожної букви і викликати функцію cairo_show_glyphs, яка виглядає наступним чином:
void cairo_show_glyphs (cairo_t* cr,
const cairo_glyph_t* glyphs,
int num_glyphs) ;
Параметр cr вказує на площину малювання, glyphs вказівник на масив об'єктів cairo_glyph_t, а num_glyphs кількість об'єктів у num_glyphs.
Для встановлення розміру символів можна скористатися функцією:
void cairo_set_font_size (cairo_t *cr,
double size) ;
Програма
Проста демонстраційна програма, яка малює примітиви і текст може виглядати наступним чином.
#include <gtk/gtk.h> /* усе необхідне для GTK*/
#include <iostream>
using namespace std ;
/* обробник події малювання */
gboolean draw_callback (GtkWidget *widget,
cairo_t *cr,
gpointer data) ;
/* вказівники */
GtkWidget* window, /* вікно */
* box, /* контейнер */
* drawarea ; /* віджет малювання */
const unsigned int GLYPHS_SIZE = 26 ;
/* масив для збереження символів і їх координат */
cairo_glyph_t glyphs [GLYPHS_SIZE] ;
/* головна функція програми */
int main (int argc, char** argv)
{
/* Дуже важливо! Перед використанням бібліотеки, її необхідно
** ініціалізувати, в іншому випадку ваша програма не буде працювати */
gtk_init (&argc, &argv) ;
/* створюємо текст */
for (unsigned int iter='A', jter=0;
iter<='Z' && jter<GLYPHS_SIZE;
++iter, ++jter)
{
/* індекс символу */
glyphs [jter].index = iter - 29 ;
/* x-координати не будуть змінюватися */
glyphs [jter].x = 350 ;
/* змінюються координати осі y - усі
** букви будуть розміщені в стовпець */
glyphs [jter].y = (jter*10+10) + (iter-'A') ;
}
/* створюємо звичайне вікно */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;
/* віджет */
drawarea = gtk_drawing_area_new () ;
/* контейнер для упакування */
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5) ;
/* упаковування віджета */
gtk_box_pack_start (GTK_BOX(box), drawarea, TRUE, TRUE, 1) ;
/* упаковуємо контейнер у вікно */
gtk_container_add (GTK_CONTAINER(window), box) ;
/* встановлюємо типові розміри вікна window в 500*500 пікселів */
gtk_window_set_default_size (GTK_WINDOW(window), 500, 500) ;
/* в добавок встановлюємо заголовок вікна */
gtk_window_set_title (GTK_WINDOW(window),"Головне вікно програми") ;
/* За допомогою даної функції ми прив'язуємо події до
** спеціальних функцій-обробників. */
g_signal_connect (window, "destroy", G_CALLBACK(gtk_main_quit), NULL) ;
g_signal_connect (drawarea, "draw", G_CALLBACK(draw_callback), NULL) ;
/* показуємо створене вікно */
gtk_widget_show_all (window) ;
/* Запускаємо головний цикл GTK-програми. Без цього вікно не
** буде реагувати на події*/
gtk_main () ;
/* знищуємо вікно і його елементи, і вивільняємо пам'ять */
gtk_widget_destroy (window) ;
/* у разі закриття вікна (спрацювання функції
** gtk_main_quit) - вихід з програми */
return 0 ;
}
gboolean draw_callback (GtkWidget *widget, /* віджет */
cairo_t *cr, /* область малювання віджета */
gpointer data) /* дані користувача */
{
/* змінна, яка буде містити колір */
GdkRGBA color ;
/*
** Малюємо круг заповнений зеленим
*/
color.red = 0.0 ;
color.green = 1.0 ;
color.blue = 0.0 ;
color.alpha = 1.0 ;
gdk_cairo_set_source_rgba (cr, &color) ;
cairo_arc (cr,
100,
100,
50,
0,
2 * G_PI) ;
cairo_fill (cr) ;
/*
** Малюємо чотирикутник з синіми межами
*/
color.red = 0.0 ;
color.green = 0.0 ;
color.blue = 1.0 ;
color.alpha = 1.0 ;
gdk_cairo_set_source_rgba (cr, &color) ;
cairo_rectangle (cr, 200, 50, 100, 100) ;
cairo_stroke (cr) ;
/*
** Малюємо пряму чорну лінію
*/
cairo_set_line_width (cr, 2) ;
cairo_move_to (cr, 50 , 200) ;
cairo_line_to (cr, 300, 200) ;
cairo_stroke (cr) ;
/*
** Малюємо криву
*/
cairo_curve_to (cr,
50 , 205,
175, 305,
300, 205) ;
cairo_stroke (cr) ;
/*
** Відображаємо текст в стовпець
*/
cairo_show_glyphs (cr, glyphs, GLYPHS_SIZE) ;
return true ;
}
Компілюємо даний код за допомогою команди в терміналі:
І отримуємо результат: