How to draw a line in a GtkDrawingArea using Cairo with Gtk3
Asked Answered
C

4

6

Could someone please show me a minimal working example of using C language for Cairo with Gtk3 to draw a single line in a GtkDrawingArea. I've tried to modify testcairo.c in the Gtk3 tests folder but I can't get it to work. Please don't suggest the tutorials at the Cairo site; Zetcode.com or gnome.org which are either not for use with Gtk3 or not minimal working examples.

Coverall answered 19/1, 2012 at 19:55 Comment(0)
C
13

I got it. The key difference is that for gtk+3 you must draw from within a "draw" signal handler. With gtk+2 it's from within the "expose-event" signal handler. Here's a minimal working example.

Coverall answered 11/7, 2012 at 16:41 Comment(3)
This helped a lot. I wanted to mention that if you are using the "draw" signal, the tutorial in ZetCode.com is now up to date with that.Arapaho
This link now leads to a random advertiser every time you click it, as though the domain had been lost. I searched on the title but I didn't find anything. The core message of the answer is valid but the demonstration is lost.Downhill
@cardiffspaceman I dug it up from the web archive, one of the few sites which might outlast Stack Overflow. You have to scroll down two page-fulls to find any code, though.Cinquecento
M
8

Here is a complete working example:

  • Make sure gtk3-devel is installed (in Fedora #dnf install gtk3-devel)

  • In Ubuntu: sudo apt install libgtk-3-dev

to compile: 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;
}
Mercator answered 30/1, 2017 at 17:49 Comment(0)
B
5

Anyone doing this in 2020. This is the Zetcode example refactored to work with GTK3, and it draws what you want so the lines are not weirdly connected. I've added comments to explain what's happening.

/* 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;
}
Beggs answered 20/6, 2020 at 12:12 Comment(2)
You can port it to GTK4 now ;) This will be released any time soon.Guide
.. Also GTK4 will have GTKSnapshot, and will no longer use Cairo.Guide
B
0
// 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

enter image description here

More examples of Genie + Gtk + Cairo in http://genie.webierta.skn1.com

Burgwell answered 31/1, 2017 at 16:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.