2010-01-10 5 views
2

J'ai le code suivant qui utilise GTK + widget toolkit pour afficher une fenêtre avec un bouton. Cliquez sur ce bouton pour afficher une boîte de dialogue modale. Notez que l'appel à gtk_dialog_run commencera récursivement une autre instance de la boucle principale, c'est-à-dire que la fonction on_click ne retournera pas jusqu'à ce que la boîte de dialogue soit fermée.Comment créer des boîtes de dialogue modales multiples mais indépendantes dans GTK +?

Je voudrais avoir deux de ces fenêtres de haut niveau, chacune avec un bouton et la capacité de générer leur propre dialogue modal. L'affichage de la boîte de dialogue ne ferait que désactiver la fenêtre qui l'a engendrée et il pourrait y avoir jusqu'à deux boîtes de dialogue modales actives en même temps, une pour chaque fenêtre de niveau supérieur.

Dans Win32, je pourrais accomplir cela simplement en exécutant chaque fenêtre de niveau supérieur dans un thread séparé. Cependant, il semble que gtk_main ne peut être exécuté qu'à partir d'un thread. Alors, comment puis-je gérer plusieurs piles de fenêtres dans GTK + (sans sacrifier la simplicité de gtk_dialog_run si possible)?

Mise à jour: Le code affiche maintenant les deux fenêtres et les ajoute à leurs groupes de fenêtres respectifs.

#include <gtk/gtk.h> 

struct modal_stack 
{ 
    GtkWindowGroup * group; 
    GtkWidget * window; 
}; 

static void on_click(GtkWidget *widget, gpointer sptr) 
{ 
    modal_stack * s = (modal_stack *)sptr; 
    GtkWidget * dialog = gtk_file_chooser_dialog_new(
     "Open File", 0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, 
     GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); 
    gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(s->window)); 
    gtk_window_group_add_window(s->group, GTK_WINDOW(dialog)); 
    gtk_dialog_run (GTK_DIALOG (dialog)); 
    gtk_window_group_remove_window(s->group, GTK_WINDOW(dialog)); 
    gtk_widget_destroy(dialog); 
} 

void create_window(modal_stack & s) 
{ 
    s.group = gtk_window_group_new(); 
    s.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 

    gtk_widget_set_usize(s.window, 200, 200); 
    g_signal_connect(G_OBJECT (s.window), "destroy", 
     G_CALLBACK(gtk_main_quit), NULL); 
    g_signal_connect(G_OBJECT (s.window), "delete_event", 
     G_CALLBACK(gtk_main_quit), NULL); 

    GtkWidget * button = gtk_button_new(); 
    gtk_button_set_label(GTK_BUTTON(button), "show dialog"); 
    g_signal_connect(G_OBJECT (button), "clicked", 
     G_CALLBACK(on_click), (gpointer) &s); 
    gtk_widget_show(button); 

    gtk_container_add(GTK_CONTAINER (s.window), button); 
    gtk_widget_show(s.window); 

    gtk_window_group_add_window(s.group, GTK_WINDOW(s.window)); 
} 

int main(int argc, char *argv[]) 
{ 
    gtk_init (&argc, &argv); 
    modal_stack wnd1, wnd2; 
    create_window(wnd1); 
    create_window(wnd2); 
    gtk_main(); 
} 

Répondre

3

Il y a une mention discrète dans le gtk_dialog_run docs que gtk_dialog_run va bloquer uniquement l'interaction avec les fenêtres de la même window group que votre boîte de dialogue modale.

+0

Merci d'avoir répondu.J'ai essayé de placer chaque fenêtre dans son propre groupe, mais après avoir cliqué sur le bouton dans l'une des fenêtres, l'autre fenêtre n'accepte toujours pas les clics (bien qu'elle réponde de manière surprenante aux mouvements de la souris). Même si cela a fonctionné correctement, je ne peux toujours pas imaginer comment cela pourrait fonctionner sans utiliser plusieurs threads. Si je clique sur le bouton 1, puis sur le bouton 2 puis cache le dialogue 1, si les boucles sont exécutées dans le même thread, le 'gtk_dialog_run' pour le premier dialogue ne peut pas retourner jusqu'à ce que le dialogue 2 soit également fermé. Je pense qu'il y a un point fondamental qui me manque. – avakar

+0

Etes-vous sûr d'avoir ajouté les boîtes de dialogue aux groupes de fenêtres? – Tobu

+0

Bonne prise. Malheureusement, rien n'a changé après que je l'ai réparé. J'ai mis à jour la question avec mon code actuel si vous pouviez l'examiner. Dans les deux cas, vous avez mon +1. – avakar

5

Passez votre appel à gtk_dialog_run à l'intérieur d'une fonction appelée par g_idle_add().

2

Pour moi votre code de test fonctionne très bien, avec deux bugs que je vois:

  • que vous notez le gtk_dialog_run() sont imbriquées de manière l'externe est en attente sur l'intérieur. ce n'est pas vraiment corrigible je ne pense pas, autre que d'éviter gtk_dialog_run() dans cette situation.
  • il y a un avertissement sur GTK_IS_WINDOW_GROUP, je pense manuellement l'ajout/suppression du dialogue/du groupe de fenêtre est mutiler le refcounting

Cependant, je peux cliquer sur les boutons fins et naviguer dans le système de fichiers dans la boîte de dialogue de fichier, et je peux utiliser les boutons d'ouverture/fermeture.

Je suppose qu'il y a un bug spécifique à la version de GTK que vous utilisez qui empêche de cliquer sur la boîte de dialogue. Sans savoir ce qu'est le bogue, je crois complètement que l'idée de Kurt d'exécuter la boîte de dialogue au ralenti au lieu du gestionnaire cliqué pourrait l'éviter. Il se peut que GTK soit confus en ajoutant la saisie modale dans le gestionnaire cliqué. Juste la spéculation. Cependant, je ne vois pas le bug ici sur GTK 2.20.1.

Un couple de choses que je remarquai dans le code:

  • gtk_widget_set_usize devrait être gtk_window_set_default_size probablement
  • vous n'avez pas besoin d'ajouter/supprimer manuellement la boîte de dialogue du groupe de fenêtre. set_transient_for ajoute automatiquement la boîte de dialogue au groupe de fenêtres du parent, et la destruction d'une fenêtre la supprime automatiquement de son groupe de fenêtres. la suppression de ces appels corrige l'avertissement que je vois sur l'annulation de la boîte de dialogue.