【问题标题】:Why the canvas widget into Tkinter with Python 3 is slow?为什么使用 Python 3 进入 Tkinter 的画布小部件很慢?
【发布时间】:2013-02-05 14:19:25
【问题描述】:

我刚开始在 Python(3.2) 中使用 Tkinter 模块,所以我决定用这个模块重写我的旧程序(我使用 curses 模块)。 该程序是一个Game of Life 模拟器。 我实现的算法在没有用户界面的情况下运行得如此之快。这是我的程序(这是一个快速的实验,我从未使用过画布小部件):

#!/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()

我没有报告 gol,但问题不在该模块中。 问题是程序很慢,我认为我不能很好地使用画布。

编辑:这是 gol 模块,但我认为这不是问题...

#!/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

【问题讨论】:

  • evolve 是做什么的?
  • Evolve() 根据生命游戏规则改变矩阵。

标签: python performance canvas tkinter conways-game-of-life


【解决方案1】:

这是您个人资料的一部分:

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}

让我们在这里提取你感兴趣的内容:

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)

您大部分时间都在 draw 中度过,包含在 Application 类的方法 play 中。

线条:

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)

表明你实际上是在花时间来创建你的矩形。

因此,如果您想获得更好的性能,请停止实例化。只需更新它们!顺便说一句,如果您使用矩阵而不是双循环,则对draw 的调用会少得多。 draw 很慢(6.787 秒),但请记住,您在这些循环中也浪费了近 1.5 秒。

顺便说一句,gol.py 中的evolve 也可以通过去掉这些双循环得到很大的改进。我认为您也可以对算法进行一些改进。

编辑:

哦,gol 模块导致了“问题”的十分之一:)

【讨论】:

    【解决方案2】:

    我的猜测是,这是因为您每次重绘棋盘时都会创建一个包含 900 个对象的新网格。众所周知,当您创建数以万计的对象时,画布会出现性能问题。由于您在每次迭代中绘制 900 个对象,因此很快就会添加很多对象。

    我的建议是重构您的代码以绘制一次 30x30 方格的网格,然后在每次迭代中您只需要更改每个网格元素的颜色。这应该足以让您非常快速地循环几代人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-04
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      相关资源
      最近更新 更多