getkey() funktion

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
G-Rizzle
User
Beiträge: 90
Registriert: Donnerstag 18. Februar 2021, 12:26

Hi,

aus einem Tutorial habe ich die Funktion, um kontinuierlichen Tastendruck zu prüfen. Die Funktion sieht folgend aus:

Code: Alles auswählen

def getKey(keyName):
    ans = False
    for event in pygame.event.get():
        pass
    keyInput = pygame.key.get_pressed()

    myKey = getattr(pygame,'K_{}'.format(keyName))
    if keyInput[myKey]:
        ans = True
    return ans
    


es funktioniert wie gewollt. ABER: was genau bringt die for schleife? also warum wird durch sämtliche events geloopt und dann nichts damit gemacht? wenn ich die auskommentiere funktioniert es nicht mehr


Danke!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@G-Rizzle: Durch die Schleife werden die Ereignisse intern verarbeitet und die Datenstruktur die von `get_pressed()` zurückgegeben wird aktualisiert. Dafür gibt es aber auch die `pump()`-Funktion. Dann braucht man keine Schleife schreiben die im eigenen Code dann nichts macht.

Die Namen in dem Codeschnippsel sind auch nicht gut. Mal davon abgesehen, dass sie sich nicht an die Konventionen bei der Schreibweise halten (klein_mit_unterstrichen) sind bis auf `event` und `keyName` auch alle Namen inhaltlich verbesserungswürdig.

`getKey()` holt gar keine Taste sondern beantwortet die Frage ob eine gegebene Taste gedrückt ist oder nicht.

`ans` ist eine kryptische Abkürzung. Der Name wird auch viel zu früh an einen Wert gebunden und letztlich braucht man den Namen auch überhaupt gar nicht, denn der Wert entspricht dem Wert von ``keyInput[myKey]`` — *das* wäre einfach das Ergebnis der Funktion.

`keyInput` hiesse besser `pressed_keys` ö.ä.

Bei `myKey` ist ein unnützes `my` im Namen.

Letztlich lässt sich der Funktionskörper auf folgenden Zweizeiler eindampfen:

Code: Alles auswählen

def is_key_pressed(key_name):
    pygame.event.pump()
    return pygame.key.get_pressed()[getattr(pygame, "K_" + key_name)]
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Irgendwo müssen ja die Events verarbeitet werden.
Die for-Schleife ist unnötig, aber der get-Aufruf ist wichtig.
Funktionsnamen schreibt man wie Variablennamen komplett klein. Das my bei myKey ist völlig nichtssagend und kann weg. keyName sollte sowieso nicht so übergeben werden, sondern direkt das pygame.K_X-Attribut. Das kennst Du ja beim Aufruf der Funktion schon.
Variablen definiert man dann, wenn man sie braucht und nicht 5 Zeilen vorher.

Code: Alles auswählen

def get_key(key_code):
    _= pygame.event.get()
    key_input = pygame.key.get_pressed()
    ans = False
    if key_input[key_code]:
        ans = True
    return ans
Es ist aber überraschend, wenn in irgendeiner tief verschachtelten Funktion die Events gelesen werden.
Das muß irgendwo in der Hauptschleife passieren. Wenn man nur ein bool setzt, braucht man kein if.
Bleibt also:

Code: Alles auswählen

def get_key(key_code):
    key_input = pygame.key.get_pressed()
    return bool(key_input[key_code])
G-Rizzle
User
Beiträge: 90
Registriert: Donnerstag 18. Februar 2021, 12:26

danke für deine ausführliche antwort! habe es verstanden. namen wurden so im tutorial vorgegeben, für mein projekt ändere ich eh nochmal alles.

weitere frage (dazu hole ich etwas aus):

ich programmiere die steuerung einer tello drohne in python. ich möchte, dass die drohne startet, wenn ich space >3 sekunden halte. dazu wird, sobald space gedrückt wird, eine liste in jedem loop um die momentane sekundenzeit erweitert. wenn die letzte sekundenzeit nicht mehr mit der vorletzten sekundenzeit in der liste übereinstimmt, bedeutet das, dass eine sekunde vergangen ist. somit wird der sekundenzähler um 1 erhöht. wenn ich space <3 sekunden loslasse, soll der interne sekundenzaehler wieder auf 0 gehen. an sich funktioniert es, nur werden sekundenzaehler und drueckzeiten nicht wieder auf 0, bzw [0, 0] gesetzt wenn ich space loslasse (trotz des keyup events). findet hier wer meinen fehler?
DANKE!!!

mein code:

Code: Alles auswählen

def getKey(keyName):
    ans = False
    for event in pygame.event.get():
        pass
    keyInput = pygame.key.get_pressed()

    myKey = getattr(pygame,'K_{}'.format(keyName))
    if keyInput[myKey]:
        ans = True
    return ans
    

def steuern(drone, drueckzeiten, sekundenzaehler):


    # starten, landen


    if getKey("SPACE") == True and drone.is_flying == False:
        
        drueckzeiten.append(datetime.now().second)
        letzteZeit = drueckzeiten[-1]
        vorletzteZeit = drueckzeiten[-2]
        if letzteZeit != vorletzteZeit:
            sekundenzaehler += 1

            if sekundenzaehler > 3:
                drone.is_flying = True
                sekundenzaehler = 0
                drueckzeiten = [0, 0]


    for event in pygame.event.get():
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_SPACE:
                print('losgelassen')
                sekundenzaehler = 0
                drueckdauer = [0, 0]






    steuern_return = {'drueckzeiten': drueckzeiten, 'sekundenzaehler': sekundenzaehler}
    print(steuern_return)
    return steuern_return
    
    class Tello():

    def __init__(self, is_flying):
        self.is_flying = is_flying






def main():
    steuerung_return = {'drueckzeiten': [0, 0], 'sekundenzaehler': 0}
    
    pygame.init()
    fensterBreite = 1500
    fensterHoehe = 1000
    screen = pygame.display.set_mode((fensterBreite, fensterHoehe))
    pygame.display.set_caption("Tello Controll")
    drone = Tello(False)



    while True:
        
        steuerung_return = steuern(drone, steuerung_return['drueckzeiten'], steuerung_return['sekundenzaehler'])



if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Da ist ja das von mir beschriebene Problem, dass wenn einmal die Events abgearbeitet sind, dann gibt es in der zweiten for-Schleife keine mehr, oder umgekehrt.
Und in Deinem anderen Thread hatte ich ja schon geschrieben, dass Du einmal auf KEYDOWN und einmal auf KEYUP hören mußt.
Statt hier Wörterbucher als Datenspeicher zu benutzen, nimm besser eine Klasse, oder wenn es so wenig ist, dann kann man es gleich ins Hauptprogramm packen.

Code: Alles auswählen

FENSTER_BREITE = 1500
FENSTER_HOEHE = 1000
    
class Tello():
    def __init__(self, is_flying):
        self.is_flying = is_flying

def main():
    pygame.init()
    screen = pygame.display.set_mode((FENSTER_BREITE, FENSTER_HOEHE))
    pygame.display.set_caption("Tello Controll")
    drone = Tello(False)

    space_down_time = None
    while True:
        current_time = time.monotonic()
        # starten, landen
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    print('gedrückt')
                    space_down_time = current_time
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_SPACE:
                    print('losgelassen')
                    space_down_time = None
        if space_down_time is not None and space_down_time - current_time > 3:
            drone.is_flying = True
            space_down_time = None

if __name__ == '__main__':
    main()
G-Rizzle
User
Beiträge: 90
Registriert: Donnerstag 18. Februar 2021, 12:26

Sirius3 hat geschrieben: Dienstag 30. März 2021, 12:13 Da ist ja das von mir beschriebene Problem, dass wenn einmal die Events abgearbeitet sind, dann gibt es in der zweiten for-Schleife keine mehr, oder umgekehrt.
Und in Deinem anderen Thread hatte ich ja schon geschrieben, dass Du einmal auf KEYDOWN und einmal auf KEYUP hören mußt.
Statt hier Wörterbucher als Datenspeicher zu benutzen, nimm besser eine Klasse, oder wenn es so wenig ist, dann kann man es gleich ins Hauptprogramm packen.

Code: Alles auswählen

FENSTER_BREITE = 1500
FENSTER_HOEHE = 1000
    
class Tello():
    def __init__(self, is_flying):
        self.is_flying = is_flying

def main():
    pygame.init()
    screen = pygame.display.set_mode((FENSTER_BREITE, FENSTER_HOEHE))
    pygame.display.set_caption("Tello Controll")
    drone = Tello(False)

    space_down_time = None
    while True:
        current_time = time.monotonic()
        # starten, landen
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    print('gedrückt')
                    space_down_time = current_time
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_SPACE:
                    print('losgelassen')
                    space_down_time = None
        if space_down_time is not None and space_down_time - current_time > 3:
            drone.is_flying = True
            space_down_time = None

if __name__ == '__main__':
    main()

merci für deinen vorschlag.

ins hauptprogramm würde ich das ungern packen, da dies noch deutlich erweitert werden soll.
ich bin info-fremd und habe vor kurzer zeit erst von oo-programmierung erfahren. bin also ganz neu. verstehe hier den sinn nicht ganz, das dafür oo zu programmieren. klasse "tastatur" mit einzelnen tasten instanzieren, welche als attribute "gedrückt" usw haben? oder wie schlägst du das vor?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du komplexere Dinge machst, darfst Du halt doch nur ein Event-Schleife haben.
Für jede Funktionalität, die sich ja den früheren Zustand merken muß, brauchst Du eine Klasse.

So könnte das mit der Klasse aussehen, die dann auf Events horcht:

Code: Alles auswählen

FENSTER_BREITE = 1500
FENSTER_HOEHE = 1000
    
class Tello():
    def __init__(self, is_flying):
        self.is_flying = is_flying
        self.space_down_time = None
        
    def process_event(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                print('gedrückt')
                self.space_down_time = time.monotonic()
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_SPACE:
                print('losgelassen')
                self.space_down_time = None

    def do_tick(self):
        if not self.is_flying:
            if self.space_down_time is not None and self.space_down_time - time.monotonic() > 3:
                self.is_flying = True
                self.space_down_time = None

def main():
    pygame.init()
    screen = pygame.display.set_mode((FENSTER_BREITE, FENSTER_HOEHE))
    pygame.display.set_caption("Tello Controll")
    drone = Tello(False)
    while True:
        for event in pygame.event.get():
            drone.process_event(event)
        drone.do_tick()

if __name__ == '__main__':
    main()
Antworten