Neuling brach hilfe bei einfachem Counter

Fragen zu Tkinter.
Antworten
Zett
User
Beiträge: 9
Registriert: Samstag 26. Januar 2013, 00:01

Ich lerne hobbymässig nen bisschen Python und versuch grad nen einfachen Couner zu basteln.
Ich hab 2 Buttons, einen zum Hochzählen und einen zum Runterzählen(jeweils um 1) und das Ausgabelabel.
Die Darstellung funktioniert auch so wie sie soll, nur wird keine funktion ausgeführt, das Label bleibt bei 0 stehen.

Code: Alles auswählen

import tkinter as tk

class Application(tk.Frame):
    def __init__(self,master=None):
        tk.Frame.__init__(self,master)
        self.pack()
        self.createWidgets()

    def createWidgets(self):
        self.b1=tk.Button(text="Count +",command=Zahlenwert1.count_mehr())
        self.b2=tk.Button(text="Count -",command=Zahlenwert1.count_weniger())
        self.b3=tk.Button(text="Ende",command=root.destroy)
        self.l1=tk.Label(text=Zahlenwert1.Ausgabe())
        self.b1.pack()
        self.l1.pack()
        self.b2.pack()
        self.b3.pack()
   
class counter:
    def __init__(self,z):
        self.Anzahl=z
    def count_mehr(self):
        self.Anzahl+=1
        print(self.Anzahl)
    def count_weniger(self):
        self.Anzahl-=1
        print(self.Anzahl)
    def Ausgabe(self):
        print (self.Anzahl)
        return self.Anzahl
Zahlenwert1=counter(0)
root=tk.Tk()
app=Application(master=root)
app.mainloop()
Die print() in den Funktionen hab ich nur drinnen um zu sehen ob sich überhaupt was tut.
Warum tut sich da nix?
Zuletzt geändert von Anonymous am Samstag 26. Januar 2013, 00:32, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Der Parameter command muss eine Funktion sein und kein Rückgabewert der Funktion.

Code: Alles auswählen

self.b1=tk.Button(text="Count +",command=Zahlenwert1.count_mehr)
Außerdem muss dem Label ein StringVar-Objekt übergeben werden, das dann beim Counterzählen
geändert wird.
BlackJack

@Zett: Du übergibst nicht die Methoden als `command`-Argumente, sondern Du *rufst sie auf* und übergibst ihren *Rückgabewert*, also in diesem Fall `None` als `command`. `None` kann tkinter nicht aufrufen wenn die Schaltflächen gedrückt werden.

Bezüglich der Namensschreibweise könntest Du mal einen Blick in den Style Guide for Python Code werfen. Neben der Schreibweise sollte man keine Denglisch-Mix verwenden. Entweder Deutsch oder Englisch. Da die Programmiersprache und die Standardbibliothek in Englisch ist, bietet es sich an auch für den Rest dabei zu bleiben.

Widgets sollten sich nicht selbst „layouten”. Das macht keines der Widgets die von `tkinter` angeboten werden. Aus guten Grund, denn man nimmt damit dem Benutzer die Entscheidungsfreiheit sich selbst ein passendes Layout auszuwählen und die Widgets überall zu verwenden.

`createWidgets()` greift auf Namen auf Modulebene zurück die dort nicht hingehören. Man kann die `Application`-Klasse nur benutzen wenn man vorher bestimmte Objekte auf Modulebene an Namen gebunden hat. So eine Kopplung gehört nicht in ein sauberes Programm. Funktionen und Methoden sollten nur auf Werte zurückgreifen, die als Argumente übergeben wurden. Ausnahmen sind Konstanten. Wenn die `Application` also ein Zählerobjekt benötigt, dann sollte das in der `__init__()` übergeben und an das `Application`-Exemplar gebunden werden.
Zett
User
Beiträge: 9
Registriert: Samstag 26. Januar 2013, 00:01

Ich hab das nach dem TK-Manual zusammengeschraubt.
In die verschiedenen Konstruktoren muss ich mich erst noch einlesen, irgendwo.
Aber wie bekomme ich jetzt in dem Code den StringValue hin?
Ein einfaches str(self.Anzahl) hilft nicht weiter, auch die Methode __str__() nich, da bleibt das Label einfach leer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Zett

Willkommen im Forum. Habe dein Skript ein wenig aufgepeppt:

Code: Alles auswählen

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, title='', counter_set_value=0):
        
        self.app_win = tk.Tk()
        self.app_win.title(title)
        self.app_win.geometry('{}x{}+{}+{}'.format(100, 200, 20, 20))
        self.counter = Counter(counter_set_value)
        
        tk.Frame.__init__(self, self.app_win, relief='sunken', bd=2)
        self.pack(expand=True)
        self.display_value = tk.StringVar()
        
        self.createWidgets()

    def createWidgets(self):

        tk.Label(self, textvariable=self.display_value, font=('helvetica', 20),
            bg='white', fg='blue', relief='raised', bd=1).pack(fill='x')

        tk.Button(self, text="Count +",command=self.count_up).pack(fill='x')
        tk.Button(self, text="Count -",command=self.count_down).pack(fill='x')
        tk.Button(self, text="Ende",command=self.master.destroy).pack(fill='x')
                
        self.display(self.counter.count)
        
    def count_up(self):
        self.counter.up()
        self.display(self.counter.count)
        
    def count_down(self):
        self.counter.down()
        self.display(self.counter.count)
        
    def display(self, value):
        self.display_value.set(value)

    def run(self):
        self.app_win.mainloop()
        
class Counter:
    def __init__(self, start_value):
        self.count = start_value
        
    def up(self):
        self.count += 1
        
    def down(self):
        self.count -= 1
            
app = Application('Counter', 100).run()
Gruß wuf :wink:
Take it easy Mates!
BlackJack

@Zett: Mir fällt gerade noch ein Fehler auf: Widgets müssen bei der Erstellung als erstes das Widget übergeben bekommen in dem sie dargestellt werden. Sonst wird automatisch das `Tk`-Exemplar genommen. Es ist nur Zufall, dass das bei Deiner GUI nicht auffällt, dass Du einen leeren `Frame` (`Application`) hast und die Schaltflächen und das Label im `Tk`-Exemplar darstellst, statt in dem `Application`-Frame.

In einem ordentlichen Programm trennt man Programmlogik und GUI, das heisst wenn Du nach dem ändern des Zählers den geänderten Zählerstand in der GUI anzeigen möchtest, dann ist das Code der zur GUI gehört. Deshalb sollten die Schaltflächen mit Methoden in der UI-Klasse verbunden werden, die dann ihrerseits die Programmlogik auf dem Zähler-Objekt aufruft und danach die Anzeige aktualisiert.

Einbuchstabige Namen und solche mit angehängten Nummern sollten in der Regel auch nicht vorkommen. Der Name sollte dem Leser vermitteln was ein Objekt im Programmkontext bedeutet und nicht zum raten zwingen. An Objekte sollte man Werte nur binden, wenn sie auch tatsächlich dort gebraucht werden. Bei der Benutzeroberfläche trifft das nur auf das `Label`-Objekt zu.

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk


class Counter(object):
    def __init__(self, start_value=0):
        self.value = start_value
    
    def increase(self):
        self.value += 1
    
    def decrease(self):
        self.value -= 1


class CounterUI(tk.Frame):
    def __init__(self, master, counter):
        tk.Frame.__init__(self, master)
        self.counter = counter
        tk.Button(self, text='Count +', command=self.increase).pack()
        tk.Button(self, text='Count -', command=self.decrease).pack()
        tk.Button(self, text='End', command=self.master.destroy).pack()
        self.counter_label = tk.Label(self, text=self.counter.value)
        self.counter_label.pack()
    
    def increase(self):
        self.counter.increase()
        self.counter_label['text'] = self.counter.value

    def decrease(self):
        self.counter.decrease()
        self.counter_label['text'] = self.counter.value


def main():
    root = tk.Tk()
    ui = CounterUI(root, Counter())
    ui.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
Antworten