Seite 1 von 1

pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Donnerstag 2. Juli 2020, 21:20
von blbltheworm
Hallo zusammen,

die Situation ist etwas verzwickt und ich bekomme den Knoten nicht gelöst, vielleicht hat ja jemand einen Rat.
Vor einiger Zeit habe ich einen Emulator geschrieben, der auf dem PC die Pins eines Raspberry Pi emuliert, sodass RasPi Code auf dem PC getestet werden kann (https://github.com/blbltheworm/yarpie/). Da es in der Zwischenzeit einige andere Ansätze zu dem Thema gibt, habe ich es nicht weiterverfolgt. Es gibt jedoch noch einen Bug der mich fuchst und da mittlerweile noch einmal danach gefragt wurde, würde ich ihn gerne Lösen. Das Tool läuft unter Linux einwandfrei, aber unter Windows friert das pygames Fenster ein, wenn man eine MainLoop programmiert.
Zum Aufbau des Emulator:

Beim Importieren des Pakets wird pygames gestartet und ein Fenster mit der grafischen Repräsentation des Emulators erstellt. Das ganze läuft als Thread

Code: Alles auswählen

class emugui(threading.Thread)
Die Klasse hat zudem eine Funktion in der die Mainloop des Emulators und das Eventhandling von Pygames läuft:

Code: Alles auswählen

def run(self): 
        """
            Mainloop of the emulated GPIO. Is called when the emulator class is initialized.
        """
        
        self._running = True
    
        # create clock to reduce framerate
        clock = pygame.time.Clock()
        
        while self._running:
            clock.tick(30) #reduce framerate to 30 fps
            # get all events and check them 
            events = pygame.event.get()
            
            for event in events:
                print(event.type)
                if event.type == pygame.QUIT:
                    self._running = False
                
                #Weiterer Code zum Event-Handling
Davon bekommt der Benutzer aber alles nichts mit. Er soll sein Programm schreiben und kann dann über ein spezielles Modul mit der emugui kommunizieren, als wäre es die Hardware eines Raspberry Pi. Das funktioniert solange, bis der Benutzer seine eigene Mainloop schreibt

Code: Alles auswählen

import time
while(1):
        #Code zur Interaktion mit der RasPi-Hardware
        time.sleep(0.1)
 
Unter Linux funktioniert das. Unter Windows friert das Fenster ein, wenn die zwei Schleifen (die der emugui und die des Hauptprogramms) gleichzeitig laufen.
Hat jemand eine Idee woran das liegt und an welcher Stelle hier der Eventaustausch zwischen pygames und Win10 verloren geht? Ich bin für jede Idee dankbar. Wichtig ist nur, dass in der Schleife des Hauptprogramms kein pygames Code auftaucht, denn der Emulator ist so geschrieben, dass er Code verarbeiten kann, der für den Raspberry geschrieben ist.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Donnerstag 2. Juli 2020, 23:31
von __blackjack__
@blbltheworm: Ich vermute mal das Problem fängt schon da an das Du PyGame nicht vom Hauptthread aus bedienst. Andere GPIO-Emulatoren lösen das mit `multiprocessing`.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Sonntag 5. Juli 2020, 15:16
von blbltheworm
Hi und Danke für die Antwort.

Hm, "multiprocessing" ist ein großes Wort. Hast du zufällig ein Beispiel, das ich mir anschauen kann?

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Mittwoch 8. Juli 2020, 19:54
von blbltheworm
Hallo,

ich hab mal ein stark reduziertes Beispiel geschrieben. Wenn mir jemand helfen kann, das in eine multiprocessing-Variante zu überführen, wäre ich sehr dankbar. Ziel ist es, dass pygame in Hintergrund läuft und sich um die Darstellung der Daten kümmert, während das Hauptprogramm nur mit einer Klasse (hier bg_pygame) kommuniziert.

Code: Alles auswählen

import pygame
import threading
import time


class cbg_pygame(threading.Thread):
    """
        Main class emulating the GPIO and i2c and draws the main window.
    """
    def __init__(self, xoffset = 150, yoffset = 20):
        
        threading.Thread.__init__(self)
                
        # Initialise pygame and create screen, surfaces and Gadgets (Input/Buttons/Lists...)    
        pygame.init()
        
        # Define some colors
        self.pos=[0,0] #Position of the ball on the screen
        self.dimension=[800,600] #screen dimensions
        self.size=30 #size of the ball

        self._screen = pygame.display.set_mode((self.dimension[0], self.dimension[1]))
                
        pygame.display.set_caption('pygames-test')
        
        
        #load the background image
        pygame.mouse.set_visible(1)
        pygame.key.set_repeat(1, 30)
        
        
    def run(self): 
        """
            Mainloop of the emulated GPIO. Is called when the emulator class is initialized.
        """
        
        self.running = True
    
        # create clock to reduce framerate
        clock = pygame.time.Clock()
        
        while self.running:
            clock.tick(30) #reduce framerate to 30 fps
    
            # get all events and check them 
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.QUIT:
                    self.running = False
                
            self._screen.fill((255, 255, 255))
            pygame.draw.ellipse(self._screen, (0, 0, 0), [self.pos[0], self.pos[1], self.size, self.size], 4)
            pygame.display.flip()

        pygame.quit()
        
    def stop(self):
        self.running = False
        
        

bgpg = cbg_pygame()
bgpg.start()

direction=[1,1]
while(bgpg.running): #Program is stopped if pygame window is closed
    for i in range(2):
        if direction[i]: #direction[0]=x, direction[1]=y, traveling with a velocity of 20 pixels/circle
            if bgpg.pos[i]+20 < bgpg.dimension[i]-bgpg.size:
                bgpg.pos[i]=bgpg.pos[i]+20
            else:
                bgpg.pos[i]=bgpg.dimension[i]-bgpg.size
                direction[i]=0
        else:
            if bgpg.pos[i]-20 > 0:
                bgpg.pos[i]=bgpg.pos[i]-20
            else:
                bgpg.pos[i]=0
                direction[i]=1
                
    time.sleep(0.1)

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Mittwoch 8. Juli 2020, 20:30
von Sirius3
Du brauchst da keine Threads und kein multiprocessing und ich sehe auch nicht, wo da GPIOs emuliert werden. Das ist eine ganz normale Ereignisschleife die Du mit Deiner Updateschleife Kombinieren mußt.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Donnerstag 9. Juli 2020, 20:54
von blbltheworm
Hi Sirius,

bitte den ganzen Thread lesen. Das das Beispiel keinen Sinn ergibt ist mir bewusst, es soll nur mein Vorgehen im Emulator demonstrieren. Das ist das einfachste Beispiel, das mir eingefallen ist. Die Frage ist, wie man das Beispiel unter Windows zum laufen bekommt, ohne, dass die Events in der Hauptschleife bearbeitet werden müssen. Wenn du dir die Mühe machen möchtest, dir den gesamten Code anzuschauen, bin ich dir natürlich auch für jeden Tipp dankbar. Der vollständige Code liegt unter https://github.com/blbltheworm/yarpie/.

Viele Grüße,
blbl

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Freitag 10. Juli 2020, 05:54
von Sirius3
Wenn dein stark reduziertes Beispiel nicht das Problem hat, dass du lösen möchtest, dann ist es wohl zu stark reduziert. Die Antwort bleibt gleich: es darf nur eine Schleife geben, in der sowohl die pygame-Events als auch die gpio-Events abgearbeitet werden.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Freitag 10. Juli 2020, 09:36
von __blackjack__
@Sirius3: Das Problem ist das da ein Benutzer Code schreiben können soll, der nichts von der GUI wissen muss. Das heisst der soll genau so frei sein Code zu schreiben als wenn er nur `RPi.GPIO` als Abhängigkeit hätte. Also *er* schreibt eine Endlosschleife im Hauptthread die *nichts* von einer GUI weiss. Die GUI-Hauptschleife muss aber parallel zu seinem Code laufen. Und das geht halt mit Multiprocessing, weil in diesem Szenario die GUI-Hauptschleife nur in einem anderen Prozess im Hauptthread laufen kann.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Freitag 10. Juli 2020, 09:51
von Sirius3
Ah, ok, das Emulatorfenster soll unabhängig vom eigentlichen Programm laufen. Dann würde ich nicht zu Multiprocessing greifen, denn das ist unter Windows auch eklig, sondern den Emulator komplett als eigenständiges Skript laufen lassen und per IPC ans eigentliche Programm anbinden.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Freitag 10. Juli 2020, 17:45
von blbltheworm
das Beispiel hat genau das Problem, das auch der Emulator hat. Es läuft unter Linux und hängt unter Windows. Aber es emuliert halt nichts :)

Danke für eure Vorschläge.
@Sirius Ist ein IPC-Ansatz nicht etwas überdimensioniert (nicht, dass ich Ahnung davon hätte).
Vom Verständnis her: IPC würde heißen, dass ich zwei unabhängige Skripte habe. (z.B. emulator.py und prog.py). Um den Emulator zu verwenden, müsste der User dann entweder emulator.py manuell starten und in seinem prog.py den richtigen Port angeben, sodass die beiden auf der 127.0.0.1 kommunizieren können, oder? Der Emulator wäre dann der Server und das prog.py der Client. Alternative wäre es auch möglich, dass prog.py eine neue Python-Instanz aufruft, die emulator.py ausführt. Ich hab mal angefangen nach IPC zu suchen, bin aber ehrlich gesagt von der Fülle an Lösungen erschlagen. Sirius, kannst du mir ein Paket vorschlagen? Letztendlich sollte ja ein simpler Austausch von Bytecodes in beide Richtungen reichen. Zudem sollte das ganze unter Linux und Windows laufen, sonst bin ich keinen Schritt weiter.

@blackjack: Ich hab leider noch nicht verstanden, wo der Unterschied zwischen threading und multiprocessing liegt. Wie würde das Beispiel denn mit multiprocessing aussehen?

Danke schon Mal für eure Hilfe.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Freitag 10. Juli 2020, 18:22
von __deets__
Threading ist ein einem Prozess. Und der kann eben nur eine Ereignisbehandlung haben, speziell unter Windows. Du willst aber zwei, respektive deinem User keine Beschränkungen auferlegen. Also braucht’s 2 Prozesse.

Ein Weg für IPC wäre, das Netzwerk Protokoll von pigpio umzusetzen. Damit kann der Code dann 1:1 übernommen werden auf einem PI, der den pigpiod laufen lässt. Oder eben dein eigenes. Nimm zb HTTP, das kommt mit Python ja alles schon mit.

Und ich würde immer so vorgehen, dass der Emulator zuerst gestartet werden muss. Schlussendlich läuft ja auch ein Programm auf einem existierend PI, statt einen irgendwie her zu zaubern. .

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Sonntag 12. Juli 2020, 09:35
von blbltheworm
Hi,
ich sehe schon, ich muss mir das Thema Threads auch nochmal genauer anschauen, hab es wohl doch noch nicht so recht verstanden.
pigpio kannte ich noch nicht, das ist auch ein Interessanter Ansatz, wenn auch nicht der, den ich eigentlich verfolgen wollte.
Meine ursprüngliche Intension war ein Paket anzubieten, dass analog zum guten alten RPi.GPIO eingebunden werden kann. Daher würde ich auch gerne auf einen zusätzlichen Deamon vermeiden.
Wie würde das simple Beispiel oben denn mit Multiprocessing aussehen? Kann mir da jemand weiterhelfen?
Die Frage ist, ob ich alternativ weiterkomme, wenn ich auf pygame für die Darstellung verzichte und z.B. über TK gehe. Da müsste ich mich dann halt entsprechend einarbeiten. Oder laufe ich dort in ein ähnliches Problem unter Windows?

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Sonntag 12. Juli 2020, 09:53
von Sirius3
Du kommst immer in die selben Probleme. Der einzig funktionierende Weg ist es, Emulator und individuelles Programm komplett zu trennen. Du kannst ja beim nutzen deines rpi.gpio per subprocess den Emulator automatisch starten. Aber nur so bist du und der Nutzer völlig frei, alles zu tun, was ihr wollt.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Sonntag 12. Juli 2020, 10:04
von __deets__
Ich verstehe auch nicht, worin der Reiz eines implizit gestarteten emulators liegt. Nehmen wir mal an du kannst da fortgeschrittene Dinge wie periodische Signale oder grafische Darstellung der Ausgaben machen. Dann soll man jedes mal, wenn der Anfänger den Code Verbockt und das Skript abschmiert, alles wieder neu gestartet werden, eingerichtet, angeschaltet? Das ist doch öde. Du hast viel mehr freiheitsgrade und bessere Features, wenn ud das sauber trennst.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Dienstag 14. Juli 2020, 19:28
von blbltheworm
Hallo zusammen,

naja, der Reiz wäre gewesen, dass der Benutzer in seinem Code nur das import RPi.GPIO durch ein import RPIemu.GPIO austauschen muss und alles läuft. Aber OK. Könnt ihr ein IPC-Paket empfehlen? Ich habe mit dem Thema null Erfahrung.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Dienstag 14. Juli 2020, 19:40
von __deets__
Das hat ja damit nix zu tun. Der Code sieht ja immer noch so aus.

Und ich habe zb HTTP vorgeschlagen.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Mittwoch 15. Juli 2020, 09:10
von Sirius3
Genau das was Du schreibst, man startet erst den Emulator und kann dann das eigentliche Programm ohne Änderungen laufen lassen.

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Samstag 18. Juli 2020, 14:14
von blbltheworm
@__deets__ das mit dem HTTP hatte ich gelesen, dachte aber, dass du einfach das HTTP-Protokoll meinst. Mittlerweile habe ich gesehen, dass es auch ein gleichnamiges Pythonmodul gibt.
Ich werde mal mein Glück versuchen ;)

Re: pygames und Win10: Fenster friert ein trotz Aufruf von pygame.event.get()

Verfasst: Samstag 18. Juli 2020, 21:20
von __deets__
Wird schon. Python bringt da genug mit. Und Performance Erwägungen gilden erst, wenn’s knirscht ;)