Seite 1 von 1
Pygame time.set_timer
Verfasst: Mittwoch 14. Januar 2015, 05:26
von lemonbutterfly
servus,
die timer anwendung, wie sie zu sehen ist funktioniert für den einzelfall schon richtig gut (verbesserungsvorschläge wären super)
jetzt versuche ich auf den 3 buttons auch 3 verschiedene funktionen laufen zu lassen.
verschiedene laufzeiten funktionieren schonmal ABER die ausgeführte funktion ist immer die gleiche und bis jetzt ist jeder versuch gescheitert, das zu ändern.
dazu möcht ich noch sagen das ich erst ein anfänger in der programmierung bin.
Code: Alles auswählen
import pygame as pg
import sys
import gfx
import sfx
from pygame.locals import*
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
pg.init()
show = gfx.Image()
play = sfx.Sound()
clock = pg.time.Clock()
fps = 60
screen = pg.display.set_mode((1024, 768), 0, 32)
pg.display.set_caption("timed funktion")
button1 = (409, 362)
button2 = (409, 449)
button3 = (408, 537)
timer_pos = button1
lenght = height = 70
########## timer #####################
wait1 = 3
wait2 = 4
wait3 = 5
pg.time.set_timer(USEREVENT + 1, 1000)
class Timer(object): # macht timer zu einem objekt
hour = 0
min = 0
sec = 0
timerun = 0
def print_timer(self):
return print("(%.2d:%.2d:%.2d)" % (start_timer.hour, start_timer.min, start_timer.sec))
def show_timer(self): # ausgabe auf sceen
fontmy = pg.font.Font("fonts/miso.ttf", 36)
text01 = fontmy.render("Timer:" + str("%.2d:%.2d:%.2d" %
(start_timer.hour, start_timer.min, start_timer.sec)), True, white)
screen.blit(text01, timer_pos)
pg.display.update()
def inc_timer(self, x, dummy):
dummy.sec = x
if x >= 60:
dummy.min, dummy.sec = divmod(x, 60)
if dummy.min >= 60:
dummy.hour, dummy.min = divmod(dummy.min, 60)
return start_timer
def counter(self):
start_timer.sec -= 1
if start_timer.hour > 0 > start_timer.sec and start_timer.min == 0:
start_timer.hour -= 1
start_timer.min += 60
if start_timer.min > 0 > start_timer.sec:
start_timer.min -= 1
start_timer.sec += 60
dummytimer.print_timer()
if start_timer.hour == 0 and start_timer.min == 0 and start_timer.sec == 0:
print("countdown has endet -> start function")
play.click3()
Timer.timerun = 0
start_timer = Timer() # objekte der klasse Timer
load_timer = Timer()
about_timer = Timer()
dummytimer = start_timer
######### mainloop ####################
game = True
while game:
keys = pg.key.get_pressed()
for event in pg.event.get():
if Timer.timerun == 1:
if event.type == USEREVENT + 1: # counter tick
dummytimer.counter()
if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
game = False
sys.exit()
if event.type == MOUSEBUTTONDOWN:
mx, my = event.pos
if (button1[0] < mx < button1[0]+lenght) and (button1[1] < my < button1[1]+height):
if start_timer.sec == 0:
timer_pos = button1
dummytimer.inc_timer(wait1, start_timer)
dummytimer.print_timer()
Timer.timerun = 1
if (button2[0] < mx < button2[0]+lenght) and (button2[1] < my < button2[1]+height):
if start_timer.sec == 0:
timer_pos = button2
dummytimer.inc_timer(wait2, start_timer)
dummytimer.print_timer()
Timer.timerun = 1
if (button3[0] < mx < button3[0]+lenght) and (button3[1] < my < button3[1]+height):
if start_timer.sec == 0:
timer_pos = button3
dummytimer.inc_timer(wait3, start_timer)
dummytimer.print_timer()
Timer.timerun = 1
screen.blit(show.start, (0, 0))
if Timer.timerun == 1: # blit nur wenn timer aktiv
dummytimer.show_timer()
pg.display.update()
clock.tick(fps)
pg.quit()
Re: Pygame time.set_timer
Verfasst: Mittwoch 14. Januar 2015, 10:57
von BlackJack
@lemonbutterfly: Du möchtest Dir objektorientierte Programmierung vielleicht ausserhalb von so einem für Anfänger doch schon recht komplexem Programm anschauen und lernen wie man das richtig macht. Die Klasse `Timer` ist keine Klasse. Die Attribute gehören nicht auf die Klasse, und keine der ”Methoden” benutzt den Umstand das es Methoden auf einem Objekt sind, das sind alles Funktionen. Die zudem auch noch teilweise auf globalen Werten operieren.
Steck mal das Hauptprogramm in eine Funktion (wird konventionell `main()` genannt). Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. Das hat dann auch zur Folge dass man das Hauptprogramm und definitionn von Funktionen oder Klassen die davon benutzt werden, nicht so unübersichtlich mischen kann. Der Quelltext Deines Hauptprogramms fängt ja an, wird von einer Klassendefinition unterbrochen und geht dann erst weiter.
``return print(…)`` macht keinen Sinn. `print()` gibt etwas aus und nichts sinnvolles zurück. An der Stelle würde ich auch eher die `__str__`-Methode implementieren und dann das `Timer`-Exemplar selbst mit `print()` ausgeben. Die `show_timer()`-Methode bräuchte dann nicht die gleiche Umwandlung eines `Timer`-Exemplars noch mal machen, sondern kann einfach `str()` mit sich selbst aufrufen.
Das `datetime`-Modul enthält Datentypen die einen mit Zeitwerten rechnen lassen ohne dass man sich das selber basteln muss.
Eine Methode auf einem `Timer`-Objekt würde ich kein ``pg.display.update()`` machen lassen. Stell Dir mal vor Du willst mehrere Timer gleichzeitig darstellen, dann würde jeder das Display aktualisieren, statt das man erst alle Blittet und dann *einmal* aktualisiert. Die Kommunikation zwischen Arbeitsspeicher und Grafikkarte kann potentiell verhältnismässig teuer sein.
`dummy` ist etwas was nicht gebraucht wird. Es sieht extrem komisch aus wenn jemand anfängt mit etwas was `dummy` heisst, unmengen an Operationen durchzuführen.
Die ”Methoden” von `Timer` dürften wie gesagt gar nicht auf `start_timer` und `dummytimer` zugreifen. Funktionen und Methoden sollten ausser auf Konstanten nur auf Werte zugreifen die als Argument übergeben wurden. Und nicht einfach so auf Namen die irgendwie aus der Umgebung kommen. Das schafft undurchsichtige Zusammenhänge.
Die `game` Variable erscheint mir unsinnig. Die Hauptschleife kannst Du durch ``while True:`` ersetzen, denn `game` kann an dieser Stelle niemals `False` werden. Desweiteren kann die letzte Zeile, die nach der ``while``-Schleife niemals erreicht werden, ist also entweder überflüssig, oder sollte nicht *dort* stehen, sondern bevor das Programm beendet wird ausgeführt werden.
Die `button*` würde ich als `pygame.Rect` anlegen, dann kann man leichter testen ob die Mausposition darin liegt.
Wenn man anfängt Namen durchzunummerieren will man in der Regel eigentlich eine Datenstruktur verwenden statt der Einzelwerte. Meistens eine Liste. Das trifft hier auf die `button*` und `Timer`-Exemplare zu. Dann müsste man auch nicht dreimal fast gleiche ``if``-Abfragen samt davon abhängigen Code schreiben, sondern kann das einmal schreiben in einer Schleife über die zusammengehörenden `button*` und `Timer`-Exemplare. Stichwort `zip()`-Funktion. Oder man führt einen Datentyp ein der jeweils Button und Timer zu einem Objekt zusammenfasst.
Re: Pygame time.set_timer
Verfasst: Mittwoch 14. Januar 2015, 14:06
von BlackJack
@lemonbutterfly: Nachtrag: Grundsätzlich ist es auch keine gute Idee so einen Countdown selber jede Sekunde zu aktualisieren. Das ist ungenau. Du lässt einmal die Sekunde ein Timer-Event erzeugen und wartest in jedem Schleifendurchlauf auch 1 Sekunde, das heisst es kann passieren, dass das Timer-Event etwas länger braucht und deshalb erst im nächsten Frame verarbeitet wird.
Man würde so einen Timer eher so modellieren dass man die Differenz von Start- und aktueller Zeit berechnet, beziehungsweise von aktueller Zeit und dem Zeitpunkt an dem der Countdown zuende sein soll. Dann ist man auch unabhängig von der Framerate, muss nicht ständig und regelmässig den Zustand des `Timer` aktualisieren sondern kann jederzeit den aktuellen Zustand berechnen.
Re: Pygame time.set_timer
Verfasst: Sonntag 18. Januar 2015, 18:26
von lemonbutterfly
vielen dank BlackJack für die schnelle und ausführliche antwort,
auch wenn ich nich alles verstehe, was du geschrieben hast, hab ich die ersten änderungen schon vorgenommen.
...vieleicht hab ich mir für den anfang ein kleinwenig viel vorgenommen, aber ich lerne mit jedem tag dazu
der meinloop ist jetzt eine funktion
die überflüssigen sachen hab ich alle rausgeschmissen und bin grad dabei die buttons in rect umzubauen.
das timer objekt hab ich auf:
reduziert.
du hattest das "datetime" modul erwähnt, das hab ich auf pygame.org nen paar infos zu gefunden werd aber bis jetzt noch nicht so richtig schlau draus, wie ich des am besten einbinden soll
...da mir durch die arbeit nen bisschen die zeit fehlt, dauerts manchmal nen bisschen länger bis ich was gelernt hab,
ich wäre sehr dankbar für hilfreiche ansätze
Re: Pygame time.set_timer
Verfasst: Sonntag 18. Januar 2015, 18:37
von lemonbutterfly
ps. dadurch das der mainloop jetzt eine funktion ist, wird die variable timer_pos nicht mehr akzeptiert
...vom gefühl aus würde ich sagen, die werte als attribut in die funktion packen aber irgenwie erscheint mir das falsch...
Re: Pygame time.set_timer
Verfasst: Sonntag 18. Januar 2015, 19:50
von EyDu
lemonbutterfly hat geschrieben:du hattest das "datetime" modul erwähnt, das hab ich auf pygame.org nen paar infos zu gefunden werd aber bis jetzt noch nicht so richtig schlau draus, wie ich des am besten einbinden soll
Das datetime-Modul ist ein Standardmodul von Python. Du musst daher in der Dokumentation von Python nachschauen und nicht in der von PyGame.
lemonbutterfly hat geschrieben:ps. dadurch das der mainloop jetzt eine funktion ist, wird die variable timer_pos nicht mehr akzeptiert

Was heißt "nicht mehr akzeptiert?" Gibt es eine Fehlermeldung dazu? Am besten postest du diese und den Quellcode deines Programms. Dann lässt sich das schnell nachvollziehen.
lemonbutterfly hat geschrieben:
Das funktioniert so nicht. hour, min und sec sind nun Attribute von der Klasse Timer. Wenn du einem Objekt ein Attribut geben möchtest, dann musst du das in der __init__-Methode machen.
Code: Alles auswählen
class Timer(object):
def __init__(self, hour, min, sec):
self.hour, self.min, self.sec = hour, min, sec
Re: Pygame time.set_timer
Verfasst: Donnerstag 22. Januar 2015, 11:15
von BlackJack
@lemonbutterfly: Hier mal grob skizziert wie man so einen Timer schreiben könnte der nicht regelmässig aktualisiert werden muss und von dem man jederzeit die aktuelle Restzeit abfragen kann:
Code: Alles auswählen
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from datetime import datetime as DateTime, timedelta as TimeDelta
from time import sleep
ZERO_TIME_DELTA = TimeDelta()
class Timer(object):
def __init__(self, hours=0, minutes=0, seconds=0):
self.end_time = (
DateTime.today()
+ TimeDelta(hours=hours, minutes=minutes, seconds=seconds)
)
def __str__(self):
minutes, seconds = divmod(self.time_left.seconds, 60)
hours, minutes = divmod(minutes, 60)
return '{0:02d}:{1:02d}:{2:02d}'.format(hours, minutes, seconds)
@property
def time_left(self):
return max(self.end_time - DateTime.today(), ZERO_TIME_DELTA)
@property
def has_finished(self):
return self.time_left == ZERO_TIME_DELTA
def main():
timer = Timer(0, 1, 30)
while not timer.has_finished:
print(timer)
sleep(0.5)
print(timer, 'Kaboom!')
if __name__ == '__main__':
main()
Re: Pygame time.set_timer
Verfasst: Sonntag 25. Januar 2015, 16:19
von lemonbutterfly
@BlackJack vielen Dank für deinen "timer" der code funktioniert echt prima.
ich hoffe es hat dir nicht zu viel mühe bereitet.
es werden sich bestimmt noch viele nach mir darüber freuen.
hab das modul in mein hauptprogramm eingebunden und übergebe die countdownzeit als parameter,
hab auch noch nen "sys.exit" in die while schleife gepackt, sonst ließ sich das programm nicht beenden während der timer lief.
...nur steh ich momentan wieder vor der problematik, was eigentlich auch meine erste frage war.
Code: Alles auswählen
def main(hour, minute, sec):
timer = Timer(hour, minute, sec)
while not timer.has_finished:
print(timer)
sleep(1) # sleep(0.5)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
print('Kaboom!')
play.wolf()
die auszuführende funktion nach ablauf des timers steht in der main und mir gelint es einfach nicht anstelle der funktion eine variable zu platzieren
...ich hab auch schon überlegt eine variable als switch einzubauen, so dass der timer sie auf 1 und die ausgührte fkt wieder auf 0 setzt aber dann braucht es ja für jede eizelne abfrage nen eigenen switch
Re: Pygame time.set_timer
Verfasst: Sonntag 25. Januar 2015, 16:26
von BlackJack
@lemonbutterfly: Ich verstehe das Problem nicht was Du da lösen möchtest. Sorry.
Re: Pygame time.set_timer
Verfasst: Sonntag 25. Januar 2015, 17:07
von lemonbutterfly
also:
in meinem hauptprogramm (was mal irgendwann ein spiel werden will), hab ich zb. auf dem startbild 3 buttons
Code: Alles auswählen
import timer_fkt
...
countdown = timer_fkt
...
if event.type == MOUSEBUTTONDOWN:
if show.main == show.start:
if event.button == 1:
if start_rect.collidepoint(event.pos): # test ob mouse innerhalb rect
show.main = show.home
countdown.main(0, 0, 3)
play.bg()
elif load_rect.collidepoint(event.pos):
countdown.main(0, 0, 5)
elif about_rect.collidepoint(event.pos):
countdown.main(0, 0, 1)
bei einem klick fängt der timer an zu zählen mit den gegebenen parametern
aber wenn der timer abgelaufen ist, wird immer die kunftion "play.wolf" gestartet(siehe letzten post)
und funktionen kann man schlecht als parameter übergeben(zumindest gibt das bei mir nen haufen fehler)
Re: Pygame time.set_timer
Verfasst: Sonntag 25. Januar 2015, 17:41
von BlackJack
@lemonbutterfly: Ich glaube so ganz verstanden habe ich es immer noch nicht. Allerdings sind Funktionen in Python Objekte, können also auch als Argumente übergeben werden wie jedes andere Objekt auch.
Re: Pygame time.set_timer
Verfasst: Sonntag 1. Februar 2015, 12:50
von lemonbutterfly
@BlackJack das klingt einfacher als getan...bei jedem versuch bekomm ich immer nur ne fehlermeldung im pycharm
wie zb. wenn ich die fkt so einbauen will:
Code: Alles auswählen
# hauptfunktion #
if event.type == MOUSEBUTTONDOWN:
if show.main == show.start:
if event.button == 1:
if start_rect.collidepoint(event.pos): # test ob mouse innerhalb rect
show.main = show.home
190 countdown.main(0, 0, 3, play.click())
play.bg()
Code: Alles auswählen
#timer funktion#
def main(hour, minute, sec, funktion):
timer = Timer(hour, minute, sec)
while not timer.has_finished:
print(timer)
sleep(1) # sleep(0.5)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
print('Kaboom!')
45 funktion()
und denn kommt halt der folgende fehler bei raus:
Traceback (most recent call last):
File "E:/python/simcraft 0.6.5/simcraft.py", line 190, in <module>
countdown.main(0, 0, 3, play.click())
File "E:\python\simcraft 0.6.5\timer_fkt.py", line 45, in main
funktion()
TypeError: 'NoneType' object is not callable
Re: Pygame time.set_timer
Verfasst: Sonntag 1. Februar 2015, 13:07
von BlackJack
@lemonbutterfly: Du übergibst ja auch nicht die Funktion, sondern Du rufst die Funktion auf und übergibst deren Rückgabewert. Der ist `None` und das kann man nicht aufrufen — da bekommt man dann die Ausnahme die Du siehst.
Re: Pygame time.set_timer
Verfasst: Mittwoch 4. Februar 2015, 17:53
von lemonbutterfly
genau da liegt das problem, ich bekomm es einfach nicht hin die fkt zu übergeben...ist auch genaugenommen das ursprüngliche problem gewesen
das netz bietet mir auch keine konkrete antwort auf die frage(oder ich hab sie noch nicht gefunden)
Re: Pygame time.set_timer
Verfasst: Mittwoch 4. Februar 2015, 18:08
von EyDu
BlackJack hat dir die Lösung schon verraten. Wenn du dich Funktion über geben möchtest, dann darfst du sie vorher nicht aufrufen. Das machen aber die () in deinem countdown.main-Aufruf, wo du play.click übergeben möchtest. Und dann wird als Funktion nicht play.click übergeben, sondern das Ergebnis des play.click-Aufrufs. Und das ist None.
Re: Pygame time.set_timer
Verfasst: Mittwoch 4. Februar 2015, 18:37
von lemonbutterfly
danke für den zaupfahl, hab den wald vor lauter klammern nicht gesehen...jetzt läuft es
...jetzt muß ich nur noch was basteln, das des ganze grafisch angezeigt wird.
Re: Pygame time.set_timer
Verfasst: Mittwoch 4. Februar 2015, 20:29
von lemonbutterfly
irgendwie komm ich mir grad vor wie beim domino
ein problem gelöst, schon entsteht ein neues
durch die while schleife in der timerfunktion updatet sich nur der timer auf dem screen und nicht der rest, der in dem hauptprogram steht.
...wenn ich das richtig interpretiere, steht das hauptprogramm solange still, bis der timer beendet ist und dann wird erst die whileschleife im hauptprogramm fortgesetzt.
gibts da eine elegante möglichkeit das zu ändern?
ich möchte ungern mein funktionierendes timer modul mit in dem maincode schmeißen
es wäre zwar eine möglichkeit ein farbiges rect drüber zu blitten aber das sieht dann doof aus
btw verhält der timer sich auch merkwürdig, indem er die erste secunde überspringt
Code: Alles auswählen
def main(hour, minute, sec, funktion, timer_pos):
timer = Timer(hour, minute, sec)
fontmy = pg.font.Font("fonts/miso.ttf", 36)
while not timer.has_finished:
print(timer)
text01 = fontmy.render("Timer:" + str(timer), True, white)
screen.blit(text01, timer_pos)
pg.display.update()
sleep(1) # sleep(0.5)
for event in pg.event.get():
if event.type == QUIT:
pg.quit()
sys.exit()
print('Kaboom!')
funktion()
if __name__ == '__main__':
main(0, 0, 3, play.saw, (0, 0))
Re: Pygame time.set_timer
Verfasst: Mittwoch 4. Februar 2015, 20:50
von BlackJack
@lemonbutterfly: Es ist extrem verwirrend das Du hier eine Funktion `main()` nennst, von einem „maincode” sprichst, der aber nicht diese `main()`-Funktion ist sondern der Code der die `main()` aufruft.
Du musst das halt irgendwie in die Hauptschleife einbauen statt eine weitere Schleife zu starten. Zum Beispiel eine Klasse schreiben die so einen Timer kapselt und eine `update()`-Methode besitzt die den Timer, die Position, und vielleicht auch das Surface auf dem der Timer geblittet werden soll kennt und die regelmässig in der Hauptschleife aufgerufen wird.
Die erste Sekunde wird nicht übersprungen sondern der Timer ”läuft” ja in dem Augenblick los in dem das Objekt erstellt wird. Und egal wie klein die Zeitspanne von dort bis zum ersten Auswerten als Zeichenkette auch ist, ist die erste Sekunde halt schon nicht mehr voll sondern angebrochen.