Привіт усім. В даній статті ми розглянемо віджет 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 ;
}
Компілюємо даний код за допомогою команди в терміналі: gtkdrawarea_compile_sample_code_gcc І отримуємо результат: gtkdraw_area_circle_line_rectabgle_text_demmo
Категорії: