Problem "Flow in Games" Umsetzung mit Tkinter

Fragen zu Tkinter.
Antworten
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo,

ich habe mal versucht, die Performance von Tkinter/Canvas und damit seine Eignung für den Flow-in-Games Klon zu testen. Ich habe folgenden Ansatz geschrieben:

Code: Alles auswählen

from Tkinter import *   ##  ich hasse den Sternchenimport ja auch, aber...
from random import randint
from thread import start_new_thread as snThread
import time

class ME:
    def __init__(self, wCanvas, sBBox):
        self.wCanvas = wCanvas
        self.iItem = wCanvas.create_oval(sBBox)

    def get_bbox(self):
        return self.wCanvas.bbox(self.iItem)

    def get_center(self):
        x1, y1, x2, y2 = self.get_bbox()
        return ((x1+x2)/2, (y1+y2)/2)

    def next_frame(self):
        iMoX, iMoY = self.wCanvas.Mouse.x, self.wCanvas.Mouse.y
        iMeX, iMeY = self.get_center()
        dX = iMoX - iMeX
        dY = iMoY - iMeY
        self.wCanvas.move(self.iItem, dX, dY)

        x1, y1, x2, y2 = self.get_bbox()
        lFoundItems = self.wCanvas.find_overlapping(x1, y1, x2, y2)
        for iFoundItem in lFoundItems:
            if iFoundItem != 1:
                self.wCanvas.remove_plankton(iFoundItem)
                self.wCanvas.delete(iFoundItem)

class MOUSE:
    def __init__(self, wCanvas):
        self.isbuttonpressed = False
        self.isinrange = False
        self.x = 0
        self.y = 0

        wCanvas.bind("<Enter>", self.event_enter, "+")
        wCanvas.bind("<Leave>", self.event_leave, "+")
        wCanvas.bind("<1>", self.event_button_press_1, "+")
        wCanvas.bind("<ButtonRelease-1>", self.event_button_release_1, "+")
        wCanvas.bind("<Motion>", self.event_motion, "+")

    def event_enter(self, Event): self.isinrange=True
    def event_leave(self, Event): self.isinrange=False
    def event_button_press_1(self, Event): self.isbuttonpressed=True
    def event_button_release_1(self, Event): self.isbuttonpressed=False
    def event_motion(self, Event): self.x, self.y = Event.x, Event.y

class PLANKTON:
    def __init__(self, wCanvas, tPos=(-1, -1), iSize=2):
        self.wCanvas = wCanvas
        if tPos == (-1, -1):
            tPos = (randint(50, 250), randint(50, 250))
        x, y = tPos
        self.iItem = wCanvas.create_oval("%i %i %i %i"%(x-iSize, y-iSize, x+iSize, y+iSize))

    def next_frame(self):
        self.wCanvas.move(self.iItem, randint(-2, 2), randint(-2, 2))
        

class My_Canvas(Canvas):
    def __init__(self, wMaster):
        Canvas.__init__(self, wMaster, width=400, height=300, bg="#7bf")
        self.grid()
        self.dPlankton = {}
        self.Me = ME(self, "10 10 20 20")       ##  Spielerobjekt anlegen
        self.Mouse = MOUSE(self)                ##  Infos ueber Maus auf dem Canvas

    def remove_plankton(self, iItem):
        if iItem in self.dPlankton:
            self.master.unregister(self.dPlankton[iItem])
            del self.dPlankton[iItem]

class Hour_Glass(Tk):
    def __init__(self, iFPS):
        Tk.__init__(self)
        self.iDelay = 1000 / iFPS
        self.isrunning = False
        self.lObjects = []
        self.fLast = time.clock()

    def start(self):
        self.isrunning = True
        snThread(self.loop_starter, ())

    def stop(self):
        self.isrunning = False

    def register(self, Object):
        if not Object in self.lObjects:
            self.lObjects.append(Object)

    def unregister(self, Object):
        if Object in self.lObjects:
            self.lObjects.remove(Object)

        print "Restliches Plankton:", len(self.lObjects)-1, self.lObjects
        if len(self.lObjects)==0: raise SystemExit("alles Plankton gefressen")

    def loop_starter(self):
        while self.isrunning:
            self.next_loop()
        
    def next_loop(self):
        fStartTime = time.clock()   ##  starte Stoppuhr

        for Object in self.lObjects:
            try:
                Object.next_frame()
            except Exception, sMessage: ##  wenn Fehler auftritt, entferne das Objekt
                if isinstance(Object, PLANKTON):
                    self.unregister(Object)
                else:
                    print "Objekt", Object,"entfernt:", sMessage

        iDauer = int((time.clock() - fStartTime) * 1000)    ##  Dauer der Verarbeitung ausrechnen in ms
        iRest = max(self.iDelay - iDauer, 0)                ##  Restdauer des Frames in ms
        time.sleep(0.001*iRest)

        
wRoot = Hour_Glass(25)
wCanvas = My_Canvas(wRoot)
wRoot.register(wCanvas.Me)      ##  Registriere Spieler in Update-Liste
for i in xrange(50):
    Plankton = PLANKTON(wCanvas)
    wCanvas.dPlankton[i] = Plankton
    wRoot.register(Plankton)
wRoot.start()
wRoot.mainloop()
Wenn ich den Code ausführe, stürzt Python meistens ab. Wenn man doch alles sichtbare Plankton gefressen hat, bleiben immernoch zwei im Stack. Habt ihr dasselbe Problem und wenn ja, könnt ihr euch vorstellen, woran das liegt?

Vielen Dank für eure Gedanken dazu,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Ich war zu faul zum Lesen, bin aber immerhin bis zum Import für Threads gekommen: Du greifst nicht etwa aus einem anderen Thread als dem, wo die `mainloop()` läuft, auf die GUI zu?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Hi,

wie blackjack schon sagte, du greift von 2 threads (main + Hour_Glass) auf die gui zu. tkinter ist (wie alle gui toolkits?) nicht threadsave.

trotzdem lief es eben bei mir durch - top-spiel :)

habe gestern abend angefangen mein pygame-test zu erweitern, bin aber nicht weit gekommen. leider habe ich mom nur sehr wenig zeit.
evtl poste ich heute nachmittag noch eine neue version.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo Black, hi Dill!

Das soll der ganze Grund sein?

Ich habe ja alles versucht, das mit der after-Methode hinzubekommen. Aber die wurde einfach nicht nach der angegebenen Zeit aufgerufen, sondern tendenziell schwankend und um einiges später. :-( Egal, ob ich die After-Methode zu Beginn der Hour-Glass loop oder zu deren Ende mit der Restdauer für den Frame aufgerufen habe.

Gut, ich versuche es nochmal damit, vielleicht habe ich nur irgendetwas übersehen, dass es so instabil lief. :-)

Danke und Grüße,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Wenn ich den Code ausführe, stürzt Python meistens ab. Wenn man doch alles sichtbare Plankton gefressen hat, bleiben immernoch zwei im Stack. Habt ihr dasselbe Problem und wenn ja, könnt ihr euch vorstellen, woran das liegt?
Hi Michel.

Hab dein Game gestern kurz getestet. Bei mir stürzt es immer ab. Habs mehrmals probiert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sape hat geschrieben:Bei mir stürzt es immer ab. Habs mehrmals probiert.
Es wäre gut, wenn du die Fehlermeldung auch angeben könntest. Oder wird gar der Python-Interpreter mitgerissen?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Leonidas hat geschrieben:
sape hat geschrieben:Bei mir stürzt es immer ab. Habs mehrmals probiert.
Es wäre gut, wenn du die Fehlermeldung auch angeben könntest. Oder wird gar der Python-Interpreter mitgerissen?
Hi,

leider genau das! :-(

Gruß,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:
sape hat geschrieben:Bei mir stürzt es immer ab. Habs mehrmals probiert.
Es wäre gut, wenn du die Fehlermeldung auch angeben könntest. Oder wird gar der Python-Interpreter mitgerissen?
Hi Leonidas.

Ne leider gibt es da kein Traceback, da es den ganzen Interpreter mitreist.
Es kommt so ein Fenster von Windows Xp mit überschrift:
tk: python.exe - Feler in Anwendung

Und der Text lautet:
Die Anweisung in "0x......." verweist auf Speicher in "0x......". Der Vorgang "read" konnte nicht auf dem Speicher durchgeführt werden.

Speicherzugriffsverletzung?
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo allerseits,

ich weiß nicht genau, wie ich es gemacht habe, aber das Problem ist behoben. Nach ein paar wenigen Änderungen lief es mit dem Thread, dann habe ich den aber ganz rausgenommen und nun gibt es überhaupt keine Probleme mehr (wenn ich das Programm von der cmd-Shell aus starte).
Aus dem Prove-of-Concept ist schon fast ein kleines Spiel herangewachsen. Probiert es bitte mal aus und sagt mir, was man verbessern kann. Ich erwarte konspirative Kritik. ;-)

EDIT: Habe die Vorschläge vom Schlangenbeschwörer beherzigt (vielen Dank nochmals) und das Posting durch Version 1.2 upgedated. Die mainloop wird nun verlassen und man kann die wichtigsten Einstellungen in Klasse CONST vornehmen. /EDIT

EDIT2: Der Code wurde auf den hoch geschätzten Vorschlag Sapes hin nochmals überarbeitet und durch v1.4 ersetzt. In dem Zusammenhang fällt mir auf, dass ich v1.3 völlig ausgelassen habe... :oops:
Ihr findet den aktuellen Code hier: http://paste.pocoo.org/show/1213/ /EDIT2

Grüße,
der Michel
Zuletzt geändert von Michael Schneider am Samstag 17. März 2007, 22:44, insgesamt 2-mal geändert.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Michael Schneider hat geschrieben:Probiert es bitte mal aus ...
Mein Rekord bis jetzt ist 17.711 Sekunden! :-)

Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Hi Michel,
echt witziges Spielchen. Aber da fehlt noch ein

Code: Alles auswählen

        self.quit()
in Zeile 204. Sonst läuft die mainloopschleife nämlich ewig weiter.
Und die print-Anweisungen könntest du auch noch irgendwie schön mit Tkinter darstellen, dann kann man sich auf das Fenster konzentrieren und das Programm auch ohne Console laufen lassen.
Aber sonst echt cool. Kann man die Maus-Trägheit eigentlich einstellen?

Gruß, jj
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

schlangenbeschwörer hat geschrieben:Hi Michel,
echt witziges Spielchen. Aber da fehlt noch ein

Code: Alles auswählen

        self.quit()
in Zeile 204. Sonst läuft die mainloopschleife nämlich ewig weiter.
Und die print-Anweisungen könntest du auch noch irgendwie schön mit Tkinter darstellen, dann kann man sich auf das Fenster konzentrieren und das Programm auch ohne Console laufen lassen.
Aber sonst echt cool. Kann man die Maus-Trägheit eigentlich einstellen?
Gruß, jj
Hi JJ,

habe alles Vorgeschlagene eingebaut und mein Posting oben ersetzt. Bei mir liegt die Auslastung (unten rechts = Restzeit pro Frame in Prozent) bei Spielstart bei etwa 4% (bei 25 FPS, 60 Plankton). Für meinen Rechner ist da also noch ne Menge Luft. Wie sieht es bei euch aus?

Grüße,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi Michel.

Super nun funktioniert es auch bei mir. :)
Probiert es bitte mal aus und sagt mir, was man verbessern kann. Ich erwarte konspirative Kritik
.
Ja, werde den *-Import los ;) Und dann könntest du das ganzen ein wenig PEP-8 refactoren.
My_Canvas -> MyCanvas
class PLANKTON -> Plankton
...
Klassennamen sonten CamelCase sein und keine unterstirche beinhalten und auch nicht UPPER sein.

Über die Inlinekomentare kann man sich streiten.

Ansonsten, saubere Arbeit :D Werde mal versuche deinen Record von 17.711 zu schlagen :D



Ach ein bitte habe ich: Könntest du dein Code in http://paste.pocoo.org/ auslagern und es aus deinen Posts entfernen? Der Thread blockiert leider und daher dauert es ewig bis sich der thread bei mir aufbaut :-[

EDIT: Auslastung ist bei mir auch ca. zwischen 2-4%.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi Sape,

ich habe auch schon gemerkt, dass das Laden inzwischen ein wenig länger dauert. :roll: Aber ich wusste nicht, wo ich den Code sonst hätte hinlegen können. Ich habe jetzt die neueste Version 1.4 bei pocoo abgelegt: http://paste.pocoo.org/show/1213/
Was muss ich bei Pocoo beachten, wieviel darf man da zwischenlagern?

Das mit der Namenskonvention ist bei mir historisch gewachsen. Ich habe eigentlich für alles außer Klassen meine festen Regeln: kleine Führende Buchstaben zur Andeutung des Typs (bei Standardtypen), Teilworte groß und zusammen für Instanzen (weil ich die von Klassen trennen wollte, habe ich die Konvention nicht für Klassen verwendet), Methoden und Funktionen klein, getrennt durch '_' und beginnend mit einem Verb (i. d. R.) und letztlich Konstanten komplett groß geschrieben.

Übrig blieb nur großgeschrieben und durch '_' getrennt - und wenn sie nur aus einem Wort bestehen eben völlig groß geschrieben. Das hat mir auch nicht wirklich gefallen, aber ich wollte sie eben getrennt halten. :?

Trotzdem hast Du recht, das ist wirklich konstruktive Kritik, wie ich sie gern lese.

Vielen Dank und schöne Grüße,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Michael Schneider hat geschrieben:Was muss ich bei Pocoo beachten, wieviel darf man da zwischenlagern?
Beachten musst du dass das Lodgeit ist und nicht Pocoo ;) Davon abgesehen kannst du dort so viel hinterlegen wie du willst. An sich ist das eines der Besten Pastebins die im Internet, vielleicht mal vom Lisp-Pastebin abgesehen, welches Lisp-Code sehr schön highlightet.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Die Auslastung liegt bei mir konstant bei 0%.

Aber in 2 Sekunden habe ich das Spiel nun wirklich nicht geschafft:
Alles Plankton gefressen!
Bravo! Nur 2.170 Sekunden!
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

apollo13 hat geschrieben:Die Auslastung liegt bei mir konstant bei 0%.

Aber in 2 Sekunden habe ich das Spiel nun wirklich nicht geschafft:
Alles Plankton gefressen!
Bravo! Nur 2.170 Sekunden!
Uuups... sieht nach einem (oder mehreren) Bugs aus. Wie wäre es mit einem ausführlichen Bugreport a la Microsoft (Rechnername, -hardware, -software, Lizenzen, Peripherie, Vertragspartner...) ;-)

Aber im Ernst, passiert das jedes mal? Was sagt die Stopuhr unten im "Spielfenster"?

Grüße,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Okay kannst du haben^^
Also neuer Versuch:
Alles Plankton gefressen!
Bravo! Nur 0.960 Sekunden!

Systemmonitor (gnome) zeigt mir 2% Auslastung auf beiden Prozessoren an, die 0% von follow.py dürften stimmen. Die Zeit verstreicht total langsam, kA warum^^

Mein Sys:
- Intel Core 2 Duo Prozessor T7200 (2.0GHz/4MB/667FSB)
- 2048 MB DDR2-RAM 667FSB
- 512 MB Geforce Go 7700
- Ubuntu 6.10
Python:
Python 2.4.4c1 (#2, Oct 11 2006, 21:51:02)
[GCC 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)] on linux2


Wenn du noch etwas brauchst nur sagen :)

EDIT: Außer Lizenzen etc. versteht sich ;)
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hi Apollo,

wie hier im parallelen Thread beschrieben liegt das Problem an time.clock.
http://www.python-forum.de/viewtopic.php?p=62434#62434
Bitte ersetzen durch time.time und dann funzt es. :-)

Grüße,
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Ja, geht eindeutig besser :)
Antworten