signal_connect 函数中的指针工作不正确

Posted

技术标签:

【中文标题】signal_connect 函数中的指针工作不正确【英文标题】:The pointer in signal_connect function is not working correct 【发布时间】:2019-06-15 12:03:01 【问题描述】:

我想使用g_signal_connect() 函数来更改特定struct/class 中的数据。所以,在我看来,最好的方法是使用指向struct 的指针。问题是指针的信息似乎一直在变化。

我花了很多时间来弄清楚为什么会发生这种情况,但我不知道。 我可以编译和运行代码而没有任何错误,但输出总是不同。

以后我想用几个event_box来连接一个struct数组或者一个class数组(event_box[0]连接到data[0],...)。

我希望有人能理解我的意思,我会很高兴得到任何帮助。

#include<gtk/gtk.h>

struct d

bool status;
int ID;
;

void end_program(GtkWidget *wid, gpointer ptr)

gtk_main_quit();


void box_click(GtkWidget *wid, gpointer user_data)

    struct d *data = (struct d*)user_data;
    printf("status  = %i\n", data->status);
    printf("ID      = %i\n", data->ID);


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

    struct d data;
    data.status = 0;
    data.ID = 1;

    gtk_init(&argc, &argv);

    GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    GtkWidget *event_box  = gtk_event_box_new();
    g_signal_connect(G_OBJECT(event_box), "button_press_event", G_CALLBACK(box_click), &data); 

    gtk_container_add(GTK_CONTAINER(win), event_box);
    gtk_widget_show_all(win);
    g_signal_connect(win, "delete_event", G_CALLBACK(end_program),NULL);

    gtk_main();

    return 0;

如果我点击该框几次输出:

status  = 4
ID      = 32193184
status  = 5
ID      = 32193184
status  = 4
ID      = 32193184
status  = 6
ID      = 32193184
status  = 4
ID      = 32193184

【问题讨论】:

【参考方案1】:

我希望有人能理解我的意思,我会为任何事情感到高兴 帮助。

嗯,是的.. 您为 button-press-event 使用了错误的函数原型。 button-press-event 的原型是:

The “button-press-event” signal

gboolean
user_function (GtkWidget *widget,
               GdkEvent  *event,
               gpointer   user_data)

注意:正确的信号是 "button-press-event" 而不是 "button_press_event",尽管有一个 #define 允许第二种形式工作)

见GtkWidget (Gtk+3 Manual)。所以你的函数应该是这样的:

gboolean box_click(GtkWidget *wid, GdkEvent *event, gpointer user_data)

    struct d *data = user_data;            /* no need for cast, gpointer is void* */
    g_print("status  = %d\n", data->status);
    g_print("ID      = %d\n", data->ID);

    return TRUE;    /* to prevent further handling, FALSE otherwise */

    (void)wid;      /* cast to void to avoid unused var warning */
    (void)event;

额外的尼特

使用g_print 代替printf,使用gboolean 代替bool。虽然传递 address of 对于小型结构来说很好,但对于大型结构,您应该使用 g_slice_new 进行分配。

【讨论】:

谢谢!到目前为止,这对我来说非常有效。我只是不明白为什么我必须使用struct d *data = (struct d*)user_data 而不是struct d *data = user_data。如果我尝试在没有(struct d*) 的情况下使用它,则会收到此错误:main_.cpp: In function ‘gboolean box_click(GtkWidget*, GdkEvent*, gpointer)’: main_.cpp:16:22: error: invalid conversion from ‘gpointer aka void*’ to ‘d*’ [-fpermissive] struct d *data = user_data; 哦,如果您使用 C++ 编译,那么是的,您需要 (struct d*) 演员表。 Gtk 通常用 C 编译,gtkmm 是 C++。 (这也解释了你为什么选择bool 而不是gboolean)。谁让你在 Gtk 中使用 .cpp 好的,我明白了。没有人告诉我使用 .cpp 我只是使用它,因为在我开始使用 gtk 之前,我总是用 C++ 编写程序。大约 2 个月前,我开始自学 gtk。所以我想最好还是用 C 代码来学习如何将 C++ 与 gtk 一起使用?我认为 C 对于我的应用程序来说应该足够了。 是的,我会鼓励 C 用于 Gtk+,这就是它的设计目的。我用gcc -Wall -Wextra -o evboxstruct evboxstruct.c `pkg-config --cflags --libs gtk+-3.0` 构建了你的代码 好的,谢谢。在我使用gtkmm-3.0 之前,因为我在某处读过它。现在我把我的整个程序改成了 C 并用gtk+-3.0 编译它。我对此有点困惑,所以谢谢你的解释。【参考方案2】:

我将稍微扩展一下@David C. Rankin 所说的这一部分:

(void)wid;      /* cast to void to avoid unused var warning */
(void)event;

因为有很多人不知道的重要事情。

强制转换的需要一直都是错误的,这是因为有另一个函数被调用:

g_signal_connect_swapped()

通过将第一个参数与最后一个参数交换来避免需要如此多的转换。

举个例子:

#include <gtk/gtk.h>

GtkWidget *createWindow ( const gint width, const gint height );
void user_function      ( GtkWidget *object, gpointer   user_data );

int main ( void )

    GtkWidget *window;
    gtk_init ( NULL, NULL );
    /// ***
    window = createWindow( 300, 300 );
    /// ***
    g_signal_connect ( window, "destroy",       G_CALLBACK( user_function ),  NULL );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();


GtkWidget *createWindow ( const gint width, const gint height )

    GtkWidget *window = gtk_window_new  ( GTK_WINDOW_TOPLEVEL );
    gtk_widget_set_size_request ( window, width, height );
    gtk_container_set_border_width ( GTK_CONTAINER ( window ), 50 );
    return window;


void user_function ( GtkWidget *object, gpointer   user_data )

    (void)object;
    (void)user_data;
    g_print( "Goodbye\n" );
    gtk_main_quit();

这里你可以看到回调函数签名是这样的:

void user_function ( GtkWidget *object, gpointer user_data )

现在在这种情况下需要两个强制转换,但我们可以通过调用 g_signal_connect_swapped 来删除其中一个(例如 gpointer 数据),如下所示:

g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  NULL 

甚至更好:

g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  (gpointer)"Goodbye" );

现在我们的程序如下所示:

#include <gtk/gtk.h>

GtkWidget *createWindow ( const gint width, const gint height );
void user_function      ( gpointer data );

int main ( void )

    GtkWidget *window;
    gtk_init ( NULL, NULL );
    /// ***
    window = createWindow( 300, 300 );
    /// ***
    g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  (gpointer)"Goodbye" );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();


GtkWidget *createWindow ( const gint width, const gint height )

    GtkWidget *window = gtk_window_new  ( GTK_WINDOW_TOPLEVEL );
    gtk_widget_set_size_request ( window, width, height );
    gtk_container_set_border_width ( GTK_CONTAINER ( window ), 50 );
    return window;


void user_function ( gpointer data )

    g_print( "%s\n", (char*)data );
    gtk_main_quit();

而且不再有 CAST。

这是一个更好的例子:

#include <gtk/gtk.h>

gboolean scroll_callback    ( GtkWidget *widget, GdkEvent  *event );
gboolean show_mouse_pressed ( GtkWidget *widget, GdkEventButton *event );

int main ( void )

    GtkWidget *window;
    GtkWidget *button;
    gtk_init( NULL, NULL );
    /// ***
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size( GTK_WINDOW( window ), 300, 250 );
    g_signal_connect( window, "destroy", gtk_main_quit, NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 50 );
    /// ***
    button = gtk_button_new_with_mnemonic( "_Click me" );

    gtk_widget_add_events( button, GDK_BUTTON_PRESS );
    gtk_widget_add_events( button, GDK_SCROLL_MASK );

    ///g_signal_connect( button, "clicked", gtk_main_quit, NULL );
    gtk_container_add( GTK_CONTAINER( window ), button );
    /// ***
    g_signal_connect_swapped( button, "button_press_event", G_CALLBACK( show_mouse_pressed ), window );
    g_signal_connect_swapped( button, "scroll_event", G_CALLBACK( scroll_callback ), window );
    /// ***
    gtk_widget_show_all( window );
    gtk_main();


gboolean scroll_callback ( GtkWidget *widget, GdkEvent  *event )

    if ( event->type == GDK_SCROLL ) /// Scroll was Catched ?
    
        if ( event->scroll.direction == GDK_SCROLL_DOWN ) /// It is down?
        
            g_print( "Scroll-Down Detected\n" );
            gtk_window_set_title( GTK_WINDOW( widget ), "Scroll-Down Detected" );
        

        if ( event->scroll.direction == GDK_SCROLL_UP ) /// It is up?
        
            g_print( "Scroll-UP Detected\n" );
            gtk_window_set_title( GTK_WINDOW( widget ), "Scroll-UP Detected" );
        
        return FALSE;
    
    return TRUE;


gboolean show_mouse_pressed ( GtkWidget *widget, GdkEventButton *event )

    assert ( widget != NULL );
    if ( gtk_widget_get_has_window ( widget ) )
    
        if ( event->type == GDK_BUTTON_PRESS )
        
            g_print( "The mouse clicked the button\n" );
            gtk_window_set_title( GTK_WINDOW( widget ),  "The mouse was Clicked" );
        
        return TRUE;
    
    return FALSE;

【讨论】:

以上是关于signal_connect 函数中的指针工作不正确的主要内容,如果未能解决你的问题,请参考以下文章

结构中的指针分配不工作,为啥值没有改变?

lambda 是不是应该衰减为模板代码中的函数指针?

成员结构的值在作为指针传递给函数后丢失

函数指针和继承

这是C ++中的未定义行为从悬空指针调用函数

静态库中的外部指针为空,当不是静态库时工作正常