Hallo,
ich habe gerade mit der tkinter Programmierung angefangen und will einen Zähler programmieren:
immer wenn ich auf einen Button drücke soll eine variable "zahl" um 1 erhöht werden und ausgegeben werden.
Ich komm einfach nicht weiter
es wäre nett wenn mir einer mit einem kleinem Quelltext helfen würde.
Tkinter Zähler
Code: Alles auswählen
from tkinter import*
count=0#anfangs counter bei 0
def druck(count):#counter Funktion
print(count)
return(count+1)
meinfenster = Tk()
Label(meinfenster,text="drücke den Knopf").pack()
Button(meinfenster,text="drück mich",command=druck(count)).pack()
count=druck(count)
print(count)
meinfenster.mainloop()
@hallo: Das `command`-Argument muss ein aufrufbares Objekt sein, also zum Beispiel eine Funktion oder Methode. Du übergibst dort aber eine 1, denn das ist an der Stelle der Rückgabewert vom ``druck(count)``-Aufruf. Und eine 1 ist offensichtlich nicht aufrufbar:
``return`` ist übrigens keine Funktion, darum sollte man das auch nicht schreiben als wäre es eine. Die Klammern gehören dort nicht hin weil sie völlig überflüssig sind.
Kommentare sollten dem Leser einen Mehrwert gegenüber dem Code bieten. Beide Kommentare tun das nicht. Das `count` am Anfang 0 ist und das ``def druck(…)`` eine Funktion definiert ist trivial aus dem Code ersichtlich. Der Name der Funktion könnte besser sein und beschreiben was die Funktion tut, zum Beispiel `update_counter()`.
Eine saubere Lösung würde hier wohl eine Klasse enthalten. IMHO sollte man objektorientierte Programmierung (OOP) halbwegs drauf haben *bevor* man sich an die GUI-Programmierung wagt.
Code: Alles auswählen
In [1]: 1()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-1-769e123a5ede> in <module>()
----> 1 1()
TypeError: 'int' object is not callable
Kommentare sollten dem Leser einen Mehrwert gegenüber dem Code bieten. Beide Kommentare tun das nicht. Das `count` am Anfang 0 ist und das ``def druck(…)`` eine Funktion definiert ist trivial aus dem Code ersichtlich. Der Name der Funktion könnte besser sein und beschreiben was die Funktion tut, zum Beispiel `update_counter()`.
Eine saubere Lösung würde hier wohl eine Klasse enthalten. IMHO sollte man objektorientierte Programmierung (OOP) halbwegs drauf haben *bevor* man sich an die GUI-Programmierung wagt.
Mal so als Beispiel:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import print_function, unicode_literals
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
class CounterUI(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.counter = 0
self.counter_label = tk.Label(self)
self.counter_label.pack()
tk.Button(self, text='drück mich', command=self.count_up).pack()
self._update_counter()
def _update_counter(self):
self.counter_label['text'] = str(self.counter)
def count_up(self):
self.counter += 1
self._update_counter()
def main():
root = tk.Tk()
CounterUI(root).pack()
root.mainloop()
if __name__ == '__main__':
main()
Meine Lösung:
Code: Alles auswählen
from tkinter import*
klicks=0
root=Tk()
label=Label(root,text="")
label.pack()
def counter():
global label
global klicks
label.configure(text=klicks)
label.update()
klicks+=1
Button(root,text="Klick mich",command=counter).pack()
mainloop()
Eine Variante, die ohne "global" auskommt, sie basiert auf der letzten Version von hallo:
ACHTUNG! Dieses Programm funktioniert zwar, die Anwendung wird jedoch nicht empfohlen!
Die zuvor von BlackJack vorgestellte Anwendung ist auf jeden Fall die empfehlenswertere, da leichter erweiterbare!
ACHTUNG! Dieses Programm funktioniert zwar, die Anwendung wird jedoch nicht empfohlen!
Die zuvor von BlackJack vorgestellte Anwendung ist auf jeden Fall die empfehlenswertere, da leichter erweiterbare!
Code: Alles auswählen
#!/usr/bin/python
import sys
if sys.version_info.major < 3:
import Tkinter as tkinter
else:
import tkinter
root=tkinter.Tk()
label=tkinter.Label(root,text="")
label.pack()
def counter(klicks=[]):
label.configure(text=len(klicks))
label.update()
klicks.append(None)
tkinter.Button(root,text="Klick mich",command=counter).pack()
tkinter.mainloop()
@jqz4n: Naja, funktionieren ist relativ. Das funktioniert genau dann, wenn man genau einen Button für den Zähler hat, sonst müsste man für jeden Zähler eine neue Funktion schreiben. Der default-Wert für alle ``klicks`` ist nämlich immer der selbe (darauf baut deine Implementierung sogar auf). Wenn man das in Kauf nehmen will, dann ist die Implementierung über das Verlängern der Liste natürlich ganz unsinnig. Genau so gut könntest du für klicks eine Liste verwenden, welche genau ein Element als Inhalt hat: die Anzahl der Klicks:
Das ist natürlich noch immer ein grausamer Hack. Ein wenig schöner wird es, wenn du die Funktionen verpackst:
Das ganze ist übrigens noch immer total unpraktisch, da von außen nicht auf den Zähler zugegriffen werden kann. Daher bietet sich, wie von BlackJack schon vorgeschlagen, der Ansatz über eine eigene Klasse an.
Bei der Gelegenheit solltest du auch gleich noch einen weiteren Blick auf seinen Code werfen. Dein Vorgehen beim Importieren von Tkinter ist nicht gerade ideomatisch. Der Weg über die Ausnahme ist deutlich schöner.
Code: Alles auswählen
def counter(klicks=[0]):
...
klicks[0] += 1
Code: Alles auswählen
def make_counter(initial=0):
value = initial
def counter():
...
value += 1
return counter
...
tkinter.Button(root,text="Klick mich",command=make_counter(0)).pack()
Bei der Gelegenheit solltest du auch gleich noch einen weiteren Blick auf seinen Code werfen. Dein Vorgehen beim Importieren von Tkinter ist nicht gerade ideomatisch. Der Weg über die Ausnahme ist deutlich schöner.
Das Leben ist wie ein Tennisball.
So gut wie sämtliche Lösungen für das Problem, die nicht auf Klassen bassieren, sind grausame Hacks. Gut, die Variante mit dem einen Wert in der Liste geht, wie auch die Variante des Tkinter-Imports "ein wenig" schonender mit dem Arbeitsspeicher umEyDu hat geschrieben:Das ist natürlich noch immer ein grausamer Hack.
Die zweite Variante des Codes funktioniert meines Erachtens nach nicht, weil die counter-Funktion nicht auf die value-Variable zugreifen kann. (Da value nicht in den locals der counter-Funktion ist, sondern nur in denen der make_counter-Funktion)
Aber schlechte Beispiele sind auch Beispiele
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
`couter` kann auf `value` zugreifen (-> Closure) ... nur aendern kann man `value` nicht. Da es aber beides gleichzeitig tun will fuehrt es zu einem `UnboundLocalError`.
Globale Variablen, direkt als Name mit `global` oder als mutable Datenstruktur wir Dictionaries, sind allerdings weit weniger grausam als eine Funktion die Parameter hat, allerdings nur dann korrekt funktioniert, wenn man sie nicht benutzt.
Globale Variablen, direkt als Name mit `global` oder als mutable Datenstruktur wir Dictionaries, sind allerdings weit weniger grausam als eine Funktion die Parameter hat, allerdings nur dann korrekt funktioniert, wenn man sie nicht benutzt.
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte
Der Error wird allerdings bereits geworfen, wenn ich in der counter-Subfunktioncofi hat geschrieben:`couter` kann auf `value` zugreifen (-> Closure) ... nur aendern kann man `value` nicht. Da es aber beides gleichzeitig tun will fuehrt es zu einem `UnboundLocalError`.
Code: Alles auswählen
...
def make_counter(initial=0):
...
def counter():
label.configure(text=value)
...
schreibe. – Obgleich zu diesem Zeitpunkt "value" noch gar nicht geändert wurde. Warum?
@jqz4n: Weil in den ``...`` in der inneren Funktion die Zeile steht die `value` in der Funktion neu bindet und damit zu einem lokalen Namen macht, der in der ersten Zeile halt noch nicht gebunden ist.