Newbie Frage zu Tkinter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Boardgameingo
User
Beiträge: 10
Registriert: Dienstag 29. März 2022, 11:26

Hallo zusammen,

bin ganz neu am Programmieren als Hobby. Ich versuche die Beispiele im Tutorial für Tkinter nachzuvollziehen und komme nicht weiter. Wenn ich Klassen verwende, bekomme ich keine Bilder angezeigt, ohne Klasses geht das. Kann mir das jemand erklären oder sagen, wo es erklärt wird.
Danke vorab
Ingo

Ohne Klasse_
from tkinter import *
root = Tk()
mainframe = Frame(root)#, padding="3 3 12 12")
mainframe.grid(column=0, row=0 )#, sticky=(N, W, E, S))

imgobj = PhotoImage(file='WaPo_small.gif')
Label(mainframe, image=imgobj).grid(column=0,row=0)#,sticky=W)

root.mainloop()

--------------------
Mit Klasse:
from tkinter import *
#from tkinter import ttk

class FeetToMeters:

def __init__(self, root):

root.title("Feet to Meters")

mainframe = Frame(root)#, padding="3 3 12 12")
mainframe.grid(column=0, row=0 )#, sticky=(N, W, E, S))
#root.columnconfigure(0, weight=1)
#root.rowconfigure(0, weight=1)

imgobj = PhotoImage(file='WaPo_small.gif')
Label(mainframe, image=imgobj).grid(column=0,row=0)#,sticky=W)
root = Tk()
FeetToMeters(root)
root.mainloop()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist leider ein der Fallstricke von tkinter, die hier oft auftauchen. Das Problem besteht darin, dass dein imgobj nur eine lokale Variable in __init__ ist. Damit wird es von Python nach dem Ende der Methode auch konsequent abgeraeumt. Das muss man also verhindern, indem man das Objekt "festhaelt".

So sieht eine moegliche Loesung dafuer aus:

Code: Alles auswählen

class FeetToMeters:
    def __init__(self):
        wapo_image = PhotoImage(...)
        wapo_label = Label(.., image=wapo_image)
        wapo_label.image = wapo_image  # Hier wird nochmal eine explizite Referenz hochgehalten
Sirius3
User
Beiträge: 17844
Registriert: Sonntag 21. Oktober 2012, 17:20

*-Importe benutzt man nicht, weil unklar ist, welche Namen denn da in den Namensraum geladen werden. tkinter wird üblicherweise per `import tkinter as tk` importiert.
Eine Klasse, die nur erzeugt wird, und dann passiert nichts damit, sieht komisch aus.

Code: Alles auswählen

import tkinter as tk

class FeetToMeters:
    def __init__(self, root):
        root.title("Feet to Meters")

        mainframe = tk.Frame(root)
        mainframe.grid(column=0, row=0 )
        
        self.image = tk.PhotoImage(file='WaPo_small.gif')
        tk.Label(mainframe, image=self.image).grid(column=0,row=0)

def main():
    root = tk.Tk()
    feet_to_meters = FeetToMeters(root)
    root.mainloop()

if __name__ = "__main__":
    main()
Boardgameingo
User
Beiträge: 10
Registriert: Dienstag 29. März 2022, 11:26

__deets__ hat geschrieben: Dienstag 29. März 2022, 11:47 Das ist leider ein der Fallstricke von tkinter, die hier oft auftauchen. Das Problem besteht darin, dass dein imgobj nur eine lokale Variable in __init__ ist. Damit wird es von Python nach dem Ende der Methode auch konsequent abgeraeumt. Das muss man also verhindern, indem man das Objekt "festhaelt".

Perfekt. Besten Dank!
Boardgameingo
User
Beiträge: 10
Registriert: Dienstag 29. März 2022, 11:26

Sirius3 hat geschrieben: Dienstag 29. März 2022, 12:54 *-Importe benutzt man nicht, weil unklar ist, welche Namen denn da in den Namensraum geladen werden. tkinter wird üblicherweise per `import tkinter as tk` importiert.
Eine Klasse, die nur erzeugt wird, und dann passiert nichts damit, sieht komisch aus.
In "meinem" Tutorial steht: Notice that we've imported everything (*) from the tkinter module. That way, we can call tkinter functions, etc., without prefixing them with the module name. This is standard Tkinter practice.
Ich denke, da gibts also viele Meinungen zu, aber dennoch danke für die deine.

Deinen zweiten Satz kapier ich nicht. Natürlich passiert mit meiner Klasse irgendwann etwas, sonst würde ich sie ja nicht bauen. Nur wenn es schon bei den paar Zeilen nicht funktioniert, dann ist es halt doof.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Joa, leider gibt es viele schlechte Tutorials da draussen. tkinter hat etwa 140(!) Namen, darunter auch welche, die man dann gerne anders nutzen wuerde, wie END/START/Event etc.

Was Sirius3 mit der Klasse meint: deine Klasse betritt bereits im __init__ den Mainloop. Das ist ueberraschend. Wer ein Objekt erzeugt, erwartet auch erstmal, dass er es benutzen kann. Kann man bei dir aber nicht.

Code: Alles auswählen

foo = FeetToMeters() 
print(foo) # wird niemals erreicht, weil wir schon im mainloop stehen.
Stattdessen macht man das eben so, wie von Sirus3 gezeigt, mit dem root Element und dessen Mainloop.
Benutzeravatar
__blackjack__
User
Beiträge: 13268
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Boardgameingo: Dann hat das Tutorial halt eine falsche Meinung. Es werden ca. 200 Namen in den aktuellen Namensraum importiert, von denen nur ein ganz kleiner Bruchteil verwendet wird. Es macht es schwieriger zu sehen wo Namen im Modul eigentlich her kommen. Und es besteht die Gefahr von Namenskollisionen. Dann kommt es auf die Reihenfolge beim Importieren an was ein Name am Ende bedeutet. Beispielsweise gibt es `Image` sowohl in `tkinter` als auch im `PIL`-Modul. Ausserdem importiert man nicht nur alles was in dem Modul definiert wird, sondern auch was *das* Modul *selbst* von woanders her importiert hat. Bei `tkinter` beispielsweise auch das `re`-Modul. Allerdings nicht in jeder Python-Version.

„Standard practice“ ist generell eher keine *-Importe zu verwenden, wegen den ganzen Problemen die das nach sich ziehen kann. Und bei `tkinter` ein ``import tkinter as tk`` und dann `tk` als Präfix zu verwenden. Das ist der Kompromiss zwischen, nicht jeden verwendeten Namen einzeln importieren zu müssen und ausnahmsweise eine Abkürzung zu verwenden, um nicht immer so einen langen Präfix schreiben zu müssen, der oft verwendet wird.

Der zweite Satz meint das da einfach nur ``FrameToFeet(root)`` steht und mit dem `FrameToFeet`-Objekt dann nichts gemacht wird. Die Änderung die Sirius3 gemacht hat, sieht immer noch ein bisschen komisch aus, weil es auch an einen Namen gebunden dann ja ”unbenutzt” ist, das ist hier aber wohl der `tkinter`-API geschuldet. Wobei man auch überlegen könnte einfach vom `Tk`-Objekt zu erben, dann wäre das Problem auch beseitigt, weil dann bei einem ``FrameToFeet().mainloop()`` das Objekt tatsächlich benutzt wird.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Boardgameingo
User
Beiträge: 10
Registriert: Dienstag 29. März 2022, 11:26

Danke Leute! Ich merke schon, doch nicht so einfach wie gedacht :) Muss mehr lesen!!
Antworten