LINUX.ORG.RU

GTK: создание нового окна в новом потоке


0

0

Такая проблема:

У меня есть основное окно (main_window). При нажатии в нем на кнопку вызывается новый поток. В свою очередь в какой-то момент (например глабальная переменная становится True) в этом потоке создается новое окно (win2). Так вот, когда это окно содалось оно не обновляется и ни на что не реагирует. И при завершеении потока это окно (win2) уничножается. А мне надо чтобы оно оставалось до тех пор пока я его сам не уничтожу.

Как мне это сделать?

Вот пример программы реазизующей это:

<pre>
#include "stdio.h"
#include "gtk/gtk.h"
#include "windows.h"

void * thread_func(void * data) {
// Создаем новое окно (в потоке)
GtkWidget * win2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget * lab2 = gtk_label_new("Label");
GtkWidget * btn2 = gtk_button_new_with_label("Button");
GtkWidget * vbox = gtk_vbox_new(FALSE, 0);

gtk_box_pack_start(GTK_BOX(vbox), lab2, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), btn2, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(win2), vbox);
gtk_window_present (win2);

gtk_widget_show_all(win2);

// Делаем цикл, чтобы еще задержаться в потоке
int i;

for (i = 0; i < 10; i++) {
printf(" %d thread helloo\n", i);
Sleep(500);
}
}

void btn1_click() {
DWORD tid;
printf("ctreaed new thread\n\n");
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, (LPVOID)NULL, 0, &tid);
}

int main(int argc, char **argv)
{
GtkWidget *main_window;
GtkWidget *btn1;
GtkWidget *lab1;

g_thread_init(NULL);
gdk_threads_init();

gtk_init(&argc, &argv);

// Создаем главное окно
main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(main_window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

btn1 = gtk_button_new_with_label("Create window in new thread");
// При нажатии на кнопку запускается новые поток
g_signal_connect(G_OBJECT(btn1), "clicked",
G_CALLBACK(btn1_click), NULL);

gtk_container_add(GTK_CONTAINER(main_window), btn1);
gtk_widget_show_all(main_window);

gdk_threads_enter();
gtk_main();
gdk_threads_leave();

return 0;
}
</pre>

Ответ на: комментарий от xstream

Там есть:

>With the Win32 backend, GDK calls should not be attempted from multiple threads at all.

Dr_ZLO
()
Ответ на: комментарий от xstream

Добавлю. Для X11 версии пропущены gdk_threads_enter()/gdk_threads_leave() в thread_func()

Dr_ZLO
()
Ответ на: комментарий от xstream

> Хе. Как бедто я это не читал. Ты сам то читал ? Там ничего про это нет.

внимательно читал? и примеры смотрел?

gdk_thread_* надо использовать во всех потоках, а не только в основном. а иногда еще и gdk_flush выполнять.

ananas ★★★★★
()
Ответ на: комментарий от ananas

> gdk_thread_* надо использовать во всех потоках, а не только в основном. а иногда еще и gdk_flush выполнять.

Я пробова а внутри thread_func вызывать gdk_threads_enter/gdk_threads_leave и вызывать gdk_flash. Все равно окно win2 остается мертвым.

>>With the Win32 backend, GDK calls should not be attempted from multiple threads at all.

Получатеся gdk_threads_* вообще нельзя использовать в Win32 ?

Я так понимаю, что дело не в gdk_threads, а в том, что окно создается в другом потоке отличном от того в котором было вызвано gtk_main(), и переменная win2->window привязывается именно ко второму потоку. И именно из-за этого во-первых (1) события исходяшии от win2 не обрабатываются в main_loop и во-вторых (2) это окно уничтожается во время унижтожения второго потока (чего не хочется).

Так вот как мне связать вновь созданное окно win2 с главным потоком???

xstream
() автор топика
Ответ на: комментарий от xstream

в X11 второе и последующие окна рабочие, проверил

>Так вот как мне связать вновь созданное окно win2 с главным потоком???

Придется выделить фрагмент, работающий с gtk в функцию и запланировать её вызов в контексте главного потока с помощью g(tk)_idle_add(func, arg_for_func).

Никуда не дется, это особенность венды - окна принадлежат создавшему их потоку, и даже GetMessage() в каждом потоке выбирает сообщения только для своих окон.

Dr_ZLO
()
Ответ на: комментарий от xstream

> Так вот как мне связать вновь созданное окно win2 с главным потоком???

Ну и создавай его в главном потоке. А для общения дочернего потока с главным пользуйся любым IPC по вкусу.

kpanic ★★
()
Ответ на: комментарий от xstream

во всяком случае в линухе все работает на ура.

а чтобы окна не убивались, надо, по-идее, порождать отсоединенные потоки

ananas ★★★★★
()
Ответ на: комментарий от Dr_ZLO

>в X11 второе и последующие окна рабочие, проверил

а ты чем вызывал новый покот: pthread_create или g_thread_create?

(я сейчас не могу проверить. линуха под рукой нет. купил новый комп, а на него старые линуксы не ставятся. жду Debian Ech)

>А для общения дочернего потока с главным пользуйся любым IPC по вкусу.

Я даже не представляю как это можно сделать :( . А можно ли послать главному циклу (main_loop) сообщение чтобы он выполнил заданную функцию ?

>а чтобы окна не убивались, надо, по-идее, порождать отсоединенные потоки

а что такое "отсоединенные потоки" ?

xstream
() автор топика
Ответ на: комментарий от xstream

>а ты чем вызывал новый покот: pthread_create или g_thread_create?

В данном случае не имеет значения, важно, чтобы обращения к gtk_* были огорожены gdk_threads_enter/leave. Но это всё равно не кросплатформенно.

Dr_ZLO
()
Ответ на: комментарий от xstream

> А можно ли послать главному циклу (main_loop) сообщение чтобы он выполнил заданную функцию ?

Ну тебе ж про IPC не зря сказали :-)

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

>> А можно ли послать главному циклу (main_loop) сообщение чтобы он выполнил заданную функцию ?

>Ну тебе ж про IPC не зря сказали :-)

А тогда как это сделать? Функции поскажите?

xstream
() автор топика

Честно говоря вообще идея дурацкая какая-то. Модель вычислений описать можно? Зачем окно в потоке другом создавать, не понятно.

welkam ★★
()
Ответ на: комментарий от Dr_ZLO

Всем спасибо :) Решением было g_idle_add()

Не очень красиво, но результат тот что нужен был.

Вот конечный вариант:

#include <stdio.h>
#include <gtk/gtk.h>
#include <windows.h>

int create_window2()
{
GtkWidget * win2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget * lab2 = gtk_label_new("Label");
GtkWidget * btn2 = gtk_button_new_with_label("Button");
GtkWidget * vbox = gtk_vbox_new(FALSE, 0);

gtk_box_pack_start(GTK_BOX(vbox), lab2, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), btn2, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(win2), vbox);
gtk_window_present (win2);

gtk_widget_show_all(win2);

return 0;
}

void * thread_func(void * data) {

// Создаем новое окно (в потоке)
gdk_threads_enter();

g_idle_add(create_window2, NULL);

gdk_threads_leave();

// Делаем цикл, чтобы еще задержаться в потоке
int i;

for (i = 0; i < 10; i++) {
printf(" %d thread helloo\n", i);
Sleep(500);
gdk_flush();
}
}

void btn1_click() {
DWORD tid;
printf("ctreaed new thread\n\n");
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, (LPVOID)NULL, 0, &tid);
}

int main(int argc, char **argv)
{
GtkWidget *main_window;
GtkWidget *btn1;
GtkWidget *lab1;

g_thread_init(NULL);
gdk_threads_init();

gtk_init(&argc, &argv);

// Создаем главное окно
main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(main_window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

btn1 = gtk_button_new_with_label("Create window in new thread");
// При нажатии на кнопку запускается новые поток
g_signal_connect(G_OBJECT(btn1), "clicked",
G_CALLBACK(btn1_click), NULL);

gtk_container_add(GTK_CONTAINER(main_window), btn1);
gtk_widget_show_all(main_window);

gdk_threads_enter();
gtk_main();
gdk_threads_leave();

return 0;
}

xstream
() автор топика

А вот интересно, не помогает ли вызвать в этом потоке gtk_main? Я с многопоточностью дело не имел, но без неё вложенные вызовы gtk_main работают отлично.

philip
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.