Moving Figures
Python, Tkinter, Frame, Canvas
Script in Listing 1 displays figures, which are moving in the canvas (Figure 1).
Figure 1
Listing 1 Script for moving figures.
import tkinter as tk
import numpy as np
root = tk.Tk()
frame = tk.Frame(root)
button = tk.Button(frame, text =
'Close')
button.grid(row = 0, column = 10, padx
= 5, sticky = 'e')
def f_close(event):
"""
A
call_back or event handler function
"""
root.destroy()
print('Event...\n{}'.format(event))
print('Good-bye.')
#
# Bind the
event of clicking on the button to the function f_close
#
button.bind('<Button-1>',
f_close)
canvas = tk.Canvas(frame, bg =
'#ffffff', width = 500, height = 500)
canvas.grid(row = 1, columnspan = 11, rowspan
= 1)
frame.pack()
ctr_x = 250 # x-coordinate of the center of the
canvas
ctr_y = 250 # y-coordinare of the center of the
canvas
r0 = 120
# radius of the large
disc
# Angle of
rotation
class CAngle:
def __init__(self):
self.angle = 0.0
cangle = CAngle()
# A large
green disc created at the center
canvas.create_oval(ctr_x - r0, ctr_y -
r0, ctr_x + r0, ctr_y + r0,
fill = '#00ff00', tags = 'circle0')
r1 = 20
# The radius of the
small disc
r = r0 + r1 # The radius of the circle of the
trajectory of the small disc
x0 = ctr_x + r *
np.cos(cangle.angle) # The x-coordinate of the small disc
y0 = ctr_y + r *
np.sin(cangle.angle) # The y-coordinate of the small disc
#
The
small blue disc created
canvas.create_oval(x0 - r1, y0 - r1,
x0 + r1, y0 + r1, fill = '#0000ff', tags = 'circle1')
#
# Creating moving discs
#
def re_draw(cangle):
# Incrementing the angle, which is
kept not to be larger than 2*pi
cangle.angle += 0.1
if cangle.angle > 2 *
np.pi:
cangle.angle -= 2 * np.pi
# The large disc is rotated by the
amount of v_ang
scale = np.cos(cangle.angle)
# Delete the current disc
canvas.delete('circle0')
# Create the new disc
canvas.create_oval(ctr_x -
scale * r0, ctr_y - r0,
ctr_x + scale * r0, ctr_y + r0,
fill = '#00ff00', tags = 'circle0')
#
# Get the current position of the
small disc
#
coords = canvas.coords('circle1')
# coords = [x1, y1, x2, y2], which
are the corners of the box enclosing the disc
crrntx = (coords[0] +
coords[2]) / 2 # the x-coordinate of the center of
the disc
crrnty = (coords[1] +
coords[3]) / 2 # the y-coordinate of the center of
the disc
nxtx = ctr_x + r *
np.cos(cangle.angle)
# the new x-coordinate
nxty = ctr_y + r *
np.sin(cangle.angle)
# the new y-coordinate
# Move the disc from the current to
the new position
canvas.move('circle1', nxtx -
crrntx, nxty - crrnty)
root.after(100, re_draw,
cangle)
# Call this function
after 100msec
re_draw(cangle)
root.mainloop()
The large green disc is moved by deleting the current one and re-creating a new one.
canvas.delete('circle0')
canvas.create_oval(ctr_x -
scale * r0, ctr_y - r0,
ctr_x + scale * r0, ctr_y + r0,
fill = '#00ff00', tags = 'circle0')
The small blue disc is moved by the method canvas.move, which is executed by calling the function re_draw.
canvas.move('circle1', nxtx
- crrntx, nxty - crrnty)
These movements are repeated by calling the function after as follows.
root.after(100, re_draw,
cangle)
The above code sets the function re_draw(cangle) to be executed after 100 msec.
Figure 2
The form in Figure 2 has three buttons, Clockwise, Counter, and Stop, adding to Close button. When you click on these buttons, the direction of rotation changes or the movement stops. The script for Figure 2 is developed from the script of Listing 1, adding a few codes with minor changes. The complete script for Figure 2 is shown in listing 2 as follows.
Listing
2 Script for the window of Figure 2
import tkinter as tk
import numpy as np
root = tk.Tk()
frame = tk.Frame(root)
button = tk.Button(frame, text =
'Close')
button.grid(row = 0, column = 10, padx
= 5, sticky = 'ew')
buttonC = tk.Button(frame, text = 'Clockwise')
buttonC.grid(row = 0, column = 0, padx
= 2, sticky = 'ew')
buttonA = tk.Button(frame, text =
'Counter')
buttonA.grid(row = 0, column = 1, padx
= 2, sticky = 'ew')
buttonS = tk.Button(frame, text =
'Stop')
buttonS.grid(row = 0, column = 2, padx
= 2, sticky = 'ew')
def f_close(event):
"""
A
call_back or event handler function
"""
root.destroy()
print('Event...\n{}'.format(event))
print('Good-bye.')
#
# Bind the event
of clicking on the button to the function f_close
#
button.bind('<Button-1>',
f_close)
global step
# The amount of change
in angle for iteration
step = 0.1
def f_clock(event):
global step
step = 0.1 # clockwise rotation
buttonC.bind('<Button-1>',
f_clock)
def f_counter(event):
global step
step = -0.1 # counter clockwise rotation
buttonA.bind('<Button-1>',
f_counter)
def f_stop(event):
global step
step = 0.0 # no rotation
buttonS.bind('<Button-1>',
f_stop)
canvas = tk.Canvas(frame, bg =
'#ffffff', width = 500, height = 500)
canvas.grid(row = 1, columnspan = 11,
rowspan = 1)
frame.pack()
ctr_x = 250 # x-coordinate of the center of the
canvas
ctr_y = 250 # y-coordinare of the center of the
canvas
r0 = 120
# radius of the large
disc
# A class of rotation angle
class CAngle:
def __init__(self):
self.angle = 0.0
cangle = CAngle() # The object of CAngle
# A large
green disc created at the center
canvas.create_oval(ctr_x - r0, ctr_y -
r0, ctr_x + r0, ctr_y + r0,
fill = '#00ff00', tags = 'circle0')
r1 = 20
# The radius of the
small disc
r = r0 + r1 # The radius of the circle of the
trajectory of the small disc
x0 = ctr_x + r *
np.cos(cangle.angle) # The x-coordinate of the small disc
y0 = ctr_y + r *
np.sin(cangle.angle) # The y-coordinate of the small disc
# The small
blue disc created
canvas.create_oval(x0 - r1, y0 - r1, x0
+ r1, y0 + r1, fill = '#0000ff', tags = 'circle1')
#
# Creating moving discs
#
def re_draw(cangle):
global step
#
# The next
angle is set
#
cangle.angle += step
if cangle.angle > 2 *
np.pi: # angle is kept not to be larger
than 2 * pi
cangle.angle -= 2 * np.pi
if cangle.angle < -2 *
np.pi:
cangle.angle += 2 * np.pi
# angle is kept not to
be smaller than -2 * pi
# The large disc is rotated by the
amount of v_ang
scale = np.cos(cangle.angle)
# Delete the current disc
canvas.delete('circle0')
# Create the new disc
canvas.create_oval(ctr_x -
scale * r0, ctr_y - r0,
ctr_x + scale * r0, ctr_y + r0,
fill = '#00ff00', tags = 'circle0')
#
# Get the current position of the
small disc
#
coords =
canvas.coords('circle1')
# coords = [x1, y1, x2, y2], which
are the corners of the box enclosing the disc
crrntx = (coords[0] +
coords[2]) / 2 # the x-coordinate of the center of
the disc
crrnty = (coords[1] +
coords[3]) / 2 # the y-coordinate of the center fo
the disc
nxtx = ctr_x + r *
np.cos(cangle.angle)
# the new x-coordinate
nxty = ctr_y + r *
np.sin(cangle.angle) # the new y-coordinate
# Move the disc from the current to
the new position
canvas.move('circle1', nxtx
- crrntx, nxty - crrnty)
root.after(100, re_draw,
cangle)
# Call this function
after 100msec
re_draw(cangle)
root.mainloop()
The script of Listing 2 has three more buttons than that of Listing1 as follows
buttonC = tk.Button(frame, text =
'Clickwise')
buttonC.grid(row = 0, column = 0, padx
= 2, sticky = 'ew')
buttonA = tk.Button(frame, text =
'Counter')
buttonA.grid(row = 0, column = 1, padx
= 2, sticky = 'ew')
buttonS = tk.Button(frame, text =
'Stop')
buttonS.grid(row = 0, column = 2, padx = 2, sticky = 'ew')
These buttons are bound to the functions, which control rotation by setting the amount of change in rotation angle.
def f_clock(event):
global step
step = 0.1 # clockwise rotation
buttonC.bind('<Button-1>',
f_clock)
def f_counter(event):
global step
step = -0.1 # counter clockwise rotation
buttonA.bind('<Button-1>', f_counter)
def f_stop(event):
global step
step = 0.0 # no rotation
buttonS.bind('<Button-1>', f_stop)