Button - keine Referenz beim Erstellen

Fragen zu Tkinter.
Antworten
McBarby
User
Beiträge: 4
Registriert: Samstag 4. Januar 2014, 09:20

Hallo Community,
für Sublime Text will ich mir eine grafische Oberfläche für ein paar Tools erstellen. Seltsamerweise erhalte ich vom Konstruktor der Widgets keine Referenz zurück. Das folgende Bsp. stellt das Problem dar.

Code: Alles auswählen

import Tkinter as tk

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Test')
        self.Button = tk.Button(self, text='Toggle', bg='yellow', command=self.toggleColor).place(x=50, y=20, width=100, height=24)
        # check the type - get: <type 'NoneType'> 
        print type(self.Button)

    def toggleColor(self):
		sColor = 'yellow'
		if self.iState == 0:
			sColor = 'red'
			self.iState = 1
		else:
			self.iState = 0

		self.Button.configure(bg=sColor)    #  FAIL

    iState = 0

app = App()
app.mainloop()
Was mache ich da verkehrt?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Doch, der tk.Button-Aufruf erzeugt einen neuen Button und liefert dessen Referenz zurück. Allerdings rufst du darauf gleich die place-Methode auf, welche None als Ergebnis liefert. Dies Ergebnis bindest du dann an self.Button. Du musst also den place-Aufruf machen, nachdem du die Referenz gespeichert hast.

Allerdings solltest du place gar nicht verwenden, sondern besser auf pack oder grid zurückgreifen. place funktioniert nur auf deinem System, auf allen anderen ist die Oberfläche wahrscheinlich unbenutzbar, da Schriften in einander verschoben sind, Buttons auf Buttons liegen oder sonstige seltsame Dinge passieren. Dazu reicht schon eine andere Schriftgröße, eine andere Schriftart oder eine andere Bildschirmauflösung. Und natürlich noch viele kleine andere Dinge, welche sich von System zu System unterscheiden.

Das iState macht nicht das, was du erwartest. Es sollte zu einer konkreten Instanz gehören und nicht zu der Klasse. Das es in deinem Fall funktioniert ist mehr oder weniger Glück. Ansonsten solltest du dir auch die ungarische Notation der Namen abgewöhnen. "sColor" und "iState" wird seit Ewigkeiten schon nicht mehr verwendet, in Python, wo sich Typen gerne mal ändern, schon gar nicht.

Ansonsten könntest du noch einen Blick auf PEP 8 werfen. Du solltest mit 4 Leerzeichen einrücken und zur Namensvergabe gibt es dort auch einige Regeln. Daran solltest du dich halten, wenn du mit Python arbeitest. Das vereinfacht das Verständnis des Codes für dich und, besonders wichtig wenn du Fragen in einem Forum stellst, für andere.
Das Leben ist wie ein Tennisball.
McBarby
User
Beiträge: 4
Registriert: Samstag 4. Januar 2014, 09:20

Vielen Dank EyDu.
Ja, mit der ungarischen Notation das sitzt noch so in Fleisch und Blut - da fällt es schwer sich umzustellen. Bisher habe ich kaum mit OOP zu tun gehabt, die letzten 10 Jahre habe ich ausschließlich prozedural programmiert.
Ich werde mir die Regeln für Python dann mal einverleiben.
Die Verwendung von place ist einfach dem Umstand geschuldet, dass ich hier nicht umdenken brauche, da ich bisher auch mit exakter Größendefinition gearbeitet habe. Und bei Verwendung von Standardfonts und Definition der Fontsize sehe ich da eher wenig Probleme.
grid sieht ganz interessant aus, erinnert mich an Containerpositionierung in css. Aber ich stecke in Python noch in den Kinderschuhen, also immer schöne kleine Schritte machen. :wink:

Viele Grüße
McBarby
BlackJack

@McBarby: Selbst wenn es bei den Schriften und deren Grössen keine Probleme gäbe, die gibt es nämlich weil die von den Einstellungen des Systems abhängen, sind solche GUIs sehr unflexibel und nur sehr umständlich zu ändern wenn man mal etwas anders anordnen, Elemente hinzufügen oder Wegnehmen, Texte ändern möchte und so weiter. Mit `pack()` und `grid()` arbeitet man tatsächlich so ähnlich wie in HTML, wo man ja auch keine absoluten Layouts verwendet.

Etwas mehr nach Python könnte das zum Beispiel so aussehen:

Code: Alles auswählen

import Tkinter as tk

 
class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Test')
        self.button = tk.Button(self, text='Toggle', command=self.toggle_color)
        self.button.pack()
        self.colors = ['yellow', 'red']
        self.state = False
        self._update()
 
    def _update(self):
        self.button['bg'] = self.colors[self.state]

    def toggle_color(self):
        self.state = not self.state
        self._update()
 

def main():
    app = App()
    app.mainloop()
Und neben OOP bietet Python auch einige eher funktionale Ansätze. Wenn man den `state` hier zum Beispiel nicht wirklich braucht und es reicht die Farben zu alternieren, kann man das beispielsweise mit `itertools.cycle()` umsetzen:

Code: Alles auswählen

from itertools import cycle

 
class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title('Test')
        self.button = tk.Button(self, text='Toggle', command=self.toggle_color)
        self.button.pack()
        self.colors = cycle(['yellow', 'red'])
        self.toggle_color()
 
    def toggle_color(self):
        self.button['bg'] = next(self.colors)
McBarby
User
Beiträge: 4
Registriert: Samstag 4. Januar 2014, 09:20

@BlackJack
Danke für die interessanten Ausführungen. Als Neueinsteiger in Python muß ich mir erst mal die Möglichkeiten, die die Sprache bietet, erarbeiten. Da sind solche Anregungen sehr willkommen.
Antworten