winfo_width() gibt 1 trotz update(), update_idletasks(), after() ...

Fragen zu Tkinter.
Antworten
Zinober
User
Beiträge: 16
Registriert: Freitag 26. Dezember 2014, 15:58

Hallo,

ich muss mal wieder um Hilfe bitten und würde mich über einen Tipp freuen: Ich möchte auf einem Canvas mathematische Graphen abhängig von der Fenstergröße rendern. Die benötigte Größe lese ich über winfo_width() bzw. winfo_height() aus. Das funktioniert auch eigentlich hervorragend - außer beim ersten Mal.

Im Tk()-Element erstelle ich mehrere Frames: eine Navigationsleiste mit Buttons, ein Infofeld mit Labels und einen Frame mit Scrollbars und einem Canvas, auf dem die Graphen gezeichnet werden. Jeder Button in der Navigationsleiste ruft einen anderen Graphen mit anderen Infos etc auf, immer wird alles korrekt gerendert. Wenn ich aber die erste Ansicht direkt nach dem Erstellen aller Widgets ohne Button-Klick aufrufen will, wird der entsprechenden Funktion über winfo 1 zurückgegeben und folglich wird der Graph Murks.

Ich habe es mit einer wahren Kaskade von update() und update_idletasks() versucht, nämlich tatsächlich auf allen Widgets.
Ich dachte: Nach einem Button-Klick funktioniert es. Dann lasse ich den Button eben mit Button.invoke() automatisch klicken.
Ich habe es mit mehreren automatischen Button-Klicks probiert.
Ich habe probiert vorher ein Dummy-Canvas zu erstellen, damit der Platz schon mal benutzt worden ist.
Ich habe probiert, die Funktion zum Anzeigen mit after() auzurufen. Von jedem Widget von Canvas bis Tk hoch und mit überreichlichen 3000ms.

Nichts davon hat funktioniert. Wenn ich dieselben Funktionen per Button-Klick starte, geht es. - Hat da noch jemand eine Idee?
Vielen Dank im Voraus!
Sirius3
User
Beiträge: 17792
Registriert: Sonntag 21. Oktober 2012, 17:20

Code wäre praktisch, weil wie Du das beschreibst, wird nicht recht klar, was Du da eigentlich machst.
Das direkteste wäre ja, das passende Event für Größenänderungen zu benutzen.

Code: Alles auswählen

import tkinter as tk

root = tk.Tk()
canvas = tk.Canvas(root, bg='red')
canvas.pack(fill=tk.BOTH, expand=True)
canvas.bind("<Configure>", lambda event: print(canvas.winfo_width(), canvas.winfo_height()))
root.mainloop()
Zinober
User
Beiträge: 16
Registriert: Freitag 26. Dezember 2014, 15:58

Hallo Sirius3,

danke für deine Antwort!

Vom <configure>-Event habe ich noch nichts gehört, aber so wie ich es auf die Schnelle verstehe, triggert das dann, wenn die Fenstergröße verändert wird - wahrscheinlich durch User-Interaktion?
Vielleicht könnte ich damit einen Workaround basteln, aber eigentlich müssen die Graphen sich nicht automatisch anpassen. Wenn sie einmal richtig erstellt sind, kann ich darin scrollen und zoomen.

Ich habe hier mal versucht, die entscheidende Stelle aus meinem Code rauszuschälen:

Code: Alles auswählen

class App(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.parent = parent

        self.menueleiste = tk.Frame(self)
        self.menueleiste.grid(row=0, sticky="new", pady=5)

        self.fensterinhalt_rahmen = tk.Frame(self, background="blue")
        self.fensterinhalt_rahmen.grid(row=1, sticky="news")

        self.grid_rowconfigure(0, weight=0)
        self.grid_rowconfigure(1, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.knopf_1 = tk.Button(
            self.menueleiste, text="Startseite", command=self.seite_startseite
        )
        self.knopf_1.grid(row=0, column=0, padx=10, pady=2)

        self.knopf_2 = tk.Button(
            self.menueleiste, text="Herunterladen", command=self.seite_herunterladen
        )
        self.knopf_2.grid(row=0, column=1, padx=10, pady=2)

        self.knopf_3 = tk.Button(
            self.menueleiste, text="Erzeugen", command=self.seite_erzeugen
        )
        self.knopf_3.grid(row=0, column=2, padx=10, pady=2)

        self.knopf_4 = tk.Button(
            self.menueleiste, text="Auswählen", command=self.seite_auswaehlen
        )
        self.knopf_4.grid(row=0, column=3, padx=10, pady=2)

        self.knopf_5 = tk.Button(self.menueleiste, text="Ende", command=self.ende)
        self.knopf_5.grid(row=0, column=4, padx=10, pady=2)

        self.menueleiste.grid_rowconfigure(0, weight=1)
        self.menueleiste.grid_columnconfigure(0, weight=0)
        self.menueleiste.grid_columnconfigure(1, weight=0)
        self.menueleiste.grid_columnconfigure(2, weight=0)
        self.menueleiste.grid_columnconfigure(3, weight=0)

        self.fensterinhalt = tk.Frame(self.fensterinhalt_rahmen, background="yellow")
        self.fensterinhalt.grid(row=0, column=0, sticky="news")
        self.fensterinhalt_rahmen.grid_columnconfigure(0, weight=1)
        self.fensterinhalt_rahmen.grid_rowconfigure(0, weight=1)
                
        self.update_idletasks()
                
        self.knopf_1.invoke()
        

    def seite_startseite(self):

        self.fensterinhalt.destroy()
        self.fensterinhalt = tk.Frame(self.fensterinhalt_rahmen)
        self.fensterinhalt.grid(row=0, column=0, sticky="news")
        self.fensterinhalt_rahmen.grid_columnconfigure(0, weight=1)
        self.fensterinhalt_rahmen.grid_rowconfigure(0, weight=1)

        self.linkerFrame = tk.Frame(self.fensterinhalt)
        self.linkerFrame.grid(row=0, column=0, sticky="nws", padx=10, pady=2)
        self.fensterinhalt.grid_columnconfigure(0, weight=0)

        self.rechterFrame = tk.Frame(self.fensterinhalt)
        self.rechterFrame.grid(row=0, column=1, sticky="news", padx=10, pady=2)
        self.fensterinhalt.grid_columnconfigure(1, weight=1)
        self.fensterinhalt.grid_rowconfigure(0, weight=1)

        self.E_titel_label = tk.Label(
            self.linkerFrame,
            text="dhqmm",
            font="bold",
            bd=5,
        )
        self.E_titel_label.grid(row=0, column=0)

        f = open(
            path.join(dateiname"),
            "r",
            encoding="utf8",
        )

        netzwerk = yaml.load(f, Loader=yaml.Loader)
        f.close()
        
        self.update_idletasks()

        self.netzwerk_anzeigen(netzwerk)
 
In dieser Version rufe ich die Funktion über self.knopf_1.invoke() auf. Ich könnte sie auch direkt aufrufen. Das macht im Ergebnis keinen Unterschied.
Wenn ich das invoke() auskommentiere und den Button selbst klicke funktioniert es aber und winfo liefert die richtigen Werte.
Zuletzt geändert von Zinober am Sonntag 14. November 2021, 22:48, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Was passiert, wenn du das invoke via after verzögerst? Denn jetzt gerade sind die ganzen Widgets frisch angelegt & im Zweifel noch gar nicht gelayoutet.
Zinober
User
Beiträge: 16
Registriert: Freitag 26. Dezember 2014, 15:58

Ich hatte das hier an der Stelle statt des invoke() probiert:

Code: Alles auswählen

self.after(1000, self.seite_startseite())
Das ist die Funktion, die auch beim Button-Klick aufgerufen wird. Ich hatte es auch an andere Widgets gehängt und mit 3000ms ausprobiert. Aber das hat alles nicht geholfen.
Sirius3
User
Beiträge: 17792
Registriert: Sonntag 21. Oktober 2012, 17:20

Du benutzt `after` ja auch falsch.

Code: Alles auswählen

self.after(1000, self.seite_startseite)
Zinober
User
Beiträge: 16
Registriert: Freitag 26. Dezember 2014, 15:58

Ja, du hast völlig Recht!

Interessanterweise funktioniert es ohne die Klammern auch dann, wenn ich 0 ms als Zeitintervall angebe und eine Fehlermeldung gibt's bei der falschen Version nicht.

Ein Hoch auf deine scharfen Augen! Und noch viel mehr deine Hilfsbereitschaft!!!
Antworten