【问题标题】:GTK Application crashes when I try to call GtkWidget object in other Thread当我尝试在其他线程中调用 GtkWidget 对象时,GTK 应用程序崩溃
【发布时间】:2012-12-06 23:15:41
【问题描述】:

我有一个非常简单的应用程序,带有文本输入和按钮。

当用户按下按钮时,应用程序从 URL 下载文件(在其他线程中),成功时会打开所有完成的对话框消息。在下载期间我激活微调器(如忙)

由于我不知道连接下载文件需要多长时间,我为此使用了单独的线程。但是在“对话框显示”上,我的应用程序失败了,并且出现了以下错误:

(enter_license.exe:210232): Gdk-WARNING **: gdkdrawable-win32.c:1873: GetDC failed: Invalid window handle.

(enter_license.exe:210232): Gdk-WARNING **: gdkgc-win32.c:968: GetCurrentObject failed: The handle is invalid.

(enter_license.exe:210232): Gdk-WARNING **: gdkgc-win32.c:970: RestoreDC failed: The handle is invalid.

(enter_license.exe:210232): Gdk-CRITICAL **: _gdk_win32_drawable_release_dc: assertion `impl->hdc_count > 0' failed

(enter_license.exe:210232): Gdk-WARNING **: gdkwindow-win32.c:2216: SetWindowLongPtr failed: Invalid window handle.

当我尝试从单独的线程调用 GTK 对象时,听起来好像有问题。 也许不知何故我需要调用句柄(回调)来在主线程中实现“show_dialog”?

编译:

 gcc -IC:/MinGW/include -o enter_license enter_license.c  `pkg-config --libs --cflags gtk+-2.0 gthread-2.0``

流程:main -> 调用“do_something” -> 创建线程并调用“argument_thread” ->

这是一段sn-ps的代码:

typedef struct _Data
{
 GtkWidget *win; 
 } Data;

主要

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

 gtk_init(&argc, &argv);
 window = do_something(NULL, argv);
 g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
 gtk_main();    

return 0;
}

做某事

GtkWidget * do_something(GtkWidget *do_widget, char **argv){
  ....
 GtkWidget *window;

if (!window){
window = gtk_dialog_new_with_buttons ("GtkSpinner",
                                      GTK_WINDOW (do_widget),
                                      0,
                                      GTK_STOCK_CLOSE,
                                      GTK_RESPONSE_NONE,
                                      NULL);
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);

g_signal_connect (window, "response", G_CALLBACK (gtk_widget_destroy), NULL);
g_signal_connect (window, "destroy",  G_CALLBACK (gtk_widget_destroyed), &window);


 ....

if (!gtk_widget_get_visible (window)){
 gtk_widget_show_all (window);
}
else{
 gtk_widget_destroy (window);
}

    // define thread
    GThread*  thread;
    GError*   err;  
    Data data;

data.win = window;

     thread = g_thread_create((GThreadFunc)argument_thread,&data,FALSE, &err);
 return window;
}

show_dialog

gboolean show_dialog( GtkWidget* mw)
{
 GtkWidget *dialog;

  printf("BOO: \n");

  // here all works fine
  sleep(3000);
  gtk_widget_show(spinner_sensitive);
  gtk_spinner_start (GTK_SPINNER (spinner_sensitive));
  sleep(3000);
  gtk_spinner_stop (GTK_SPINNER (spinner_sensitive));
  sleep(3000);
  gtk_widget_hide(spinner_sensitive);

  printf("BOO\n");
  // here dialog is shown for 1-10 milisec and get error.

     dialog = gtk_message_dialog_new (GTK_WINDOW(mw),
                     GTK_DIALOG_DESTROY_WITH_PARENT,
                     GTK_MESSAGE_INFO,
                     GTK_BUTTONS_CLOSE,
                     "Downloaded successfully");

             g_signal_connect_swapped (G_OBJECT (dialog), "response",
                     G_CALLBACK (gtk_widget_destroy),
                     G_OBJECT (dialog));
             gtk_widget_show(dialog);
printf("BOO\n");

}

argument_thread

void *argument_thread( gpointer ptr ) {
  Data *data = (Data*)ptr;
gdk_threads_enter();
 show_dialog (data->win);
 gdk_threads_leave();
  return( NULL );
}

请帮帮我,

我们将不胜感激任何和所有建议

【问题讨论】:

    标签: c multithreading gtk


    【解决方案1】:

    GTK 不是线程安全的,因此任何与 GUI 交互的东西都必须在主线程上运行。

    下载完成时使用g_idle_add函数通知你的主线程。

    【讨论】:

    • 所以我从线程中调用g_idle_add(callback_func, NULL),其中callback_func 是主线程中的方法,对吧?
    • nm,我在错误的地方使用了g_idle_add,谢谢,你节省了我的时间。
    • 奖励:在优秀的 GTKmm 中,它变成了Glib::signal_idle().connect( [&] { /* some lambda containing the code that you need delayed until the GTK-owning thread */ } );。因此,您甚至可以在同一函数中物理编写延迟代码,同时防止因而导致的恐惧
    【解决方案2】:

    根据@VincentPovirk 的说法,仅针对寻求答案的人,这是一个有效的实现:

    进入我们调用g_idle_add 和调用callback_func 的线程,这将在第二个线程(又名主线程)之外实现:

    gboolean show_dialog( GtkWidget* mw)
    {
    sleep(3000);
    gtk_widget_show(spinner_sensitive);
    gtk_spinner_start (GTK_SPINNER (spinner_sensitive));
    sleep(3000);
    gtk_spinner_stop (GTK_SPINNER (spinner_sensitive));
    gtk_widget_hide(spinner_sensitive);
    
    g_idle_add(callback_func, NULL);  
    }
    
    gint callback_func(void *unused)
    {
      GtkWidget *dialog;
      dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                         GTK_DIALOG_DESTROY_WITH_PARENT,
                         GTK_MESSAGE_INFO,
                         GTK_BUTTONS_CLOSE,
                         "Succeeded.");
    
                 g_signal_connect_swapped (G_OBJECT (dialog), "response",
                         G_CALLBACK (gtk_widget_destroy),
                         G_OBJECT (dialog));
                 gtk_widget_show(dialog);
    return FALSE;
    
    }
    

    【讨论】:

    • 你应该从 callback_func 返回 FALSE。
    • 为了方便其他读者快速参考,idle或timeout回调的布尔返回值表示是否应该重复;如果为 false,则回调被取消/注销。
    猜你喜欢
    • 1970-01-01
    • 2017-05-22
    • 1970-01-01
    • 2021-03-17
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多