I'm making a program to control the mouse with WASD but whenever I press the button the mouse just moves to the top of the screen when I need it to move slightly then be able to move again if I press the key again
I've tried to have the loop turn off then back on but it just ends the loop before it can switch back on.
import keyboard
from pynput.mouse import Controller
mouse = Controller()
repeat = True
while repeat:
if keyboard.is_pressed('w'):
mouse.move(0, -5)
if keyboard.is_pressed('s'):
mouse.move(0, 5)
I need it to move up slightly when I press w but stop when it isn't pressed so I can press it again.
You might want your keyboard not to be so sensitive, so you may need to sample the pressing time. sleep can help in this case. In the code below, the program will sleep for 0.2 seconds every time it enters while loop.
import keyboard
import time
repeat = True
while repeat:
time.sleep(0.2)
if keyboard.is_pressed('w'):
mouse.move(0, -5)
if keyboard.is_pressed('s'):
mouse.move(0, 5)
# do something
EDIT: I was misunderstanding what you want, just sleep is enough for this. The problem is because your computer does everything too fast, so a while loop might be completed in a microsecond or less. So it sees that you are pressing the key for too long (though you think you did fast) then it just repeats the loop until you release the key. That's why your mouse always moves to the top.
Sampling is the way to tell computer "if you've done, then sleep, don't do anything else".
Another way is to count how many times it loops (ie, scaling), but it looks like dump since the computer has to do worthless things.
import keyboard
from pynput.mouse import Controller
mouse = Controller()
repeat = True
w_count, s_count = 0, 0
while repeat:
if keyboard.is_pressed('w'):
w_count += 1
if w_count == 10: # i'm scaling it
mouse.move(0, -5)
w_count = 0
if keyboard.is_pressed('s'):
s_count += 1
if s_count == 10: # i'm scaling it
mouse.move(0, 5)
s_count = 0
There are also other ways: measure the pressing time, edge detection (detect key release)... You can search about "software debouncing" for more ideas and find one best fits your requirement.
Related
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 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.
For those who don't know, repeat delay is the slight pause between, when holding down a key, the letter first appearing and it repeating. This might be a useful feature when typing, however when you start to write games, it becomes very annoying. An example is when you need to move a character; it will move a tiny bit, pause, and then start to move again. Tkinter code:
ball = canvas.create_rectangle(50, 50, 100, 100)
def move():
canvas.move(ball, 0, 3)
canvas.bind_all("<space>", move)
If space is pressed, the ball will move down 3 pixels, pause, and then start moving normally. I was wondering if there is any way to avoid that pause, for example a module that reads directly from the keyboard, and not the windows-processed keyboard. I know that it is possible to "cheat" by, for example, automatically running the function when you expect the delay to occur; sadly that is inaccurate and can result in choppy movement. Thanks in advance
Make a recursion loop for while it is pressed. Or that's how I do it, at least.
moving = False
def move():
global moving
moving = True
def stop_moving():
global moving
moving = False
def myloop():
global moving
if moving == True:
canvas.move(ball, 0, 3)
root.after(1, myloop)
root.bind('<space>', lambda e: move())
root.bind('<KeyRelease-space>', lambda e: stop_moving())
just make sure you call your loop once before root.mainloop()
#like this
root.after(1, myloop)
root.mainloop()
I am a high school programming student and I have a small question. I have been tasked with writing a simple game in Tkinter where an icicle falls from the ceiling and you have to avoid it with your mouse. Simple enough. However, I have hit an issue. Whenever I run a loop in a Tkinter application, it won't open. I've tried with a for loop that pauses every .5 seconds using time.sleep() and the window opens as soon as the loop finishes. Is there some special thing I need to do to make loops work in Tkinter?
from Tkinter import *
import time
import random
class App:
def __init__(self, parent):
self.frame = Frame(root, bg= '#1987DF', width=800, height=800)
self.frame.bind("<Motion>", self.motionevent)
self.frame.pack()
#self.run()
def randhex(self):
b = "#"
for i in range(1, 7):
a = random.randint(0, 15)
if a == 10:
a = "A"
elif a == 11:
a = "B"
elif a == 12:
a = "C"
elif a == 13:
a = "D"
elif a == 14:
a = "E"
elif a == 15:
a = "F"
b = b+str(a)
return b
def motionevent(self, event):
xpos, ypos, bg = event.x, event.y, self.randhex()
str1 = "X : %d Y : %d BG : %s" % (xpos, ypos, bg)
root.title(str1)
x,y, delta = 100, 100, 10
self.frame.config(bg=bg)
def run(self):
for i in range(0, 10):
time.sleep(.5)
print 'i'
self.frame.config(bg=self.randhex())
root = Tk()
app = App(root)
root.mainloop()
Currently all it is supposed to do is change the background when the mouse moves. When the line in init that says self.run() is uncommented it will print 'i' 10 times then the window will open. Help?
Writing an event based program is not the same as writing traditional programs. There is already an infinite loop running, and like any infinite loop, if you place another loop inside, the outer loop can't continue until the inner loop finishes. Since the outer loop is what causes the screen to refresh and events to be processed, inner loops effectively freeze your app until they are done.
Since there is already a loop running you don't need to create another loop. All you need to do is add little jobs to the event queue one at a time. Each job is, in effect, one iteration of your inner loop.
For example, if you wanted to write an inner loop like this:
for i in range(10):
print "i:", i
... you would instead add an event to the event queue and each time the event loop iterates (or more precisely, each time it finishes processing any other events) it will do one iteration of your loop. You do it like this:
def do_one_iteration(i):
print "i:", i
if i < 9:
root.after_idle(do_one_iteration, i+1)
Then, after the first time you call do_one_iteration, it will place the next iteration of itself on the event queue, and continue to do so until it decides it is done.
Typically you would call do_one_iteration when the user presses a button (eg: the "start" button). Call it once, then it does one bit of work (ie: moving the icicle down a couple of pixels) and then reschedules itself.
In game development you might have a function called update_display which is in charge of redrawing everything. It could, for example, subtract 1 from the Y coordinate of each icicle. You can add bindings for the left and right arrows to move the player (by incrementing or decrementing the X coordinate), and your update function would use these new coordinates to redraw the player.
By the way, you can slow down your program by using after instead of after_idle to call the function after a slight delay. This delay can be used to control the frame rate. For example, assuming the update is nearly instantaneous (and it probably will be in your case), calling after with an argument of 41 (milliseconds) yields a framerate of approximately 24 fps (24 frames times 41 milliseconds equals 984 milliseconds, or roughly 24 frames per second)
It may sound complicated but it's really pretty easy in practice once you do it once or twice.