テキスト修正
「いちばんやさしいPython入門教室」1で演習をすすめてきました.このテキストはひとつひとつのcodeがどのような考え方でそこにあるのかを丁寧に解説してくれています.なので,coding初心者がcodingを理解するために,じっくり読むのにはとてもいいテキストです.ただ,論理の流れを重視しているためか,すこしcodingの常識からずれているところがあります.これをclass化の段階で修正します.
example07-08-01.pyの修正(tag版)
このあたりのコード,まずいです.著者も新刷ではp.194のコラムでメモリリークのバグがあることを認識しています.長くやったり,たくさんやったりしたらもたもたしてきます.はじめは,cpuとかのせいかとおもってあきらめてたんですが,あまりに遅くって...
修正のアイデアはどっかにあったんですが,もうだいぶむかしに修正しちゃったのでなにが原典だったかわかりません.
が最初のアイデアとしては同じです.
問題は,
canvas.create_oval(self.x - 20, self.y - 20,
self.x + 20, self.y + 20, fill="white", width=0)
です.'white'で上書きして,消しています.これはcodeで絵をかくときの常とう手段です.画面全体をpictとして管理して書いていくときには効率がいいんです.でも,objectに対して,これをやるとobjectがどんどん増殖します.結果として必要なメモリーが増えて,描画がもたもたしてくるんです.たぶん
なんで正しくはobjectをdeleteします.
まず,create_ovalで識別のためにtagをつけて生成します.
canvas.create_oval(self.x - 20, self.y - 20,
self.x + 20, self.y + 20,
fill=self.color, width=0,
tag=self.color)
それを white
で上書きする代わりに,つぎのようにしてdeleteします.
canvas.delete(self.color)
これで試してみてください.とっても速くなるはずです.
ただ,windowsでは形がいびつな感じがするかもしれません.これは,描画ライブラリのせいのようです2.mac版ではばっちりです.
さらに正統派(obj版)
さらにobjectを消すという考え方の方がわざわざtagで識別するより筋が良さそうです.
基本的には,
canvas.delete(self.ball)
... 中略 ...
self.ball = canvas.create_oval(self.x - 20, self.y - 20,
self.x + 20, self.y + 20,
fill=self.color, width=0)
としてself.ballというobjectにしてしまって,それを,deleteするだけです.でも,元テキストからはcodeの順番を相当いじる必要があります.
完成形をつけておきますので,時間のある人は,どこがどうなっているか,なんでか悩んで見てください.順番は,
- 上のように修正してみる
- self.ballがないと怒られるので,initでnoneしておく
- canvasがないと怒られるので,ballsを生成する前に持っていく
です.
1 import tkinter as tk
2 speed = 10
3 class Ball:
4 global speed
5 def __init__(self, x, y, dx, dy, color):
6 self.x = x
7 self.y = y
8 self.dx = dx
9 self.dy = dy
10 self.color = color
11 self.ball = None
12
13 def move(self, canvas):
14 canvas.delete(self.ball)
15 self.x = self.x + self.dx
16 self.y = self.y + self.dy
17 self.ball = canvas.create_oval(self.x - 20, self.y - 20,
18 self.x + 20, self.y + 20,
19 fill=self.color, width=0)
20 if self.x >= canvas.winfo_width():
21 self.dx = -speed
22 if self.x < 0:
23 self.dx = +speed
24 if self.y >= canvas.winfo_height():
25 self.dy = -speed
26 if self.y < 0:
27 self.dy = +speed
28
29 root = tk.Tk()
30 root.geometry("600x400")
31
32 canvas = tk.Canvas(root, width=600, height=400, bg="white")
33 canvas.place(x=0, y=0)
34
35 balls = [
36 Ball(100, 400, -10, 10, "red"),
37 Ball(200, 400, 10, -10, "green"),
38 Ball(300, 200, 10, 10, "blue")
39 ]
40 def loop():
41 for b in balls:
42 b.move(canvas)
43 root.after(10, loop)
44
45
46 root.after(10, loop)
47 root.mainloop()
Footnotes
1 「いちばんやさしいPython入門教室」大澤文孝著,(ソーテック社出版,2017).
2 (2020/06/16追記)バックを黒くして,ボールを明るくするとうまく出ます.人間の目のせいでしょうか..
- source ~/Desktop/lecture_23s/compA23/d7_12_python/c7_bouncing_balls_class.org