Python Loop Capability - python

I am attempting to write a program for my school project, on a raspberry pi. The program concept is fairly simple. The pi is looking at 4 GPIO pin inputs, and will output a different basic animation to the LED matrix depending on the input.
The main loop is a simple while(1) loop that always runs. Within this loop, the program is constantly checking to see what the input is from the 4 GPIO input pins on the pi. When the input is matched to one of the if statements, then it runs a short 'animation' in which is displays an image on the LED matrix, waits, clears the matrix, displays another image, waits, and clears again. For example, this could be a 'blinking smiley face' animation where the first image displayed is a smiley face with its eyes open, and the second image is a smiley face with its eyes closed. With the pausing in between the pictures getting displayed, it appears the image on the screen is actually blinking.
This setup is shown below for clarity (not actual code, gets the idea across though):
while(1) {
currentState = [GPIO.input(pin1), GPIO.input(pin2), GPIO.input(pin3), GPIO.input(pin4)]
if((currentState[0] == 1) and (currentState[1] == 0) and (currentState[2] == 1) and (currentState[3] == 0)) {
matrix.SetImage(open_eyes)
time.sleep(.3)
matrix.Clear()
matrix.SetImage(closed_eyes)
time.sleep(.3)
matrix.Clear()
}
if((currentState[0] == 0) and (currentState[1] == 1) and (currentState[2] == 0) and (currentState[3] == 1)) {
matrix.SetImage(closed_mouth)
time.sleep(.3)
matrix.Clear()
matrix.SetImage(open_mouth)
time.sleep(.3)
matrix.Clear()
}
}
The issue I am having with this setup is if the input changes during an animation, then it will not cut off the animation it is currently on to start the next. This is obvious the way the code is structured since the currentState variable is only being set at the beginning of the while loop.
To accomplish this immediate switch, I attempted to make each animation a function, and just run the function within the if statements. However, then the program never would break out of those functions to check to see what the input is. I am now stuck, and if anyone has any ideas on how to accomplish this, I would love to hear them.

The issue here is time.sleep. During the sleep, the program won't detect changes to the currentState - as you correctly recognise. One way to handle this (I'm not going to write the code for you, but explain the general idea), is to keep your animation state in an object which gets updated on every while loop cycle. Things become a bit more complex than what you have because you will need to track the .3 seconds using some kind of time monitoring. So you'd set your object state to something like:
animation.state = 'blinking'
animation.started = datetime.now()
Then once you detect .3 seconds have passed, you can change your animation state. If the currentState changes, you can reset the animation state.
Alternatively you could spawn a thread for the animation. That would be natural in some ways (and easy in Go for example) but a bit trickier if you're not used to Python threading.

Related

Python3, TkInter: Box fade-out with itemconfig evidently waits time between steps, but does not shift box's color as should

I'm a human trying to make an animated musical keyboard type thing so keystrokes play noises and flash pretty colors on screen that make ape brain happy. I don't want to bore you too much, that's all you really need to know to get what's going on. I think? Let me know.
Point is, I want to make the rectangle that flashes colors on keypress, fade back to gray on key release. I figured I'd do this with time, having a list of colors, iterate through them in steps back to gray setting the rectangle's fill to each with canvas.itemconfig with a like 0.01-second delay after each step. But the rectangle behaves as it did before I added the flair: key-press, cyan, key-release, gray. No animation. I tried lengthening the steps to 1 second (looong, but on purpose to see if I was just missing something; the same cyan shade stuck for 4 steps' worth of time, it should have iterated), and setting the rectangle color in the final step to an ugly magenta. The magenta stuck obviously because I never bothered changing it until another keypress cyanned it again. So why aren't the shades iterating. Also, feel free to call your band The Iterating Shades.
My code, slimmed down as much as possible:
# Blah blah blah, setup, but here's the key release handler (The part *I think* that's messing crap up).
def key_release(e):
if (e.char == 'q'):
for i in ["#0088dd", "#005e99", "#002841", "#2e2e2e"]:
canvas.itemconfig(rect, fill=i)
time.sleep(0.04)
canvas.itemconfig(kaq, fill="gray20")
elif (e.char == 'e'):
for i in ["#ff9500", "#9e5d00", "#523000", "#2e2e2e"]:
canvas.itemconfig(rect, fill=i)
time.sleep(0.04)
canvas.itemconfig(kae, fill="gray20")
time.sleep(0.04)
There. Short(?) code, and I didn't blab on too long. All's well that ends well!
Got any ideas? Fixes? Solutions? Pitchforks to throw? Let me know, with a healthy amount of thanks from me to you. I hope this glitch is just not me being silly! Have a great day! And let me know too if you need more code-- but some people are big on Minimum Reproducible Examples, so I kept it short here.
Edit. The answer below does not work. Sorry Jason, not your fault!!
My new code looks like this, but does the same thing. I had to declare a global variable called blue_step and have the method reference it because I was having trouble figuring out how to use .after() with arguments ... but that's beside the point. Anyway:
# ...
def cycle_blue():
global blue_step, rect
canvas.itemconfig(rect, fill=["#0088dd", "#005e99", "#002841", "#2e2e2e"][blue_step])
# ...
def key_release(e):
global blue_step
blue_step = 0
if (e.char == 'q'):
# for i in ["#0088dd", "#005e99", "#002841", "#2e2e2e"]:
# canvas.itemconfig(rect, fill=i)
# time.sleep(0.04)
for i in range(0, 3):
canvas.after(400, cycle_blue)
blue_step += 1
# ...
Any help?
Previous thoughts
According to #jasonharper:
The only thing that time.sleep() accomplishes in a Tkinter program is to
lock up the GUI completely. Delays and event-driven programming don't get
along very well - the proper approach is to use .after() to schedule the
next step of your animation a little bit into the future, and then return to
the mainloop.
I'll try this method and update in a few days. It may not work, but I'll mark this correct until then. Thank you, #jasonharper.
Edit: I cannot mark correct for two days.

PyAutoGui While loop keep going when if statement remains true

I've just started learning python and started working on a project.
I'm moving my mouse cursor from one area to another. I just want it to stop when it reach a certain point/area/zone. I'm using the mouse position to tell me where it is, currently.
For some reason, when this loops starts, it keeps looping even when the IF statement is true.
But if I started when the IF statement is true, the loops kinda works as intended, so far it's only reading the 'X' values.
I couldn't find an answer to this, or any questions like it. If anyone has an idea or can point me to a similar post, I'll appreciate it.
import pyautogui, sys, time, autoit
#Search for a position on screen manually
try:
while True:
x, y = pyautogui.position()
print(pyautogui.position())
print('Stopping for 1 seconds, keep searching or CTRL + C to end')
time.sleep(1)
#Confirmed location on screen.
if pyautogui.position(x,y) >= pyautogui.position(710, 15):
pyautogui.leftClick()
print('The Eagle has landed')
print(pyautogui.position())
break
Update: I got it! Following mkrieger1 advice, I manage to get the 'x, y' values to update. Code was rewritten.

Python: implement a "software-wide" setting that does not change often without running an if statement in every loop

I want Python to kind of ignore a statement that is unlikely to be called in a function that is often called.
I do not have a formal education in programming, so please excuse my lackluster ability to desribe things. I will try to explain the concept by example.
Say I am writing a video game, first-person shooter, drawing 60 frames per second.
In the settings menu, the user can select whether or not to display the name of other players above their head. If they disable this, I store this value as showplayernames = False.
Then in my drawing function that outputs the graphics I have:
def draw():
#code that draws the graphics on screen
if showplayernames:
#code that draws the name of players on screen
I call this function 60 times a second, but there is absolutely no point for checking if showplayernames is True 60 times a second. It will not change that often, in fact I could make this a kind of "constant" during play by preventing it to change. If showplayernames is False, then the third and fourth lines of the code are completely redundant, but they are executed nevertheless. The computer isn't smart enough to know it can ignore it, and there is a performance difference: reading a value and then checking if it is false takes time.
I could write two copies of the game (or at least the draw() function), one with only the first two lines when the user selects not to show names in the settings, and another without the if statement, and then run the appropriate one.
def draw_with_names():
#code that draws the graphics on screen
#code that draws the name of players on screen
def draw_without_names():
#code that draws the graphics on screen
Although looping through either of these 60 times a second is more efficient than running draw() ,this is obviously not the way to go. There are dozens of settings like this.
So how do software and game designers implement these kind of "software-wide" settings efficiently?
I'm not a game developer, but here's one option. Create a list of function pointers and add or remove from the list based on settings. Example:
def draw_player_names():
# Code to overlay names
def draw_fps():
# Code to overlay fps
def draw():
# Main code to draw a frame of the game
# Hold all the functions to call
optional_funcs = []
if showplayernames: optional_funcs.append(draw_player_names)
if show_fps: optional_funcs.append(draw_fps)
# Main game loop
while True:
draw()
for f in optional_funcs: f()
This can be extended for any number of functions/options.
not an game designer, but here is my voice.
You could store settings inside json file next to you python, but then you need to cover reading, getting right values etc.
You could use Environment variables to store value but that would end up using still "if" in the code.
Game designers use triggers and events to get things done, and on the lowest level I would assume those things also use if's.
system-wide-settings will in the end be used with if's
You could use overwrites based on event/trigger and use "same" draw function in both times but that only complicates code, and we all know to "keep it simple".
Sorry if this is not the answer you were looking for.
As Matthias said, you shouldn't really bother about this kind of optimization. Look at these two very silly functions:
def lot_of_ifs(settings):
#do something
a=2**10
b=2**10
c=2**10
for setting in settings:
if setting:
#do something more
a=2**10
b=2**10
c=2**10
def no_ifs():
#do something
a=2**10
b=2**10
c=2**10
timeit("lot_of_ifs([0,0,0,0,0])", globals=globals())
0.2630380000000514
timeit("no_ifs()", globals=globals())
0.10232830000040849
The cost of creating a list, looping over it, and executing five ifs is about 0.16 seconds for one million iterations, or 160 nanoseconds per iteration, while at 60 fps you have 16.6 million nanoseconds to execute your code.

What is the best way to make a player move at every interval in pygame?

Is there a library or a simple way to only loop something every 0.5 seconds without interrupting the rest of the program?
I have just started using pygame and have made a simple platformer and a Pong replica so far. I decided to try and make a Snake replica (I only currently have the head) and I need the snake to only move every 0.5 seconds while inputs can be registered at the 30 fps which I have the rest of the game running at. This is my current workaround:
while running: #this is tabbed back in my code
# keep loop running at the right speed
clock.tick(FPS)
# get time at each iteration
currentTime = str(time.time()).split(".")[0]
gameTime = int (currentTime) - int (startTime)
# this is used to check for something every 0.5 second (500 ms)
currentTimeMs = str(time.time()).split(".")[1]
# snake will move evry 0.5 second in a direction
if currentTimeMs[0] in ["5","0"] and moveDone == False:
moveDone = True
player1.move(direction)
elif currentTimeMs[0] not in ["5","0"]:
moveDone = False
There is more code within the while running: loop to get the direction and display the sprites but its not necessary for this. My current code works fine and will repeat the move function for my player1 every time that x in mm:ss:x is 0 or 5 (0.5 seconds apart) and will not repeat if it is that multiple times over a few frames.
This code needs to work within the running loop and not stop the program so time.sleep() doesn't work. I have also tried using the schedule library but it will not work as it cannot seem to allow the direction variable to change when passing it into the function.
My question therefore is; Is there a library or a shorter way to accomplish what I need?
Thanks in advance and I can message you the whole code if you need.
I suggest using pygames event mechanics and pygame.time.set_timer() (see here for docs).
You would do something like this:
pygame.time.set_timer(pygame.USEREVENT, 500)
and in the event loop look for the event type.
if event.type == pygame.USEREVENT:
If this is the only user defined event that you are using in your program you can just use USEREVENT.
When you detect the event the timer has expired and you move your snake or whatever. A new timer can be set for another 1/2 second. If you need more accuracy you can keep tabs on the time and set the timer for the right amount of time, but for you situation just setting it for 1/2 sec each time is okay.
If you need multiple timers going and need to tell them apart, you can create an event with an attribute that you can set to different values to track them. Something like this (though I have not run this particular code snippet, so there could be a typo):
my_event = pygame.event.Event(pygame.USEREVENT, {"tracker": something})
pygame.time.set_timer(my_event , 500)
You can store the moment of last move and then compare to actual time. To do it you can use perf_counter from time. Here is an example
last_move_time = perf_counter()
while running:
if perf_counter() - last_move_time > 0.5:
# move player
last_move_time = perf_counter()

Tkinter, help a noob understand Tk's loops

I am Python 3 noob creating a checkers game, I have a bunch of functions... and my game's loop is as follows:
while EndGame==0:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
PrintBoardGui() that I run at the beggining of each turn and creates a Tkinter window and draws the board on a new Canvas in a Tkinter Frame.
After that, I have to close the window in order for the program to continue.
I know this is a sub-optimal solution.
I have looked around trying to understand Tkinter's loops and read a bit about the after() function but i really don't know how I could implement it in my code.
Ultimatly I would like my Tkinter window to stay open (mabye disabled or something) while I input stuff in the console to move the pieces. Can you help me?
At first, how do you want to interact with your game ?
With text in the console ?
With buttons ?
With keyboard/mouse event ?
The "after" method is used to refresh your screen after an amount of time.
It will call the method that you pass through the parameters.
You shouldn't have to put an infinite loop in it. But you will have to check with a simple condition the game ending, to display another screen.
If you have to use the console entry, it can be a bit hard for a beginner to manage the GUI update and the console.
I am not sure what ur question was but, If you would want to run the code forever (until you stop it). you would have to use the while loop like this:
while "thing" == True:
PrintBoard()
PrintBoardGui()
print("################","\n","Player 1","\n","################","\n")
Player=1
PlayerSelectPiece()
MovePiece()
PrintBoard()
PrintBoardGui()
print("################","\n","Player 2","\n","################","\n")
Player=2
if NbrPlayer==2:
PlayerSelectPiece()
else:
AiSelectPiece()
MovePiece()
thing = False
at the end you change thing to False otherwise it would be infinite and it would bug.

Categories