Image zeigen

Fragen zu Tkinter.
Antworten
DMD-OL
User
Beiträge: 315
Registriert: Samstag 26. Dezember 2015, 16:21

hi jungs
ich versteh grad nix mehr.
hier:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter
from Tkinter import *
import PIL
from PIL import ImageTk, Image

#baseheight = 75
#img = Image.open('C:\\Users\\DMD-OL\\Desktop\\qrcode.png')
#hpercent = (baseheight / float(img.size[1]))
#wsize = int((float(img.size[0]) * float(hpercent)))
#img = img.resize((wsize, baseheight), PIL.Image.ANTIALIAS)
#img.save('C:\\Users\\DMD-OL\\Desktop\\resized_image.png')

imp_fenster = Tkinter.Tk()

imp_fenster.geometry('380x150+1200+673')

im = PIL.Image.open("C:\\Users\\DMD-OL\\Desktop\\resized_image.png")
photo = PIL.ImageTk.PhotoImage(im)
w = Label(imp_fenster, image=photo)
w.place(x=50,y=35)
text = Label(imp_fenster,text=("Hallo, hier steht ein Text."),font=('Arial', 8, 'bold'), height=10, width=25, fg="#000000000",justify='left')
text.place(x=150,y=0)

imp_fenster.mainloop()
funktioniert alles wie es soll.
wenn ich aber dieses habe:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter
from Tkinter import *
import PIL
from PIL import ImageTk, Image

backwin = Tkinter.Tk()
backwin.geometry('680x650+500+73')
backwin.configure(background='black')

def impressum(b3):

    b3.destroy()
    imp_fenster = Tkinter.Toplevel(backwin)
    imp_fenster.geometry('380x150+1200+673')

    im = PIL.Image.open("C:\\Users\\DMD-OL\\Desktop\\resized_image.png")
    photo = PIL.ImageTk.PhotoImage(im)
    w = Label(imp_fenster, image=photo)
    w.place(x=50,y=35)
    text = Label(imp_fenster,text=("Hallo, hier steht ein Text."),font=('Arial', 8, 'bold'), height=10, width=25, fg="#000000000",justify='left')
    text.place(x=150,y=0)

    def impressum_schliessen(b3):
        imp_fenster.destroy()
        b3 = Tkinter.Button(backwin, text=("Click mich nochmal...:)"), font=('Arial', 8, 'bold'), height=2, width=25, relief="raised", borderwidth=2, fg='#000000000', justify='center',command=lambda: impressum(b3))
        b3.place(relx=.88, rely=.97, anchor="c")

    b_imp1 = Tkinter.Button(imp_fenster, text=("Schließen"), font=('Arial', 8, 'bold'), width=15, relief="raised", borderwidth=2, fg='#000000000', justify='center',command=lambda: impressum_schliessen(b3))
    b_imp1.place(relx=.5, rely=.8, anchor="c")

b3 = Tkinter.Button(backwin, text=("Click mich"), font=('Arial', 8, 'bold'), height=2, width=25, relief="raised", borderwidth=2, fg='#000000000', justify='center',command=lambda: impressum(b3))
b3.place(relx=.88, rely=.97, anchor="c")
backwin.mainloop()
wird bei mir das image nicht mehr angezeigt. wenn ich aber anstatt
text = Label(imp_fenster,text=("Hallo, hier steht ein Text."),font=('Arial', 8, 'bold'), height=10, width=25, fg="#000000000",justify='left')
einfach einen kleinen fehler einbaue:
textd = Label(imp_fenster,text=("Hallo, hier steht ein Text."),font=('Arial', 8, 'bold'), height=10, width=25, fg="#000000000",justify='left')
wird das Bild (ich hab einfach einen QR-Code genommen) natürlich mit einer kleinen fehlermeldung angezeigt!
woran liegt das denn??
Zuletzt geändert von Anonymous am Freitag 27. Januar 2017, 15:50, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@DMD-OL: Du benutzt irgendeine IDE, die bei Exceptions das Programm nicht beenden, sondern nur stoppen und den Stacktrace inklusive aller lokaler Variablen speichern. Dadurch bleibt auch die Variable photo im Speicher und wird nicht abgeräumt, wie es normalerweise beim Beenden der Funktion wäre. Ergo, um ein Bild darzustellen, mußt Du die Referenz auf das Image-Objekt speichern, weil sonst der Speicher freigegeben wird.
DMD-OL
User
Beiträge: 315
Registriert: Samstag 26. Dezember 2015, 16:21

yeah ich habs. vielen dank dir
Pykons
User
Beiträge: 11
Registriert: Dienstag 14. Februar 2017, 09:45

Hallo,

ich glaube ich habe grade ein ähnliches Problem. Leider kann ich mit der Lösung von Sirius nicht wirklich was anfangen. Eventuell könnt ihr mir helfen. Ich möchte auf einem Frame ein Bild darstellen, das Bild wird dargestellt, sobald ich einen Fehler erzeuge, so wie beim Ursprungsproblem.

Hier mein Code:

Code: Alles auswählen

import tkinter

PATH="bild.png"

class App:
    def __init__(self):
        self.master=tkinter.Tk()
        self.master.geometry("290x120")
        self.master.resizable(0,0)
        self.Frame()

    def Frame(self):
        self.frame=tkinter.Frame(master=self.master,bg="white")
        pic=tkinter.PhotoImage(file=PATH)
        self.Label=tkinter.Label(master=self.frame, image=pic)
        self.frame.pack()
        self.Label.pack()
        _thread.start_new_thread(self.sinnlosaktion,())

app=App()
Ohne den neuen Thread, in dem ich die Sinnlosaktion starte, wird mir das Bild nicht angezeigt. Ich wüsste also gerne, wie ich das Bild auch ohne Erzeugen einer Fehlermeldung anzeigen lassen kann.... :K

Danke schon jetzt für eure Hilfe! :D
Zuletzt geändert von Anonymous am Montag 20. Februar 2017, 12:09, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Pykons: `_thread` ist nicht Teil der öffentlichen API, das sollte man also nicht verwenden. Die öffentliche API für Threads ist das `threading`-Modul.

Ich kann das Problem nicht nachvollziehen denn da wird gar nichts dargestellt, weil das Programm mit einem `AttributeError` abgebrochen wird. Selbst wenn es das an der Stelle nicht würde, ist nirgends ein Aufruf der Tk-Hauptschleife, das heisst auch ohne diese Ausnahme würde da nie etwas angezeigt.

Sonstige Anmerkungen: Um das ``=`` Zuweisungen (ausser bei Schlüsselwortargumenten) und nach Kommas sollte man aus gründen der besseren Lesbarkeit Leerzeichen setzen.

Ausserhalb der `__init__()`-Methode sollte man keine neuen Attribute einführen. Das erstellen der Widgets würde ich sowieso nicht aus der `__init__()` heraus nehmen.

Ausser bei Namen für Klassen (MixedCase) und Konstanten (GROSSBUCHSTABEN_MIT_UNTERSTRICHEN) schreibt man konventionell alle klein_mit_unterstrichen. Also `frame()` statt `Frame()` für die Methode. Das kollidiert dann mit dem `frame`-Attribut an das ein `tkinter.Frame`-Exemplar gebunden wird. Deshalb benennt man Funktionen und Methoden üblicherweise nach Tätigkeiten die beschreiben was die Funktionen oder Methode tut.

Für das eigentliche Problem gilt weiterhin der Hinweis von Sirius3. Du darfst das Bild-Objekt nicht an einen lokalen Namen binden der ja nach der Abarbeitung der Methode verschwindet, und damit auch das Bildobjekt.
Pykons
User
Beiträge: 11
Registriert: Dienstag 14. Februar 2017, 09:45

@BlackJack: Der gepostete Code war nur eine Abbildung meines Problems. Mein eigentliches Programm ist mittlerweile mehrere hundert Zeilen lang. Das kann ich hier ja schlecht alles posten. Deshalb habe ich ein neues Programm geschrieben, in dem ich den Fehler reproduziert habe.

Habe jetzt einen globalen Namen definiert, jetzt funktioniert alles so, wie es soll! Ich danke dir!!
BlackJack

@Pykons: Bitte nicht dafür danken, denn das ist keine Lösung. Warum einen *globalen* Namen? Globale Variablen sind Böse™ und es gibt hier auch gar keinen Grund dafür.
Pykons
User
Beiträge: 11
Registriert: Dienstag 14. Februar 2017, 09:45

Aber wie soll ich das dann verstehen?
Du darfst das Bild-Objekt nicht an einen lokalen Namen binden der ja nach der Abarbeitung der Methode verschwindet, und damit auch das Bildobjekt.
Zuvor war es ein lokaler Name, nun ist es ein globaler Name, der nach abarbeiten des Programms eben nicht verschwindet - und somit auch nicht das Bildobjekt. Wie würde ich mein Problem denn "sauber" lösen?
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@Pykons: Du sollst das so verstehen, dass das Bild nicht nur an einen lokalen Namen gebunden sein darf, sondern auch noch als Attribut der Instanz Deiner Klasse oder Deines Labels.
Pykons
User
Beiträge: 11
Registriert: Dienstag 14. Februar 2017, 09:45

Also wäre die "sauberere" Lösung meines Problems, indem ich aus dem Namen "pic" ein "self.pic" mache. Habe ich das richtig verstanden?

Sorry, ich bin in erster Linie Pragmatiker, wenn etwas funktioniert und keine Fehlermeldung ausgibt bin ich zufrieden. Allerdings bin ich natürlich schon sehr daran interessiert, eine wirklich saubere Lösung zu erhalten.
Danke euch beiden! :)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Pykons hat geschrieben:Also wäre die "sauberere" Lösung meines Problems, indem ich aus dem Namen "pic" ein "self.pic" mache. Habe ich das richtig verstanden?
Das hast du richtig verstanden.
Pykons hat geschrieben:Sorry, ich bin in erster Linie Pragmatiker, wenn etwas funktioniert und keine Fehlermeldung ausgibt bin ich zufrieden.
Du bist aber (vermutlich) auch Anfänger. Das was erstmal funktioniert, kann dir später dennoch Probleme einhandeln. Du würdest ja sicher auch kein Haus bauen, das "erstmal steht", oder? ;)
Pykons hat geschrieben:Allerdings bin ich natürlich schon sehr daran interessiert, eine wirklich saubere Lösung zu erhalten.
Danke euch beiden! :)
Bist du gar nicht daran interessiert, aus welchem Grund dies eine saubere Lösung ist...? Ich nehme es mal vorweg:

Zum Einen sollte man Dinge, die zusammen gehören, auch möglichst nahe beieinander schreiben. Sowohl auf logischer Ebene als auch bezogen auf die Platzierung im Code. "pic" stellt ein Imlementierungsdetail deiner frame()-Methode dar. Wenn dieser Name aus technischen Gründen außerhalb der Methode definiert werden muss, dann ist die __init__()-Methode die nächsthöhere Ebene, um es dort sinnvoll einzubauen.

Zum Anderen überleben globale Namen die komplette Lebenszeit eines Moduls. Wenn dort eine oder mehrere Unterfunktionen deines Moduls lustig Werte zuweisen, dann verliert man sehr schnell den Überblick über die Funktionsweise und darüber, was gerade hinter dem globalen Namen steht. Insofern sollte man sich angewöhnen, dass Funktionen nur das verwenden dürfen, was ihnen als Funktionsargumente übergeben wird. Für Methoden gilt ähnliches, nur dass die zusätzlich noch auf "self" arbeiten dürfen. Nur feststehende Werte, die möglicherweise auch vom Benutzer angepasst werden dürfen, sollten auf Modulebene definiert werden. An denen sollten dann während der Laufzeit des Programms keine Neuzuweisungen vorgenommen werden. Bei der Verwendung ist es oft Geschmackssache, ob man sozusagen als Ausnahme auch aus Funktionen/Methoden heraus die Konstante nur lesend verwendet (schreibend wäre sowieso nicht ohne Weiteres möglich) oder ob man die Konstante als Default-Wert per Argument übergibt.

Wenn du dies beachtest, dann ist dies schon ein guter Punkt, um ein sauberes (d.h. leicht lesbares und leicht wartbares) Programm zu haben. Neben vielen weiteren Punkten natürlich. :)
Pykons
User
Beiträge: 11
Registriert: Dienstag 14. Februar 2017, 09:45

@snafu: Wow, danke für deine Mühe! Natürlich bin ich auch an dem Grund interessiert! :wink:
Hast schon recht, bin seit einem guten halben Jahr dabei und stoße dementsprechend häufig auf neue Probleme :D Aber hier wird einem ja super geholfen, danke dafür! :)
Antworten