Kontrolle über Toplevel-Fenster

Fragen zu Tkinter.
Antworten
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Von meinem Hauptprogramm, das mit tkinter läuft, öffne ich Toplevel-Fenster.
Nun möchte ich bei einer bestimmten Anwendung, daß immer nur ein Toplevel-Fenster geöffnet ist.
Das geöffnete Toplevel-Fenster soll immer zuerst geschlossen werden, bevor ich ein neues Toplevel-Fenster aufrufe.
Wie kann ich das erreichen, ohne dies manuell zu tun?

Grüße Nobuddy
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Ich habe schon einige Alternativen versucht.
Meine Vermutung ist, daß Toplevel-Fenster als Kindprozess gestartet wird und daher dieser nicht separat beendet werden kann.
Bitte korrigiert mich, wenn ich falsch liege.

Ich habe hier http://www.python-kurs.eu/forking.php etwas gefunden, das so hoffe ich mein Problem lösen könnte.
Folgendes habe ich versucht:

Code: Alles auswählen

            def child():
                print('A new child ',  os.getpid( ))
                os._exit(0)

            def parent():
                while True:
                    newpid = os.fork()
                    if newpid == 0:
                        child()
                    else:
                        try:
                            print('kill')
                            print(self.oldprocess)
                            os.kill(self.oldprocess, 9)
                            print('kill')
                        except:
                            pass
                        pids = (os.getpid(), newpid)
                        print("parent: %d, child: %d" % pids)
                        self.oldprocess = newpid
                        result = TWork.Controller(listresult, name,
                            max_width).run() 
                    if input( ) == 'q':
                        break
            parent()
Leider scheitert es noch immer daran, daß der Aufruf 'result = TWork.Controller(listresult, name, max_width).run()' immer noch ein Kindprozess ist und nicht der Fork mit der PID 'self.oldprocess' ist.

Wo liegt der Fehler und wie wäre es richtig?

Grüße Nobuddy
BlackJack

@Nobuddy: Richtig wäre es sofort diese furchtbare URL zu vergessen, und nicht schon wieder irgendwas total überkompliziertes zu machen. Wenn nur ein Toplevel-Fenster (von einer bestimmten Art) offen sein soll, dann musst Du Dir ja nur an enstrpechender Stelle merken *das* bereits eines offen ist. Wenn Du es öffnest Flag setzten, wenn es geschlossen wird Flag zurücksetzen. Wobei statt einem Flag (`True`/`False`) auch `None` und das `Toplevel`-Objekt verwendet werden können. Immer wenn das `Toplevel` geöffnet werden soll, kann man vorher prüfen ob es bereits da ist oder nicht. Falls es bereits da ist, könnte man das vorhandene dann vielleicht auch in den Vordergrund holen.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack

Wenn es einfacher geht, nichts lieber als das. :wink:

Du sprichst von Flag setzen, nur ist mir noch nicht klar, wie ich dies bei mir umsetzen kann.

Ich habe es am Anfang meines Threades schon mal dargestellt.
Von meinem Hauptprogramm, erstelle ich für einen bestimmten Zweck Buttons (Telefon, Fax, EMail, usw. ...).
Diese Buttons, rufen ein externes Modul auf, das dann die Daten in einem Toplevel-Fenster ausgibt und Veränderungen an den Daten zurück gibt.
Momentan ist es so, daß ich alle dieser Fenster aufrufen kann, ohne das Vorherige zu schließen.
Oder ich schließe es manuell, was aber vermeiden möchte.

Der Aufruf des Moduls ist:

Code: Alles auswählen

result = TWork.Controller(listresult, name, max_width).run() 
Wie kann ich das mit Flags erreichen?
BlackJack

@Nobuddy: Dort wo ein neuer Dialog aufgerufen werden soll:

Code: Alles auswählen

if self.dialog is None:
    self.dialog = TWork.Controller(listresult, name, max_width)
    result = self.dialog.run()
    self.dialog = None
else:
    self.dialog.focus()
Sollte der aufgerufene Dialog das Hauptfenster blockieren dürfen, also wenn nur noch in diesem Fenster eingaben erfolgen dürfen bis es geschlossen wird, dann kann man auch einfach einen modalen Dialog erstellen. Die nötigen Methoden dazu auf `Toplevel` sind `transient()`, `focus()`, `grab_set()`, und `mainloop()`.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack

Danke für Deine Hilfe, jetzt funktioniert es so, wie ich es mir vorgestellt habe. :wink:

Bei 'self.dialog.focus()' kam ich kurz ins Schleudern, aber dann war mir klar, daß es eine Funktion im Toplevel-Fenster-Modul sein muß, das Fenster beendet.

Bei mir funktioniert es so:

Code: Alles auswählen

            if self.dialog is None:
                self.dialog = TWork.Controller(listresult, name, max_width)
                result = self.dialog.run()
                self.dialog = None
            else:
                self.dialog.end()
                self.dialog = TWork.Controller(listresult, name, max_width)
                result = self.dialog.run()
Kann mir vorstellen, daß Du Dir jetzt denkst, daß das Untere ja doppelgemoppelt ist (ist es ja auch).
Somit wird aber das bestehende Toplevel-Fenster geschlossen und das Neue geöffnet.
BlackJack

@Nobuddy: `focus()` beendet nichts sondern setzt den Eingabefokus auf das Widget auf dem es aufgerufen wird.

Der Code ist tatsächlich ungünstig strukturiert und funktioniert so auch nicht, denn wo wird denn ein Dialog der im ``else`` geöffnet wird, jemals wieder auf `None` gesetzt. Das sieht mir ziemlich verworren aus und scheint mir auch von der Bedienung komisch zu sein. Man kann doch nicht einfach so einen offenen Dialog beenden. Vielleicht war der Benutzer damit ja noch gar nicht fertig. Sollte es nur im die Anzeige zu gehen, dann würde man auch keinen Neuen aufmachen und den Alten beenden, sondern einfach die Daten im Alten, und damit Einzigen, aktualisieren.
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack

self.dialog.focus() gab bei mir die Fehlermeldung aus, daß focus View nicht bekannt ist, daher habe ich angenommen daß es sich um eine Funktion in meinem Toplevel-Fenster-Modul handelt, das ...

focus() stellt dann eine Funktion dar, die den Eingabefokus auf das Widget setzt, ist das richtig?

Ich habe das Ganze unter der Vorraussetzung, daß meine erste Vermutung richtig gewesen wäre, so umgesetzt:

Code: Alles auswählen

            try:
                self.dialog.end()
                self.dialog = TWork.Controller(listresult, name, max_width)
                result = self.dialog.run()
            except AttributeError:
                self.dialog = TWork.Controller(listresult, name, max_width)
                result = self.dialog.run()
Das funktioniert genauso, wie ich es mir vorgestellt habe.
'if self.dialog is None' brauche ich hier nicht.
Der alte dialog wird immer dann beendet, wenn ein Neuer aufgerufen wird.

Unterliege ich da einem Irrtum, oder ist das so ok?

Die Daten, die in den Toplevel-Fenstern ausgegeben werden, beziehen sich immer auf einen Kunden bzw. Lieferanten.
Wenn ich also z.B. von einem Kunden die Telefonnummern aufrufe, so kann ich dort Daten verändern, löschen und hinzufügen. Habe ich dort Daten verändert ohne diese zu speichern und rufe z.B. die Faxnummern des Kunden auf, so erhalte ich ein Messagebox-Fenster, um die Daten zu speichern oder zu verwerfen.

Grüße Nobuddy
BlackJack

@Nobuddy: Das ist doch vom Programmablauf her Unsinn den selben Code zweimal hinzuschreiben. Und unsauber ist es IMHO auch das über den `AttributeError` zu lösen. Ausserdem stellt sich die Frage was eigentlich passiert wenn der Dialog geschlossen wird und dann auf dem geschlossenen Dialog noch einem `end()` aufgerufen wird. Das sollte auch nicht sein. Wenn es um Kontaktdaten geht würde ich wahrscheinlich noch nicht einmal verhindern das mehrere Datensätze davon angeschaut werden können. Denn dann kann man auch mal zwei nebeneinander Aufrufen um die zu vergleichen oder um Daten vom einen in den anderen Dialog zu kopieren.

Das was Du da jetzt stehen hast, ist jedenfalls äquivalent zu diesem hier, ohne Code-Wiederholung:

Code: Alles auswählen

            try:
                self.dialog.end()
            except AttributeError:
                pass  # TODO Erklärender Kommentar warum das in Ordnung ist.
            self.dialog = TWork.Controller(listresult, name, max_width)
            result = self.dialog.run()
Nobuddy
User
Beiträge: 1015
Registriert: Montag 30. Januar 2012, 16:38

Hallo BlackJack
BlackJack hat geschrieben:@Nobuddy: Das ist doch vom Programmablauf her Unsinn den selben Code zweimal hinzuschreiben. Und unsauber ist es IMHO auch das über den `AttributeError` zu lösen.
Da gebe ich Dir Recht, werde es nochmal mit Deinem ersten Vorschlag versuchen.
BlackJack hat geschrieben: Ausserdem stellt sich die Frage was eigentlich passiert wenn der Dialog geschlossen wird und dann auf dem geschlossenen Dialog noch einem `end()` aufgerufen wird. Das sollte auch nicht sein.
Sollte eigentlich nicht sein, 'end()' kommt doch vor dem Schließen des Dialogs, oder?
BlackJack hat geschrieben: Wenn es um Kontaktdaten geht würde ich wahrscheinlich noch nicht einmal verhindern das mehrere Datensätze davon angeschaut werden können. Denn dann kann man auch mal zwei nebeneinander Aufrufen um die zu vergleichen oder um Daten vom einen in den anderen Dialog zu kopieren.
Das ist einerseits richtig, kann aber auch irritierend sein, wenn zu viele Fenster auf einmal offen sind.
Mir persönlich ist es lieber, immer nur das Fenster geöffnet zu haben, mit dem ich gerade arbeiten will.

Das was Du da jetzt stehen hast, ist jedenfalls äquivalent zu diesem hier, ohne Code-Wiederholung:

Code: Alles auswählen

            try:
                self.dialog.end()
            except AttributeError:
                pass  # TODO Erklärender Kommentar warum das in Ordnung ist.
            self.dialog = TWork.Controller(listresult, name, max_width)
            result = self.dialog.run()
[/quote]
Auf jeden Fall, werde mich aber zuerst nochmal mit Deinen ersten Vorschlag befassen.

Vielleicht gibt es von Deiner Seite noch einen anderen Vorschlag, bzw. vielleicht kannst Du mir auch noch etwas genauer erklären, wie Du das mit `focus()` gemeint hast?

Grüße Nobuddy
Antworten