问题
I've just started to use the Tkinter module with Python(3.2), so I decided to rewrite my old program(I used curses module) with this module. The program is a Game of Life simulator. The algorithm I've implemented works so quickly without the user interface. This is my program(it is a fast experiment, I never used the canvas widget):
#!/usr/bin/python3
import gol
import Tkinter as tk
class Application(tk.Frame):
def __init__(self):
self.root = tk.Tk()
self.root.wm_title('Canvas Experiments')
tk.Frame.__init__(self, self.root)
self.draw_widgets()
self.world = gol.World(30, 30)
self.world.cells[25][26] = True
self.world.cells[26][26] = True
self.world.cells[27][26] = True
self.world.cells[25][27] = True
self.world.cells[26][28] = True
def draw_widgets(self):
self.canvas = tk.Canvas(
width = 300,
height = 300,
bg = '#FFF')
self.canvas.grid(row = 0)
self.b_next = tk.Button(
text = 'Next',
command = self.play)
self.b_next.grid(row = 1)
self.grid()
def play(self):
def draw(x, y, alive):
if alive:
self.canvas.create_rectangle(x*10, y*10, x*10+9, y*10+9, fill='#F00')
else:
self.canvas.create_rectangle(x*10, y*10, x*10+9, y*10+9, fill='#FFF')
for y in range(self.world.width):
for x in range(self.world.height):
draw(x, y, self.world.cells[x][y])
self.world.evolve()
app = Application()
app.mainloop()
I didn't report the gol but the problems are not in that module. The problem is that the program is very slow, I think that I'm not able to use the canvas very good.
EDIT: Here is the gol module, but I don't think this is the problem...
#!/usr/bin/python3
class World:
def __init__(self, width, height):
self.width, self.height = width, height
self.cells = [[False for row in range(self.height)] for column in range(self.width)]
def neighbours(self, x, y):
counter = 0
for i in range(-1, 2):
for j in range(-1, 2):
if ((0 <= x + i < self.width) and (0 <= y + j < self.height) and not (i == 0 and j == 0)):
if self.cells[x + i][y + j]:
counter += 1
return counter
def evolve(self):
cells_tmp = [[False for row in range(self.height)] for column in range(self.width)]
for x in range(self.width):
for y in range(self.height):
if self.cells[x][y]:
if self.neighbours(x, y) == 2 or self.neighbours(x, y) == 3:
cells_tmp[x][y] = True
else:
if self.neighbours(x, y) == 3:
cells_tmp[x][y] = True
self.cells = cells_tmp
回答1:
Here is part of your profile:
ncalls tottime percall cumtime percall filename:lineno(function)
125112 1.499 0.000 1.499 0.000 {method 'call' of 'tkapp' objects}
125100 1.118 0.000 6.006 0.000 /usr/lib/python3.2/tkinter/__init__.py:2190(_create)
125109 0.942 0.000 1.749 0.000 /usr/lib/python3.2/tkinter/__init__.py:69(_cnfmerge)
125106 0.906 0.000 3.065 0.000 /usr/lib/python3.2/tkinter/__init__.py:1059(_options)
125599 0.851 0.000 0.851 0.000 main.py:10(neighbours)
500433 0.688 0.000 0.688 0.000 {built-in method isinstance}
125100 0.460 0.000 6.787 0.000 main.py:64(draw)
250210 0.341 0.000 0.341 0.000 {method 'update' of 'dict' objects}
125100 0.321 0.000 6.327 0.000 /usr/lib/python3.2/tkinter/__init__.py:2219(create_rectangle)
250205 0.319 0.000 0.319 0.000 {built-in method _flatten}
139 0.255 0.002 8.093 0.058 main.py:63(play)
139 0.181 0.001 1.051 0.008 main.py:19(evolve)
125109 0.134 0.000 0.134 0.000 {method 'items' of 'dict' objects}
125108 0.107 0.000 0.107 0.000 {built-in method callable}
1 0.056 0.056 0.056 0.056 {built-in method create}
Let's extract what is interesting for you here:
cumtime filename:lineno(function)
0.851 main.py:10(neighbours)
6.787 main.py:64(draw)
8.093 main.py:63(play)
1.051 main.py:19(evolve)
You spend most of your time in draw
, contained in the method play
of the class Application.
The lines:
ncalls tottime percall cumtime percall filename:lineno(function)
125100 1.118 0.000 6.006 0.000 /usr/lib/python3.2/tkinter/__init__.py:2190(_create)
125106 0.906 0.000 3.065 0.000 /usr/lib/python3.2/tkinter/__init__.py:1059(_options)
show that your are actually spending your time to create your rectangles.
So if you want to get better performances, just stop instanciating stuff. Just update them! By the way, you'll get far fewer calls to draw
if you use matrices instead of double loops. draw
is slow (6.787 cum. seconds), but remember that you also waste almost 1.5 seconds in these loops.
By the way, evolve
in gol.py
can also be greatly improved by removing these double loops. I think you can make some improvement in the algorithm too.
EDIT:
Oh, and the gol
module contributes to on tenth of the "problem" :)
回答2:
My guess is, it's because you're creating a new grid of 900 objects each time you redraw the board. The canvas is known to have performance issues when you create tens of thousands of objects. Since you're drawing 900 objects on each iteration, that will quickly add up to a lot of objects.
My advice is to refactor your code to draw the grid of 30x30 squares once, then on each iteration you need to only change the color of each grid element. That should be enough to let you cycle through generations very quickly.
来源:https://stackoverflow.com/questions/14709636/why-the-canvas-widget-into-tkinter-with-python-3-is-slow