繁体   English   中英

使用 cairo 绘制到 GTK/GDK 绘图区

[英]Drawing to GTK/GDK drawing area with cairo

在 GDK/GTK 中检索 cairo 上下文的机制已损坏和/或文档中未正确描述。 我试图从按钮按下回调中绘制 GtkDrawingArea ,但没有成功。 文档建议做

GdkWindow *window = gtk_widget_get_window( widget );
cairo_region_t *cairoRegion = cairo_region_create();
GdkDrawingContext *drawingContext = gdk_window_begin_draw_frame( window, cairoRegion );
cairo_t *cr = gdk_drawing_context_get_cairo_context( drawingContext );

然后使用 cairo 上下文绘制,然后正确结束

gdk_window_end_draw_frame( window, drawingContext );
cairo_region_destroy( cairoRegion );

所有这些似乎都适用于 GTK 3.22.30(和 cairo 1.15.12),但不适用于 GTK 3.24.30(和 cairo 1.16.0)。 显然有些东西已经改变了。 任何想法? 一个完整的例子如下:

// gcc -Wextra -o hauki hauki.c `pkg-config --cflags --libs gtk+-3.0`
#include <gtk/gtk.h>

gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{   
   // This works
   cairo_set_source_rgb(cr, 1, 1, 1);
   cairo_paint(cr);
   return FALSE;
}

gboolean button_cb( GtkWidget *widget, GdkEventButton *event, gpointer object )
{
    GdkWindow *window = gtk_widget_get_window( GTK_WIDGET(widget) );

    cairo_region_t *cairoRegion = cairo_region_create();
    GdkDrawingContext *drawingContext = gdk_window_begin_draw_frame( window, cairoRegion );
    cairo_t *cr = gdk_drawing_context_get_cairo_context( drawingContext );

    cairo_set_source_rgb(cr, 1, 0, 0);
    cairo_move_to(cr, 0, 0);
    cairo_line_to(cr, 300, 300);
    cairo_stroke(cr);
    
    gdk_window_end_draw_frame( window, drawingContext );
    cairo_region_destroy( cairoRegion );
}


int main (int argc, char *argv[])
{
   gtk_init( &argc, &argv );

   GtkWidget *window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
   g_signal_connect( window, "destroy", G_CALLBACK (gtk_main_quit), NULL );

   GtkWidget *da = gtk_drawing_area_new();
   gtk_widget_add_events( da, GDK_BUTTON_PRESS_MASK );
   gtk_widget_set_size_request( da, 300, 300 );
   g_signal_connect( da, "draw", G_CALLBACK(draw_cb), NULL );
   g_signal_connect( da, "button_press_event", G_CALLBACK(button_cb), NULL );
   
   gtk_container_add( GTK_CONTAINER (window), da );
   gtk_widget_show( da );
   gtk_widget_show( window );

   gtk_main();

   return 0;
}

gdk_window_begin_draw_frame()是一个低级调用,通常只有在您尝试使用 GDK/Cairo 绘制东西而不涉及 GTK 时才需要它。 由于您显然使用的是 GTK,因此您不应该使用它。

在 GTK 中使用 Cairo 进行绘图的方法是使用GtkDrawingArea (或者如果您想实现非常自定义的行为:继承GtkWidget并实现您自己的draw() vfunc)。 正如您已经看到的,它有一个draw信号,您可以将回调连接到该信号:每次小部件需要重绘自身时都会调用该回调。

换句话说,您的绘制回调应该根据某个 state 改变它自己绘制的方式:

#include <gtk/gtk.h>

gboolean button_clicked = FALSE;

gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    if (button_clicked) {
        cairo_set_source_rgb(cr, 1, 0, 0);
        cairo_move_to(cr, 0, 0);
        cairo_line_to(cr, 300, 300);
        cairo_stroke(cr);
    }

    return FALSE;
}

gboolean button_cb( GtkWidget *widget, GdkEventButton *event, gpointer object )
{
    button_clicked = TRUE;
    // Explicitly notify the drawing area it should redraw itself
    gtk_widget_queue_draw (widget);
}

int main (int argc, char *argv[])
{
   gtk_init( &argc, &argv );

   GtkWidget *window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
   g_signal_connect( window, "destroy", G_CALLBACK (gtk_main_quit), NULL );

   GtkWidget *da = gtk_drawing_area_new();
   gtk_widget_add_events( da, GDK_BUTTON_PRESS_MASK );
   gtk_widget_set_size_request( da, 300, 300 );
   g_signal_connect( da, "draw", G_CALLBACK(draw_cb), NULL );
   g_signal_connect( da, "button_press_event", G_CALLBACK(button_cb), NULL );
   
   gtk_container_add( GTK_CONTAINER (window), da );
   gtk_widget_show( da );
   gtk_widget_show( window );

   gtk_main();

   return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM