So I followed the instructions here on how to take two inputs at the same time to execute a different function than either key press individually.
My code doesn't want to work but only the function that requires two key presses ('w', 'a'): upLeft and I can't figure out why. I tested my function in the provided solution and it worked. I can't seem to see any tangible differences between what I have here and what is provided in the link.
import turtle
import time
# Draw initial screen
wn = turtle.Screen()
wn.title("Cool Game Bro")
wn.bgcolor("grey")
wn.setup(width=800, height=600)
wn.tracer(0)
# main player character
character = turtle.Turtle()
character.speed(0)
character.shape("circle")
character.color("yellow")
character.penup()
character.goto(0, 0)
def process_events():
events = tuple(sorted(key_events))
if events and events in key_event_handlers:
(key_event_handlers[events])()
key_events.clear()
wn.ontimer(process_events, 200)
def Up():
key_events.add('w')
def Left():
key_events.add('a')
def Down():
key_events.add('s')
def Right():
key_events.add('d')
def charUp():
y = character.ycor()
y += 10
character.sety(y)
def charLeft():
x = character.xcor()
x -= 10
character.setx(x)
def charRight():
x = character.xcor()
x += 10
character.setx(x)
def charDown():
y = character.ycor()
y -= 10
character.sety(y)
def upLeft():
y = character.ycor()
y+=10
character.sety(y)
x = character.xcor()
x -=10
character.setx(x)
key_event_handlers = { \
('w',): charUp,\
('a',): charLeft,\
('d',): charRight,\
('s',): charDown,\
('w', 'a'): upLeft,\
}
key_events = set()
wn.onkeypress(Up, 'w')
wn.onkeypress(Left, 'a')
wn.onkeypress(Right, 'd')
wn.onkeypress(Down, 's')
wn.listen()
process_events()
while True:
wn.update()
Appreciate the help!
A set stores things alphabetically. Use
('a','w'): upLeft
and it will work fine.
BTW1, you could have learned this if you had just added print(events) in your process_events function, as I did.
BTW2, you don't need backslashes in your definition. Anything contained in parens, square brackets, or curly brackets can automatically be continued onto another line.
Related
The code is part of a program for moving an avatar "X" around a room, but I can't get past the initialization of the X and the room:
import curses
from curses import wrapper
def main(stdscr):
test = curses.newpad(30, 50)
test.border()
test.noutrefresh(0,0,20,70,66,239)
guy = Agent([32, 100], "X")
guy.move(stdscr,1,1)
stdscr.noutrefresh()
curses.doupdate()
class Agent():
def __init__(self, position, avatar):
self.position = postion
self.avatar = avatar
def move(self, stdscr, direction, distance):
stdscr.addstr(self.position[0], self.position[1], self.avatar)
while True:
curses.wrapper(main)
curses.curs_set(0)
input()
I've tried all sorts of permutations, with essentially the same result; either the X prints before the room borders, or else it doesn't show up at all
I'm guessing that I am probably misunderstanding something fundamental, like inheritance of classes or something, but I can't think what.
I want to create a game about guessing the Mexico states, so i want the coordinates of the map be saved within a dictionary. My problem is that the for loop doesn't work properly because this program only prints the first thing in the dictionary and doesn't iterate through the entire dictionary.
# pylint: disable=E1101
import pandas
import turtle
#Adding the shape to the turtle then printing the map on the screen
screen = turtle.Screen()
screen.title('Estados de Mexico')
image = "map.gif"
screen.addshape(image)
turtle.shape(image)
#importing the names
states_data = pandas.read_csv('datos.csv')
names_dict={}
#global x and global y
gx = 0
gy = 0
def get_mouse_click_coor(x, y):
global gx
global gy
gx = x
gy = y
print(x,y)
for name in states_data['estado']:
print(name)
turtle.onscreenclick(get_mouse_click_coor)
names_dict[name] = [gx, gy]
turtle.mainloop()
turtle.onscreenclick(None)
onscreenclick() doesn't work as you expect.
It only assigns function to mouse and later mainloop() will execute this function when you click mouse button. It doesn't block code and it doesn't wait for your click at this moment.
You can run it only once and you have to do all inside get_mouse_click_coor()
Minimal working code (but without image)
import pandas
import turtle
def get_mouse_click_coor(x, y):
global name_index
if name_index < len(names):
# add coordinates with next `name` from
name = names[name_index]
names_dict[name] = [x, y]
print(name, x, y)
name_index += 1
else:
# close window
turtle.bye()
print(names_dict) # it will print after closing window
# --- main ---
names_dict = {}
#states_data = pandas.read_csv('datos.csv')
#names = states_data['estado'].to_list()
names = ['a', 'b', 'c']
name_index = 0
# assign function to mouse button
turtle.onscreenclick(get_mouse_click_coor)
# run loop which gets mouse/key events from system, sends them to widgets, and it will execute `get_mouse_click_coor` when you click mouse
turtle.mainloop()
# it will print after closing window
#print(names_dict)
Result:
a -267.0 116.0
b 426.0 -136.0
c 437.0 139.0
{'a': [-267.0, 116.0], 'b': [426.0, -136.0], 'c': [437.0, 139.0]}
The first function calls to make the tic tac toe board, and the code before creates the screen and turtle.
#import needed modules
import turtle
#make a screen
ttts = turtle.Screen()
#set up the pen
d = turtle.Turtle()
d.color("indigo")
d.pensize(4)
d.speed(5)
d.penup()
#make number list
number_list = ["1","2","3","4","5","6","7","8","9"]
current_number = "1","2","3","4","5","6","7","8","9"
#set beginning position
d.goto(-150,100)
#make square (function)
def square(*squares):
squares = 0
columns = 0
rows = 0
x = -75
y = 100
while (squares < 4):
d.pendown()
d.forward(225)
d.right(90)
squares = squares + 1
d.right(90)
while (columns < 2):
d.goto(x,y)
d.pendown()
d.forward(225)
d.penup()
x = x + 75
columns = columns + 1
d.right(90)
while (rows < 3):
d.goto(x,y)
d.pendown()
d.forward(225)
d.penup()
y = y + -75
rows = rows + 1
square()
This code above is fine, I have made sure to debug all the functions below as well. I'm just having trouble calling the functions at the end.
#make the numbers
def numbers():
d.penup()
d.color("yellow")
d.goto(-120, 50)
d.write(1, align='left', font = ('arial', 16))
d.goto(-45, 50)
d.write(2, align='left', font = ('arial', 16))
d.goto(30, 50)
d.write(3, align='left', font = ('arial', 16))
d.goto(-120, -25)
d.write(4, align='left', font = ('arial', 16))
d.goto(-45, -25)
d.write(5, align='left', font = ('arial', 16))
d.goto(30, -25)
d.write(6, align='left', font = ('arial', 16))
d.goto(-120, -100)
d.write(7, align='left', font = ('arial', 16))
d.goto(-45, -100)
d.write(8, align='left', font = ('arial', 16))
d.goto(30, -100)
d.write(9, align='left', font = ('arial', 16))
d.hideturtle()
numbers()
#function for defining symbols(x and o)
def draw_symbol(number_list):
number_list
symbolc()
symbolxs()
#functions for x and o
c = turtle.Turtle()
xs = turtle.Turtle()
def symbolc(circle):
circle = 0
def check_number_1():
if (current_number == "1"):
c.goto(-120,70)
ttts.onkeypress(check_number_1, "1")
def check_number_2():
if (current_number == "2"):
c.goto(-45,70)
ttts.onkeypress(check_number_2, "2")
def check_number_3():
if (current_number == "3"):
c.goto(30,70)
ttts.onkeypress(check_number_3, "3")
def check_number_4():
if (current_number == "4"):
c.goto(-120,-5)
ttts.onkeypress(check_number_4, "4")
def check_number_5():
if (current_number == "5"):
c.goto(-45,-5)
ttts.onkeypress(check_number_5, "5")
def check_number_6():
if (current_number == "6"):
c.goto(30,-5)
ttts.onkeypress(check_number_6, "6")
def check_number_7():
if (current_number == "7"):
c.goto(-120,-80)
ttts.onkeypress(check_number_7, "7")
def check_number_8():
if (current_number == "8"):
c.goto(-45,-80)
ttts.onkeypress(check_number_8, "8")
def check_number_9():
if (current_number == "9"):
c.goto(30,-80)
ttts.onkeypress(check_number_9, "9")
#--- main ---
if (circle < 1):
c.penup()
c.color("red")
c.circle(20)
circle = circle + 1
#crosses
def symbolxs(cross):
cross = 0
while (cross < 4):
def check_number_1():
if (current_number == "1"):
xs.goto(-120,70)
ttts.onkeypress(check_number_1, "1")
def check_number_2():
if (current_number == "2"):
xs.goto(-45,70)
ttts.onkeypress(check_number_2, "2")
def check_number_3():
if (current_number == "3"):
xs.goto(30,70)
ttts.onkeypress(check_number_3, "3")
def check_number_4():
if (current_number == "4"):
xs.goto(-120,-5)
ttts.onkeypress(check_number_4, "4")
def check_number_5():
if (current_number == "5"):
xs.goto(-45,-5)
ttts.onkeypress(check_number_5, "5")
def check_number_6():
if (current_number == "6"):
xs.goto(30,-5)
ttts.onkeypress(check_number_6, "6")
def check_number_7():
if (current_number == "7"):
xs.goto(-120,-80)
ttts.onkeypress(check_number_7, "7")
def check_number_8():
if (current_number == "8"):
xs.goto(-45,-80)
ttts.onkeypress(check_number_8, "8")
def check_number_9():
if (current_number == "9"):
xs.goto(30,-80)
ttts.onkeypress(check_number_9, "9")
xs.penup()
xs.color("lime")
xs.right(45)
xs.pendown()
xs.forward(10)
cross = cross + 1
#turns - Circles goes first always. I hope you know how to play tic tac toe.
c.stamp()
xs.stamp()
c.stamp()
xs.stamp()
c.stamp()
xs.stamp()
c.stamp()
xs.stamp()
c.stamp()
ttts.listen()
turtle.mainloop()
If you see an immediate error, please comment on enter code hereit. However, keep in mind that this is code made by a very young (early teen) student. Thanks a bunch!
ps, sorry if the code looks bad on this. I do not know how to use this app, as it is my first question here. :)
Ok, some things to think about when coding:
OOP (Object orientated programming):
OOP is creating your own object which will make your code cleaner and mean you don't use excessive global variables when using a structured/function approach.
In python, OOP is done by creating a class:
# OOP Example
class MyObject:
def __init__(self): # this is the constructor class and it is called when the object is initialised
# Below are two types of useful variable types that can be accessed in the object. They are properties of the object and act as global variables across all of the functions in the class (with the exception of static functions)
self.example_public_variable = None # public variable (can be accessed from the object variable
self._example_private_variable = None # private variable (can only be accessed from within the object)
# This is a function as part of the object and can be called from an object instance. It takes one parameter that can be passed in. A parameter is a variable that the function can access from the script that has called it.
def example_function(self, param):
pass
obj = MyObject() # I am creating a variable of my new object type which is calling the __init__ function. NOTE: the self parameter is the current instance of the object and is automatically passed in.
var = obj.example_public_variable # accessing the public variable
obj.example_function() # calling an object's function
Object Orientated programming is very helpful in keeping your code neat and can be used in your case like this:
class Game:
def __init__(self):
# do the turtle initialisation, window drawing and listener binds
def _on_right_click(self, event):
# handle right click event
# ... (and so on)
if __name__ == "__main__": # clean way of starting your executable code
Game()
OOP is quite advanced and you can find help here:
https://www.geeksforgeeks.org/introduction-of-object-oriented-programming/
https://realpython.com/python3-object-oriented-programming/
IDE (Integrated Development Environment):
It is significantly easier to code with help, and luckily, there are special text editors dedicated to this. IDEs check for syntax errors, tell you how you could improve the look and style of your code and it tells you how to follow PEP. I would recommend Pycharm for python. Jetbrains also do a student programme where you can get the pro version for free as well as their other tools which could help you if you move on to a more complicated language.
Pycharm: https://www.jetbrains.com/pycharm/
Nested functions:
I see you have used a lot of nested functions which is not best practice because it negates the need for functions in the first place as they are for cleaning up your code and making pieces of code accessible again and again.
Global Variables:
If you don't want to use OOP, then try to stay away from too many global variables. These litter your namespace and make it harder to keep track of the variables your current function is interested in. Try to have all external variables passed in as parameters. This also makes it easier for you to keep track of the variables needed to call a function and reduces your chances of coming across one of those annoying bugs that don't throw an error.
Don't repeat code:
A rule I go by is: 'if I have to rewrite the code, even once, throw it into a function. It is cleaner and easier to understand.'
Use __name__:
This is more for when you start writing libraries or you have multiple files, use the statement if __name__ == "__main__": before writing any code that is run when the file is run. It doesn't litter the namespace when imported and means the user that imports your code doesn't call any functions that they don't want to.
Create meaningful variable names:
A lot of your variables do not describe what they contain (e.g. c, d, tttk). To make it easier for yourself, name variables appropriately.
This will all make your code easier to debug and read.
Really new to Python and I'm stuck. I can't figure out how to get HP and DMG to randomize when it's called when the button I've created is clicked.
Here's currently what I have:
# find your fav character images and pass it here
Char1 = Character('Snart.png','CAPTAIN COLD',DISPLAYSURF,(100,300),200)
Char2 = Character('Flash.png','FLASH',DISPLAYSURF,(700,300),200)
def displayButtons(bList):
for x in bList:
x.display()
def main():
B1.active = True
clickCount = 1
B2.active = False
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
## MOUSE EVENTS
elif event.type == MOUSEBUTTONDOWN:
mouse = pygame.mouse.get_pos()
if B1.clicked(mouse):
B1.highlight = True
print("Hello") ## Just to see if it actually get's pressed
clickCount = 2
elif B2.clicked(mouse):
B2.highlight = True
print("Bye") ## Just to see if it actually get's pressed
clickCount = 1
elif event.type == MOUSEBUTTONUP:
if B1.clicked(mouse):
Char1.Randomize() ## Randomize the HP DMG
B1.highlight = False
B1.active = False
B2.active = True
elif B2.clicked(mouse):
Char2.Randomize() ## Randomize the HP DMG
B2.highlight = False
B2.active = False
B1.active = True
Char1.display()
Char2.display()
displayButtons(BUTTONLIST)
pygame.display.update()
main()
And for the class that it's creating:
class Character(object):
def __init__(self, imagefile,charname,surf,pos,scalesize):
self.SURF = surf
self.POS = pos
self.IMAGESURF = pygame.image.load(imagefile)
self.IMAGESURF = pygame.transform.scale(self.IMAGESURF, (scalesize,scalesize))
self.HP = (0, 300) # should range from (0 - 300) ## randint: return a random integer(start,stop)
self.DMG = (0, 100) # should range from (0 - 100)
self.GameFont = pygame.font.SysFont("Sylfaen", 50)
# this text has a black background. Can you make it transparent ?. DONE
self.NAME = self.GameFont.render(charname, True,(255,255,255),None)
self.Randomize()
self.__drawText()
self.__displayText()
# complete this function
# this function should randomize HP, DMG and should display on the screen
# this function should be called on a button press
def Randomize(self):
#pass
self.HP = randint(0, 300)
self.DMG = randint(0, 300)
## DON'T UNCOMMENT UNLESS YOU WANT IT TO RANDOMLY GENERATE NON-STOP
## self.HPText = self.GameFont.render('HP : ' +str(self.HPrand), True,(255,255,255),None)
## self.DMGText = self.GameFont.render('DMG: ' +str(self.DMGrand), True,(255,255,255),None)
def __displayText(self):
self.SURF.blit(self.HPText,(self.POS[0]+200,self.POS[1]+50))
self.SURF.blit(self.DMGText,(self.POS[0]+200,self.POS[1]+150))
self.SURF.blit(self.NAME,(self.POS[0]+20,self.POS[1]-100))
# fix the error in this function, DONE
def __drawText(self):
# this text has a black background. Can you make it transparent ?.
self.HPText = self.GameFont.render('HP : ' +str(self.HP), True,(255,255,255),None)
self.DMGText = self.GameFont.render('DMG: ' +str(self.DMG), True,(255,255,255),None)
# fix the errors in this function, DONE
def display(self):
self.Randomize()
self.__displayText()
self.SURF.blit(self.IMAGESURF,self.POS)
After you randomize the HP and DMG values, you need to re-render the text values for each of them. You have a function to do that, named __drawText, but you're not calling it when the button is pressed. This is why you keep drawing the old values even after Randomize has been called.
I'm not sure exactly how you want your class to work, but perhaps __drawText should be called from Randomize? You can't rely upon the external code that that runs Randomize to also call __drawText since you've given it a name starting with two underscores (which invokes Python's name mangling system). If it's supposed to be part of the class API, you certainly don't want to be doing that. External code can still call __drawText, but only by manually doing the mangling (and calling e.g. Char1._Character__drawText).
One final thing, which is unrelated to your current issues. Your variables are being named in a way that is a bit unusual for Python code. The more usual Python style is to use lowercase_with_underscores names for most variables and functions (including methods), and reserve TitleCase names for classes. ALL_CAPS is used occasionally for variables that are notionally constant, but the regular variable style is also pretty common even for constants (e.g. math.pi).
Using a different naming convention doesn't make your code wrong, but it may be harder for other programmers to follow than if you followed the standard conventions. See PEP 8 for the style used for the official Python interpreter. A lot of other Python code follows that guide (with perhaps a little more leeway given on line lengths). Google also has a Python style guide, which is (as far as I can tell) pretty much compatible with PEP 8.
I am trying to do an animation of a Particle Swarm Optimization using Python and Mayavi2.
The animation is working fine, my problem is that it is not possible to interact with the plot while it is animating the movement. Specifically i would like to turn the graph and zoom. Maybe someone has experience doing animations?
The way i do it is first to calculate the positions of the particles and then to store them. After the calculation is finished i plot the positions of the particle at the first instace of time with point3d() and then i iterate through time updating the data using the set() method.
Is there a way to make it possible to turn the graph? I have heard about something with threads, disabeling the the rendering, but i could not figure out how to do it in my code. Besides lots of other stuff, I have read:
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/mlab_animating.html
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/tips.html#acceleration-mayavi-scripts
but it can't see how to use it.
Any suggestions?
Here is my code:
#!/usr/bin/env python
'''
#author rt
'''
import pylab as plt
from numpy import *
from mayavi import mlab
from threading import Thread # making plotting faster?
import ackley as ac
class Swarm(Thread, object):
'''
constructor for the swarm
initializes all instance variables
'''
def __init__(self,objective_function):
Thread.__init__(self)
# optimization options
self.omega = 0.9 # inertial constant
self.c1 = 0.06 # cognitive/private constant
self.c2 = 0.06 # social constant
self.objective = objective_function # function object
self.max_iteration = 100 # maximal number of iterations
# Swarm stuff
self.number = 0
self.best = [] # gbest; the global best position
self.particles = [] # empty list for particles
# temporary
self.min = self.objective.min
self.max = self.objective.max
self.best_evolution = []
# self.dimensions = 2 # dimensions NB!
'''
add particles to the swarm
find the best position of particle in swarm to set global best
'''
def add_particles(self, n):
for i in range(n):
particle = Particle(self)
if i == 0: # initialize self.best
self.best = particle.position
if particle.eval() < self._eval(): # check if there is a better and if, set it
self.best = copy(particle.position)
self.particles.append(particle) # append the particle to the swarm
def _eval(self):
return self.objective.evaluate(self.best)
def plot(self):
for i in range(self.max_iteration):
pos_x = []
pos_y = []
pos_z = []
#print pos_x
for particle in self.particles:
[x,y,z] = particle.trail[i]
pos_x.append(x)
pos_y.append(y)
pos_z.append(z)
#print pos_x
if i ==0:
g = mlab.points3d(pos_x, pos_y,pos_z, scale_factor=0.5)
ms =g.mlab_source
ms.anti_aliasing_frames = 0
ms.set(x=pos_x, y = pos_y, z = pos_z,scale_factor=0.5) #updating y value
#print pos_y
#ms.set(x=pos_x) # update x values
#ms.set(y=pos_y) #updating y value
#ms.set(z=pos_z) #updating y value
#for p in self.particles:
#p.plot()
def plot_objective(self):
delta = 0.1
v = mgrid[self.min:self.max:delta,self.min:self.max:delta]
z = self.objective.evaluate(v)
#mlab.mesh(v[0],v[1],z)
mlab.surf(v[0],v[1],z) # surf creates a more efficient data structure than mesh
mlab.xlabel('x-axis', object=None)
mlab.ylabel('y-axis', object=None)
mlab.zlabel('z-axis', object=None)
def _info(self):
self.plot()
print '----------------------------'
print 'The best result is:'
print 'Coordinates:', self.best
print 'Value: ', self._eval()
#print 'with ', nreval, 'evaluations'
print 'nr of particles: ', len(self.particles)
print '----------------------------'
def run(self):
self.plot_objective()
self.best = self.particles[0].get_position()
iteration = 0
while iteration < self.max_iteration:
#if iteration!= 0: obj.scene.disable_render = True
#disable_render = True
for particle in self.particles:
rnd_c1 = array([random.uniform(0,1),random.uniform(0,1)])
rnd_c2 = array([random.uniform(0,1),random.uniform(0,1)])
particle.velocity = self.omega * array(particle.velocity) + \
self.c1 * rnd_c1 * (array(particle.best) - array(particle.position)) + \
self.c2 * rnd_c2 * (array(self.best) - array(particle.position)) # TODO: change so independent rnd for components
particle.position = array(particle.position) + particle.velocity
if particle.eval() < particle.best_eval():
particle.best = copy(particle.position)
if particle.eval() < self._eval():
self.best = copy(particle.position)
particle.update() # add the point to the trail
iteration +=1
self.best_evolution.append(self._eval())
#obj.scene.disable_render = False
print 'finished: ', iteration
self._info()
'''
Class modeling particle
'''
class Particle():
def __init__(self, swarm):
self.swarm = swarm
x_rand = random.uniform(self.swarm.min,self.swarm.max)
y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.position = array([x_rand,y_rand])
v_x_rand = random.uniform(self.swarm.min,self.swarm.max)
v_y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.velocity = array([v_x_rand, v_y_rand])
self.size = 0.5
self.best = self.position
# visualization
self.trail = []
def plot(self):
[x,y] = self.position
z = self.eval()
mlab.points3d(x,y,z,scale_factor=self.size)
def eval(self):
return self.swarm.objective.evaluate(self.position)
def best_eval(self):
return self.swarm.objective.evaluate(self.best)
def get_position(self):
return self.position
def update(self):
[x,y] = self.position
z = self.eval()
#print [x,y,z]
self.trail.append([x,y,z])
def plot_trail(self,index):
[x,y,z] = self.trail[index]
mlab.points3d(x,y,z,scale_factor=self.size)
# Make the animation
mlab.figure(1, bgcolor=(0, 0, 0), size=(1300, 700)) # create a new figure with black background and size 1300x700
objective = ac.Ackley() # make an objective function
swarm = pso.Swarm(objective) # create a swarm
nr_of_particles = 25 # nr of particles in swarm
swarm.add_particles(nr_of_particles)
swarm.run()
#swarm.start()
mlab.show()
print '------------------------------------------------------'
print 'Particle Swarm Optimization'
#objective.info()
print 'Objective function to minimize has dimension = ', objective.get_dimension()
print '# of iterations = ', 1000
print '# of particles in swarm = ', nr_of_particles
print '------------------------------------------------------'
In my case, even though I was somewhat able to do what Brandon Rhodes suggested for a mock program (https://stackoverflow.com/questions/16617814/interacting-with-mlab-scene-while-it-is-being-drawn), I could not manage to convert my already existing larger program.
Then I found this link: http://wiki.wxpython.org/LongRunningTasks
So, I just sprinkled a lot of wx.Yield() s inside my loops. This way I did not need to change my program structure, and I am able to interact with the window. I think better ways are explained in the link.
Your problem is that the wx event loop, which runs the Mayavi GUI window and listens for mouse clicking and dragging and responds by moving the scene, is not getting any time to run during your animation because you are keeping Python captive in your loop without ever letting it return control.
Instead of keeping control of the program with a loop of your own, you need to create a wx.Timer class that advances the scene by one frame update, and that then returns control to the wx event loop after scheduling itself again. It will look something like this:
import wx
...
class Animator(wx.Timer):
def Notify(self):
"""When a wx.Timer goes off, it calls its Notify() method."""
if (...the animation is complete...):
return
# Otherwise, update all necessary data to advance one step
# in the animation; you might need to keep a counter or
# other state as an instance variable on `self`
# [DATA UPDATE GOES HERE]
# Schedule ourselves again, giving the wx event loop time to
# process any pending mouse motion.
self.Start(0, oneShot=True) # "in zero milliseconds, call me again!"
I played with slightly higher values like 1 for the number of milliseconds that wx gets to run the UI with, but could not really tell a difference between that and just choosing 0 and having control returned "immediately".