Reputation: 112
In chapter 20 of Automate the Boring Things, there is an example that uses pyautogui to draw a spiral. The code uses .drag()
to draw each segment, but on each corner the mouse releases the cursor and starts a new line. This creates a break in each corner for whatever brush type you are using.
To address this aesthetic issue I tried to modify the code to use .mouseDown()
and .move()
instead. However this ended up drawing nothing except a line going from the top left corner to the bottom left corner, as if each iteration of the while loop was repeating a click.
By testing:
while True:
pyautogui.mouseDown()
In mspaint, the .mousedown() function is indeed performing correctly when I move the mouse myself.
Trying:
while True:
pyautogui.mouseDown()
pyautogui.move(300, 0, duration=0.5)
Also correctly draws a line, but does so in 300 pixel chunks. I.e. every time the loop iterated the line segment would load in on mspaint.
Repeating this experiment in a square:
while True:
pyautogui.mouseDown()
pyautogui.move(300, 0, duration=0.5)
pyautogui.move(0, 300, duration=0.5)
pyautogui.move(-300, 0, duration=0.5)
pyautogui.move(0, -300, duration=0.5)
Ended up drawing nothing.
What is going on here? Why is the .mouseDown()
behavior so inconsistent? How do I get my code to draw a nice unbroken spiral?
Full original code for reference here:
#! python3
# spiralDraw.py - draws a spiral in mspaint
import pyautogui
import time
time.sleep(5) # give time to go to paint app
distance = 300
change = 20
pyautogui.mouseDown()
while distance > 0:
pyautogui.move(distance, 0, duration=0.5)
distance = distance - change
pyautogui.move(0, distance, duration=0.5)
pyautogui.move(-distance, 0, duration=0.5)
distance = distance - change
pyautogui.move(0, -distance, duration=0.5)
if distance == 0:
pyautogui.mouseUp()
Upvotes: 0
Views: 4013
Reputation: 112
While I am unsure why this is the case, the desired result can be achieved by dividing the mouse hold and the mouse movements into separate threads and running them simultaneously.
Here is the output of the non-multi threaded code with the crayon brush, notice the blocky corners implemented by drag as each line is a separate mouse press:
And here is the result from code that separates the tasks into two threads and runs them simultaneously, as you can see the corners are smooth, and it is all one long line as when I press ctrl+z
the entire drawing disappears:
Here is the code that simply implements multithreading:
import pyautogui
import time
import threading
def mouseHoldDown():
global distance
while distance > 0:
pyautogui.mouseDown()
if distance == 0:
time.sleep(1)
pyautogui.mouseUp()
def movingSpiralMouse():
global distance
change = 20
while distance > 0:
pyautogui.move(distance, 0, duration=0.25)
distance = distance - change
pyautogui.move(0, distance, duration=0.25)
pyautogui.move(-distance, 0, duration=0.25)
distance = distance - change
pyautogui.move(0, -distance, duration=0.25)
distance = 300
time.sleep(5)
mouseObj = threading.Thread(target=mouseHoldDown)
mouseObj.start()
moveObj = threading.Thread(target=movingSpiralMouse)
moveObj.start()
I would still greatly appreciate an explanation as to why this needs to be separated into multiple threads to achieve the desired results.
Upvotes: 0
Reputation: 283
I found that you have to use drag
to draw a line, because the move
function only moves the pointer without doing the click down (documentation).
However you can still use the mouseDown
function instead of the click
function, because it "does the same thing as a left-button mouse click" (documentation).
The drag
function (at least in the last version of the package) need a parameter called button
which could be "left"
, "right"
or "middle"
representing each one of the three clicks of the mouse (documentation).
Based on the previous information, the code is:
import pyautogui
import time
time.sleep(5) # give time to go to paint app
distance = 300
change = 20
pyautogui.mouseDown()
while distance > 0:
pyautogui.drag(distance, 0, duration=0.5, button='left')
distance = distance - change
pyautogui.drag(0, distance, duration=0.5, button='left')
pyautogui.drag(-distance, 0, duration=0.5, button='left')
distance = distance - change
pyautogui.drag(0, -distance, duration=0.5, button='left')
if distance < 0:
pyautogui.mouseUp()
Upvotes: 0