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

时间:2012-01-19 19:55:58

标签: gtk cairo gtk3

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

6 个答案:

答案 0 :(得分:12)

我明白了。关键区别在于对于gtk + 3,您必须从“绘制”信号处理程序中进行绘制。使用gtk + 2,它来自“expose-event”信号处理程序。这是一个最小的工作示例: http://www.gtkforums.com/viewtopic.php?f=3&t=988&p=195286=Drawing+with+Cairo+in+GTK3#p195286

答案 1 :(得分:6)

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

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

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

编译:{{1​​}}

gcc draw.c pkg-config --cflags gtk+-3.0 --libs gtk+-3.0 -o draw

答案 2 :(得分:3)

任何在2020年进行此操作的人。这是Zetcode示例,可重构为与GTK3一起使用,它可以绘制所需的内容,因此不会出现奇怪的连接。我添加了评论以解释发生了什么。

/* 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;
}

答案 3 :(得分:0)

我不明白为什么它不起作用,它应该有用。

http://developer.gnome.org/gtk3/3.2/GtkDrawingArea.html

如您所见,带有cairo的gtk2与gtk3的几乎相同。

答案 4 :(得分: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

http://genie.webierta.skn1.com

中更多Genie + Gtk + Cairo的例子

答案 5 :(得分:0)

是-我同意...看起来应该对我有用。

但是我有一个单独的问题……我试图使用旋转框来控制要绘制多少条“线”(或图案..)。

我已经将一个信号连接到DrawingArea进行“绘制”,但是我不知道如何在旋转发生变化时重新调用“绘制”回调函数?

任何想法?


自己解决了!

需要使用 gtk_widget_queue_draw_area()函数。


main.c:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <gtk/gtk.h>
#include <cairo.h>

GtkWidget *window1;
GtkWidget *fixed1;
GtkWidget *frame1;
GtkWidget *draw1;
GtkWidget *spin1;
GtkWidget *spin2;

GtkBuilder *builder1;


// Neural Network Setup
gint N = 2;             // number of layers (hard-coded for now)
gint n1;                // number of neurons in first layer
gint n2;                // number of neurons in second layer
gint L;                 // index for tracking each neuron layer

// Graphics Plotting Setup
gdouble W = 500.0;          // hard-coded for now
gdouble H = 300.0;          // hard-coded for now
gdouble mx = 20.0;          // fixed x-margin
gdouble my = 20.0;          // fixed y-margin
gdouble X;                  // total "plotting" width
gdouble Y;                  // total "plotting" height
gdouble dx;                 // equal x-spacing between neurons
gdouble dy;                 // equal y-spacing between neurons
gdouble x;
gdouble y;
gdouble r = 20.0;       // radius



/*
    - redraw (on event..?)
 */

static gboolean on_draw1_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
{
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_set_line_width(cr, 1);

    int i = 0;

    // draw first layer neutrons
    L = 0;
    dy = Y / (n1 + 1);
    x = mx + ((L + 1) * dx);    // x-coordinate of given neuron
    for (i = 0; i < n1; i++)
    {
        y = my + ((i + 1) * dy);        // y-coordinate of given neuron
        cairo_move_to(cr, x + r, y);
        cairo_arc(cr, x, y, r, 0.0, 2.0 * M_PI);
        cairo_close_path(cr);
    }

    // draw second layer neutrons
    L = 1;
    dy = Y / (n2 + 1);
    x = mx + ((L + 1) * dx);
    for (i = 0; i < n2; i++)
    {
        y = my + ((i + 1) * dy);
        cairo_move_to(cr, x + r, y);
        cairo_arc(cr, x, y, r, 0.0, 2.0 * M_PI);
        cairo_close_path(cr);
    }

    cairo_stroke(cr);
    //cairo_fill(cr);

    // FALSE -> allow further events
    return FALSE;
}


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

    builder1    = gtk_builder_new_from_file("main2.ui");
    window1     = GTK_WIDGET(gtk_builder_get_object(builder1, "window1"));

    fixed1      = GTK_WIDGET(gtk_builder_get_object(builder1, "fixed1"));
    draw1       = GTK_WIDGET(gtk_builder_get_object(builder1, "draw1"));
    spin1       = GTK_WIDGET(gtk_builder_get_object(builder1, "spin1"));
    spin2       = GTK_WIDGET(gtk_builder_get_object(builder1, "spin2"));

    g_signal_connect(window1, "destroy", G_CALLBACK(gtk_main_quit), NULL);




    g_signal_connect(G_OBJECT(draw1), "draw", G_CALLBACK(on_draw1_draw), NULL);
    gtk_widget_add_events(draw1, 0);




    gtk_builder_connect_signals(builder1, NULL);

    g_object_unref(builder1);


    n1 = 3;
    n2 = 2;
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin1), (gdouble)n1);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin2), (gdouble)n2);

    X = W - (2 * mx);
    Y = H - (2 * my);
    dx = X / (N + 1);



    gtk_widget_show_all(window1);

    gtk_main();

    return 0;
}

void on_spin1_value_changed(GtkSpinButton *b)
{
    n1 = gtk_spin_button_get_value_as_int(b);
}

void on_spin2_value_changed(GtkSpinButton *b)
{
    n2 = gtk_spin_button_get_value_as_int(b);
}

刀形文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkAdjustment" id="adjustment1">
    <property name="upper">100</property>
    <property name="step_increment">1</property>
    <property name="page_increment">10</property>
  </object>
  <object class="GtkAdjustment" id="adjustment2">
    <property name="upper">100</property>
    <property name="step_increment">1</property>
    <property name="page_increment">10</property>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkFixed" id="fixed1">
        <property name="width_request">520</property>
        <property name="height_request">400</property>
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkSpinButton" id="spin2">
            <property name="width_request">100</property>
            <property name="height_request">35</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="adjustment">adjustment2</property>
            <property name="climb_rate">1</property>
            <signal name="value-changed" handler="on_spin2_value_changed" swapped="no"/>
          </object>
          <packing>
            <property name="x">150</property>
            <property name="y">360</property>
          </packing>
        </child>
        <child>
          <object class="GtkSpinButton" id="spin1">
            <property name="width_request">100</property>
            <property name="height_request">35</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="adjustment">adjustment1</property>
            <property name="climb_rate">1</property>
            <signal name="value-changed" handler="on_spin1_value_changed" swapped="no"/>
          </object>
          <packing>
            <property name="x">20</property>
            <property name="y">360</property>
          </packing>
        </child>
        <child>
          <object class="GtkFrame" id="frame1">
            <property name="width_request">500</property>
            <property name="height_request">300</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_left">10</property>
            <property name="margin_right">10</property>
            <property name="margin_top">10</property>
            <property name="margin_bottom">10</property>
            <property name="label_xalign">0</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkAlignment">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="margin_left">10</property>
                <property name="margin_right">10</property>
                <property name="margin_top">10</property>
                <property name="margin_bottom">10</property>
                <child>
                  <object class="GtkDrawingArea" id="draw1">
                    <property name="width_request">500</property>
                    <property name="height_request">300</property>
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                  </object>
                </child>
              </object>
            </child>
            <child type="label">
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Neural Network</property>
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>