如何使用 Cairo 和 Gtk3 在 GtkDrawingArea 中绘制一条线

Posted

技术标签:

【中文标题】如何使用 Cairo 和 Gtk3 在 GtkDrawingArea 中绘制一条线【英文标题】:How to draw a line in a GtkDrawingArea using Cairo with Gtk3 【发布时间】:2012-01-19 19:55:58 【问题描述】:

有人可以向我展示一个使用 C 语言在 Cairo 和 Gtk3 中绘制 GtkDrawingArea 中的一条线的最小工作示例。我试图在 Gtk3 测试文件夹中修改testcairo.c,但我无法让它工作。请不要在开罗网站上推荐教程; Zetcode.com 或 gnome.org,它们要么不适用于 Gtk3,要么不是最小的工作示例。

【问题讨论】:

【参考方案1】:

我明白了。关键区别在于,对于 gtk+3,您必须从“绘制”信号处理程序中进行绘制。使用 gtk+2 它来自“expose-event”信号处理程序。这是minimal working example。

【讨论】:

这很有帮助。我想提一下,如果您使用的是“draw”信号,ZetCode.com 上的教程现在已经是最新的了。 这个链接现在每次点击它都会导致一个随机的广告客户,就好像这个域已经丢失了一样。我搜索了标题,但没有找到任何东西。答案的核心信息有效,但演示丢失。 @cardiffspaceman 我从网络档案中找到了它,这是少数几个可能比Stack Overflow 更长寿的网站之一。不过,您必须向下滚动两页才能找到任何代码。【参考方案2】:

这是一个完整的工作示例:

确保已安装 gtk3-devel(在 Fedora #dnf install gtk3-devel 中)

在 Ubuntu 中:sudo apt install libgtk-3-dev

编译:gcc draw.c `pkg-config --cflags gtk+-3.0 --libs gtk+-3.0` -o draw

#include <gtk/gtk.h>
gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)

    guint width, height;
    GdkRGBA color;
    GtkStyleContext *context;
    
    context = gtk_widget_get_style_context (widget);
    width = gtk_widget_get_allocated_width (widget);
    height = gtk_widget_get_allocated_height (widget);
    gtk_render_background(context, cr, 0, 0, width, height);
    cairo_arc (cr, width/2.0, height/2.0, MIN (width, height) / 2.0, 0, 2 * G_PI);
    gtk_style_context_get_color (context, gtk_style_context_get_state (context), &color);
    gdk_cairo_set_source_rgba (cr, &color);
    gdk_cairo_set_source_rgba (cr, &color);
    cairo_fill (cr);
    return FALSE;


gint main(int argc,char *argv[])

    GtkWidget *window, *drawing_area;
    
    gtk_init (&argc, &argv);
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    
    drawing_area = gtk_drawing_area_new();
    gtk_container_add (GTK_CONTAINER (window), drawing_area);
    gtk_widget_set_size_request (drawing_area, 200, 100);
    g_signal_connect (G_OBJECT (drawing_area), "draw", G_CALLBACK (draw_callback), NULL);
    gtk_widget_show_all (window);
    gtk_main ();
    return 0;

【讨论】:

【参考方案3】:

任何人都在 2020 年这样做。这是重构为使用 GTK3 的 Zetcode 示例,它绘制了您想要的内容,因此线条不会奇怪地连接。我添加了 cmets 来解释发生了什么。

/* To compile: gcc linetest.c -o linetest `pkg-config --cflags --libs gtk+-3.0`
* C program for basic drawing with GTK+ and cairo.
* Working 2020 example if this got you stuck, http://zetcode.com/gfx/cairo/basicdrawing/
* Note: the above command line uses backticks (`), it's right before 1 on your keyboard.
*/
#include <cairo.h>
#include <gtk/gtk.h>

//function prototypes
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
static void do_drawing(cairo_t *cr);
static gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
//end of function prototypes

/* Global variables for storing mouse coordinates,
* count is index of arrays, coordx and coordy are x and y coordinates of the mouse
*/
struct 
  int count;
  double coordx[100];
  double coordy[100];
 glob;

/* Function: on_draw_event
*Parameters: GtkWidget, cairo_t, gpointer
*Use: This is the function we attach to the main method when we want to draw. It calls the do_drawing method.
*Example: g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);
*/
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)

  do_drawing(cr);
  return FALSE;



/* Function: do_drawing
*Parameters: cairo_t
*Use: It sets cairo canvas settings, and draws shapes with a for loop
*Settings: are commented
*Note: printf is used during debugging to find mouse click coordinates :)
*/
static void do_drawing(cairo_t *cr)

  cairo_set_source_rgb(cr, 0, 0, 0);//Line colour
  cairo_set_line_width(cr, 0.5);//Line width

  if (glob.count > 1) 
    cairo_move_to(cr, glob.coordx[0], glob.coordy[0]);
    //printf("from: x:%f, y:%f\n",glob.coordx[0],glob.coordy[0]);
  

  //Connect lines.
  for (int i = 1; i < glob.count; ++i) 
    cairo_line_to(cr, glob.coordx[i], glob.coordy[i]);
    //printf("to: x:%f, y:%f\n",glob.coordx[i],glob.coordy[i]);
  

  // Draw the above.
  cairo_stroke(cr);
  //resets array so shape can be drawn again.
  glob.count = 0;



/* Function: clicked
*Parameters: GtkWidget, GdkEventButton, gpointer
*Use: Registers mouse clicks, 1 is right, 3 is left on laptop. Clicks may be 1, 2 or 3 on a desktop
*Note: printf is used during debugging to find mouse click coordinates :)
*/
static gboolean clicked(GtkWidget *widget, GdkEventButton *event,
  gpointer user_data)

  if (event->button == 1) 
       // printf("Right Click");
    glob.coordx[glob.count] = event->x;
    glob.coordy[glob.count++] = event->y;

        // int i;
        // for (i =0; i <= glob.count-1; i++) 
        //   printf("%f\n", glob.coordx[i]);
        // 
  

  if (event->button == 3) 
        //printf("left Click");
    gtk_widget_queue_draw(widget);
  

  return TRUE;


//Main method.
int main(int argc, char *argv[])

  //widget variables, window and drawing area.
  GtkWidget *window;
  GtkWidget *darea;

  //Set global count 0, so array is at beginning whenver program starts.
  glob.count = 0;

  //Always have this to start GTK.
  gtk_init(&argc, &argv);

  //Set new window, set new drawing area.
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  darea = gtk_drawing_area_new();

  //Add the drawing area to the window.
  gtk_container_add(GTK_CONTAINER(window), darea);

  //You need this to register mouse clicks.
  gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);

  //Attaching draw function to the main method.
  g_signal_connect(G_OBJECT(darea), "draw",
    G_CALLBACK(on_draw_event), NULL);

  //You can close window when you exit button.
  g_signal_connect(window, "destroy",
    G_CALLBACK(gtk_main_quit), NULL);

  //Register if left or right mouse click.
  g_signal_connect(window, "button-press-event",
    G_CALLBACK(clicked), NULL);

  //Set window position, default size, and title.
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
  gtk_window_set_title(GTK_WINDOW(window), "Lines");

  //Show all widgets.
  gtk_widget_show_all(window);

  //start window
  gtk_main();

  return 0;

【讨论】:

您现在可以将它移植到 GTK4 ;) 这将很快发布。 .. GTK4 也会有 GTKSnapshot,并且不再使用 Cairo。【参考方案4】:
// compila con valac --pkg gtk+-3.0 nombre_archivo.gs
uses 
    Gtk
    Cairo

init
    Gtk.init (ref args)
    var TestCairo = new Ventana ()
    TestCairo.show_all ()
    Gtk.main ()

class Ventana : Window

    area: Gtk.DrawingArea   

    init        
        title = "Test Genie + GTK + Cairo"
        set_default_size (400, 400)     
        window_position = WindowPosition.CENTER
        destroy.connect(Gtk.main_quit)

        // área de dibujo
        area: Gtk.DrawingArea = new Gtk.DrawingArea ()
        // conecta el área de dibujo al método dibujar
        area.draw.connect (dibujar) 
        // añade el área de dibujo a la ventana
        add (area)

    def dibujar (context : Context) : bool      

        context.set_source_rgba (1, 0, 0, 1)        
        context.set_line_width (2)

        context.move_to (200, 100)
        context.line_to (200, 300)

        context.move_to (100, 200)
        context.line_to (300, 200)      

        context.stroke ()

        return true

更多 Genie + Gtk + Cairo 的例子在http://genie.webierta.skn1.com

【讨论】:

以上是关于如何使用 Cairo 和 Gtk3 在 GtkDrawingArea 中绘制一条线的主要内容,如果未能解决你的问题,请参考以下文章

将 cairo 与 gtk3 一起使用

Gtk3 和 cairo g_timeout_add 不起作用

使用 Cairo 的 Ruby Gtk3 内存泄漏

Gtk3和开罗动画抽搐

如何在 Gtk 下创建 cairo-gl 曲面

如何在Haskell gtk2hs中将Cairo绘图渲染到打印机