Python pygame odd sequence - python

On my current Python script I have it counting down from 3 to 1, then taking a picture as you can intepret from this code below, doing this 4 times. However, it seems to sneak in 1 picture before counting down, and leaving the last one out, so the last countdown doesn't seem to matter.
Can anyone tell me why this is?
def start_photobooth():
import config
now = time.strftime("%d-%m-%Y-%H:%M:%S")
try:
for i, filename in enumerate(camera.capture_continuous(config.images_path + now + '-' + '{counter:02d}.jpg')):
print "[" + current_time() + "] [PHOTO] Producing " + filename
surface.fill((0,0,0,0))
surface.set_alpha(288)
textSurfaceObj = fontObj.render('', True, red)
surface.blit(textSurfaceObj, textRectObj)
pygame.display.update()
time.sleep(float(config.countdown))
for y in range(3,0,-1):
surface.fill((0,0,0,0))
surface.set_alpha(288)
textSurfaceObj = fontObj.render(str(y), True, red)
surface.blit(textSurfaceObj, textRectObj)
pygame.display.update()
pygame.time.wait(1000)
if i == total_pics-1:
break

Your code will take a picture at the very beginning of your loop as the capture_continuous method is executed at that point.
Your code will then run its countdown and restart the loop at which point it takes another photo.
What your loop is really doing is just:
Take picture
Countdown
Repeat
You want it to be:
Countdown
Take picture
Repeat
You could therefore change the start of your loop to:
for i in range(total_pics):
remove the if section at the end of your code (as this is handled by the for loop now) and insert a line to take the photo after the countdown. Assuming this is rasperry pi camera then the line would be:
filename = camera.capture("{0}{1}-{2:02d}.jpg".format(config.images_path,now,i))
I'm not familiar with the picamera module, so it may be that you do it this way:
filename = "{0}{1}-{2:02d}.jpg".format(config.images_path,now,i)
camera.capture(filename)

Related

Why is my function triggering multiple times?

This code:
Looks for an image a
If it finds a it tries to find a match for any image in the array
image_list
If it finds an image in image_list it looks for e
If it finds e it logs it and moves the mouse to x, y checks for pixel colour and then clicks when when it finds a match, clicks.
This is where my problem arises my avoidLog() function is being called 2-3 times per click. I've added print("click") to check if it's actually clicking and it isn't, it's clicking at the right time but for some reason, my log is triggering more than once.
It's logging correctly, just an odd number of times.
I know python is operating in a single thread in my example so I'm not sure why it's looping back round and not clicking. It would make more sense to me if it was clicking multiple times and logging multiple times.
import pyautogui as py
def avoidLog():
avoidLog = open('Avoided.txt', 'a')
avoidLog.write("Found at: " + str(f) + " at: " + str(skipTime))
avoidLog.write("\n")
avoidLog.close()
image_list = []
while True:
if py.locateOnScreen('a.jpg') != None:
for image in image_list:
found = py.locateCenterOnScreen(image)
if found != None:
skipTrigger = py.locateOnScreen('e.jpg')
if skipTrigger != None:
avoidLog()
py.moveTo(x, y)
r = py.pixelMatchesColor(x,y, (r,g,b))
if r == True:
py.sleep(2)
print("click")
py.click()
break
avoidLog() is called whenever e.jpg is located. However, py.click() is only called if pixelMatchesColor is True

How to make scrolling text, without inhibiting the functionality of a game? Pygame

Below is a snippet from a game I am currently making for A-Level coursework. I am making an intro cutscene in which I wish to make text be revealed one letter at a time (scroll, pokemon style). However my current solution requires the use of a for loop per line of text. This is fine for visual effect however it prevents the user from being able to interact with the window. I would like to add a skip button but cannot do so sue to this problem. I tried using more if statements but the code became messy, buggy, and a lot less efficient. Is there an easier, more efficient fix?
screen.blit(introImage4,(0,16))
if flags["IntroStage3"] == True:
for i in range(len(introText[0])):
introTextImage1 = myFont.render(introText[0][i], True, white)
screen.blit(introTextImage1,(50 + (myFont.size(introText[0][:i])[0]), 50))
pygame.display.update()
clock.tick(textFPS)
for i in range(len(introText[1])):
introTextImage2 = myFont.render(introText[1][i], True, white)
screen.blit(introTextImage2,(50 + (myFont.size(introText[1][:i])[0]), 100))
pygame.display.update()
clock.tick(textFPS)
for i in range(len(introText[2])):
introTextImage3 = myFont.render(introText[2][i], True, white)
screen.blit(introTextImage3,(50 + (myFont.size(introText[2][:i])[0]), 150))
pygame.display.update()
clock.tick(textFPS)
flags["IntroStage4"] = True
flags["IntroStage3"] = False
if flags["IntroStage4"] == True:
introTextImage1 = myFont.render(introText[0], True, white)
introTextImage2 = myFont.render(introText[1], True, white)
introTextImage3 = myFont.render(introText[2], True, white)
screen.blit(introTextImage1,(50, 50))
screen.blit(introTextImage2,(50, 100))
screen.blit(introTextImage3,(50, 150))
flags["IntroStage5"] = True
the issue here is that the event handler can't run a new check until your for loop is done.
the solution is to write an animation function for your text. you can do this by adding a variable which contains the text shown on screen, you can then change the value of this variable to another part of the complete text you want to have scroll based on some time dependent value.
this time dependent value can be the time that has passed since the event that triggered the scrolling text.
just to make it a bit more clear here's an example:
say i want the complete text to be "Alice has a big basket of fruit" but i can only fit one word in my scrolling text box and i want to show it for two seconds:
text = "Alice has a big basket of fruit"
def animate_text(text,t): #here t is current unix time minus the unix time whenthat the event that triggered the text scrolling
text_batches=text.split(' ')
return text_batches[t//2] if t//2 <= len(text_batches) else return False
so now we've split the text into batches instead of nesting a loop in your main loop you can blit the batch corresponding to the time that has passed since the animation started
while 1!=0:
# game loopy stuff here
blit(animate_text(text,time.time()-time_when_animation_started))
now that's all a little messy and pseudocodey and it doesn't perfectly handle your particular situation but you should get the idea here

Inadvertently Stacking While Loops

I've got a program right now that is meant to display an image, and allow for various functions on keypress (e.g., load a new frame, crop a region of interest, etc). It's also set up so that if too much time passes with no action, it'll automatically attempt to load a new image.
def triggeredbytimer():
global f, x1i, y1i, x2i, y2i, sid, keepgoing
keepgoing = False
print "Inactivity has lead to an automatic refresh. Loading newest image."
f = 0; t = 0
incoming = imagerequest(sid, f, t, x1i, y1i, x2i, y2i)
imagecallback(incoming)
def imagecallback(data):
print "----------------------- imagecallback"
global sid, f, x1i, y1i, x2i, y2i, img, refPt, cropping, keepgoing
keepgoing = True
########### \\// change this to change wait for autorefresh
tmr = Timer(10.0, triggeredbytimer)
tmr.start()
##################################
b = data.b; t = data.tr; sid = data.sid; error = int(data.error)
t = float(t); sid = int(sid)
expandedt = time.strftime("%m-%d-%Y %H:%M:%S", time.localtime(t))
print str(t) + " ----> " + expandedt
print "----------------------- image incoming"
timestr = time.strftime("%H:%M:%S", time.gmtime(t))
print "Loaded image from %s."%timestr
imagedata = bridge.imgmsg_to_cv2(b, "bgr8")
npimg = cv2.imencode('.jpg', imagedata)
cv2.imwrite('temp_image_np.jpg', imagedata)
img = cv2.imread('temp_image_np.jpg')
clone = img.copy()
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.setMouseCallback("image", mouseclickcb)
cv2.imshow("image", img)
while keepgoing == True:
key = cv2.waitKey(0) & 0xFF
## (here there are a lot of keypress functions, for brevity I included only one)
elif key == ord("t"):
print "Looking for newest frame..."
tmr.cancel()
t = 0; f = 0
incoming = imagerequest(sid, f, t, x1i, y1i, x2i, y2i)
imagecallback(incoming)
(NOTE: The images are fetched from a ROS program via services, and that is where the "imagerequest" function comes from. All of that works fine: the problem is specifically as prescribed.)
To summarize here- since there's a bit of irrelevant clippings in this snippet - an image is loaded by imagecallback. When that function is called, a timer starts (time limit is arbitrary, set low here for testing). When the timer expires, triggeredbytime() requests a new image. It acquires the image via a ROS service, then takes the return from that and uses it as an input to imagecallback(data), thus loading the image and allowing all the keypress functions to still be valid.
So the problem is that whenever the program refreshes with a new frame, the while keepgoing == True loop does not stop, leading to stacked loops that go on and get worse with every refresh. this leads to a single keypress executing multiple times, which quickly overwhelms the program it's pulling images from. (Originally, this was just a while True: loop, but I added the "keepgoing" variable to try to remedy the situation - unsuccessfully, as it were.) I'm not sure how to resolve this issue, and attempting to turn the loop "off" long enough to kill the first loop but allowing the next one to execute has not worked out so far. Is there any way to exit this while keepgoing == True loop cleanly to prevent things from stacking as such, or is there a better way in general to address the problem of loading a new image after a certain amount of time?
Thanks!
EDIT: to be clear, the problem is with ending the 'while' loop when the timer hits 0. setting keepgoing = False doesn't seem to work (probably because I can't 'return' it), so I need something similar to that if it's possible...

How to delay blits being iterated from a list

I'm trying to create a typewriter effect for text being blitted. By typewriter effect, I simply mean that Im trying to avoid the entirety of the text being blitted on screen at once. Instead, im trying to have each letter appear individually, with a slight delay before the next character in the string appears.
The catch is that im not using pygame's font.render. Instead, i've made my own custom fonts, each letter being saved as a separate image file. Now each alphanumeric character has it's own variable to which it's image is attached and each is appended to a list.
e.g:
letter_IMGs = []
a = "a" == pygame.image.load("IMG/letter_a.gif)
letter_IMG.append(a)
Lower, I have something along these lines:
letter_pos_x = 0
text = "Hello"
for i, c in enumerate(text):
screen.blit(letter_IMGs[i], (letter_pos_x,0))
letter_pos_x += 20
scroll_wait #this is a clock.delay variable. It's value was set outside the loop. I'm just calling it here.
Now as you'd guess, the result with that code is that the entire line of text appears simultaneously after the delay. I've been trying to code it as needed from there, but most of what I come up with returns with a "cannot iterate through surface objects" error.
I'm pretty much at a loss on how I should proceed next. Note that, ive been learning a bit of code on my own, on and off, for the past year and that I don't really know what im doing yet. Any and all help will be much appreciated.
Thanks in advance for your time.
Without getting into the pygame specifices too much, you just need to change the iterator so it returns substrings rather than letters:
def iterate_text(text):
for r in range(len(text)):
yield text[:r + 1]
which will return the substring iteratively:
for t in iterate_text('hello'):
print t
# h
# he
# hel
# hell
# hello
use a separate function to draw the string:
def draw_text(x, y, text):
characters = [letter_IMGs[t] for t in text]
cursor = x
for char in characters:
screen.blit(char, cursor, y)
cursor += 20
in your main loop you can decide when to get the next character. You'll basically do something like:
typewriter = iter_text('hello world')
text_to_draw = None
advance_text = False
at a level outside the loop that survive from frame to frame. When you want to draw the next character, you set advance_text to True, in and in the main loop:
if typewriter and advance_text:
text_to_draw = typewriter.next()
advance_text = False # until you set it again
if text_to_draw :
draw_text(0,0, draw_text)
You can start over by resetting the typewriter with new text, and control the timing of the new character appearing by setting advance_text to True before the next frame

python display a character at a time

So I'm recreating a part of pokemon yellow (trying to make it as close to the original as possible) And for 2 days now I'm searching a smart and efficient way to render and display a string one character at a time in the textbox, just like the pokemon games!(By the way I'm using pygame and python). Does anyone know any way of achieving this? I've tried many things but when rendering one character at a time, there is always inadequate space between them.
Sorry for the long question!
Cheers,
Alex
(edit) Thanks for your interest guys!!!
I'm not sure if I know the correct way to display my code here, If I should just
copy paste it in here or upload in dropbox or somewhere else..
(edit2) Just to clarify, i use the font at size 28, so the way I'm trying to render the characters right now, is to make a list where every element has the format (character_to_render,x_pos_to_render,y_pos_to_render). The next character would be (character2_to_render,x_pos_to_render + 28,y_pos_to_render). But approaching the problem this way, leaves inadequate space between some characters and some others are just fine.
(Edit 3) : Thanks for all your answers guys ! After closely observing the emulator, I noticed that the inadequate spacing between rendered characters is apparent there as well! So I'll just ignore this issue Andover on with my project !! Cheers and have a nice day!
Ok so here is the best solution that I have come up with so far.
You want to be able to display a string, but you only want to do it one character at a time. With strings you can do something like string[0:len(string)] which will return the whole string. So what I am thinking and please correct me if I'm wrong, but say you lower the FPS for a couple seconds, or If you do not want to do this because you still want to accept user input to skip the text.
So you have your while loop, and you check for if text is being displayed. If it is, you want to add a new letter to the string that you are displaying to the screen. I would recommend using a class for the text displayed on the screen.
surf = pygame.Surface(80, 80)
Text = TextBoxString("Hello World")
font = pygame.font.SysFont("Arial", 18)
while true:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
elif event.type == MOUSEBUTTONUP:
Text.showAll()
surf.fill((0,0,0))
text = font.render(Text.currentString, (0,0,0))
surf.blit(text, (0,0))
Text.addOn()
class TextBoxString:
def __init__(self, string):
#string that you will be dealing with
self.totalString = string
self.currentString = string[0]
#how many characters you want shown to the screen
self.length = 0
#this means that every four times through your 
#while loop a new char is displayed
self.speed = 4
def addOn(self) #adds one to the loop num and then checks if the loop num equals the speed
self.loopNum += 1
if self.loopNum == self.speed:
self.length += 1
self.loopNum=0
self.currentString = totalString[0: self.length]
def showAll(self):
self.length = len(self.totalString)
self.currentString = [0: self.length]

Categories