Tkinter Struktur - Labels ohne global benutzen

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
Zoja
User
Beiträge: 145
Registriert: Freitag 28. Februar 2014, 14:04

Hallo zusammen, ich arbeite wieder etwas mit Tkinter und will alles perfekt von der Struktur haben und alles richtig initiieren. Habe gerade ein Problem die Labels bild_size und bild_preview zu benutzen, wenn ich global entferne sind die natürlich nicht defined, es muss ja eigentlich self.bild_size etc. heißen, dann muss aber auch bei der methoden bildinhalt_aussuchen(self) das self weitergegeben werden, wenn ich es nicht tue ist self nicht defined, aber wenn ich self bei der methode überall als argument hinzufüge verhält such das programm merkwürig, es führt beim start direkt die methode aus und zeigt mir nicht den Button.

Im Allgemeinen ist meine Struktur so in Ordnung? Und wie löse ich das best practice?

Ach und ich habe oft gelesen place() wäre nicht gut, ist das denn so eine Katastrophe? Ich habe andere Programme auf unterschiedlichen Geräten probiert mit place(), war nie ein Problem, wenn es aber der Fall ist, dass es echt crap ist, wäre eine Empfehlung gut!

Vielen Dank!

hier der Code:

Code: Alles auswählen

# -*- coding: utf-8 -*-
""" allgemeine Importe """
from tkFileDialog import askopenfilename, asksaveasfile
from Tkinter import Button, Frame, Tk, Label, Toplevel, Text, END, Menu, Entry, Listbox, Scrollbar, VERTICAL, RIGHT, Y
from tkMessageBox import askokcancel, showinfo, showwarning
import PIL
from PIL import Image, ImageTk
import os
import ttk

class Blog_Post_Tool(Frame):

    # Variablen nur in der init initialisieren
    def __init__(self, parent):
        Frame.__init__(self, parent, background="white")         
        self.parent = parent    
        self.initUI()
    
    # Hier werden alle GUI Elemente initialisiert
    def initUI(self):        
        self.place()
        
        """ Labels """
        global bild_size
        bild_size = Label(root, text = "")
        bild_size.place(x = 10, y = 40)
        
        global bild_preview
        self.bild_preview = Label(root, image = None)
        self.bild_preview.place(x = 10, y = 60)
        
        """ Buttons """
        self.choose_image = Button(root, text = "Bild aussuchen", bg = "LightBlue2", command=bildinhalt_aussuchen)
        self.choose_image.place(x = 10, y = 10)
            
""" Hier beginnen die Methoden """
# Fuer das Schließen des Programms
def callback():
    if askokcancel("Achtung", "Möchten Sie das Programm wirklich beenden?"):
        root.destroy()
    else:
        return

def bildinhalt_aussuchen():
    try:
        """ Hier wird nach dem Bild gefragt """
        pfad = askopenfilename()
        """ Fuer die size """
        im_for_size = Image.open(pfad)
        """ Erstellt die Vorschau """
        im_thumb = Image.open(pfad)
        basewidth = 500
        wpercent = (basewidth / float(im_thumb.size[0]))
        height = int((float(im_thumb.size[1]) * float(wpercent)))
        im_thumb = im_thumb.resize((basewidth, height), PIL.Image.ANTIALIAS)
        photoimg = ImageTk.PhotoImage(im_thumb)
        bild_preview.image = photoimg
        bild_preview.config(image = photoimg)
        bild_size.config(text = (str(im_for_size.size[0]) + " x " + str(im_for_size.size[1])))
    except IOError:
        return
        
if __name__ == "__main__":
    root = Tk()
    app = Blog_Post_Tool(root)
    root.title("Branding Tool")
    root.resizable(0, 0)
    root.geometry('800x600')
    root.protocol("WM_DELETE_WINDOW", callback)
    root.mainloop()
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Was "Best Practice" ist, lasse ich unkommentiert, weil ich selbst noch wenig Erfahrung in GUI-Programmierung habe und lieber nichts Falsches sagen möchte^^.

Wegen dem "self als Parameter wird sofort ausgeführt" meinst du vermutlich die Zeile:

Code: Alles auswählen

self.choose_image = Button([...] command=bildinhalt_aussuchen)
Wenn du im "command=" einen Parameter mitgibst, wird das von Python direkt als Funktionsaufruf interpretiert und das *Ergebnis* an self.choose_image gebunden. Die einfachste Möglichkeit (und IMHO in diesem Fall ein unschöner Workaround) wäre hier ein lambda-Operator:

(Code nicht getestet)

Code: Alles auswählen

self.choose_image = Button([...] command=lambda: bildinhalt_aussuchen(self))
Möglichkeit Nr. 2: Aus der (für sich alleinstehenden) Funktion, die aufgerufen wird, machst du eine Methode innerhalb(!) deiner Klasse. Dann kannst du in der Methode gleich auf die Attribute zurückgreifen, z.B.:

(Code nicht getestet)

Code: Alles auswählen

self.choose_image = Button([...] command=self.bildinhalt_aussuchen)
bild_size und bild_preview musst du in dem Fall natürlich auch noch zu Attributen innerhalb der Klasse machen ("self." davor).

Generell sieht das alles aber ziemlich seltsam aus (z.B. wird mehrmals "root" als Parent fürs Tk-Fenster verwendet, auch in der Klasse, wo es nirgends definiert wird). Ich für meinen Teil würde die "callback"-Funktion ebenfalls eher als Methode innerhalb der Klasse implementieren... Aber wie gesagt, da überlasse ich das Feld lieber den Forenschreibern, die deutlich mehr Erfahrungen haben als ich^^...
BlackJack

@Zoja: Zeichenketten haben nur am Anfang von Modulen, Klassen- und Funktionsdefinitionen eine spezielle Bedeutung als Dokumentation für das jeweilige Konstrukt. Mitten im Quelltext sind das einfach nur tote Ausdrücke/Daten und sollte besser mit Kommentaren ausgezeichnet werden, die dann tatsächlich vom Compiler in jedem Fall ignoriert werden.

Der Kommentar „Hier beginnen die Methoden“ ist falsch weil dort keine Methoden stehen sondern Funktionen. Und mindestens die Funktionen die auf die globalen Variablen zugreifen sollten stattdessen tatsächlich Methoden sein. Dazu muss man nicht nur `self` als erstes Argument haben sondern die müssen syntaktisch auch zur Klasse gehören. Woher soll Python sonst wissen das es nicht einfach nur Funktionen sind?

Alternativen zu `place()` sind `pack()` und `grid()` und gegebenfalls zusätzliche Containerwidgets um Widgets zu sinnvollen Gruppen zusammen zu fassen.

Was beim Layout auch nicht passieren sollte ist das sich Widgets selbst layouten wie das Dein `Blog_Post_Tool` macht. Das macht kein bereits bestehendes Widget weil die dann auch wesentlich weniger brauchbar wären wenn man nicht selber entscheiden könnte wie und wo es angeordnet wird.
Zoja
User
Beiträge: 145
Registriert: Freitag 28. Februar 2014, 14:04

Vielen Dank schonmal das mit dem self als Argument beim Button im command habe ich nicht gewusst, das hilft weiter!

Der Code ist ja gerade sehr kurz, und jeder strukturiert das unterschiedlich, zumindest finde ich die unterschiedlichsten Tutorials.

@BlackJack da ich dir schon sehr vertraue und du mir schon sehr oft geholfen hast, würde ich gerne versuchen mein Tkinter so zu strukturieren wie du es machen würdest, es wäre sehr nett, wenn du den gegebenen Code umstrukturierst/anders einrückst, halt so wie du es machen würdest.

Ansonsten vielen Dank wie immer!
Antworten