简体   繁体   English

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

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

The mechanism for retrieving the cairo context in GDK/GTK is broken and/or not properly described in documentation.在 GDK/GTK 中检索 cairo 上下文的机制已损坏和/或文档中未正确描述。 I am trying to draw in a GtkDrawingArea from a button press callback with no success.我试图从按钮按下回调中绘制 GtkDrawingArea ,但没有成功。 The documentation suggests doing文档建议做

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 );

and then drawing using the cairo context and then ending properly on然后使用 cairo 上下文绘制,然后正确结束

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

All this seems to work on GTK 3.22.30 (and cairo 1.15.12), but not on GTK 3.24.30 (and cairo 1.16.0).所有这些似乎都适用于 GTK 3.22.30(和 cairo 1.15.12),但不适用于 GTK 3.24.30(和 cairo 1.16.0)。 Obviously something has been changed.显然有些东西已经改变了。 Any idea?任何想法? A full example follows:一个完整的例子如下:

// 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() is a low-level call which you should normaly only ever need if you're trying to draw something using GDK/Cairo without involving GTK. gdk_window_begin_draw_frame()是一个低级调用,通常只有在您尝试使用 GDK/Cairo 绘制东西而不涉及 GTK 时才需要它。 Since you're obviously using GTK, you shouldn't be using it.由于您显然使用的是 GTK,因此您不应该使用它。

The way to do drawing with Cairo in GTK is by using a GtkDrawingArea (or if you want to implement very custom behaviour: subclassing GtkWidget and implementing your own draw() vfunc).在 GTK 中使用 Cairo 进行绘图的方法是使用GtkDrawingArea (或者如果您想实现非常自定义的行为:继承GtkWidget并实现您自己的draw() vfunc)。 As you already saw, it has a draw signal, which you can connect a callback to: that callback will be called each time the widget needs to redraw itself.正如您已经看到的,它有一个draw信号,您可以将回调连接到该信号:每次小部件需要重绘自身时都会调用该回调。

In other words, your draw callback should change how it draws itself depending on a certain state:换句话说,您的绘制回调应该根据某个 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