2012-12-06 10 views
1

我有非常簡單的應用程序與文本輸入和按鈕。當我嘗試在其他線程中調用GtkWidget對象時,GTK應用程序崩潰

當用戶按下按鈕時,應用程序從URL下載文件(在其他線程中),並在成功時打開全部完成的對話框消息。在下載過程中我激活微調(如佔線)

enter image description here

因爲我不知道多久才能連接到下載文件我用單獨的線程用於這一目的。但在「對話框顯示」我的應用程序失敗,我得到錯誤被跟隨:

(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`` 

流程:主 - >稱之爲 「do_something」 - >創建線程,並稱之爲 「argument_thread」 - >

這裏是一個代碼片段:

typedef struct _Data 
{ 
GtkWidget *win; 
} Data; 

main

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

do_something

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); 
} 

請幫助我,

任何和所有的建議將不勝感激

回答

3

GTK是不是線程安全的,所以任何與GUI交互的東西將不得不在主線程上運行。

使用g_idle_add函數在下載完成時通知主線程。

+0

因此我從線程中調用'g_idle_add(callback_func,NULL)',其中'callback_func'是主線程中的方法,對嗎? –

+0

nm,我在錯誤的地方使用過'g_idle_add',謝謝,你節省了我的時間。 –

+0

獎勵:在出色的GTKmm中,它變成了'Glib :: signal_idle()。connect([&] {/ *一些lambda包含你需要延遲的代碼,直到GTK擁有的線程* /});'。因此,您甚至可以將延遲代碼寫入物理函數中,同時防止由於 –

0

根據@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; 

} 
+0

導致的恐慌,您應該從callback_func返回FALSE。 – ergosys

+0

@ergosys謝謝,我修正了 –

+0

對於其他讀者的快速參考,空閒或超時回調的布爾返回值指示是否應該重複;如果爲false,則回調被取消/取消註冊。 –

相關問題