Counter Modul

Fragen zu Tkinter.
Antworten
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

Hey,

ich bin neu in python und will mal fragen was ihr alles verbessern/verändern würdet.

Gewünscht ist, wie der Counter hier auch schon macht, das er die Versuche zählt und nach 22 Versuchen "speren" ausgibt.

Code: Alles auswählen

from Tkinter import *

class init(Frame):

    def createWidgets(self):
        self.draw = Canvas(self, width="500", height="5i")
        self.draw.create_rectangle(0, 0, 10, 10, tags="thing", fill="black")
        self.draw.pack(side=LEFT)

    def moveThing(self, *args):
        self.checkCount()
        self.draw.move("thing",  1, 0)
        self.after(2000, self.moveThing)

    def count(self):
        global count
        count += 1 

    def checkCount(self):
        global count
        if count >= 22:
            print 'sperren'
        count = 0

    def testThing(self):
        print 'testThing'
        
    def __init__(self, master=None):
        Frame.__init__(self, master)
        Pack.config(self)

        global count
        count = 0
        
        self.createWidgets()
        self.moveThing()
Vielen Danke und schönen Gruß mayx
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Sternchen Importe sind böse, lass die weg.

global ist ebenfalls böse, lass das ebenfalls weg.

Es ist praktisch wenn man für Klassennamen, Funktionsnamen usw. andere Namenskonventionen verwendet, praktischerweise hält man sich dabei an Pep 8.

Die __init__ Methode definiert man üblicherweise als erstes in einer Klasse, dass ist aber nicht wirklich wichtig.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

mayx hat geschrieben:ich bin neu in python und will mal fragen was ihr alles verbessern/verändern würdet.
1. Im richtigen Unterforum posten.
2. Kein Sternchenimport von Tkinter.
3. Klassennamen besser wählen und mit Großbuchstaben beginnen.
4. Keine global-Anweisung - schon gar nicht, wenn du eine Klasse hast!
5. Die __init__-Methode an erster Stelle.
6. Keine Großbuchstaben in Methodennamen.
7. Nicht Pack verwenden.
8. Bei checkCount nicht jedesmal den counter auf 0 setzen.
9. mainloop() aufsetzen.
10. Tkinter anders einsetzen.

Nicht, dass das alles wäre, aber fürs erste sollte das reichen. :wink:
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

Alles klar, danke für die schnelle Antworten.
Werd es mal verbessern.

Was benutzt man statt global-Anweisung und Pack?
Stichwort für Google ;)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

mayx hat geschrieben:Alles klar, danke für die schnelle Antworten.
Werd es mal verbessern.

Was benutzt man statt global-Anweisung und Pack?
Stichwort für Google ;)
Wenn du Klassen verstanden hättest, würdest du die Frage nach global nicht stellen. Also: Klassen verstehen!

Zu Pack: Der pack()-Manager ist gut, aber die Art wie du ihn verwendest, ist sehr unüblich.
Die ausführlichste Doku zu Tkinter ist diese: http://effbot.org/tkinterbook/tkinter-index.htm

In deinem Fall würde ich als Einstiegslektüre empfehlen: http://www.ferg.org/thinking_in_tkinter ... grams.html
problembär

Boah, Dein Programm funktioniert ja noch nicht mal!
Was Du wahrscheinlich willst, könnte z.B. so gehen:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import Tkinter as tk

class RectangleMover:

    def __init__(self):
        
        self.count = 0
        self.createWidgets()

    def createWidgets(self):

        self.mw = tk.Tk()
        self.cv = tk.Canvas(self.mw, width="500", height="50")
        self.cv.create_rectangle(0, 0, 10, 10, tags="thing", fill="black")
        self.cv.pack(side = tk.LEFT)
        self.mw.after(500, self.moveThing)
        self.mw.mainloop()

    def moveThing(self):
        self.count += 1
        self.checkCount()
        self.cv.move("thing", 1, 0)
        self.mw.after(500, self.moveThing)

    def checkCount(self):
        if self.count >= 22:
            print 'sperren'
            self.count = 0

RectangleMover()
Poste doch bitte erst, wenn's auch funktioniert, oder wenn Du an einer konkreten Stelle nicht weiterkommst.

Gruß
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

numerix hat geschrieben:1. Im richtigen Unterforum posten.
FTFY, wie man so schön auf reddit sagt ;)

Außerdem: Magische Konstanten sollte man auch unterlassen. Was sind denn diese ``22``? Und bei diesem Programm zwar weniger nötig, aber wo wir schon mal dabei sind: sinnvoll kommentieren, damit sich andere (und du selbst in Zukunft) besser zurechtfinden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

Ja, super. Jetzt seh ich den Unterschied!
Ich importiere die Klasse und nutze Sie in mein Programm.
Da lief sie, entschuldige.

Ich importieren deine umgeschriebe Klasse im mein Programm
und nutze Sie dann so. Das funktioniert wunderbar. Gibt es auch
hier möglicherweise Fehler von mir?

Code: Alles auswählen

import RectangleMover as checkCount2
checkCount = checkCount2.RectangleMover()
checkCount.moveThing()
Ach ich sehe gerade, wenn ich keine grafische Ausgabe in der Klasse haben möchte, das das interval after ja über TK läuft.
Gibt es ähnliches von Pyhton? Eine Timerklasse die alle 200 Milisekunden taktet? Oder rufe ich mit after aus den Programm die Mehtode der Klasse auf?

Vielen Dank und Schönen Gruß mayx
problembär

Ach so, Du wolltest die Klasse aus einem anderen Skript importieren.
Dann würde ich das z.B. eher so schreiben:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import Tkinter as tk

class RectangleMover(tk.Frame):

    def __init__(self, master):

        tk.Frame.__init__(self, master)  
        self.count = 0
        self.createWidgets()
        self.pack()

    def createWidgets(self):

        self.cv = tk.Canvas(self, width="500", height="50")
        self.cv.create_rectangle(0, 0, 10, 10, tags="thing", fill="black")
        self.cv.pack(side = tk.LEFT)

    def moveThing(self):
        self.count += 1
        self.checkCount()
        self.cv.move("thing", 1, 0)
        self.cv.after(500, self.moveThing)

    def checkCount(self):
        if self.count >= 22:
            print 'sperren'
            self.count = 0

class MainWindow:

    def __init__(self):

        self.mw = tk.Tk()
        self.rm = RectangleMover(self.mw)
        self.rm.moveThing()
        self.mw.mainloop()

if __name__ == "__main__":
   app = MainWindow()
Anmerkungen:
- Man unterscheidet einfache Variablen wie "a", die nur innerhalb einer Funktion bekannt sind, und Klassenvariablen wie "self.a", die auch in anderen Funktionen (Methoden) derselben Klasse bekannt sind. Dadurch braucht man kein "global".
- Die Funktion "__init__()" wird automatisch ausgeführt, sobald ein Objekt der Klasse instantiiert wird (hier: "self.rm = RectangleMover(self.mw)")
- Man sollte keine Namen für Klassen oder Variablen verwenden, die mit anderen Namen verwechselt werden können: "init" kann man z.B. mit "__init__()" verwechseln, "draw" wird auch oft verwendet (wenn auch nicht in Tkinter.Canvas), "def count():" kann man mit der Variablen "count" oder "self.count" verwechseln ...
Ach ich sehe gerade, wenn ich keine grafische Ausgabe in der Klasse haben möchte, das das interval after ja über TK läuft. Gibt es ähnliches von Pyhton? Eine Timerklasse die alle 200 Milisekunden taktet? Oder rufe ich mit after aus den Programm die Mehtode der Klasse auf?
Ohne GUI/Tkinter könnte so etwas z.B. so aussehen:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import time

class Counter:

    def __init__(self):
        self.count = 0

    def increaseCounter(self):
        self.count += 1
        print self.count
        self.checkCount()
        time.sleep(0.2)
        self.increaseCounter()

    def checkCount(self):
        if self.count >= 22:
            print 'sperren'
            self.count = 0

class MainWindow:

    def __init__(self):
        self.counter = Counter()
        self.counter.increaseCounter()

if __name__ == "__main__":
   app = MainWindow()
"time.sleep()" stoppt aber die gesamte Anwendung. Wenn Du eine Konsolenanwendung bauen möchtest, die mehrere Sachen nebeneinander macht, wirst Du wahrscheinlich "Threads" verwenden müssen ("pydoc thread", "pydoc threading"). Ich vermeide sowas aber, weil ich's selten stabil hinkriege. Ein einzelner einfacher Thread könnte aber vielleicht klappen ...

Gruß
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

Hey, Cool Danke! Das hat mir sehr geholfen.

Eine andere Frage hab ich da doch gleich noch.
Wie kann ich Funktionen generieren?
Ich hab 13 Buttons und möchte nicht immer

Code: Alles auswählen

def pushbutton1(self, *msg):
     print self
def pushbutton2(self, *msg):
     print self
.....
Ich hab da an eine For-Schleife gedacht ;)
Geht das und wenn wie.

Code: Alles auswählen

for i in range(1,13): 
    functionsName = 'pushbutton%d'  % (i)
    def functionsName(self, *msg):
          print self
BlackJack

@mayx: Eine Funktion wird jedesmal erzeugt, wenn ein ``def`` oder ein ``lambda`` ausgeführt wird.

Wenn Deine Funktionen aber alle das gleiche machen, brauchst Du doch nur *eine* davon. Wieso will man 13 völlig identische Funktionen mit verschiedenen Namen haben? Du kannst auch an x verschiedene `Button`\s die selbe Funktion als `command` binden.

Wenn Du verschiedene Funktionen brauchst, die sich wie eine Verhalten, allerdings mit teilweise festen Werten für die Argumente, kann man `functools.partial()` verwenden.

Was Du ganz sicher nicht willst, ist Namen dynamisch erzeugen. Damit fangen Programme ganz schnell an unübersichtlich zu werden. Immer wenn Du denkst so etwas zu brauchen, denk an Listen und Dictionaries.
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

ah okay? Grübel noch.

Erstmal in den Funktionen steht natürlich nich das selbe.

Ich könnte zwar wie du meinstest immer die gleiche Methode
aufrufen, aber dann müsste ich ja in ihr eine Abfrage haben
welcher Button gedrückt worde und was passieren soll.
Dies ist doch unnötig, oder? Ich weiß ja vorher schon genau
welcher Button und welche Methode es aufrufen soll.

hmm. Ich glaube ich schreibe sie einfach aus ;)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Die gleiche Funktion immer wieder zu schreiben ist wenig sinnvoll. BlackJack hat schon auf "functools.partial" hingewiesen. Wenn das dein Problem nicht löst, dann gehören die Funktionen auch logisch nicht zusammen.

Code: Alles auswählen

>>> import functools
>>> def on_click(button_id, param):
	print(button_id, param)

	
>>> f = functools.partial(on_click, 1)
>>> g = functools.partial(on_click, 2)
>>> f("spam")
1 spam
>>> g("eggs")
2 eggs
>>> 
Das Leben ist wie ein Tennisball.
problembär

Ich könnte zwar wie du meinstest immer die gleiche Methode aufrufen, aber dann müsste ich ja in ihr eine Abfrage haben welcher Button gedrückt worde und was passieren soll. Dies ist doch unnötig, oder?
Wenn Du mehrere Buttons hast, von denen jeder etwas ganz Verschiedenes auslöst, solltest Du für jeden Button eine Methode schreiben.
Wenn mehrere Buttons etwas sehr Ähnliches auslösen, kannst Du für sie nur eine Methode schreiben, und dieser jeweils verschiedene Argumente übergeben (was bei Tkinter.Button leider ausnahmsweise etwas umständlich (aber möglich) ist).
Aber etwas wirst Du schon schreiben müssen, nur solltest Du eben möglichst nicht mehrmals oder immer wieder das gleiche schreiben.
Zum Beispiel kannst Du auch eine Liste von Buttons erzeugen oder so. Vieles ist möglich.

Gruß

P.S.: Hier noch ein Beispiel mit Thread; wohl erstmal nicht so leicht zu verstehen (da fortgeschrittene Schlangenbeschwörung):

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import threading
import time

class Counter(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.count = 0

        # Values for self.running:
        # 0 : Ready.
        # 1 : Running.
        # 2 : Finished.

        self.running = 0

    def run(self):
        self.running = 1
        self.increaseCount()

    def increaseCount(self):
        self.count += 1
        print self.count
        self.checkCount()
        time.sleep(0.2)

        if self.running == 1:
            self.increaseCount()

        elif self.running == 0:
            while self.running == 0:
                pass
            self.increaseCount()


    def checkCount(self):
        if self.count >= 22:
            print 'sperren'
            self.count = 0

class Application:

    def __init__(self):

        self.ct = Counter()
        self.ct.start()

        counteron = False

        for i in range(5):

            print "Main-Program here."

            if counteron:
                self.ct.running = 0
                print "Counter stopped, please wait."
                counteron = False
            else:
                self.ct.running = 1
                print "Counter started."
                counteron = True
            time.sleep(5)

        self.ct.running = 2
        print "Counter stopped permanently."
        print "Main-Program finished."

if __name__ == "__main__":
   app = Application()
Gruß
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

wow. Cool. Vielen Dank.
Ich werd jetzt erst alles verstehen müssen und dann schnell einbauen.
Meld mich dann wieder. (hoffe mit den fertigen Tool ;) )
mayx
User
Beiträge: 71
Registriert: Sonntag 3. Mai 2009, 02:51

Ich hab mich jetzt endlich mit threading auseinander gesetzt.
Vielen Dank nochmal für das sehr coole Beispiel.

Ich hab das ganz mal als MultiCounter umgebaut um Multithreading zu testen.
Hier der Code:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import threading
import time

class Counter(threading.Thread):

    def __init__(self, nr, increaseCountValue ):
        threading.Thread.__init__(self)
        self.count = 0
        self.nr = nr
        self.increaseCountValue = increaseCountValue

        # Values for self.running:
        # 0 : Ready.
        # 1 : Running.
        # 2 : Finished.

        self.running = 0

    def run(self):
        self.running = 1
        self.increaseCount()

    def increaseCount(self):
        self.count += self.increaseCountValue
        print 'Counter ' + str(self.nr) + '  ' + str(self.count)
        self.checkCount()
        time.sleep(0.2)

        if not self.running == 2:
            self.increaseCount()

    def checkCount(self):
        if self.count >= 1000:
            self.count = 0



class Application:

    def __init__(self):
                
        counterArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
       
        print "Create and start 15 counter"
        for i in range(15):  # 0-14       
            counterArray[i] = Counter(i,i)
            counterArray[i].start()
        
        print "Main-Programm wait"   
        time.sleep(3) 
        
        print "Stop 15 counter"
        for i in range(15):
            counterArray[i].running = 2
        
        print "Main-Program finished."
        
        
        
        
        
if __name__ == "__main__":
   app = Application()
Was gilt es zu verbessern? Dank ;)
Antworten