I want to build some visualizations for searching algorithms (BFS, A* etc.) within a grid.
My solution should show each step of the algorithm using CodeSkulptor simplegui (or the offline version using SimpleGUICS2Pygame.)
I've made a version which highlights all the cells visited by changing their color, but I've run into trouble trying to make the path display step-by-step with a time delay between each step.
I've extracted the essence of the problem and created a minimal example representing it in the code below, also run-able online here: http://www.codeskulptor.org/#user47_jB2CYfNrH2_2.py
What I want is during the change_colors() function, for there to be a delay between each iteration.
CodeSkulptor doesn't have time.sleep() available, and I don't think it would help anyway.
CodeSkulptor does have timers available, which might be one solution, although I can't see how to use one in this instance.
Code below:
import time
try:
import simplegui
except ImportError:
import SimpleGUICS2Pygame.simpleguics2pygame as simplegui
simplegui.Frame._hide_status = True
TITLE = "TEST"
FRAME_WIDTH = 400
FRAME_HEIGHT = 400
DELAY = 10
class Square:
"""This class represents a simple Square object."""
def __init__(self, size, pos, pen_size=2, pen_color="red", fill_color="blue"):
"""Constructor - create an instance of Square."""
self._size = size
self._pos = pos
self._pen_size = pen_size
self._pen_color = pen_color
self._fill_color = fill_color
def set_color(self, color):
self._fill_color = color
def get_color(self):
return self._fill_color
def is_in(self, pos):
"""
Determine whether coordinates are within the area of this Square.
"""
return self._pos[0] < pos[0] < self._pos[0] + self._size and self._pos[1] < pos[1] < self._pos[1] + self._size
def draw(self, canvas):
"""
calls canvas.draw_image() to display self on canvas.
"""
points = [(self._pos[0], self._pos[1]), (self._pos[0] + self._size, self._pos[1]),
(self._pos[0] + self._size, self._pos[1] + self._size), (self._pos[0], self._pos[1] + self._size)]
canvas.draw_polygon(points, self._pen_size, self._pen_color, self._fill_color)
def __str__(self):
return "Square: {}".format(self._pos)
def draw(canvas):
for square in squares:
square.draw(canvas)
def change_colors():
for square in squares:
# time.sleep(1) # Not implemented in CodeSkulptor and would'nt work anyway
square.set_color("green")
frame = simplegui.create_frame(TITLE, FRAME_WIDTH, FRAME_HEIGHT)
frame.set_draw_handler(draw)
width = 20
squares = []
for i in range(10):
squares.append(Square(width, (i * width, 0)))
change_colors()
frame.start()
Any help appreciated.
Yes, you need to use a timer. Something like this:
I = 0
def change_next_color():
if I < len(squares):
squares[I].set_color("green")
global I
I += 1
timer = simplegui.create_timer(1000, change_next_color)
timer.start()
http://www.codeskulptor.org/#user47_udyXzppCdw2OqdI.py
I also replaced
simplegui.Frame._hide_status = True
by simplegui.Frame._hide_controlpanel = True
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._hide_controlpanel
See also _keep_timers option of SimpleGUICS2Pygame to help you:
https://simpleguics2pygame.readthedocs.io/en/latest/simpleguics2pygame/frame.html#SimpleGUICS2Pygame.simpleguics2pygame.frame.Frame._keep_timers
Possible improvements:
Find a better solution that don't use a global counter.
Stop timer when all work is finished.
Related
im currently playing around with pythons turtle module and im trying to make a grid of opaque square shapes, say 30x30 that can change color based on some property (doesn't matter what property) my question is, is there anyway to change the color of a shape once its already been drawn down on the canvas?
Ive tried adding all the square shapes to an array, both stamps and polygons, but it seems impossible to change the color of any of them once they have been drawn.
i know stamp doesn't work because its like a footprint of where the turtle was but is there any method at all that allows for this with polygons or some other method im not aware of?
I didn't add any snippets of code because its a pretty basic question and can be used for many things.
Yes, you can do it. The key to this, and many complicated turtle problems, is using stamps. They are individually, or collectively, removable. And since they take on the shape of the turtle itself, they can be images or arbitrary polygons of any size or color you wish:
from turtle import Turtle, Screen
from random import randrange, choice
from collections import namedtuple
from math import ceil
GRID = 15 # GRID by GRID of squares
SIZE = 30 # each square is SIZE by SIZE
INCREASE = 1.5 # how much to lighten the square's color
WHITE = [255, 255, 255] # color at which we stop changing square
DELAY = 100 # time between calls to change() in milliseconds
DARK = 32 # range (ceil(INCREASE) .. DARK - 1) of dark colors
def change():
block = choice(blocks)
blocks.remove(block)
color = [min(int(primary * INCREASE), WHITE[i]) for i, primary in enumerate(block.color)] # lighten color
turtle.color(color)
turtle.setposition(block.position)
turtle.clearstamp(block.stamp)
stamp = turtle.stamp()
if color != WHITE:
blocks.append(Block(block.position, color, stamp)) # not white yet so keep changing this block
if blocks: # stop all changes if/when all blocks turn white
screen.ontimer(change, DELAY)
HALF_SIZE = SIZE // 2
screen = Screen()
screen.colormode(WHITE[0])
screen.register_shape("block", ((HALF_SIZE, -HALF_SIZE), (HALF_SIZE, HALF_SIZE), (-HALF_SIZE, HALF_SIZE), (-HALF_SIZE, -HALF_SIZE)))
screen.tracer(GRID ** 2) # ala #PyNuts
turtle = Turtle(shape="block", visible=False)
turtle.speed("fastest")
turtle.up()
Block = namedtuple('Block', ['position', 'color', 'stamp'])
blocks = list()
HALF_GRID = GRID // 2
for x in range(-HALF_GRID, HALF_GRID):
for y in range(-HALF_GRID, HALF_GRID):
turtle.goto(x * SIZE, y * SIZE)
color = [randrange(ceil(INCREASE), DARK) for primary in WHITE]
turtle.color(color)
blocks.append(Block(turtle.position(), color, turtle.stamp()))
screen.ontimer(change, DELAY)
screen.exitonclick()
The idea of the Turtle is that images, once drawn, become pixels, commited to the canvas - even though the Turtle commands themselves are "vector" commands, when one is talking about "vectorial" vs "raster" graphics.
That means the Turtle drawing context can't know about what is already drawn "by itself" - there is no way for you to reference an already drawn shape, or polygon and change its properties. (update: but for the stamps - the Turtle object does record information about those - thanks #cdlane)
What you'd have to do,in this case, is to annotate on your code data about any shapes you'd like to further modify - and them redraw them in someway, when you need it. In other words: you develop code for a vector graphics model -which allows you to do that.
However, you should note, that the tkinter Canvas widget, on top of which the Turtle runs (at least, I believe), is itself a Vector model - so it might be more appropriate for what you have in mind. Its documentation and way of working may be hard to grasp, though.
So, going back to "having your own Vector graphics implementation", you could take advantage of Python Object Orientation and data model to build "history-turtle" - which could record a log and replay blocks of commands within the same position context (but allowing for different color, width, etc, ... settings).
It is a bitty tricker done than said, but then, here is a sample one:
from turtle import Turtle
"""
Python Turtle with logging and replay capabilities
Author: João S. O. Bueno <gwidion#gmail.com>
License: LGPL 3.0+
This implements HistoryTurtle - a subclass of
Python's turtle.Turtle wich features a simple
command history and new `replay` and `log`
methods -
`turtle.replay(<start>, <end>)` will reissue
the commands logged in those positions of the history,
but with the current Pen settings.
The optional kwarg `position_independent` can be passed
as `True` to `replay` so that the turtle's position
and heading are not restored to the ones recorded
along with the command.
https://gist.github.com/jsbueno/cb413e985747392c460f39cc138436bc
"""
class Command:
__slots__ = ( "method_name", "args", "kwargs", "pos", "heading")
def __init__(self, method_name, args=(),
kwargs=None, pos=None, heading=None):
self.method_name = method_name
self.args = args
self.kwargs = kwargs or {}
self.pos = pos
self.heading = heading
def __repr__(self):
return "<{0}> - {1}".format(self.pos, self.method_name)
class Instrumented:
def __init__(self, turtle, method):
self.turtle = turtle
self.method = method
def __call__(self, *args, **kwargs):
command = Command(self.method.__name__, args, kwargs,
self.pos(), self.heading())
result = self.method(*args, **kwargs)
if (self.method.__name__ not in self.turtle.methods_to_skip or
not kwargs.pop('skip_self', True)):
self.turtle.history.append(command)
return result
def pos(self):
return self.turtle._execute(Command('pos'), True)
def heading(self):
return self.turtle._execute(Command('heading'), True)
class HistoryTurtle(Turtle):
methods_to_skip = ('replay', '_execute', 'log')
def __init__(self, *args, **kw):
self._inited = False
super().__init__(*args, **kw)
self.history = []
self._replaying = [False]
self._inited = True
def __getattribute__(self, attr):
result = super().__getattribute__(attr)
if (not callable(result) or
attr.startswith('_') or
not self._inited or
self._replaying[-1]):
return result
return Instrumented(self, result)
def replay(self, start, end=None, *,
position_independent=False,
skip_self=True,
restore=True
):
results = []
self._replaying.append(True)
if restore:
pos, heading, state = self.pos(), self.heading(), self.pen()['pendown']
for command in self.history[start:end]:
results.append(
self._execute(command, position_independent))
if restore:
self.setpos(pos)
self.setheading(heading)
(self.pendown() if state else self.penup())
self._replaying.pop()
return results
def log(self, start=0, end=None):
for i, command in enumerate(self.history[start:end], start):
print(i, command)
def _execute(self, command, position_independent):
""" Execute a method without triggering the log
"""
# Warning: not thread-safe:
self._replaying.append(True)
method = getattr(self, command.method_name)
if not position_independent:
state = self.pen()['pendown']
self.setpos(command.pos)
self.setheading(command.heading)
(self.pendown() if state else self.penup())
result = method(*command.args, **command.kwargs)
self._replaying.pop()
return result
So simply put I want different objects to have their own tick loop code running independently of each other (As in one tick loop does not stop all other tick loops in my app).
I've created a basic module that has a class for shapes and the main body for spawning them however the tick loop of the class holds up the main parts loop.
I have even tried splitting the code into two modules to see if that would work but that still ran the two loops separately.
Here is my code:
(main code)
from random import *
from tkinter import *
from time import *
import RdmCirClass
size = 500
window = Tk()
count = 0
d = 0
shapes = []
canv = Canvas(window, width=size, height=size)
canv.pack()
window.update()
while True:
col = choice(['#EAEA00'])
x0 = randint(0, size)
y0 = randint(0, size)
#d = randint(0, size/5)
d = (d + 0.01)
outline = 'white'
shapes.append(1)
shapes[count] = RdmCirClass.Shape("shape" + str(count), canv, col, x0, y0, d, outline)
shapes[count].spawn()
count = count+1
print("Circle Count: ",count)
window.update()
(Shape Class)
from random import *
from tkinter import *
from time import *
class Shape(object):
def __init__(self,name, canv, col, x, y,d,outline):
self.name = name
self.canv = canv
self.col = col
self.x = x
self.y = y
self.d = d
self.outline = outline
self.age=0
self.born = time()
def death(self):
pass
def tick(self):
self.age = time() - self.born
def spawn(self):
self.canv.create_oval(self.x, self.y, self.x + self.d, self.y + self.d, outline=self.outline, fill = self.col)
while True:
self.tick()
Roughly speaking, there are three ways to accomplish what you want. Which is best depends greatly on what exactly you're trying to do with each independent unit, and what performance constraints and requirements you have.
The first solution is to have an independent loop that simply calls the tick() method of each object on each iteration. Conceptually this is perhaps the simplest to implement.
The other two solutions involve multiple threads, or multiple processes. These solutions come with sometimes considerably complexity, but the benefit is that you get the OS to schedule the running of the tick() method for each object.
I don't understand what your code is doing, but my suggestion is to use threading: https://pymotw.com/2/threading/
Through using VPython, I am able to get the program I am working on to generate multiple balls by calling the same class. I am also able to have the balls appear within a selected random range when they are generated (across x, y and z).
However, I am currently stumped on how I get to call the pos / position function from within my loop - as I would like to have the balls move.
Please see my code so far below.
If I call Ball.pos it states as undefined, but if I place my positioning via self.position, only one ball is generated, as they are not being referred to from under the sphere details?
from visual import *
from random import *
scene.title = "Multi Balls"
wallBm = box(pos=(0,-6,0), size=(12.2,0.1,12.1), color=color.blue, opacity=0.4)
vscale = 0.1
deltat = 0.005
t = 0
scene.autoscale = False
i = 0
totalBalls = 10
class Ball:
def __init__(self):
self.velocity = vector(0,5,0)
#vel sample ([5,10,15,20,25],3)
sphere (pos=(randrange (-6,6),randrange (-6,6),randrange (-6,6)), radius=0.5, color=color.cyan)
while True:
rate(100)
if i < totalBalls:
Ball()
i = i + 1
t = 5 + deltat
Try Inheritance from frame:
class Ball(frame):
def __init__(self, pos=(randrange (-6,6),randrange (-6,6),randrange (-6,6))):
frame.__init__(self,pos=pos)
self.velocity = vector(0,5,0)
sphere(frame=self,pos=pos,radius=0.5, color=color.cyan)
listOfBalls=[]
while True:
rate(100)
for i in range(totalBalls):
listOfBalls.append(Ball())
now try again!
You can call each Ball's position by calling listOfBalls[3].pos. I hope this helps!
I have been creating a simple tile based game to help me learn python and wx python. For starters I wanted to create my own 'world' and to test the simple map generator I made, I bound the return key to generate a new map and display it.
That is when I ran into this problem. It slows down a lot every time you click return, renders each tile line by line (which is obviously slow and inefficient) and eventually just freezes.
I am quite a novice programmer and have never dealt with any form of GUI so this is all very new to me, please bear with me! I can guess that the way I have things set up is very taxing for the machine, and that perhaps I'm causing a lot of recursion. I'm simply unaware. Also I'm not too familiar with OOP so I just follow examples to create my classes (hence why I only have 1 massive class that handles everything, Im not too sure on all the '__ something__' functions.)
Here is all the code I have written so far, please ignore commented-out sections, they are for future functions etc:
import wx
import random
#main screen class, handles all events within the main screen
class MainScreen(wx.Frame):
hMap = []
tTotalX = 50
tTotalY = 50
def __init__(self, *args, **kwargs):
#This line is equivilant to wx.Frame.__init__('stuff')
super(MainScreen, self).__init__(None, -1, 'You shouldnt see this', style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)
self.renderScreen()
def genMap(self,tTotalX,tTotalY):
count1 = 0
count2 = 0
self.hMap = []
while count1 < tTotalY:
count2 = 0
newrow = []
while count2 < tTotalX:
newrow.append(random.randint(1,120))
count2 += 1
self.hMap.append(newrow)
count1 += 1
self.smooth(tTotalX, tTotalY)
self.smooth(tTotalX, tTotalY)
def smooth(self, tTotalX, tTotalY):
countx = 0
county = 0
while county < tTotalY:
countx = 0
while countx < tTotalX:
above = county - 1
below = int(county + 1)
east = int(countx + 1)
west = int(countx - 1)
if east >= tTotalX:
east = 0
if west < 0:
west = tTotalX -1
teast = self.hMap[county][east]
twest = self.hMap[county][west]
if above < 0 or below >= tTotalY:
smooth = (self.hMap[county][countx] + teast + twest)/3
else:
tabove = self.hMap[above][countx]
tbelow = self.hMap[below][countx]
smooth = (self.hMap[county][countx] + tabove + tbelow + teast + twest)/5
self.hMap[countx][county] = int(smooth)
countx += 1
county += 1
def getTileType(self, coordX, coordY, totalX, totalY):
#this is the part of map creation, getting tile type based on tile attributes
tType = ''
height = self.hMap[coordX][coordY]
#the below values are all up to tweaking in order to produce the best maps
if height <= 55:
tType = 'ocean.png'
if height > 55:
tType = 'coast.png'
if height > 60:
tType = 'grassland.png'
if height > 75:
tType = 'hills.png'
if height > 80:
tType = 'mountain.png'
if tType == '':
tType = 'grassland.png'
return tType
#render the main screen so that it dislays all data
def renderScreen(self):
frameSize = 810 #Size of the game window
tTotalX = self.tTotalX #the dimensions of the tile display, setting for in-game coordinates
tTotalY = self.tTotalY
#tsTiny = 1 #ts = Tile Size
#tsSmall = 4
tsMed = 16
#tsLrg = 32
#tsXlrg = 64
tsCurrent = tsMed #the currently selected zoom level, for now fixed at tsMed
pposX = 0 #ppos = Pixel Position
pposY = 0
tposX = 0 #tpos = tile position, essentially the tile co-ordinates independent of pixel position
tposY = 0
#The below is just an example of how to map out the grid, it should be in its own function in due time
self.genMap(tTotalX, tTotalY)
while tposY < tTotalY: #loops through all y coordinates
tposX = 0
while tposX < tTotalX: #loops through all x coordinates
pposX = tposX*tsCurrent
pposY = tposY*tsCurrent
tiletype = self.getTileType(tposX,tposY,tTotalX,tTotalY)
img = wx.Image(('F:\First Pass\\' + str(tiletype)), wx.BITMAP_TYPE_ANY).ConvertToBitmap()
wx.StaticBitmap(self, -1, img, (pposX, pposY))#paints the image object (i think)
tposX += 1
tposY += 1
self.Bind(wx.EVT_KEY_DOWN, self.onclick)
self.SetSize((frameSize-4, frameSize+16))
self.SetBackgroundColour('CYAN')
self.Centre()
self.SetTitle('Nations First Pass')
#string = wx.StaticText(self, label = 'Welcome to Nations, First Pass', pos = (tTotalX*tsCurrent/2,tsCurrent*tTotalY/2))
#string.SetFont(wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT))
self.Show()
def onclick(self, e):
key = e.GetKeyCode()
if key == wx.WXK_RETURN:
self.renderScreen()
#game loop
def main():
app = wx.App()
MainScreen(None)
app.MainLoop()
if __name__ == '__main__':
main()
You will need to fabricate your own 'ocean.png' 'coast.png' 'grassland.png' 'hills.png' and 'mountain.png' (they need to be 16x16 pixels) or you can use mine from the Imgur link:
http://imgur.com/a/uFxfn
Also please change the file path in the img code as appropriate. I need to figure out how to set that to do it itself as well but thats another challenge for another day.
If you have any insight into this I'd be very appreciative.
You are creating a new set of wx.StaticBitmaps each time renderScreen is called, and you are not deleting them before creating a new set. After a while you'll have gobs of widgets stacked up with the old ones no longer visible, but still there consuming resources. At a minimum you should change things so your program makes only one set of wx.StaticBitmaps, keeps track of them, and then call their SetBitmap method when you want to change them.
For even better performance you should forget about the StaticBitmaps and draw the images yourself in an EVT_PAINT handler. StaticBitmaps are intended to be "static", IOW not changing very much. Instead you can implement an EVT_PAINT handler for the window and it will be called whenever the window needs to be redrawn, and you can trigger new redraws simply by calling the window's Refresh method.
I have a ball generator, that "generates" and adds balls(circles) to the simulation.
The ball is to be removed when it hits a static poly in list s_boxes.
This is done by a collision handler ball_wall_collision.
The Error:
The following pop-up window does what it's name says, it pops-up
My code:
Ball Generator
class BallGenerator:
def __init__(self, min_y, max_y, x):
self.min = min_y
self.max = max_y
self.x = x
self.counter = 0
def bowl(self, balls):
global ball_bowled
y = random.randint(self.min, self.max)
pos = to_pymunk((self.x,y))
r = 10
m = 15
i = pm.moment_for_circle(m, 0, r)
b = pm.Body(m,i)
b.position = pos
f_x = random.randint(-600000,-400000)
b.apply_force( (f_x,0.0),(0,0) )
ball = pm.Circle(b, r)
ball.elasticity = 0.75
ball.friction = 0.95
balls.append(ball)
space.add(ball,b)
print 'bowled'
ball_bowled += 1
def handle(self, balls):
if self.counter == FPS:
self.bowl(balls)
self.counter = 0
self.counter += 1
Collision handler
def ball_wall_collision(space, arb, balls, s_boxes):
shapes = arb.shapes
boxes = [box[0] for box in s_boxes] # Get walls
ball = None
wall = None
for ba in balls:
if ba in shapes:
ball = ba
break
for box in boxes:
if box in shapes:
wall = box
break
if wall and ball:
print 'removing'
space.remove(ball, ball.body) # Where the runtime problem happens
balls.remove(ball)
print 'removed'
return False
else:
return True
space.add_collision_handler(0,0,begin=ball_wall_collision,
balls=balls,s_boxes=s_boxes) # Other args to function
What am I doing wrong in the collision handling??
Am I missing something in the call to space.remove?
Is the function not working as I want it to??
Or is the error elsewhere (which I don't think it is)...
It looks like the problem is that you try to remove objects from the space in a collision handler during the simulation step.
Instead you can try with either manually collect all the balls into a list and then call remove after the step, or queue up removes with the post step callback like this:
space.add_post_step_callback(space.remove, ball)
space.add_post_step_callback(space.remove, ball.body)
(untested code)
I should probably try and make this more obvious in the API docs.. I wonder if it would be a good idea to automatically schedule the remove until end of step, or the less intrusive option, trigger a assert in python so you dont get the c++ error.