PyAutoGui While loop keep going when if statement remains true - python

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.

Related

Python; "simulate" mouse and keyboard

I recently started learning Python, trying to make short programs with simple functions but that I can use in one way or another while learning.
I have studied a bit several libraries that they functions are what the title say, with some I have had better experiences than with others, but at the moment I have a problem when trying to make a "Shift+Click" in concrete positions of the screen.
Im actually using win32api and win32con since they are the ones that to my way of seeing have better results for what Im trying to do.
Its nothing very complicated, its just do a Shift + Left Click in specific positions, like same way that you can recreate it in a normally with your mouse and keyboard.
Here is a simplified version of the code:
def mouse_click(x, y):
win32api.SetCursorPos((x, y))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)
time.sleep(0.065)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)
def shift_down():
win32api.keybd_event(win32con.VK_LSHIFT, 0, 0, 0)
time.sleep(0.005)
def shift_up():
win32api.keybd_event(win32con.VK_LSHIFT, 0, win32con.KEYEVENTF_KEYUP, 0)
time.sleep(0.005)
def clicks():
for offs in coord:
x, y = offs
x += random.randint(-15, 15)
y += random.randint(-15, 15)
if random.random() > 0.0:
shift_down()
time.sleep(0.005)
mouse_click(x, y)
time.sleep(0.005)
shift_up()
print('click on', offs)
def main():
while True:
if win32api.GetAsyncKeyState(win32con.VK_INSERT):
break
elif win32api.GetAsyncKeyState(win32con.VK_F2):
clicks()
print('clicking end')
if __name__ == '__main__':
main()
Yes, Im aware that some sleep are unnecessary, but here is my current problem. The way you see above is the best way I have had the code respond correctly and in time according to my needs.if I low the time on mouse_click below 0.55 some clicks start to be ignored, from what I have read and seen it should not be necessary make pauses, but at the moment this is the solution I found.So I want to fix the basic formatting of it first.
Am I missing something or is it some kind of "optimization" problem?
Any hints or improvements are welcome even if its about using another library.
I would recommend using the PyAutoGUI module available here: https://pyautogui.readthedocs.io/
It is very beginner friendly and very easy to use!
You would perform a shift-click like this:
import pyautogui
pyautogui.keyDown('shift')
pyautogui.click()
pyautogui.keyUp('shift')
Somehow these randoms sleep are no longer necessary, and the sleep in mouse_click() I could lower it to a maximum of 0.06 without problems.
The press and release Shift seems to work fine in other applications, so I deduce that it is some kind of incompatibility with the application/window itself (Im not trying to force or use it in a particular window or screen). So maybe it can work with some other library than the ones currently in use.
The problem with the pyautogui and pynput library, is that the same thing happens but they also add some kind of delay by default.
In any case, setting the Shift press outside of if in clicks() seems to be the solution as well as using win32con.KEYEVENTF_EXTENDEDKEY inside win32api.keybd_event().
I don't see any point in going on with this and it will probably end up being better if I hold down Shift by myself.

How to get the coordinates after a mouse click?

I was using the following code to get the coordinates of a point after a mouse click (keep in mind I was clicking on a random point on the screen, not on a figure):
import win32api
posvals = [[],[]]
x = 0
state_left = win32api.GetKeyState(0x01)
while x<2:
a = win32api.GetKeyState(0x01)
if a != state_left:
state_left = a
print(a)
if a >= 0:
print('button down')
z,y = win32api.GetCursorPos()
posvals[x] = [z,y]
print(z,y)
x += 1
time.sleep(.001)
print(posvals)
Here I saved the coordinates in posvals, and the while loop is there because I only wanted to record 2 clicks. I got and tweaked this code from another question on stackoverflow, but I'm not sure which one.
My current problem is that I'm using a Linux computer and the win32api module (its official name is pywin32) won't work since it is only for windows.
How can I adjust (or completely restart) my code?
So there is no easy way to port the code to linux, unless you run in wrapped with WineLib or equivalent wrapper software. One such explanation of this practice is here.
You could try other mouse position packages like PyMouse. This might be a better option. This question also has some good examples of other more agnostic package options for python mouse coordinates.

Python Loop Capability

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.

How do I have a fail safe command in python

So I'm trying to make a program that randomly places my mouse in specific areas in python, and I'm still testing it so it can get a bit crazy. I was wondering if it were possible to make a fail safe command that would terminate the program if the key or command were entered.
Since the program is clicking and moving around on another window on my computer it deselects the window python is running in, which make the "Keyboard Interrupt" fail to work.
Here's the program:
import pyautogui
import random
import time
time.sleep(6)#gives you time to click the right window
try:
while True:
x = random.randint(239, 1536) #randomizes the position of the mouse
pyautogui.moveTo(x,663) #moves the mouse to position
pyautogui.doubleClick() #fires the gun in game twise
time.sleep(1) #gives time for the game to
pyautogui.doubleClick() #catch up with the mouse and fires gun
pyautogui.doubleClick() #fires gun twice again
except KeyboardInterrupt:
print ('Yup')
And if there is anything wrong with the way I'm coding please tell me. I'm still new to coding so any tips would be awesome.
PyAutoGUI also has a fail-safe feature. Moving the mouse cursor to the upper-left corner
of the screen will cause PyAutoGUI to raise the pyautogui.FailSafeException
exception.
Your program can either handle this exception with try and except statements
or let the exception crash your program. Either way, the fail-safe feature will stop the
program if you quickly move the mouse as far up and left as you can.
>>>import pyautogui
>>>pyautogui.FAILSAFE= True
By default FAILSAFE is True, and you can also disable it.
>>>pyautogui.FAILSAFE = False
What you are looking to do is use a sys.exit() in your Exception.
Try this:
import sys
try:
# all your code
except KeyboardInterrupt:
sys.exit()
this should work, whenever you click the key it will break the loop, install the module keyboard with
pip install keyboard
if keyboard.is_pressed('q'):
break

psychopy event based on mouse position

I am setting up a Fitts' law experiment where I would like a participant to click on a start button, move the mouse in a straight line, and then click on a target. After clicking start, if the participant moves their mouse too far up or down in the vertical direction I would like to display a white screen for 30 seconds and accept no input, afterward moving to the next trial.
Currently, no matter what, the experiment is behaving as if I'm moving outside the acceptable range and always executes the if statement.
Here is the code for it that I have right now:
from psychopy import core
start = mouse.isPressedIn(polygon, buttons=[0])
if start==True:
thisExp.addData('starttime',core.getTime())
x, y = mouse.getPos()
mouse.x.append(x)
mouse.y.append(y)
if y>10 or y<-10: #this is the statement that isn't resulting in what I would expect
thisExp.addData('penalty',1)
finish = mouse.isPressedIn(polygon2, buttons=[0])
if finish==True:
thisExp.addData('stoptime',core.getTime())
continueRoutine=False
I haven't figured out everything I need under the nested if statement yet. Right now I am just trying to make sure it works correctly. It never evaluates the if statement as true and never adds the penalty, even though looking at the collected mouse.y list data in the csv file I can see that there were instances where y was outside of whatever range I set.
It appears that mouse position data is being collected as soon as the trial begins, not just after polygon is clicked. However, starttime and stoptime do appear to be based on when polygon and polygon2 are clicked. I'm really not sure what is going on.
UPDATE:
I didn't get notified about Jonas' response until a day later for some reason. I wish I had because it would have put me on the right track a few hours earlier. It's a builder code component.
My code was all running each frame. At the beginning of the routine I added:
checkstart = False
That with the following code each frame solves this particular problem:
start = mouse.isPressedIn(polygon, buttons=[0])
if start==True:
thisExp.addData('starttime',core.getTime())
x, y = mouse.getPos()
mouse.x.append(x)
mouse.y.append(y)
checkstart=True;
if checkstart==True:
if y>10 or y <-10:
thisExp.addData('penalty',1)
finish = mouse.isPressedIn(polygon2, buttons=[0])
if finish==True:
thisExp.addData('stoptime',core.getTime())
continueRoutine=False
I still don't know why mouse position is collected before polygon is clicked but it doesn't actually matter to me. I can match the data for the frame where the mouse was clicked to the position that corresponds to the start button to get the beginning of the mouse path trace.
My code was all running each frame. At the beginning of the routine I added:
checkstart = False
That with the following code each frame solves this particular problem:
start = mouse.isPressedIn(polygon, buttons=[0])
if start==True:
thisExp.addData('starttime',core.getTime())
x, y = mouse.getPos()
mouse.x.append(x)
mouse.y.append(y)
checkstart=True;
if checkstart==True:
if y>10 or y <-10:
thisExp.addData('penalty',1)
finish = mouse.isPressedIn(polygon2, buttons=[0])
if finish==True:
thisExp.addData('stoptime',core.getTime())
continueRoutine=False
I still don't know why mouse position is collected before polygon is clicked but it doesn't actually matter to me. I can match the data for the frame where the mouse was clicked to the position that corresponds to the start button to get the beginning of the mouse path trace.

Categories