9.10.2020
V úvodním článku o programování v GDK jsme si vytvořili základní aplikaci s jedním oknem a ukázali jak zachytit požadavek na překreslení okno. Nyní si již do okna (do jeho klientské oblasti) již něco konkrétního vykreslíme. Využijeme interakce GDK s grafickou knihovnou Cairo.
Jako ukázku si vykreslíme úhlopříčky spojující rohy okna, dále text s diakritikou v utf8 a aktuální datum a čas. To vše s ukázkou nastavení barvy pozadí, čar a textu a také volby fontu. Nejprve celý kód obslužné funkce on_expose volané při zachycení události GDK_EXPOSE, jak jsem si připravili v úvodním článku.
void on_expose(GdkEventExpose* ev) { if (ev->region == NULL) return; GdkDrawingContext* dc = gdk_window_begin_draw_frame( ev->window, ev->region); cairo_t* cr = gdk_drawing_context_get_cairo_context(dc); cairo_set_source_rgba(cr, 1.0, 1.0, 0.8, 1.0); cairo_paint(cr); cairo_set_source_rgba(cr, 0.5, 0.0, 0.0, 1.0); cairo_set_line_width(cr, 5.0); cairo_move_to(cr, 0, 0); cairo_line_to(cr, ev->area.width, ev->area.height); cairo_move_to(cr, ev->area.width, 0); cairo_line_to(cr, 0, ev->area.height); cairo_stroke(cr); cairo_set_source_rgba(cr, 0.0, 0.0, 0.8, 1.0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 18); cairo_move_to(cr, 50, 50); cairo_show_text(cr, "Nějaký český text v utf8 ..."); time_t timenow; time(&timenow); static char buff[64]; strftime(buff, sizeof(buff), "%d.%m.%Y %H:%M:%S", localtime((const time_t*)&timenow)); cairo_move_to(cr, 50, ev->area.height - 40); cairo_show_text(cr, buff); gdk_window_end_draw_frame(ev->window, dc); }
Klíčem k zahájení kreslení je funkce gdk_drawing_context_get_cairo_context, která nám vrátí pointr na tzv. cairo context, na který se následně budeme odkazovat při volání různých "kreslících" funkcí. Nastavení barvu (pozadí, čar i textu) provedeme funkcí cairo_set_source_rgba, jejíž parametry následujícími po cairo kontextu jsou RGBA složky barvy vyjádřené typem double, kde 1.0 je maximální hodnota. Význam dalších uvedených funkcí a jejích parametrů je jistě již každému zřejmý z jejich názvů.
Vykreslení máme hotové, ale pokud chceme mít obsah okna stále aktuální jak při změně jeho rozměrů, tak z hlediska aktuálnosti časového údaje, musíme dodělat pár drobností.
Pokud jde o výpis aktuálního času, tedy aby program fungoval jako digitální hodiny i při nečinnosti uživatele, spustíme si timer s intervalem 1 sekundy a na něj programově vyvoláme překreslení okna, čímž se zobrazený čas aktualizuje. K tomu vyžijeme timer z knihovny GLib, který vytvoříme před vstupem do smyčky zpráv:
guint timer_clock; // globální proměnná mimo funkci main timer_clock = g_timeout_add(1000, on_timer, NULL);
V nastevené obslužné funkci pak vyvoláme překreslení okna pomocí funkce gdk_window_invalidate_rect následovně
gboolean on_timer(gpointer data) { gdk_window_invalidate_rect(window, NULL, TRUE); return G_SOURCE_CONTINUE; }
Při změně velikosti okna uživatelem dostaneme v hlavní smyčce událost GDK_CONFIGURE, při jejím zachycení opět zavoláme výše uvedenou funkci gdk_window_invalidate_rect.
Jako pro tentokrát poslední drobné vylepšení si ještě implementujeme možnost zavření okna klávesou Escape. K tomu je potřeba zachytit událost GDK_KEY_PRESS jak vidíme ve výpisu obslužné funkce smyčky zpráv rozšířené o výše zmíněnou funkčnost:
void on_gdk_event(GdkEvent* event, gpointer data) { if (event->type == GDK_EXPOSE) { on_expose((GdkEventExpose*)event); } else if (event->type == GDK_CONFIGURE) { gdk_window_invalidate_rect(window, NULL, TRUE); } else if (event->type == GDK_KEY_PRESS) { if (((GdkEventKey*)event)->keyval == GDK_KEY_Escape) on_end_app(); } else if (event->type == GDK_DELETE) { on_end_app(); } }
Vzhledem k tomu že je slušné po sobě uklízet, po zavření zastavíme a zrušíme vytvořený timer funkcí g_source_remove:
void on_end_app() { g_source_remove(timer_clock); gdk_window_destroy(window); g_main_loop_quit(main_loop); }
Na závěr opět celý výpis programu, který lze sestavit překladačem GCC následujícím příkazem (pro ladicí sestavení):
gcc gdk-okno.c -Wall -g `pkg-config --cflags --libs gdk-3.0` -ogdk-okno
#include <gdk/gdk.h> GMainLoop* main_loop; GdkWindow* window; guint timer_clock; void on_end_app() { g_source_remove(timer_clock); gdk_window_destroy(window); g_main_loop_quit(main_loop); } void on_expose(GdkEventExpose* ev) { if (ev->region == NULL) return; GdkDrawingContext* dc = gdk_window_begin_draw_frame( ev->window, ev->region); cairo_t* cr = gdk_drawing_context_get_cairo_context(dc); cairo_set_source_rgba(cr, 1.0, 1.0, 0.8, 1.0); cairo_paint(cr); cairo_set_source_rgba(cr, 0.5, 0.0, 0.0, 1.0); cairo_set_line_width(cr, 5.0); cairo_move_to(cr, 0, 0); cairo_line_to(cr, ev->area.width, ev->area.height); cairo_move_to(cr, ev->area.width, 0); cairo_line_to(cr, 0, ev->area.height); cairo_stroke(cr); cairo_set_source_rgba(cr, 0.0, 0.0, 0.8, 1.0); cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 18); cairo_move_to(cr, 50, 50); cairo_show_text(cr, "Nějaký český text v utf8 ..."); time_t timenow; time(&timenow); static char buff[64]; strftime(buff, sizeof(buff), "%d.%m.%Y %H:%M:%S", localtime((const time_t*)&timenow)); cairo_move_to(cr, 50, ev->area.height - 40); cairo_show_text(cr, buff); gdk_window_end_draw_frame(ev->window, dc); } void on_gdk_event(GdkEvent* event, gpointer data) { if (event->type == GDK_EXPOSE) { on_expose((GdkEventExpose*)event); } else if (event->type == GDK_CONFIGURE) { gdk_window_invalidate_rect(window, NULL, TRUE); } else if (event->type == GDK_KEY_PRESS) { if (((GdkEventKey*)event)->keyval == GDK_KEY_Escape) on_end_app(); } else if (event->type == GDK_DELETE) { on_end_app(); } } gboolean on_timer(gpointer data) { gdk_window_invalidate_rect(window, NULL, TRUE); return G_SOURCE_CONTINUE; } int main(int argc, char** argv) { GdkWindowAttr attributes; gint attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_TITLE | GDK_WA_WMCLASS; if (!gdk_init_check(&argc, &argv)) return EXIT_FAILURE; memset(&attributes, 0, sizeof(attributes)); attributes.window_type = GDK_WINDOW_TOPLEVEL; attributes.x = 100; attributes.y = 50; attributes.event_mask = GDK_ALL_EVENTS_MASK; attributes.width = 440; attributes.height = 480; attributes.title = "GDK - Cairo"; attributes.wclass = GDK_INPUT_OUTPUT; gdk_event_handler_set(on_gdk_event, NULL, NULL); window = gdk_window_new(NULL, &attributes, attr_mask); gdk_window_show(window); timer_clock = g_timeout_add(1000, on_timer, NULL); main_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(main_loop); g_main_loop_unref(main_loop); return EXIT_SUCCESS; }
Školení
Kontakt
739 219 991
live:radekchalupa_1
Nové články