I want to print my trial onset time to my logfile. However, I need to write to the logfile within a while (timer) loop, which means what whatever I do in that loop will be done for every screen refresh.
The problem is that I only want to write the result of the first clock.getTime() call to the logfile. If I do this:
while timer.getTime() >0: # while time isn't up (turns neg when time's up)
for key in event.getKeys():
if key in ['escape']:
core.quit() # quit if they press escape
timeText.draw(window)
timeline.draw(window)
cursorImage.draw(window)
## flip so it actually appears
window.flip()
OnsetTime = clock.getTime()
logfile.write('OnsetTime, %s' % OnsetTime)
I get a bunch of lines of my logfile that say 'OnsetTime' and the time - one for every refresh.
I only want the first one to be printed, but I'm not sure how to do that.
This is just another way of doing what CasualDemon's proposing, but one which I think is more elegant (three lines of code for the logging instead of 5):
def logOnsetTime():
"""Function which allows launching this code right after a window.flip()"""
logfile.write('OnsetTime, %s' % clock.getTime())
window.callOnFlip(logOnsetTime) # runs on first flip
while timer.getTime() >0: # while time isn't up (turns neg when time's up)
for key in event.getKeys():
if key in ['escape']:
core.quit() # quit if they press escape
timeText.draw(window)
timeline.draw(window)
cursorImage.draw(window)
## flip so it actually appears.
window.flip()
If you want a log for every keypress, put the window.callOnFlip(logOnsetTime) inside the while loop. There's also a window.logOnFlip method specifically for logging, but that just saves an input string to the log, timestamped to a global clock, so it wouldn't save the time of your clock.
I think you want something like this:
while timer.getTime() >0: # while time isn't up (turns neg when time's up)
first = True
for key in event.getKeys():
if key in ['escape']:
core.quit() # quit if they press escape
timeText.draw(window)
timeline.draw(window)
cursorImage.draw(window)
## flip so it actually appears
window.flip()
OnsetTime = clock.getTime()
if first:
logfile.write('OnsetTime, %s' % OnsetTime)
first = False
However if you only want the very first one, first = True will have to be outside the While loop instead.
Related
I need to get only the latest input from my text widget, and then append that character to a list.
I am using
Text.get(1.0,'end-1c')
, and it does not work because the loop constantly gets all the input, instead of only getting the latest input when there is a new latest input.
def main_screen():
start_time=time.time()
tk=Tk()
tk.title('Typing Test')
tk.geometry('800x500')
main_title=Label(tk,text='1 Minute Test',font=('Times New Roman',36))
main_title.pack(pady=5)
directions=Label(tk,text='Start Typing',font=('Times New Roman',14))
directions.pack()
base_text=Label(tk,text=randomizer(),bg='#E0E0EE',font=('Arial',14),wraplength=700,justify=LEFT)
base_text.pack(pady=10)
text_area=Text(tk,font=('Arial',14),width=63,height=7,wrap='word')
text_area.pack()
tk.update()
#WPM Calculation
target_text=randomizer()
typed_text=[]
wpm=0
errors=0
while True:
tk.update()
time_elapsed=max(time.time()-start_time,1)
wpm=round((len(typed_text)/60)/5)
if time_elapsed>=60:
break
#Problem Section
key=text_area.get(1.0,'end-1c')
typed_text.append(key)
for x in typed_text:
if x != target_text:
errors += 1
Alternatively, I tried using a variable in place of the 1.0 in .get, that would increase by one with each iteration of the loop. Next, I tried a try/except command, and put the #Problem Section into a function. I tried calling that function by binding the text area to
'<Key>'
'<KeyPress>'
'<KeyRelease>'
None of these attempts work. I used a print statement to see what those variables are with each iteration of the loop, and using the first method, it just keeps making a longer and longer string that repeats constantly, instead of updating with each new character. Trying the other ways I just got nothing, no output, but no error either. I am completely stuck, and don't know what else to try.
You can bind the text_area with a <KeyPress> event, but you need to pass the list typed_text as an argument so you can append the presses.
So you should do something like this:
text_area.bind("<KeyPress>", lambda _: getKey(_, typed_text))
while True:
tk.update()
time_elapsed = max(time.time() - start_time, 1)
wpm = round((len(typed_text) / 60) / 5)
if time_elapsed >= 60:
break
# Problem Section
for x in typed_text:
if x != target_text:
errors += 1
def getKey(event, list):
list.append(event.char)
print(list)
The text widget supports something called a "mark", which is like a bookmark. You can put a mark anywhere in the text and use it just like a normal index.
Assuming that data is only ever appended to the end of the widget, the simplest solution is to fetch a block of data and then move the mark to the end of the text that you fetched. The next time you fetch data, start at that mark.
Marks have something called "gravity" that defines which character the mark sticks to. For example, if the gravity is "left" and you set it to character "2.2", the mark will always stay adjacent to the character at index "2.2". If the gravity is "right", it will be stuck at the character following index "2.2" (eg: "2.3" or "3.0")
Here's a contrived example that will print only the latest additions to a text widget every five seconds, by tracking the last position that was used to fetch the data.
import tkinter as tk
def get_new_text():
data = text.get("last", "end-1c")
print(f"new data: >>>{data}<<<")
text.mark_set("last", "end-1c")
root.after(5000, get_new_text)
root = tk.Tk()
text = tk.Text(root, wrap="word")
text.pack(fill="both", expand=True)
text.mark_set("last", "1.0")
text.mark_gravity("last", "left")
root.after(5000, get_new_text)
root.mainloop()
If the pygame program is just a basic entity you can move normally with arrow keys, how could i make it so that if space is pressed, based on the arrow key that was being held at the moment of pressing, a player dashes slightly to the specified direction? My idea is that when the space is pressed, program checks if other keys are being held down and based on that it rapidly increases the x and/or y coordinate and then stops at specific time, but I don't know how to make it stop as all of this is happening inside a main game loop. Any insight is highly appreciated.
You can use time.perf_counter.
So for using that function you will need to import module time
import time
And now you can store the value of time.perf_counter in a variable when space is pressed.
import time
jumping = False
start_time = 0
while True:
if ("space is pressed"): # Replace condition with what you want
jumping = True
start_time = time.perf_counter()
# Add your code here
if jumping:
# change of x, y and other jumping mechanism code here
if jumping and time.perf_counter() - start_time > 1: # Replace 1 with the amount you want this if condition to trigger, also add value in seconds
jumping = False
I'm not sure what your code is like, but this is how I'd go about it:
def dash(self):
keys = pygame.keys.get_pressed()
if keys[pygame.K_SPACE] and not self.dashing and self.jumping:
# ^^ Can be any key
if self.facing_right:
self.x += 20
if self.facing_left:
self.x -= 20
self.dashing = True
Make sure that when you hit the ground it does self.dashing = False else you will be able to dash forever.
Just put this function in the player class if you are doing it OOP.
Else if you are not using classes, put it anywhere in the file and take out all self..
I am using a rating scale. Participants use the 't' and 'b' keys to move the cursor along the scale. Each trial is currently 6 seconds long. If a participant stops pressing 't' or 'b' before 6 seconds are up, I want to log the time of the last keypress in my logfile. However, I'm not sure how to check which keypress is the last. I was thinking of logging the RT of the last keypress in the list, but code is checking for keypresses on every refresh. This is what I have so far:
trialNum=0
for eachPic in catPictures:
prevPos = 0
key=[]
b_list=[]
t_list=[]
timer = core.CountdownTimer(TrialDuration)
event.clearEvents() # get rid of other, unprocessed events
while timer.getTime() > 0:
for key in event.getKeys():
if key in ['escape']:
core.quit() # quit if they press escape
if key in ['b']:
# add keypress to list for each keypress. then move cursor proportionally to length of this list
b_list.append(key)
prevPos+=len(b_list)
if key in ['t']:
t_list.append(key)
prevPos-=len(t_list)
I would just have one list of keys and check the last element once the timer is up, i.e. after the while-loop (upon finish trial).
Don't initiate a whole new timer in each loop. Just reset it. Much more ressource-efficient.
Indent stuff in the while loop.
I don't understand why you move the cursor the distance of the number of previous key presses in that trial. It seems more reasonable to move it a fixed distance per key press. So I did that below.
Definitely check out Jeremy Gray's proposal of using the built-in psychopy.visual.RatingScale (another answer to this question).
Untested code:
timer = core.CountdownTimer(TrialDuration)
stepSize = 1
for eachPic in catPictures:
prevPos = 0 # keeps track of the slider position
rts=[] # used to keep track of what the latest reaction time was. Reset in the beginning of every trial.
timer.reset()
event.clearEvents() # get rid of other, unprocessed events
while timer.getTime() > 0:
for key, rt in event.getKeys(timeStamped=timer): # time keys to this clock
rts += [rt] # add this reaction time to the list
if key in ['escape']:
core.quit() # quit if they press escape
if key in ['b']:
# add keypress to list for each keypress. then move cursor proportionally to length of this list
prevPos+=stepSize
if key in ['t']:
prevPos-=stepSize
# Log here instead of print
print rts[-1]
For a given rating scale, rs, all of the subject's activity is available in rs.history, both during a trial and afterwards. The history is just a list of tuples, where each tuple is (rating, time). If the scale has started, the first tuple is always (None, 0.0). If the last two ratings are the same, then the subject accepted that rating. If they are different, the subject was moving around on the scale but had not accepted a rating at the point when the scale timed out.
Example history:
[(None, 0.0), (3, 0.777), (3, 1.396)]
from psychopy import visual, core
win = visual.Window()
rs = visual.RatingScale(win)
c = core.CountdownTimer(3)
while c.getTime() > 0:
rs.draw()
win.flip()
# print or log:
print rs.history # entire history
print rs.history[-1][1] # just the time of the last rating
I play 3 videos using MovieStim2 in PsychoPy. I suspect this issue stems from not really understanding loops, though.
I want to skip to the next video when the participant presses a key. I understand how my code quits when the participant presses "q", but I'm not sure how to make it skip to the next video when they press "b", for example. Here is my current code:
vidNum = 1
for f in ['clip1.mpg', 'clip2.mpg', 'clip3.mpg']:
clock.reset()
#logfile.write("AfterForLoopTime,%s,Video %s\n" % (core.getTime(), vidNum))
# Create your movie stim.
mov = visual.MovieStim2(win, videopath+f,
size=640,
# pos specifies the /center/ of the movie stim location
pos=[0, 0],
flipVert=False,
flipHoriz=False,
) # loop=False - need to comment this to use a loop
# Start the movie stim by preparing it to play
shouldflip = mov.play()
logfile.write("AfterShouldflipLine58,%s, Video %s\n" % (clock.getTime(), vidNum))
while mov.status != visual.FINISHED:
# Only flip when a new frame should be displayed. Can significantly reduce
# CPU usage. This only makes sense if the movie is the only /dynamic/ stim
# displayed.
if shouldflip:
# Movie has already been drawn , so just draw text stim and flip
#text.draw()
win.flip()
else:
# Give the OS a break if a flip is not needed
time.sleep(0.001)
# Drawn movie stim again. Updating of movie stim frames as necessary
# is handled internally.
shouldflip = mov.draw()
# Check for action keys.....
for key in event.getKeys():
if key in ['escape', 'q']:
win.close()
core.quit()
I tried adding code similar to the last chunk which quits after q:
elif key in ['b']:
break
but I realize that I really want to break out of this loop:
for f in ['clip1.mpg', 'clip2.mpg', 'clip3.mpg']:
However, this doesn't seem to work either
for f in ['clip1.mpg', 'clip2.mpg', 'clip3.mpg']:
for key in event.getKeys():
if key in ['b']:
break
The loop you actually want to break is:
while mov.status != visual.FINISHED:
The easiest way I can think of is to just set your movie status to -1 (or visual.FINISHED) if the user hits the key.
For example:
if key in ['b']:
mov.status = -1
This will break you out of your current video from what I can tell.
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