按下按钮后如何在Cairo gtk3.0中正确绘制直线

按下按钮后如何在Cairo gtk3.0中正确绘制直线,gtk,gtk3,cairo,Gtk,Gtk3,Cairo,我正在遵循zetecode()并尝试构建我的第一个在按下按钮后绘制一条线的示例,但是失败了,我就是不知道为什么。单击按钮时,我遇到了一个分段错误错误 #include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)

我正在遵循zetecode()并尝试构建我的第一个在按下按钮后绘制一条线的示例,但是失败了,我就是不知道为什么。单击按钮时,我遇到了一个
分段错误
错误

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

static void do_drawing(cairo_t *);

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
    gpointer user_data)
{
    do_drawing(cr);
    return FALSE;
}


static void do_drawing(cairo_t *cr)
{
    cairo_set_source_rgb(cr,0,0,0);
    cairo_set_line_width(cr,0.5);

    cairo_move_to(cr,400,400);
    cairo_line_to(cr,400,200);

    cairo_stroke(cr);


}


int main(int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *darea;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 800, 480);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    darea = gtk_drawing_area_new();
    GtkWidget *btn_draw = gtk_button_new_with_label("Draw a line");


    GtkWidget *mainwindow = gtk_grid_new();
        gtk_grid_set_row_spacing (GTK_GRID (mainwindow), 16);
        gtk_grid_set_column_spacing (GTK_GRID (mainwindow), 16);
    gtk_grid_set_row_homogeneous(GTK_GRID(mainwindow), TRUE);
    gtk_grid_set_column_homogeneous(GTK_GRID(mainwindow), TRUE);

    gtk_widget_set_margin_left(mainwindow,20);
    gtk_widget_set_margin_right(mainwindow,20);
    gtk_widget_set_margin_top(mainwindow,20);
    gtk_widget_set_margin_bottom(mainwindow,20);

    gtk_grid_attach(GTK_GRID(mainwindow),btn_draw,0,0,1,1);  
    gtk_grid_attach(GTK_GRID(mainwindow),darea,1,0,5,1);  

    gtk_container_add(GTK_CONTAINER(window),mainwindow);

    g_signal_connect(G_OBJECT(btn_draw),"clicked",G_CALLBACK(on_draw_event),NULL);

    gtk_widget_show_all(window);

        gtk_main ();

        return(0);

}
#包括
#包括
静态空心图(cairo_t*);
静态gboolean on_draw_事件(GtkWidget*widget,cairo_t*cr,
gpointer用户(U数据)
{
杜鲁图(cr);
返回FALSE;
}
静态void do_图纸(cairo_t*cr)
{
cairo\u set\u source\u rgb(cr,0,0,0);
cairo_集_线_宽(cr,0.5);
开罗(cr,400400);
开罗线至(cr,400200);
脑卒中(cr);
}
int main(int argc,char*argv[])
{
GtkWidget*窗口;
GtkWidget*darea;
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
gtk_窗口设置默认大小(gtk_窗口(窗口),800480);
gtk_窗口设置位置(gtk_窗口(窗口)、gtk_WIN_位置中心);
darea=gtk_图纸_区域_新();
GtkWidget*btn_draw=gtk_按钮_新建_,带_标签(“画线”);
GtkWidget*mainwindow=gtk_grid_new();
gtk网格设置行间距(gtk网格(主窗口),16);
gtk网格设置列间距(gtk网格(主窗口),16);
gtk_网格_集合_行_齐次(gtk_网格(主窗口),真);
gtk网格集列均匀(gtk网格(主窗口),真);
gtk_小部件_设置_边距_左侧(主窗口,20);
gtk_控件_设置_边距_右侧(主窗口,20);
gtk_小部件_设置_边距_顶部(主窗口,20);
gtk_小部件_设置_边距_底部(主窗口,20);
gtk_网格连接(gtk_网格(主窗口),btn_绘图,0,0,1,1);
gtk_格栅连接(gtk_格栅(主窗口),darea,1,0,5,1);
gtk_容器添加(gtk_容器(窗口),主窗口);
g_信号连接(g_对象(btn_绘制),“单击”,g_回调(在绘制事件上),NULL);
gtk_小部件_全部显示(窗口);
gtk_main();
返回(0);
}
这就是它的样子:

附言:

我听从@jku的建议,使用gboolean draw_a_线记录按钮状态,使用gtk_widget_queue_draw(widget)重新绘制。我注意到的唯一问题是,当我单击按钮时,它不会立即绘制,但我必须隐藏窗口或拉伸窗口以使其显示。我想我需要添加一些自动_update()函数,但我是GUI设计的新手,所以有人能告诉我如何在单击按钮后立即显示行吗

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

static void do_drawing(cairo_t *);
GtkWidget *window;
GtkWidget *darea;
gboolean draw_a_line = false;

static gboolean on_draw_event(GtkWidget *widget, GdkEventExpose *event, 
    gpointer user_data)
{
    cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(widget)));
    do_drawing(cr);

    return FALSE;
}


static void do_drawing(cairo_t *cr)
{
    cairo_set_source_rgb(cr,0,0,0);
    cairo_set_line_width(cr,0.5);
    if (draw_a_line){
        cairo_move_to(cr,400,400);
        cairo_line_to(cr,400,200);
        cairo_stroke(cr);
    }

}


static void on_clicked(GtkWidget *widget, gpointer data)
{
    draw_a_line = true;
    gtk_widget_queue_draw(widget);

}

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


    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 800, 480);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    darea = gtk_drawing_area_new();
    GtkWidget *btn_draw = gtk_button_new_with_label("Draw a line");


    GtkWidget *mainwindow = gtk_grid_new();
        gtk_grid_set_row_spacing (GTK_GRID (mainwindow), 16);
        gtk_grid_set_column_spacing (GTK_GRID (mainwindow), 16);
    gtk_grid_set_row_homogeneous(GTK_GRID(mainwindow), TRUE);
    gtk_grid_set_column_homogeneous(GTK_GRID(mainwindow), TRUE);

    gtk_widget_set_margin_left(mainwindow,20);
    gtk_widget_set_margin_right(mainwindow,20);
    gtk_widget_set_margin_top(mainwindow,20);
    gtk_widget_set_margin_bottom(mainwindow,20);

    gtk_grid_attach(GTK_GRID(mainwindow),btn_draw,0,0,1,1);  
    gtk_grid_attach(GTK_GRID(mainwindow),darea,1,0,5,1);  

    gtk_container_add(GTK_CONTAINER(window),mainwindow);


    g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); 
    g_signal_connect(G_OBJECT(btn_draw),"clicked",G_CALLBACK(on_clicked),darea);

    gtk_widget_show_all(window);

        gtk_main ();

        return(0);

}
#包括
#包括
静态空心图(cairo_t*);
GtkWidget*窗口;
GtkWidget*darea;
gboolean绘制线=假;
静态gboolean on_draw_事件(GtkWidget*小部件、GdkEventExpose*事件、,
gpointer用户(U数据)
{
cairo_t*cr=gdk_cairo_create(gtk_widget_获取窗口(gtk_widget(widget)));
杜鲁图(cr);
返回FALSE;
}
静态void do_图纸(cairo_t*cr)
{
cairo\u set\u source\u rgb(cr,0,0,0);
cairo_集_线_宽(cr,0.5);
如果(画一条线){
开罗(cr,400400);
开罗线至(cr,400200);
脑卒中(cr);
}
}
单击时静态无效(GtkWidget*小部件,gpointer数据)
{
画一条线=真;
gtk_小部件_队列_绘制(小部件);
}
int main(int argc,char*argv[])
{
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
gtk_窗口设置默认大小(gtk_窗口(窗口),800480);
gtk_窗口设置位置(gtk_窗口(窗口)、gtk_WIN_位置中心);
darea=gtk_图纸_区域_新();
GtkWidget*btn_draw=gtk_按钮_新建_,带_标签(“画线”);
GtkWidget*mainwindow=gtk_grid_new();
gtk网格设置行间距(gtk网格(主窗口),16);
gtk网格设置列间距(gtk网格(主窗口),16);
gtk_网格_集合_行_齐次(gtk_网格(主窗口),真);
gtk网格集列均匀(gtk网格(主窗口),真);
gtk_小部件_设置_边距_左侧(主窗口,20);
gtk_控件_设置_边距_右侧(主窗口,20);
gtk_小部件_设置_边距_顶部(主窗口,20);
gtk_小部件_设置_边距_底部(主窗口,20);
gtk_网格连接(gtk_网格(主窗口),btn_绘图,0,0,1,1);
gtk_格栅连接(gtk_格栅(主窗口),darea,1,0,5,1);
gtk_容器添加(gtk_容器(窗口),主窗口);
g_信号连接(g_对象(darea),“绘制”,g_回调(在绘制事件上),NULL);
g_信号连接(g_对象(btn_绘图),“点击”,g_回调(点击时),darea);
gtk_小部件_全部显示(窗口);
gtk_main();
返回(0);
}

您已经为(大概)绘图区域编写了一个绘图处理程序,但将其连接到按钮的单击信号。这两者具有完全不同的功能特征,因此segfault并不令人惊讶

不要直接在单击的按钮处理程序上绘制,而是更改一些应用程序状态(例如,
gboolean应该绘制线
),并使用
gtk\u widget\u queue\u draw()
将绘图区域标记为需要重新绘制。然后在绘图区域的绘图处理程序中,根据应用程序状态决定要绘制什么(如
应该绘制线


在看了你链接到的例子之后,它似乎完全按照我的建议做了

您已经为(大概)绘图区域编写了一个绘图处理程序,但将其连接到按钮的单击信号。这两者具有完全不同的功能特征,因此segfault并不令人惊讶

不要直接在单击的按钮处理程序上绘制,而是更改一些应用程序状态(例如,
gboolean应该绘制线
),并使用
gtk\u widget\u queue\u draw()
将绘图区域标记为需要重新绘制。然后在绘图区域的绘图处理程序中,根据应用程序状态决定要绘制什么(如
应该绘制线

在看了你链接到的例子之后,它似乎完全按照我的建议做了

线路

gtk_widget_queue_draw(widget);
应该是

gtk_widget_queue_draw((GtkWidget*)data);
当按下按钮时,它将重新绘制

第一个参数“GtkWidget*widget”是“button”(自动传递),第二个参数是
static void on_clicked(GtkWidget *widget, gpointer data)
{
    draw_a_line = TRUE;
    gtk_widget_queue_draw((GtkWidget*)data);
}