UnboundedLocalError encountered when printing a variable - python-3.x

import pygame
def main():
while True:
print(window_size) # works for some reason
print(window_flag) # works for some reason
print(color_flag) # nope python says no
print(current_fps) # python says no
print(previous_fps) # python says no
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if color_flag:
... # s.o format issue
if __name__ == "__main__":
window_size = (500, 500)
window_flag = pygame.RESIZABLE
# the rejected trio
color_flag = 0
current_fps = 0.0
previous_fps = 0.0
# :(
window = pygame.display.set_mode(window_size, window_flag)
clock = pygame.time.Clock()
main()
When I attempt to print the color_key and subsequent variables I encounter an UnboundLocalError which confuses me, since window_size and window_flag are output without issue. Could someone provide insight into why this is the case?

Related

How to use event.value for another function outside of the main loop? [duplicate]

This question already has answers here:
How to run multiple while loops at a time in Pygame
(1 answer)
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 1 year ago.
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:
I can run the script without it being blocked
and how I can access and use the event.value, y outside of the loop
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()

How generators can work inside event loops like pyinput

For example, this code is not working:
from pynput import keyboard
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
play = ''
with keyboard.Listener(on_press = on_press, suppress=True) as listener:
while listener.is_alive(): # infinite loop which runs all time
def pump():
num = 0
while True:
yield num # generator
num += 1
if play == 'Play':
next(gen)
listener.join()
No matter how I put the generator function or using a global variable, I always have a StopIteration: error or others.
I want to control the generator, so I can "play" and "pause" with keyboard keys.
While this code works:
def pump():
num = 0
while True:
yield num
num += 1
gen = pump()
next(gen) #play
next(gen)
I'm so bewildered...
I don't use Jupyter notebooks but I tried the VS Code extension and it seems to work in them as well.
Here is some sample code to prove the generator is working.
from pynput import keyboard
import sys
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
# added to provide a way of escaping the while loop
elif str(key) == "'q'":
sys.exit(1)
def pump():
num = 0
while True:
# added as proof your generator is working
print(num)
yield num
num += 1
gen = pump()
play = ''
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
while listener.is_alive():
if play == 'Play':
next(gen)
listener.join()
This will infinitely call next(gen) since there is no way to unset play from "Play" in the code. Once it's set, it will continue infinitely looping and if play == 'Play': will always be True.
Now, onto your example.
This line:
next(gen)
will error.
gen is never defined in your code.
I assume this was just a typo and you have gen = pump() somewhere.
Additionally, defining pump in your loop will almost certainly have unintended consequences.
Suppose you do have gen = pump() immediately after the definition.
play will be set to "Play" when you press the x key. You will continuously call next(gen).
However, this time after each loop pump will be redefined and num will be set to 0. It will yield the 0 and continue to do so infinitely.
So what did you mean to do?
Probably something like this:
from pynput import keyboard
import sys
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
elif str(key) == "'q'":
sys.exit(1)
def pump():
global play
num = 0
while True:
play = ''
print(num)
yield num
num += 1
gen = pump()
play = ''
with keyboard.Listener(on_press = on_press, suppress=True) as listener:
while listener.is_alive():
if play == 'Play':
next(gen)
listener.join()
Personally, I would avoid the use of globals.
However this, calls the next(gen) once with the x key press. The generator sets play back to "" avoiding subsequent calls of next(gen) until x is pressed again.

RaspberryPi Stopwatch (TKinter) with Trigger

i would like to build a stopwatch for my carrera track which i built a little bit bigger.
So I have bought a Raspberry Pi 3 with an additional 7 "touch screen and the individual modules for triggering.
Everything works fine individually.
Now I found a great stopwatch on the net which I also used. Works really great.
In another script, that I've written by myself, the triggering with the gpios works also fantastic.
Now I want to combine both and fail.
Does anyone have an idea or suggested solution where my mistake is?
Here is my code
#!/usr/bin/python
import tkinter as tk
import RPi.GPIO as GPIO
import time
GPIO_TRIGGER_PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_TRIGGER_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setwarnings(False)
def update_timeText():
if (state):
global timer
timer[2] += 1
if (timer[2] >= 100):
timer[2] = 0
timer[1] += 1
if (timer[1] >= 60):
timer[0] += 1
timer[1] = 0
timeString = pattern.format(timer[0], timer[1], timer[2])
timeText.configure(text=timeString)
root.after(10, update_timeText)
def start():
global state
state = True
print('Pressed Start')
def stop():
global state
state = False
print('Pressed Stop')
def reset():
global timer
timer = [0, 0, 0]
timeText.configure(text='00:00:00')
print('Pressed Reset')
while GPIO.input(GPIO_TRIGGER_PIN) == True:
if GPIO.input(GPIO_TRIGGER_PIN):
print('CAR DETECTED')
time.sleep(0.1)
state = False
# BULDING TKinter GUI
root = tk.Tk()
root.wm_title('Stopwatch')
timer = [0, 0, 0]
pattern = '{0:02d}:{1:02d}:{2:02d}'
timeText = tk.Label(root, text="00:00:00", font=("Helvetica", 150))
timeText.pack()
startButton = tk.Button(root, text='Start', command=start)
startButton.pack()
stopButton = tk.Button(root, text='Stop', command=stop)
stopButton.pack()
resetButton = tk.Button(root, text='Reset', command=reset)
resetButton.pack()
update_timeText()
root.mainloop()
Currently I get the trigger in the console as output "CAR DETECTED". However, I do not get the TKinter panel.
If I remove
while GPIO.input(GPIO_TRIGGER_PIN) == True:
if GPIO.input(GPIO_TRIGGER_PIN):
print('CAR DETECTED')
time.sleep(0.1)
then the display appears and works. Without triggering.
If I put it all down, I get also the panel but it also triggers nothing more.
Any ideas ?
Thanks for help
Not really my area of expertise, but since nobody else answered, I'll give it a shot.
I think you want to use GPIO.add_event_callback()* to add a callback that gets called when an event happens.
# Callback function
def on_trigger(channel_number):
# Just update the flag
global state
state = False
# Set the callback
GPIO.add_event_callback(GPIO_TRIGGER_PIN , callback=on_trigger)
Then, delete the while loop.
*According to the docs, you need the darksidesync library to use this function. Instead of the callback, you could also create a separate thread. Personally, I would prefer the callback.
from threading import Thread
def thread_function(arg):
global state
# Listen for state change
while GPIO.input(GPIO_TRIGGER_PIN) == True:
if GPIO.input(GPIO_TRIGGER_PIN):
# Update var
state = False
print('CAR DETECTED')
time.sleep(0.1)
thread = Thread(target = thread_function)
thread.start()
Hope this helps.

how to fix Python pep 8 recommendations with pygame constants

I have a problem with pep 8 and my Python code concerning Pygame constant on the next piece of code, which is sending me error E0602 "Undefined variable" when i use the pygame constant "KEYDOWN" "K_F1" K_ESCAPE"
is there any trick to solve this problem with pep8
I am a beginner and I admit that I do not find a solution to solve this problem :(
def run(self):
""" start loop """
loop = True
while loop is True:
self.windowSurface.blit(self.Sprite.home, (0, 0))
self.pygame.display.flip()
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_F1:
loop = False
elif event.key == K_ESCAPE:
sys.exit(1)
Adding pygame. in front of those constants should fix the PEP8 issue.
In your case, pygame.KEYDOWN, pygame.K_F1, and pygame.K_ESCAPE.

Using shell commands at the same time as a while loop.

I'm making a game that is for teaching people python, so I'm controlling it by assigning variables, calling functions etc. screeps.com is a similar concept, except it's Javascript, and this is Python. The problem I'm having though, is that I have to use a while loop to refresh the screen, but commands given through the shell can't be executed while the while loop is running. For example, I want to be able to input x += 5, and have the player move to the right. So what I need is a way to refresh the screen at the same time as executing shell commands. Also, I'm using Python 3.4. Thanks in advance.
import pygame
pygame.init()
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('My game')
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
crashed = False
pImg = pygame.image.load('player.png')
def car(x,y):
gameDisplay.blit(pImg, (x,y))
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
car_speed = 0
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill(black)
car(x,y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
Why don't you use two threads
1) Thread - Update the display periodically based on inputs
2) Thread - Process inputs and update the shared variables
Threads are the right way to do this. Make sure you import thread at the top.
import thread
consoling = False
def threadedConsole():
global consoling
consoling = True
exec(raw_input(">>> "))
consoling = False
while True:
#Game loop
if not consoling:
thread.start_new_thread(threadedConsole,())

Resources