2011-01-11 24 views
8

Stworzyłem aplikację PyGTK, która wyświetla okno dialogowe, gdy użytkownik naciśnie przycisk. Okno jest ładowany w moim sposobie __init__ z:Jak wielokrotnie wyświetlać okno dialogowe za pomocą PyGTK/Gtkbuilder?

builder = gtk.Builder() 
builder.add_from_file("filename") 
builder.connect_signals(self) 
self.myDialog = builder.get_object("dialog_name") 

W obsługi zdarzeń, okno jest pokazany z poleceniem self.myDialog.run(), ale to działa tylko raz, ponieważ po run() okno jest automatycznie zniszczona. Jeśli kliknę przycisk po raz drugi, aplikacja ulegnie awarii.

Przeczytałem, że istnieje sposób użycia show() zamiast run(), gdzie okno dialogowe nie jest zniszczone, ale mam wrażenie, że to nie jest właściwa droga dla mnie, ponieważ chciałbym, żeby dialog zachowywał się modalnie i aby zwracał kontrolę do kod tylko po zamknięciu przez użytkownika.

Czy istnieje prosty sposób wielokrotnego wyświetlania okna dialogowego przy użyciu metody run() przy użyciu narzędzia gtkbuilder? Próbowałem przeładować całe okno dialogowe za pomocą gtkbuildera, ale tak naprawdę nie działało, w oknie dialogowym brakowało wszystkich elementów potomnych (i wolałbym używać budowniczego tylko raz, na początku programu).


[ROZWIĄZANIE] (edycja)
Jak wskazano w poniższej odpowiedzi, korzystając hide() załatwia sprawę. Najpierw myślałem, że nadal musisz złapać "usuń-wydarzenie", ale w rzeczywistości nie jest to konieczne. Prosty przykład, że działa to:


import pygtk 
import gtk 

class DialogTest: 

    def rundialog(self, widget, data=None): 
     self.dia.show_all() 
     result = self.dia.run() 
     self.dia.hide() 


    def destroy(self, widget, data=None): 
     gtk.main_quit() 

    def __init__(self): 
     self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 
     self.window.connect("destroy", self.destroy) 

     self.dia = gtk.Dialog('TEST DIALOG', self.window, 
      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) 
     self.dia.vbox.pack_start(gtk.Label('This is just a Test')) 


     self.button = gtk.Button("Run Dialog")  
     self.button.connect("clicked", self.rundialog, None) 
     self.window.add(self.button) 
     self.button.show() 
     self.window.show() 



if __name__ == "__main__": 
    testApp = DialogTest() 
    gtk.main() 

Odpowiedz

4

Właściwie przeczytać documentation na Dialog.run(). Okno dialogowe nie zostanie automatycznie zniszczone. Jeśli wyłączysz hide(), gdy metoda run() zakończy, powinieneś być w stanie run() tak wiele razy, jak chcesz.

Alternatywnie można ustawić okno dialogowe jako modalne w pliku konstruktora, a następnie tylko show() go. Osiągnie to efekt podobny, ale nie do końca podobny do run() - ponieważ run() tworzy drugą instancję głównej pętli GTK.

EDIT

Powodem są coraz błąd segmentacji jeśli nie podłączyć do sygnału delete-event jest to, że są dwa razy kliknąć przycisk zamykania. Oto, co się stanie:

  1. Kliknąć "Uruchom okno dialogowe", co wywołuje metodę okna dialogowego run().
  2. Pojawi się okno dialogowe modalne i rozpocznie się własna pętla główna.
  3. Kliknięcie przycisku zamykania. Główna pętla dialogowa wychodzi, ale ponieważ run() przesłania normalne zachowanie przycisku zamykania, okno dialogowe nie jest zamykane. Nie jest też ukryty, więc się kręci.
  4. Zastanawiasz się, dlaczego okno dialogowe nadal tam jest i kliknij ponownie przycisk zamykania. Ponieważ run() nie jest już aktywny, uruchamiane jest normalne działanie przycisku zamykania: okno dialogowe jest niszczone.
  5. Ponownie klikamy "Run Dialog", który próbuje wywołać metodę zniszczonego okna dialogowego w postaci run(). Wypadek!

Jeśli więc upewnisz się, że hide() w oknie dialogowym po kroku 3, wszystko powinno działać. Nie ma potrzeby łączenia się z sygnałem delete-event.

+0

Cóż, dokumentacja nie jest tutaj bardzo jasna. Wydaje się, że sama metoda "run" nie niszczy okna dialogowego, ale jeśli zamkniesz je, naciskając przycisk "zamknij" dostarczony przez menedżera okien (zwykle trochę "X" w prawym górnym rogu okna ", okno dialogowe zostanie zniszczone, jeśli nie przechwycisz "zdarzenia usuwania" – Julian

+0

@Julian, jest odwrotnie - zobacz drugi akapit dokumentacji Okno dialogowe nie zostanie zniszczone nawet po zamknięciu okna – ptomato

+0

Tak, przeczytałem ale jestem nieco zdezorientowany: Jeśli w podanym powyżej przykładzie skomentuję linię 'self.dia.connect (" delete-event ", self.closedialog)' Otrzymuję błąd segmentacji po drugim kliknięciu przycisku.Więc COŚ zostało zniszczone beze mnie, prawda? Dziękuję za wyjaśnienia. – Julian

0

Twoje okno dialogowe powinno zostać uruchomione tylko raz. Przyjmując pozycję menu powoduje dialogowe, kod powinien wyglądać mniej więcej tak:

def on_menu_item_clicked(self, widget, data=None): 
    dialog = FunkyDialog() 
    response = dialog.run() 

    if response = gtk.RESPONSE_OK: 
     // do something with the dialog data 

    dialog.destroy() 

dialog.run() jest blokując główną pętlę, która zwraca gdy okno wysłać odpowiedź. Zwykle odbywa się to za pomocą przycisków Ok i Anuluj. Gdy tak się stanie, okno dialogowe zostanie zakończone i musi zostać zniszczone.

Aby wielokrotnie wyświetlać okno dialogowe, użytkownik powinien wykonać ten sam proces roboczy (w powyższym przykładzie, który kliknąłby element menu). Okno dialogowe jest odpowiedzialne, w __init__, za samo ustawienie. Jeśli w oknie dialogowym pojawi się hide(), masz problem z komunikowaniem się z tym oknem dialogowym, dzięki czemu pozostaje on aktualny wraz z resztą aplikacji, nawet jeśli jest ukryty .

Jednym z powodów, dla których niektórzy ludzie chcą "wielokrotnie uruchamiać okno dialogowe", jest to, że użytkownik wprowadził nieprawidłowe informacje i chcesz dać użytkownikowi możliwość poprawienia go. Należy to zrobić w obsłudze sygnału odpowiedzi w oknie dialogowym. Kolejność zdarzeń w oknie to:

  1. użytkownika fizycznie naciska przycisk OK
  2. Dialog wysyła odpowiedź gtk.RESPONSE_OK (-5)
  3. Dialog wywołuje moduł obsługi dla sygnału odpowiedzi
  4. Dialog wywołuje Wózek dla przycisku Ok
  5. Dialog run() metoda zwraca odpowiedź

Aby zapobiec kroki 4 i 5, procedura obsługi odpowiedzi musi tłumić sygnał odpowiedzi. Osiąga się to w następujący sposób:

def on_dialog_response(self, dialog, response, data=None: 
    if response == gtk.RESPONSE_OK: 
     if data_is_not_valid: 
      # Display an error message to the user 

      # Suppress the response 
      dialog.emit_stop_by_name('response') 
+0

Cóż, ale używam GtkBuilder, aby uzyskać okno dialogowe, a gdy okno dialogowe zostanie zniszczone, pozostanie zniszczone - nie mogłem znaleźć sposobu na jego ponowne skonfigurowanie. – Julian

+0

destroy() robi dokładnie to, niszczy obiekt. Jeśli chcesz ponownie wyświetlić to samo okno dialogowe, musisz utworzyć kolejną instancję. Zapoznaj się z http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#method-gtkwidget-destroy, aby uzyskać więcej informacji na temat destroy(), w szczególności: "Jeśli widget jest tieldvel (pochodzi z gtk. Window), zostanie usunięty z listy poziomów, a referencyjny PyGTK do niego zostanie usunięty. " – Jon

2

Po prostu spędziłem trochę czasu na zastanawianie się nad tym. Ponowne pobranie tego samego obiektu z programu budującego nie spowoduje utworzenia nowej instancji obiektu, a jedynie zwróci odwołanie do starego (zniszczonego) obiektu. Jeśli jednak utworzysz nową instancję budowania i załadujesz plik do nowego programu budującego, utworzy on nowe wystąpienie.

Więc moja dialogowe funkcja tworzenia wygląda mniej więcej tak:

def create(): 
    builder = gtk.Builder() 
    builder.add_from_file('gui/main.ui') 

    dlg = builder.get_object('new_dialog') 

    def response_function(dialog, response_id): 
     ... do stuff ... 
     dialog.destroy() 

    dlg.connect('response', response_function) 
    dlg.show_all() 

Należy pamiętać, że nie jestem blokowania na odpowiedź z run() w tym przypadku, ponieważ używam skręcony, ale powinno być równoważne.

Powiązane problemy