Seite 1 von 1
Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 13:43
von hallo
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.

Re: Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 13:48
von Sirius3
@hallo: Wenn Du nicht weiter kommst, wie weit bist Du denn bisher gekommen? Kannst Du Dein bisheriges Programm posten und schreiben, was daran nicht funktioniert?
Re: Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 14:11
von hallo
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()
Problem gibt 1 aus ende
Re: Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 14:17
von Sirius3
@hallo: an welcher Stelle weist Du der Variable »count« etwas anderes zu als 0?
Re: Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 14:27
von BlackJack
@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:
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
``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.
Re: Tkinter Zähler
Verfasst: Samstag 25. Januar 2014, 15:07
von hallo
ok danke
Re: Tkinter Zähler
Verfasst: Sonntag 26. Januar 2014, 02:33
von BlackJack
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()
Re: Tkinter Zähler
Verfasst: Samstag 1. Februar 2014, 10:02
von hallo
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()
Re: Tkinter Zähler
Verfasst: Samstag 1. Februar 2014, 10:43
von BlackJack
``global`` ist keine Lösung sondern ein Problem.

Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 19:34
von jqz4n
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!
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()
Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 20:07
von EyDu
@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:
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()
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.
Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 20:27
von jqz4n
EyDu hat geschrieben:Das ist natürlich noch immer ein grausamer Hack.
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 um
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

Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 21:03
von cofi
`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.
Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 21:08
von jqz4n
cofi 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`.
Der Error wird allerdings bereits geworfen, wenn ich in der counter-Subfunktion
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?
Re: Tkinter Zähler
Verfasst: Sonntag 2. Februar 2014, 21:27
von BlackJack
@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.