0

Below is a working skeleton of my actual code. What I am attempting to do is to obtain the JOYAXISMOTION, event.value, y so that I can use it in different function outside of the loop. I have already defined the variable y.

import time
import sys
import pygame
import subprocess
import random


from pygame.locals import *
pygame.init()

y = 0

def get_percent_change(current, previous):
    if current == previous:
        return 0
    try:
        return ((float(current) - float(previous)) /abs(previous)) * 100.0
    except ZeroDivisionError:
        return float('inf')

def sendCommands():
    time.sleep(5)
    print('Hello World')

pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
for joystick in joysticks:
    print(joystick.get_name())

game_state = False
run = True

while run:
    for event in pygame.event.get():

        if event.type == JOYBUTTONDOWN:
            print(event)

            if game_state == False and event.button == 8:
                game_state = True

            if event.button == 4:
                game_state = False

        if event.type == JOYHATMOTION:
            if event.value[0] == 1 or event.value[0] == -1:
                game_state = False

        if event.type == JOYAXISMOTION:
            if event.axis == 4:
                current = event.value
                y = get_percent_change(current, -1.0001)
                print(y)

        if event.type == JOYDEVICEADDED:
            joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
            for joystick in joysticks:
                print(joystick.get_name())
        if event.type == JOYDEVICEREMOVED:
            joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]


    if game_state == True:
        sendCommands()

pygame.quit()
sys.exit()

The main problem I am running into is the time.sleep(5) that the sendCommands requires. It is blocking the script while sleeping. I have tried asyncio with no success. Threading is also very confusing to me here. I have also tried the delay logic in the answer provided to a closely related question here but that also blocks the code from running.

Allow me to pick your brain on how:

  1. I can run the script without it being blocked
  2. and how I can access and use the event.value, y outside of the loop
Ted Klein Bergman
  • 9,146
  • 4
  • 29
  • 50
Leo Optimo II
  • 63
  • 1
  • 8
  • Why do you need the function `sendCommands`? You should almost **never** use `time.sleep` in a GUI program. What is it you're trying to do? Most likely, you should post an event to the event queue, or create a timer. And threading is **almost never** the answer when using pygame. Pygame does most of the necessary threading for you. – Ted Klein Bergman Aug 06 '21 at 22:26
  • And it's a common source of bugs because pygame has special requirements for when and where you're allowed to use threads. – Ted Klein Bergman Aug 06 '21 at 22:32
  • I am not creating a game. I am just using pygame so that I can use a gamepad to control the script. The full functionality is within the skeleton posted, only that the function sendCommands change to a different one. – Leo Optimo II Aug 06 '21 at 22:39
  • I never said anything about games. Anytime you use pygame, you **must** process events regularly, as described [here](https://stackoverflow.com/a/42719689/6486738). You can't have anything that blocks the program. Most of the time it's not necessary either, as you can control events by posting it to the event queue or using timer values to control states. That's why I asked what `sendCommands` is for. Can it be processed in a reasonable amount of time (i.e. roughly 1/30 second)? Or can you process it discretely (i.e. in segments)? – Ted Klein Bergman Aug 06 '21 at 22:49
  • If you're sure all of the above is a hard no, then you actually need to use threads. However, they come with a huge warning: you shouldn't access the event queue or the window (and probably other modules as well). I'm not sure on all the requirements to be honest. And you need to make sure to synchronize data as well, which depends on what data the `sendCommands` function actually access (for example global variables, etc...). – Ted Klein Bergman Aug 06 '21 at 22:53
  • Ted, sorry, I didnt refresh my browser quick enough to see your questions :) - @Ted Klein Bergman The actual fuction is sending is a subprocess sending a command to the Command Line and I would like it to to it repeatedly every 5 seconds. I cannot figure out how to schedule repeating tasks without blocking the script!! Thats my biggest problem. – Leo Optimo II Aug 06 '21 at 23:04

1 Answers1

2

To use y outside of the loop, you could declare it as global but a better alternative is to make a function to get the value. You can implement a timer to execute your subprocess every 5 seconds instead of blocking the entire thread. The following code prints None every 5 seconds for me since I don't have any controllers connected but I think it will work for you.

import time
import sys
import pygame
from pygame.locals import *

pygame.init()

def sendCommands(waitTime, y):
    global sendCommandsStart
    #count how many seconds have passed since sendCommandsStart
    #variable was last updated
    if time.time() - sendCommandsStart > waitTime:
        sendCommandsStart = time.time() #reset the timer
        print(y)#call your subpreocess here

def getJoyStickY(events):
     for event in events:
         if event.type == JOYAXISMOTION:
            if event.axis == 4:
                return get_percent_change(current, -1.0001)
     return None

pygame.joystick.init()
run = True

sendCommandsStart = time.time() #get the current time - its just a number (time since last epoh)

while run:
    allEvents = pygame.event.get()
    sendCommands(5, getJoyStickY(allEvents))

    
pygame.quit()
sys.exit()
    
  • This will be executed synchronously though. `Thread.join` will wait until the thread is completed, so you gain no benefit from this. – Ted Klein Bergman Aug 06 '21 at 22:27
  • @TedKleinBergman Thanks for letting me know. I removed it, –  Aug 06 '21 at 22:35
  • Thank You very much for the answer. I was very excited to try this but running it crashes by IDE immediately. It prints none and basically freezes. Would you know why that is? I am using Atom (I dont think that should matter much) - Thanks again :) – Leo Optimo II Aug 06 '21 at 22:36
  • @user16564162 It's because the OS thinks the program freezes because you sleep for 5 seconds. You **must** process events regularly. The current answer is the proper way to do it. – Ted Klein Bergman Aug 06 '21 at 22:38
  • The answer was edited (thanks!) - I am checking but I believe the answer posted should work! – Leo Optimo II Aug 06 '21 at 22:46
  • @user16564162 Your welcome. I am curious as to why the function is required to wait 5 seconds though. –  Aug 06 '21 at 22:50
  • I am a noob at this - so most likely I am doing something wrong - However running this will print none and my IDE becomes unresponsive, as in I cannot Keyboard escape. But this gives me an idea to use a function..so still, its great help. – Leo Optimo II Aug 06 '21 at 22:59
  • @user16038533 the real function is a subprocess that needs to run every 5 seconds. I am still trying to figure out how to run a function repeatedly without blocking the rest of the code! – Leo Optimo II Aug 06 '21 at 23:10
  • @user16564162 You don't need to make the entire thread stop for the function to be executed every 5 seconds. Hang on lemme edit the answer. –  Aug 06 '21 at 23:24
  • @user16564162 Thanks - if you can figure out how to run the function sendCommands and still be able to access the value y (in my original code), that would be awesome. Since this is a simple script, I could use global y as I seem to be running into problems using the code posted (my own user error very likely). – Leo Optimo II Aug 06 '21 at 23:41
  • @user16564162 I dont understand what you mean. In the code I posted above, you have access to the value anywhere you want. Simply call the function `getJoyStickY` and you have the value. –  Aug 06 '21 at 23:43
  • oh niice - let me try that!! - amazed at how you are able to do so quickly what is taking me hours between research and testing – Leo Optimo II Aug 06 '21 at 23:44
  • 1
    @user16564162 This answer is a good manual way of doing it. There's also [`pygame.time.set_timer`](https://www.pygame.org/docs/ref/time.html#pygame.time.set_timer) which will post an event every x millisecond. So you can also define an event `RUN_COMMAND = pygame.USEREVENT` and call `pygame.time.set_timer(RUN_COMMAND, 5000)` at the beginning of the script. This will post the event type `RUN_COMMAND` every 5 second in the event queue (i.e. in `pygame.event.get()`). – Ted Klein Bergman Aug 07 '21 at 00:07
  • @Ted Klein Bergman I will definitely try that as well. I am glad I asked the question. Thanks for editing the question for better readability as well! – Leo Optimo II Aug 07 '21 at 02:24