getMouse tracking in Python using Zelle graphics.py - python

I am new to Python. I need to write a program to move my ball or circle when I click the mouse. How do I achieve this? I have the below code that I got started with:
from graphics import *
import time
def MouseTracker():
win = GraphWin("MyWindow", 500, 500)
win.setBackground("blue")
cir = Circle(Point(250,250) ,20)
cir.setFill("red")
cir.draw(win)
while(win.getMouse() != None):
xincr = 0
yincr = 0
for i in range(7):
cir.move(xincr, yincr)
time.sleep(.2)
win.getMouse()

Assuming you are not bound to some specific tools or implementation, you may find matplotlib useful. You can plot a circle onto the drawing area using a circle patch (http://matplotlib.org/api/patches_api.html) and then move it around when there is mouse-click in the graph axes. You will need to connect to the event-click listener and define a callback function which handles the drawing update - see http://matplotlib.org/users/event_handling.html for examples of how to do this. You can get the coordinates of the mouse press using the xdata and ydata methods.
This worked for me in python 2.7:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
fig = plt.figure()
ax = fig.add_subplot(111)
circ = Circle((0.5,0.5), 0.1)
ax.add_patch(circ)
def update_circle(event):
ax.cla()
circ = Circle((event.xdata, event.ydata), 0.1)
ax.add_patch(circ)
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', update_circle)
plt.show()

Assuming you want to stick with the graphics package you started with, you can do it but you're missing code to save the mouse position and compare it to the circle's center position:
from graphics import *
WIDTH, HEIGHT = 500, 500
POSITION = Point(250, 250)
RADIUS = 20
STEPS = 7
def MouseTracker(window, shape):
while True:
position = window.getMouse()
if position != None: # in case we want to use checkMouse() later
center = shape.getCenter()
xincr = (position.getX() - center.getX()) / STEPS
yincr = (position.getY() - center.getY()) / STEPS
for _ in range(STEPS):
shape.move(xincr, yincr)
win = GraphWin("MyWindow", WIDTH, HEIGHT)
win.setBackground("blue")
cir = Circle(POSITION, RADIUS)
cir.setFill("red")
cir.draw(win)
MouseTracker(win, cir)
You will need to close the window to break out of the tracking loop -- in a real program you'd handle this as part of the design (i.e. some user action causes a break in the while True: loop.)

Related

Particle motion with matplotlib animation (python) in 2d random walk on a grid

I have wrote a program in Python that simulates (with matplotlib animation) the random (x,y) walk of a particle confined on a 2d grid, with boundary conditions (i.e. the particle cannot escape the grid). The grid is 10x10, and the random walk has 100 steps.
The issue I'm having is at the end, when defining the animation function to give to FuncAnimation.
In this function, I plot the lines traced when the particle is moving, and I would like to represent the particle as a dot in the animation. I do this by writing ax.plot(xdata[i], ydata[i], 'go'), where i is the iteration index of the animation function. This works, but it keeps all of the points at different i, whereas I would only like to keep the point at the latest i (i.e. I would like the point to represent the particle moving).
I tried to use cla(), with no success, since it clears everything (the lines too). Is there any way to do it? I tried to check the documentation for cla(), but there's none.
Thank you for helping!
Here's my code
import numpy as np
import random
import matplotlib.pyplot as plt
from matplotlib import animation
L = 10 #grid size
N = 100 #iterations number
steps = np.array([[1,0],[-1,0],[0,1],[0,-1]]) #possible moves - right,left,up,down
locations = np.zeros((1,2))
#create array of random walk points, xdata and ydata, while respecting the boundary conditions
for n in range(N):
if (abs(locations[-1][0]) < L/2) and (abs(locations[-1][1]) < L/2):
r = random.randrange(4)
move = steps[r]
nextloc = [locations[-1] + move]
locations = np.append(locations, nextloc, axis=0)
#boundary conditions
elif (locations[-1][0] == L/2):
r = random.randrange(3)
nextloc = [locations[-1] + np.delete(steps,0,axis=0)[r]]
locations = np.append(locations, nextloc, axis=0)
elif (locations[-1][0] == -L/2):
r = random.randrange(3)
nextloc = [locations[-1] + np.delete(steps,1,axis=0)[r]]
locations = np.append(locations, nextloc, axis=0)
elif (locations[-1][1] == L/2):
r = random.randrange(3)
nextloc = [locations[-1] + np.delete(steps,2,axis=0)[r]]
locations = np.append(locations, nextloc, axis=0)
elif (locations[-1][1] == -L/2):
r = random.randrange(3)
nextloc = [locations[-1] + np.delete(steps,3,axis=0)[r]]
locations = np.append(locations, nextloc, axis=0)
xdata = locations[:,0]
ydata = locations[:,1]
#setting up animation canva
fig, ax = plt.subplots(figsize=(8,8))
ax.set_xlim(-L/2,L/2)
ax.set_ylim(-L/2,L/2)
#animation function - this is where I'm having issues
def animate(i):
line, = ax.plot([], [], lw=2)
line.set_data(xdata[:i], ydata[:i]) #random walk lines
line.set_color('blue')
#ax.cla() # supposed to remove old dots, but it doesn't work so it's commented
pt = ax.plot(xdata[i], ydata[i], 'go') #the particle
return line
anim = animation.FuncAnimation(fig, animate)
plt.show()

Make mouse movements follow windmouse algorithm

Not sure if I'm asking the right question, but is it possible to make python move my mouse with things like pyautogui/selenium actions/or preferably all mouse movements following this algorithm? I want to make my mouse movements as real as possible for a bot I'd like to make. How would I go about doing this if possible? I found the algorithm here: https://web.archive.org/web/20210621155859/https://ben.land/post/2021/04/25/windmouse-human-mouse-movement/
import numpy as np
sqrt3 = np.sqrt(3)
sqrt5 = np.sqrt(5)
def wind_mouse(start_x, start_y, dest_x, dest_y, G_0=9, W_0=3, M_0=15, D_0=12, move_mouse=lambda x,y: None):
'''
WindMouse algorithm. Calls the move_mouse kwarg with each new step.
Released under the terms of the GPLv3 license.
G_0 - magnitude of the gravitational fornce
W_0 - magnitude of the wind force fluctuations
M_0 - maximum step size (velocity clip threshold)
D_0 - distance where wind behavior changes from random to damped
'''
current_x,current_y = start_x,start_y
v_x = v_y = W_x = W_y = 0
while (dist:=np.hypot(dest_x-start_x,dest_y-start_y)) >= 1:
W_mag = min(W_0, dist)
if dist >= D_0:
W_x = W_x/sqrt3 + (2*np.random.random()-1)*W_mag/sqrt5
W_y = W_y/sqrt3 + (2*np.random.random()-1)*W_mag/sqrt5
else:
W_x /= sqrt3
W_y /= sqrt3
if M_0 < 3:
M_0 = np.random.random()*3 + 3
else:
M_0 /= sqrt5
v_x += W_x + G_0*(dest_x-start_x)/dist
v_y += W_y + G_0*(dest_y-start_y)/dist
v_mag = np.hypot(v_x, v_y)
if v_mag > M_0:
v_clip = M_0/2 + np.random.random()*M_0/2
v_x = (v_x/v_mag) * v_clip
v_y = (v_y/v_mag) * v_clip
start_x += v_x
start_y += v_y
move_x = int(np.round(start_x))
move_y = int(np.round(start_y))
if current_x != move_x or current_y != move_y:
#This should wait for the mouse polling interval
move_mouse(current_x:=move_x,current_y:=move_y)
return current_x,current_y
The algorithm you found is going to make human like mouse movement, instead of moving the mouse physically on your screen it will just help you plot the movements on the screen.
To help you see the plot, you can see in the same blog the writer/programmer has used Python matplotlib.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[13,13])
plt.axis('off')
for y in np.linspace(-200,200,25):
points = []
wind_mouse(0,y,500,y,move_mouse=lambda x,y: points.append([x,y]))
points = np.asarray(points)
plt.plot(*points.T)
plt.xlim(-50,550)
plt.ylim(-250,250)
This block of code is just missing a plt.show() that will show the plot on your screen. Simply add,
plt.show()
at the end and you are good to go.
Now for the second part how can get physical Human like mouse movement. For that you can use a Python package pyHM. To install simply run this command in your shell
pip install pyHM
Then write this script, to move your mouse physically on the screen.
from pyHM import mouse
destination_x = 1000
destination_y = 500
mouse.move(destination_x, destination_y)
You might get an error for scipy.random.randit, let me know if you get this error I will help you out with that as well.

How to animate motion of a dynamical system in Python?

Here's the how cart pendulum looks like
Imagine you have a 4 differantial equations which represents the motion of a dynamic system (pendulum on a cart) and you solved these equations using scipy.integrate.odeint for 10 seconds with interval of 0.01 seconds.
Finally you get the solution matrix with size (1000,4). For each diff eqs you get 1000 data points. Everything is ok so far. For example, If I plot one of the motion I can get beautiful graphics.(Below image shows the motion of the pendulum rod(oscillating))
Here's Graph of theta angle
But, instead of boring graphics and I want to make an animation that shows the motion of the cart as Steve Brunton did it as below link with using Matlab.
Here's link of the cart-pend video!
====================================================================
To animate the figures I actually tried to do what Steve Brunton did in Matlab, with Python. But the result is just a frozen figure instead of moving one. Actually If I run this script from Spyder IDE, I get 1000 figures in the IPython console.(Each figure represents a snapshot of the system's instantaneous motion which is good. But I want just one figure with 1000 sequantial frames on it.)
Here's the snap of frozen cart-pend
I've written two python scripts. One for only plotting the other is for solving the diff eqs and feed the results to the other one.
~~~~~~~~~~~~~~~~~~~~~~~~~
This code is for plotting the animated figures.
from math import sqrt, sin, cos
import matplotlib.pyplot as plt
from matplotlib import animation
def draw_cart(states, m, M, L):
x = states[0] # Position of the center of the cart
theta = states[3] # Angle of the pendulum rod
#Dimensions
W = 1*sqrt(M/5) # Cart width
H = .5*sqrt(M/5) # Cart Height
wr = .2 # Wheel radius
mr = .3*sqrt(m) # Mass Radius
#Positions
y = wr/2+ H/2 # Cart Vertical Position
w1x = x-.9*W/2 # Left Wheel x coordinate
w1y = 0 # Left wheel y coordinate
w2x = x+(.9*W/2) # Right Wheel x coordinate
w2y = 0 # Right Wheel y coordinate
# Pendulum Mass x-y coordinates
px = x+(L*sin(theta))
py = y-(L*cos(theta))
#Identfying Figure
plt.figure()
plt.axes(xlim=(-5, 5), ylim=(-2, 2.5))
# Plotting the base line
line = plt.Line2D((-10, 10), (0, 0), color='k', linewidth=2)
plt.gca().add_line(line)
plt.hold(True)
# Shapes
rectangle1 = plt.Rectangle((x-(W/2), (y-H/2)), W, H, fill=True, color='b') # Cart
rectangle2= plt.Rectangle((px-(mr/2), py-(mr/2)), mr, mr, fill=True, color='r') # Pendulum mass
circle2 = plt.Circle((w1x, w1y), wr/2, fill=True, color='g') #Left whell
circle3 = plt.Circle((w2x, w2y), wr/2, fill=True, color='g') #Right whell
plt.plot((x, px), (y, py), 'k', lw=2) #Pendulum rod
#Adding shapes to the figure
plt.gca().add_patch(rectangle1)
plt.gca().add_patch(rectangle2)
plt.gca().add_patch(circle2)
plt.gca().add_patch(circle3)
# Showing the figure
plt.show()
plt.hold(False)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is the other code for solving the diff eqs and feeding the solution to the above code.
from math import pi, sin, cos
import numpy as np
from scipy.integrate import odeint
import draw_cart_pend_rt
import matplotlib.pyplot as plt
# System Parameters
m = 1
M = 5
L = 2
g = -10
d = 1
u = 0
def cart_pend_dynamics(states, tspan):
Sy = sin(states[2])
Cy = cos(states[2])
D = m*L*L*(M+(m*(1-(Cy**2))))
state_derivatives = np.zeros_like(states)
state_derivatives[0] = states[1]
state_derivatives[1] = ((1/D)*(((-m**2)*(L**2)*g*Cy*Sy)+(m*(L**2)*(m*L*(states[3]**2)*Sy-d*(states[1])))))+(m*L*L*(1/D)*u)
state_derivatives[2] = states[3]
state_derivatives[3] = ((1/D)*((m+M)*m*g*L*Sy-m*L*Cy*(m*L*(states[3])**2*Sy-d*states[1])))-(m*L*Cy*(1/D)*u)+(0.01*1)
return state_derivatives
def solution_of_cartpend(dt):
# Initial conditions to solve diff eqs
states = np.array([0.0, 0.0, pi, 0.5]) # Left to right, cart; position-velocity, pend mass; angle-angular velocity
tspan = np.arange(0, 10, dt)
state_sol = odeint(cart_pend_dynamics, states, tspan)
return state_sol
# Time Interval
dt = 0.01
solution = solution_of_cartpend(dt)
x_den, y_den = solution.shape
# Validating the solution
plt.axes(xlim=(0,10), ylim=(-10,10))
t = np.arange(0, 10, dt)
plt.gca().plot(t, (solution[:, 2]), 'b', label='theta1')
# Animating the figures
for i in range(x_den):
draw_cart_pend_rt.draw_cart(solution[i,:], m, M, L)

How to do animation using Zelle graphics module?

I need help to design my graphics, without turtle nor tkinter, but with Zelle graphics.py. The problem is that I need to run 4 circles moving at the same time. Here's the code I have so far:
from graphics import *
import time #import time module
from random import randrange
def rand_color():#generates a random color and returns that color
return(color_rgb(randrange(256),randrange(256),randrange(256)))
def main():
win = GraphWin("My Circle",500,500)
c = Circle(Point(20,20),20)
c.setFill(rand_color())
c.draw(win)
for i in range(1,461):
c.move(1,1)
time.sleep(.005)
c = Circle(Point(20,20),20)
c.setFill(rand_color())
c.draw(win)
for i in range(1,461):
c.move(-1,1)
time.sleep(.005)
c = Circle(Point(20,20),20)
c.setFill(rand_color())
c.draw(win)
for i in range(1,461):
c.move(1,-1)
time.sleep(.005)
c = Circle(Point(20,20),20)
c.setFill(rand_color())
c.draw(win)
for i in range(1,461):
c.move(1,1)
time.sleep(.005)
main()
I don't know how to move multiple objects at once. How would one go about this?
Rather move each circle completely in turn, chop up the movements and alternate them so each circle moves a little at a time in round robin. I'm guessing this is close to what you're trying to do:
from random import randrange
from graphics import *
def rand_color():
""" Generate a random color and return it. """
return color_rgb(randrange(256), randrange(256), randrange(256))
win = GraphWin("My Circle", 500, 500)
circles = []
for x in [-1, 1]:
for y in [-1, 1]:
circle = Circle(Point(250, 250), 20)
circle.setFill(rand_color())
circle.draw(win)
circles.append((circle, (x, y)))
for _ in range(250):
for circle, (x, y) in circles:
circle.move(x, y)
win.getMouse() # Pause to view result
win.close() # Close window when done

Remove a line from the window in Python Zelle Graphics

I have some code below that draws lines on a circle but the lines aren't deleted during each iteration. Does anyone know how to delete object from the window?
I tried win.delete(l) but it didn't work. Thanks.
import graphics
import math
win.setBackground("yellow")
x=0
y=0
x1=0
y1=0
P=graphics.Point(x,y)
r=150
win.setCoords(-250, -250, 250, 250)
for theta in range (360):
angle=math.radians(theta)
x1=r*math.cos(angle)
y1=r*math.sin(angle)
Q=graphics.Point(x1,y1)
l=graphics.Line(P,Q)
l.draw(win)
As far as I know, normally we draw things to some buffer memory, then draw the stuff in this buffer to the screen, what you said, to me, sounds like you draw the buffer to the screen, then delete the object from the buffer, I think this won't affect your screen.
I think you may need to redraw the part of the 'previous' line with background color, or just redraw the whole screen with what you really want.
I haven't used the graphics module, but hope my idea helpful to you.
Yeah I was in the same position, I found a good solution:
l.undraw()
You can check for more information here:
http://mcsp.wartburg.edu/zelle/python/graphics/graphics.pdf
Your code doesn't run as posted so let's rework it into a complete solution incorporating #oglox's undraw() suggestion:
import math
import graphics
win = graphics.GraphWin(width=500, height=500)
win.setCoords(-250, -250, 250, 250)
win.setBackground("yellow")
CENTER = graphics.Point(0, 0)
RADIUS = 150
line = None
for theta in range(360):
angle = math.radians(theta)
x = RADIUS * math.cos(angle)
y = RADIUS * math.sin(angle)
point = graphics.Point(x, y)
if line: # None is False in a boolean context
line.undraw()
line = graphics.Line(CENTER, point)
line.draw(win)
win.close()
This presents a somewhat wispy, flickering line. We can do slightly better by drawing and undrawing in the reverse order:
old_line = None
for theta in range(360):
angle = math.radians(theta)
x = RADIUS * math.cos(angle)
y = RADIUS * math.sin(angle)
point = graphics.Point(x, y)
new_line = graphics.Line(CENTER, point)
new_line.draw(win)
if old_line: # None is False in a boolean context
old_line.undraw()
old_line = new_line
This gives a thicker looking line and slightly less flicker.

Categories

Resources