Gibt es einen timer, der eine Funktion öfter wiederholt?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Hallo zusammen,

Ich will, dass sich ein Ball in meinem Canvas selbsttätig bewegt.
Dazu brauche ich einen Timer. Mein Timer:

Code: Alles auswählen

def run():
    Berechnung()
    time.cancel()
    time.start()

time = threading.Timer(0.1, run)
soll alle 0.1 Sekunden die Funktion ausführen. Er bricht aber mit einer Fehlermeldung ab.
Ohne cancel und start wird der timer, da von Thread abgeleitet, nur 1x ausgeführt.

Gibt es eine Lösung, eine Funktion öfter auszuführen.

Danke schonmal
mintpc
deets

Canvas deutet auf GUI hin, und GUI und multi-threading gehen nicht gut zusammen. Bzw. eigentlich so gut wie gar nicht.

Welches GUI-Toolkit verwendest du? Abhaengig davon musst du dessen eingebauten Timer verwenden. Zb after in Tk.
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Hallo,

ich benutze das tkinter.

"after" soll ja ne aufrufende Wirkung haben, hab ich grade nachgesehen.

Allerdings passiert bei

Code: Alles auswählen

Fenster.after(500, NeuePopulation)  oder  Button1.after(500, NeuePopulation) 
leider nichts bzw. der Fehler: 'list' object has no attribute 'after'

Wäre denn after mein Timer-Ersatz?

Gruß

mintpc
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo,

du bekommst doch eine wunderschöne Fehlermeldung, welche dir den Fehler bereits verrät. Du probierst die after-Methode auf einer Liste aufzurufen und nicht auf einem Widget. Fenster oder Button1 sind in deinem Fall also wohl Listen.

Zusätzlich bieten die Fehlermeldungen auch noch ergänzende Informationen zur Fehlerquelle, daher solltest du immer die vollständige Fehlermeldung mit Traceback posten. Auch wäre der Code von der Fehlerstelle interessant, sonst kann man nur raten. Und das ist weder für dich, noch für die Helfenden besonders effizient.
Das Leben ist wie ein Tennisball.
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Danke erstmal für den Hinweis, allerdings steh ich immer noch auf dem Schlauch.

Ich poste einfach mal meinen Code um zu zeigen, was ich machen möchte (auch wenn
der Code evtl. nicht besonders schön ist).

Es geht mir um Conway's "Game of Life". Meine Umsetzung funktioniert auch, nur
muss für jede neue Population auf den Button geklickt werden, was nicht schön ist.

Es geht mir daher speziell um eine Art Timer. Ich möchte nämlich, dass die beiden
Prozeduren: NeuePopulation und ZellenAnzeigen immer wieder wiederholt werden
,
damit sich die Populationsentwicklung grafisch zeigt.

Ich weiß, dass hier im Forum schon ein "Spiel des Lebens" diskutiert wurde (!), hab ich
mir auch genau angesehen, aber die gezeigte Version ist ohne GUI. Ich wollte das Spiel
schon mit der tkinter GUI umsetzen, da die bei der Standard-Installation ja immer dabei ist.

Danke schonmal für jede Anregung.

mintpc

Code: Alles auswählen

import tkinter
import random
import threading
import time

### GUI

def run():
    NeuePopulation()
    ZellenAnzeigen()

def Schritt():
    NeuePopulation()
    ZellenAnzeigen()

def Neu():
    Zufallsbelegung()
    ZellenAnzeigen()

Fenster = tkinter.Tk()
t = tkinter.Text(Fenster, width= 42, height = 42)
t.pack()
bNeu = tkinter.Button(Fenster, text='Neu', command = Neu)
bNeu.pack()
b = tkinter.Button(Fenster, text='Schritt', command = Schritt)
b.pack()



a =  [  [t for t in range(0,41)] for z in range(0,41) ]
Nachbar = [[t for t in range(0,41)] for z in range(0,41)]
b =  [  [t for t in range(0,41)] for z in range(0,41) ]


def Zufallsbelegung():
 for i in range(1,40):
    for j in range(1,40):
        z = random.randint(1,2) 
        if z == 1:
            a[i][j]= True
        else:
            a[i][j] = False


def ZellenAnzeigen():
  t.delete('1.0','42.42')
  for i in range(1,40):
    t.insert("end","\n")  
    for j in range(1,40): 
        if a[i][j] == True:
            t.insert("end", 'x')
        else:
            t.insert("end", ' ')


def NeuePopulation():
    for x in range (1,40):
        for y in range (1,40):
            Nachbar [x][y] = 0
            
    for x in range (1,40):
        for y in range(1,40):
            if a[x+1][y] == True: Nachbar[x][y] = Nachbar[x][y]+1  #Rechts
            if a[x-1][y] == True: Nachbar[x][y] = Nachbar[x][y]+1  #Links
            if a[x][y-1] == True: Nachbar[x][y] = Nachbar[x][y]+1  #Oben
            if a[x][y+1] == True: Nachbar[x][y] = Nachbar[x][y]+1  #Unten
            if a[x+1][y-1] == True: Nachbar[x][y] = Nachbar[x][y]+1 # oben rechts
            if a[x-1][y-1] == True: Nachbar[x][y] = Nachbar[x][y]+1 # oben links
            if a[x+1][y+1] == True: Nachbar[x][y] = Nachbar[x][y]+1 # unten rechts
            if a[x-1][y+1] == True: Nachbar[x][y] = Nachbar[x][y]+1  # unten links

    for x in range (1,40):
        for y in range (1,40):
            if (a[x][y] == True) and (Nachbar[x][y] < 2):
                b[x][y] = False
            elif (a[x][y] == False) and (Nachbar[x][y] == 3):
                b[x][y] = True
            elif (a[x][y] == True) and (Nachbar[x][y] > 3):
                b[x][y] = False

    for x in range (1,40):
        for y in range (1,40):
           a[x][y] = b[x][y]


Zufallsbelegung()
Fenster.mainloop()
Zuletzt geändert von Hyperion am Mittwoch 12. September 2012, 19:49, insgesamt 1-mal geändert.
Grund: Quellcode in Python-Code-Tags gesetzt
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du solltest erstmal deinen Code aufräumen, so ist das ja eine Zumutung. Mal von oben nach unten abgearbeitet:

- Halte dich an PEP 8, dann kann man deinen Code leichter lesen und verstehen.
- Entscheide dich für Deutsch oder Englisch und keinen Mix.
- Benutze Konstanten. Die ganzen 42, 41, 40, 0 und 1 kann man doch alle aus der Spielfeldgröße ableiten.
- Dein Konstrukt

Code: Alles auswählen

[[t for t in range(0,41)] for z in range(0,41) ]
ist einfach nur kompliziert für

Code: Alles auswählen

[[list(range(0,41)) for z in range(0,41)]
- Warum hast du den eben genannten ausdruck dort dreimal stehen. Wenn du anfängst Code zu kopieren (abzuschreiben), dann machst du etwas falsch. So etwas musst du zusammenfassen.
- a, b und nachbar sind total nichtssagende Namen, das solltest du ändern. Wie sollst du die Code in vier Wochen noch verstehen oder wie sollen wir dir helfen, wenn der Code einfach beliebige Dinge tun kann. Ohne Namen ist man verloren.
- a, b und nachbar gehören offensichtlich zusammen, daher solltest du diese auch zusammen ablegen und nicht in drei Variablen.
- Der Block

Code: Alles auswählen

if z == 1:
            a[i][j]= True
        else:
            a[i][j] = False
ist nur kompliziert für

Code: Alles auswählen

a[i][j] = z==1
- == True ist IMMER sinnlos. Wenn a == True erfüllt wird, dann ist a offensichtlich bereits True. Entferne alle == True und ersetze die x == False durch ``not x``.
- Du kannst

Code: Alles auswählen

if a[i][j] == True:
            t.insert("end", 'x')
        else:
            t.insert("end", ' ')
einfacher als

Code: Alles auswählen

t.insert("end", "x" if a[i][j] else " ")
schreiben. Wie gesagt, doppelte Dinge sollten zusammengefasst werden.
- Du verwendest nachbar nur in der NeuePopulation-Funktion, warum hast du dann eine globale Variable dafür?
- Diesen riesigen if-Block kann man mit einer for-Schleife zusammenfassen

Code: Alles auswählen

for x ...
    for y ...
        for dx in range(-1, 2):
            for dy in range(-1, 2):
                if dx != dy and a[x+dx][y+dy]:
                    nachbar += 1
oder noch kompakter:

Code: Alles auswählen

for x ...
    for y ...
        for dx, dy in ((dx, dy) for dx in range(-1, 2) for dy in range(-1, 2) if dx != dy):
                if a[x+dx][y+dy]:
                    nachbar += 1
Das geht sogar noch kompakter aber, das will ich dir jetzt nicht zumuten.

Die Punkte solltest du erstmal alle verbessern, erst dann macht es überhautp Sinn dein Programm näher zu betrachten.
Das Leben ist wie ein Tennisball.
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Danke erstmal. Ich persönlich halte einen nicht verschachtelten Code
tatsächlich für lesbarer und klarer und besser nachzuvollziehen, auch
wenn er Redundanzen enthält.

Um eine Codeoptimierung geht es mir zwar nicht, trotzdem danke für
die Antwort. Ich habe mir die Änderungen zu Herzen genommen.

Meine Frage war aber ja, wie bekomme ich einen Timer hin?

mintpc
BlackJack

@mintpc: Noch besser als ein nicht verschachtelter Code *mit* Redundanzen wäre einer *ohne* Redundanzen. Wenn man das Gefühl hat etwas ist zu tief verschachtelt, kann man ja immer noch Code in Funktionen heraus ziehen oder Teilausdrücke heraus ziehen und deren Ergebnis an Namen binden.

Es geht auch nicht um eine Codeoptimierung in dem Sinn was man üblicherweise darunter versteht, also den Code für den Rechner und den Ablauf zu optimieren, sondern um Verbesserungen was die Les- und Wartbarkeit für Menschen angeht. Darum sollte es einem Programmierer so gut wie immer gehen.

Die Frage nach dem Timer wurde ja schon beantwortet: die `after()`-Methode auf *Widgets* und halt nicht auf Listen.

Dein Code operiert zu viel auf vorhandenen „globalen Arrays”. Das sieht nach BASIC, C, oder Pascal aus, wo man Speicher reserviert und dann dort überall mit Indizes drauf zugreift. Hier kommt man nicht ganz ohne solche Indexschleifen aus, man muss sie aber auch nicht *überall* verwenden.

`Nachbar` ist überflüssig und `b` eigentlich auch. Die vier Schleifen in `NeuePopulation()` kann man problemlos zu Einer zusammenfassen. Ungetestet:

Code: Alles auswählen

def count_neighbours(cells, x, y):
    return sum(
        int(cells[y + y_delta][x + x_delta])
        for y_delta in xrange(-1, 2)
        for x_delta in xrange(-1, 2)
        if y_delta and x_delta
    )


def new_population(cells):
    return [
        [
            count_neighbours(cells, x, y) == 3
            for x in xrange(1, len(cells[0]) - 1)
        ]
        for y in xrange(1, len(cells) - 1)
    ]
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Liebe Leute, ich finde es super, dass es ein Forum gibt, aber
es hilft nicht, bei einer einfachen Frage erstmal den Code auseinanderzunehmen.

Auch der Hinweis, in der Fehlermeldung "Liste" stecke die Lösung, ist an dieser Stelle nicht richtig,
weil das nicht der eigentliche Fehler war.

Es hätte mir mehr geholfen, einfach die Lösung zu nennen.

Die Lösung wäre gewesen:

Code: Alles auswählen

Fenster.after(100, Schritt)
und nicht wie ich zuerst geschrieben hatte

Code: Alles auswählen

Fenster.after(100, NeuePopulation)

Beste Grüße
mintpc
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

mintpc hat geschrieben:Liebe Leute, ich finde es super, dass es ein Forum gibt, aber es hilft nicht, bei einer einfachen Frage erstmal den Code auseinanderzunehmen.
Doch, weil dein Code völlig unleserlich ist. Die meisten haben keine Lust in nicht verständlichem Code nach Fehlern zu suchen. Wie denn auch, wenn man nicht einmal an den Namen erkennen kann was überhaupt passiert. Wenn du möchtest kannst du die Verbesserungsvorschläge natürlich gerne ignorieren, dann wirst du aber sicher nicht über so einfache Programme wie deins hinaus kommen und viel Hilfe wirst du auf die Dauer auch nicht erwarten können.
mintpc hat geschrieben:Auch der Hinweis, in der Fehlermeldung "Liste" stecke die Lösung, ist an dieser Stelle nicht richtig, weil das nicht der eigentliche Fehler war.
Wahrscheinlich hast du irgendwo etwas anderes geändert. Die Fehlermeldung ist auf jeden Fall so eindeutig, mehr Eindeutigkeit geht nicht mehr. In deinem gezeigten Code ist nicht einmal ein Aufruf von after enthalten!
mintpc hat geschrieben:Es hätte mir mehr geholfen, einfach die Lösung zu nennen.
Du erwartest also, dass wir in einen Logikfehler finden, in einem Stück Code dessen Logik man nur schwer nachvollziehen kann? Und dann hast du den Fehler auch noch so gut versteckt, dass du nicht einmal die entsprechende Zeile in deinem gezeigten Code hast. Da müssten wir schon hellsehen können.
Das Leben ist wie ein Tennisball.
deets

mintpc hat geschrieben:Liebe Leute, ich finde es super, dass es ein Forum gibt, aber
es hilft nicht, bei einer einfachen Frage erstmal den Code auseinanderzunehmen.

Auch der Hinweis, in der Fehlermeldung "Liste" stecke die Lösung, ist an dieser Stelle nicht richtig,
weil das nicht der eigentliche Fehler war.
Ach, wirklich nicht?

Code: Alles auswählen

>>> [1, 2, 3].after(None, None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'after'
Wenn du wild durch die Gegend bastelst, kann es schonmal vorkommen, dass sich Fehler veraendern. Aber zu dem Zeitpunkt, an dem du deine Fehlermeldung beschrieben hast, *IST* genau sowas wie hier gezeigt passiert.

Wir sind nicht dafuer verantwortlich, dass du den Ueberblick ueber deinen eigenen Code verloren hast. Hinweise, wie der zurueckzugewinnen waere hast du ja bekommen - aber die scheinen ja nicht gewollt zu sein.
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Zeiht mal eure Stöckelschuhe aus.

Ich wollte wissen, wie man einen Timer hinbekommt, das hat mir
keiner erklärt. Statt dessen wird auf meinem Programmcode herumgehackt,
obwohl ich das gar nicht wollte.

Wenn ich mit meinem Panda im Sumpf stecke bringt es mir ja auch nichts,
wenn jemand vorbei kommt und sagt: "Kauf dir erstmal einen Jeep, dann helfe
ich dir den Wagen rausschieben."
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

mintpc hat geschrieben:Ich wollte wissen, wie man einen Timer hinbekommt, das hat mir keiner erklärt.
Steht doch gleich in der ersten Antwort:
deets hat geschrieben:Welches GUI-Toolkit verwendest du? Abhaengig davon musst du dessen eingebauten Timer verwenden. Zb after in Tk.
Das Leben ist wie ein Tennisball.
mintpc
User
Beiträge: 50
Registriert: Montag 23. Januar 2012, 12:44

Stimmt.

Der Hinweis war schon richtig. Ich hatte es ja auch mit "after"
probiert, nur eben fehlte mir hier der richtige Einsatz der Funktion.

Wie dem auch sei, jetzt klappt's ja.

Bis zum nächsten Mal. Danke an alle Kommentare.

mintpc
Antworten