0

I have a simulation, where a create moves around a map. The map is drawn with tkinter. In the simulation's init I call

    root = Tk()
    root.geometry("800x800+300+300")
    self.ex = land.Example(root)
    root.mainloop()

to start tkinter, where land.Example is the class below. What should happen is the map is initially drawn, then the simulation runs and anytime the creature moves it will use self.ex to draw that in tkinter. The problem is, when it runs, the map is drawn initially, and I have to close it so that the rest of the simulation will run. In that case the whole thing breaks because the simulation is trying to use a canvas that doesn't exist anymore. How can I fix this so the map and simulation both stay running

Simulation self.ex interaction

def move(self, creature, target):
        self.map[creature.x][creature.y].creatures.remove(creature)
        self.ex.updateMap(creature.x, creature.y, "remove")

land.Example

class Example(Frame):

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.parent.title("Board")
        self.pack(fill=BOTH, expand=1)
        self.canvas = Canvas(self)
        self.upperX = 0
        self.lowerX = 0
        self.upperY = 0
        self.lowerY = 0

        for x, row in enumerate(landMass):
            for y, cell in enumerate(row):
                color = "grey"
                if isinstance(landMass[x][y], Food):
                    color = "green"
                elif isinstance(landMass[x][y], Water):
                    color = "blue"
                elif isinstance(landMass[x][y], Shelter):
                    color = "black"
                self.canvas.create_rectangle(50 * x , 50 * y , 50 * x + 50, 50 * y + 50,
                    outline=color, fill=color)
                self.canvas.create_text(3 + 50 * x, 3 + 50 * y, anchor=NW, fill="white", text=landMass[x][y].elevation)
                if color == "green":
                    self.canvas.create_text(3 + 70 * x, 3 + 50 * y, anchor=NE, fill="red", text=landMass[x][y].vegitation)
                elif color == "black":
                    self.canvas.create_text(3 + 70 * x, 3 + 50 * y, anchor=NE, fill="orange", text=landMass[x][y].quality)
        self.canvas.pack(fill=BOTH, expand=1)

    def updateMap(self, x, y, action):
        color = "grey"
        if action == "append":
            #DRAW THE CREATURE AT X,Y
        elif action == "remove":
            #DRAW THE ORIGINAL TILE AT X,Y
        ...        
        self.canvas.pack(fill=BOTH, expand=1)

Edit: Other Attempts

when I add the following to the Simulation's init() and add a function. The whole thing runs, but the tk canvas, show the end result, not the result as it is happening.

        root = Tk()
        root.geometry("800x800+300+300")
        self.tkmap = land.Example(root)
        root.after(2000, self.runSim)
        root.mainloop()
    #End of __init__() 

    def runSim(self):
        for generation in range(self.lifeCycles):
            self.simulate(generation)
        return self.evolve()
EasilyBaffled
  • 3,822
  • 10
  • 50
  • 87
  • see http://stackoverflow.com/a/459131/ – FabienAndre May 31 '13 at 18:53
  • the problem with that solution is, that task is a recursive function, where as what I'm working with is a class and object all on its own. – EasilyBaffled May 31 '13 at 19:44
  • If you remplace `print "hello"` of the linked answer with one step of your simulation, you are done. The idea is not to perform one call through `after`, but to repeat `after` call so that `Tkinter` can update the view between two steps. You can see `after` as *do your stuff and wake me up in x ms*. If you don't give hand to Tkinter, the view can not be updated until the end of you sim. – FabienAndre May 31 '13 at 22:39
  • I did that, the map prints initially, the whole sim runs, and only at the end does it fill the map, and its not through animation its just the final product so I tried replacing the self.ex.updateMap with self.root.after... but then the only difference is I have to wait a few more seconds after the simulation finishes running to see the end result. – EasilyBaffled Jun 01 '13 at 21:11

1 Answers1

1

In your after called runSim, you perform your whole simulation. The idea is to do perform only one (or a few) step of your simultation in each after call.

Here is a little example of how it could works:

import random
from Tkinter import *

c = Canvas()
c.pack()
circle = c.create_oval(100,100,110,110, fill="red")

def simulate():
    #one step of simulation
    c.move(circle, random.normalvariate(0,3),random.normalvariate(0,3))
    c.after(100, simulate)

c.after(100, simulate)
c.mainloop()
FabienAndre
  • 4,514
  • 25
  • 38
  • So then in simulate could I just stick a if statement around the after to kill the simulation when it's done, or is there something I need to do with the mainloop too? – EasilyBaffled Jun 02 '13 at 23:32
  • Yes, just no calling `after` is sufficient. You likely want to leave the mainloop only when your leave your app, likely at the user initiative. If you want to leave from code, use the `quit` method. – FabienAndre Jun 03 '13 at 05:13