使用 tkinter 在 python 中更新 canvas 上的多个椭圆坐标
Update multiple oval coordinates on canvas in python using tkinter
我正在开发一个图形用户界面,以连续不断地一次抛硬币动画。
我有两个 classes cointoss.py 和 flipbell.py.
Cointoss class 生成硬币的价值变化,并使用 flipbell 为该过程制作动画。
好像现在我的代码可以一次为一枚硬币制作动画,但不能同时为所有硬币制作动画。
当我说所有代币时,逻辑是这样的:第一个代币根据价值变化下降,下一个下降,但第一个代币的价值也会相应更新,依此类推。
我需要帮助才能继续我目前所做的尝试。我已经使用 for 循环来为过程设置动画,并且我正在考虑使用递归方法来处理逻辑部分。
任何对现有代码或想法的帮助都会很棒。
flipbell.py
from tkinter import Tk, Canvas, Button, W, E
import random
from math import pi, sin, cos
from cointoss import *
class FlipBell(object):
"""
GUI to simulate cointoss.
"""
def __init__(self, wdw, dimension, increment, delay):
"""
Determines the layout of the GUI.
wdw : top level widget, the main window,
dimension : determines the size of the canvas,
increment : step size for a billiard move,
delay : time between updates of canvas.
"""
wdw.title('Coin flips and Bell Curve')
self.dim = dimension # dimension of the canvas
self.inc = increment
self.dly = delay
self.togo = False # state of animation
# initial coordinates of the ball
self.xpt = self.dim/2
self.ypt = 0
self.cnv = Canvas(wdw, width=self.dim,\
height=self.dim, bg='white')
self.cnv.grid(row=0, column=0, columnspan=2)
self.bt0 = Button(wdw, text='start',\
command=self.start)
self.bt0.grid(row=1, column=0, sticky=W+E)
self.bt1 = Button(wdw, text='stop',\
command=self.stop)
self.bt1.grid(row=1, column=1, sticky=W+E)
def map2table(self, pnt):
"""
Keeps the ball on the canvas table.
"""
if pnt < 0:
(quo, rest) = divmod(-pnt, self.dim)
else:
(quo, rest) = divmod(pnt, self.dim)
return rest
def placecoin(self, xpt, ypt):
self.cnv.create_oval(xpt-1, ypt-1, xpt+1, ypt+1,\
width=2, outline='red', fill='red', tags='coin')
def drawball(self):
"""
Draws the ball on the canvas.
"""
xpt = self.map2table(self.xpt)
ypt = self.map2table(self.ypt)
self.cnv.delete('dot')
self.cnv.create_oval(xpt-1, ypt-1, xpt+1, ypt+1,\
width=1, outline='black', fill='red', tags='dot')
def animate(self):
"""
Performs the animation.
"""
self.drawball()
val = []
for k in range(400):
val1 = CoinToss.cointoss(3,k,self.dim//2)
val.append(val1)
points = {}
for i in range(1,401):
points[i] = 0
for i in range(0,400):
for j in range(0,400):
(xpt, ypt) = (self.xpt, self.ypt)
self.xpt = val[i][1][j]
# print("x %d",self.xpt)
self.ypt = ypt + 1
# print("y %d",self.ypt)
self.cnv.after(self.dly)
self.drawball()
self.cnv.update()
#Puts the coin on top each other
if self.ypt == 400:
if points[self.xpt]>=1:
self.placecoin(val[i][1][-1],400-points[self.xpt])
else:
self.placecoin(val[i][1][-1],400)
points[self.xpt]+=3
self.ypt = 0
def start(self):
"""
Starts the animation.
"""
self.togo = True
self.animate()
def stop(self):
"""
Stops the animation.
"""
self.togo = False
def main():
"""
Defines the dimensions of the canvas
and launches the main event loop.
"""
top = Tk()
dimension = 400 # dimension of canvas
increment = 10 # increment for coordinates
delay = 1 # how much sleep before update
num_flips = 3
num_value = dimension//2
FlipBell(top, dimension, increment, delay)
top.mainloop()
if __name__ == "__main__":
main()
cointoss.py
from random import randint
import random
class CoinToss:
coin = 0
def __init__(self, value,num_flip):
# self.id = 1
self.v = value
self.state = 1
self.flip = num_flip
CoinToss.coin += 1
def cointoss(self,coin,value):
print('The ball at the start: ball: %d, state: %d, value: %d' % (coin, self, value))
value_change = value
coin_change = []
for i in range(1,400+1):
value = value_change
value_change = CoinToss.flip(value)
print('after flip %d, ball: %d, state: %d, value: %d' % (i,coin, i, value_change))
coin_change.append(value_change)
return([coin,coin_change])
def flip(self):
rand_value = randint(0, 1)
if rand_value == 1:
self +=1
else:
self -=1
return self
您在 CoinToss 中同时命名了函数和变量 "flip",这令人困惑。此外,您使用 "tags" 关键字,它应该是 "tag"。对此进行编码的方法不止一种。下面的代码不是一个完整的解决方案,而是一个简单的示例,它展示了如何使用投币 class 创建和移动单个球(不检查 canvas 的移动)。 FlipBell class 将每个 CoinToss 实例存储在一个列表中,并在每次创建球时为每个 class 调用 "flip" 函数。您还可以在 CoinToss class 中使用 "after" 让翻转函数重复调用自身。
from tkinter import *
from random import randint
class FlipBell(object):
"""
GUI to simulate cointoss.
"""
def __init__(self, wdw, dimension, delay):
"""
Determines the layout of the GUI.
wdw : top level widget, the main window,
dimension : determines the size of the canvas,
increment : step size for a billiard move,
delay : time between updates of canvas.
"""
wdw.title('Coin flips and Bell Curve')
self.cnv = Canvas(wdw, width=dimension,
height=dimension, bg='white')
self.cnv.grid()
self.ct_instances=[]
self.colors=["blue", "red", "yellow", "gray", "green"]
self.delay=delay
self.offset=0
self.create_next()
def create_next(self):
""" create one ball for each color in self.colors
and call each existing ball's flip function to
move it a random amount
"""
x=5
y=5
incr=10*self.offset
CT=CoinToss(self.cnv, x+incr, y+incr, self.colors[self.offset])
##save each CoinToss (ball) instance in a list
self.ct_instances.append(CT)
self.offset += 1
## call flip (move ball) for each instance
for instance in self.ct_instances:
instance.flip()
if self.offset < len(self.colors):
self.cnv.after(self.delay, self.create_next)
class CoinToss:
def __init__(self, canvas, start_x, start_y, color):
self.cnv=canvas
self.cointoss(start_x, start_y, color)
def cointoss(self, start_x, start_y, color):
self.this_ball=self.cnv.create_oval(start_x-5, start_y-5, start_x+5, start_y+5,
outline='black', fill=color, tag="dot")
def flip(self):
""" move the ball created for this class instance by a random amount
"""
rand_value = randint(10, 50)
self.cnv.move(self.this_ball, rand_value, rand_value)
if __name__ == "__main__":
top = Tk()
dimension = 400 # dimension of canvas
delay = 500 # how much sleep before update --> 1/2 second
num_flips = 3
FP=FlipBell(top, dimension, delay)
top.mainloop()
我正在开发一个图形用户界面,以连续不断地一次抛硬币动画。 我有两个 classes cointoss.py 和 flipbell.py.
Cointoss class 生成硬币的价值变化,并使用 flipbell 为该过程制作动画。
好像现在我的代码可以一次为一枚硬币制作动画,但不能同时为所有硬币制作动画。
当我说所有代币时,逻辑是这样的:第一个代币根据价值变化下降,下一个下降,但第一个代币的价值也会相应更新,依此类推。
我需要帮助才能继续我目前所做的尝试。我已经使用 for 循环来为过程设置动画,并且我正在考虑使用递归方法来处理逻辑部分。
任何对现有代码或想法的帮助都会很棒。
flipbell.py
from tkinter import Tk, Canvas, Button, W, E
import random
from math import pi, sin, cos
from cointoss import *
class FlipBell(object):
"""
GUI to simulate cointoss.
"""
def __init__(self, wdw, dimension, increment, delay):
"""
Determines the layout of the GUI.
wdw : top level widget, the main window,
dimension : determines the size of the canvas,
increment : step size for a billiard move,
delay : time between updates of canvas.
"""
wdw.title('Coin flips and Bell Curve')
self.dim = dimension # dimension of the canvas
self.inc = increment
self.dly = delay
self.togo = False # state of animation
# initial coordinates of the ball
self.xpt = self.dim/2
self.ypt = 0
self.cnv = Canvas(wdw, width=self.dim,\
height=self.dim, bg='white')
self.cnv.grid(row=0, column=0, columnspan=2)
self.bt0 = Button(wdw, text='start',\
command=self.start)
self.bt0.grid(row=1, column=0, sticky=W+E)
self.bt1 = Button(wdw, text='stop',\
command=self.stop)
self.bt1.grid(row=1, column=1, sticky=W+E)
def map2table(self, pnt):
"""
Keeps the ball on the canvas table.
"""
if pnt < 0:
(quo, rest) = divmod(-pnt, self.dim)
else:
(quo, rest) = divmod(pnt, self.dim)
return rest
def placecoin(self, xpt, ypt):
self.cnv.create_oval(xpt-1, ypt-1, xpt+1, ypt+1,\
width=2, outline='red', fill='red', tags='coin')
def drawball(self):
"""
Draws the ball on the canvas.
"""
xpt = self.map2table(self.xpt)
ypt = self.map2table(self.ypt)
self.cnv.delete('dot')
self.cnv.create_oval(xpt-1, ypt-1, xpt+1, ypt+1,\
width=1, outline='black', fill='red', tags='dot')
def animate(self):
"""
Performs the animation.
"""
self.drawball()
val = []
for k in range(400):
val1 = CoinToss.cointoss(3,k,self.dim//2)
val.append(val1)
points = {}
for i in range(1,401):
points[i] = 0
for i in range(0,400):
for j in range(0,400):
(xpt, ypt) = (self.xpt, self.ypt)
self.xpt = val[i][1][j]
# print("x %d",self.xpt)
self.ypt = ypt + 1
# print("y %d",self.ypt)
self.cnv.after(self.dly)
self.drawball()
self.cnv.update()
#Puts the coin on top each other
if self.ypt == 400:
if points[self.xpt]>=1:
self.placecoin(val[i][1][-1],400-points[self.xpt])
else:
self.placecoin(val[i][1][-1],400)
points[self.xpt]+=3
self.ypt = 0
def start(self):
"""
Starts the animation.
"""
self.togo = True
self.animate()
def stop(self):
"""
Stops the animation.
"""
self.togo = False
def main():
"""
Defines the dimensions of the canvas
and launches the main event loop.
"""
top = Tk()
dimension = 400 # dimension of canvas
increment = 10 # increment for coordinates
delay = 1 # how much sleep before update
num_flips = 3
num_value = dimension//2
FlipBell(top, dimension, increment, delay)
top.mainloop()
if __name__ == "__main__":
main()
cointoss.py
from random import randint
import random
class CoinToss:
coin = 0
def __init__(self, value,num_flip):
# self.id = 1
self.v = value
self.state = 1
self.flip = num_flip
CoinToss.coin += 1
def cointoss(self,coin,value):
print('The ball at the start: ball: %d, state: %d, value: %d' % (coin, self, value))
value_change = value
coin_change = []
for i in range(1,400+1):
value = value_change
value_change = CoinToss.flip(value)
print('after flip %d, ball: %d, state: %d, value: %d' % (i,coin, i, value_change))
coin_change.append(value_change)
return([coin,coin_change])
def flip(self):
rand_value = randint(0, 1)
if rand_value == 1:
self +=1
else:
self -=1
return self
您在 CoinToss 中同时命名了函数和变量 "flip",这令人困惑。此外,您使用 "tags" 关键字,它应该是 "tag"。对此进行编码的方法不止一种。下面的代码不是一个完整的解决方案,而是一个简单的示例,它展示了如何使用投币 class 创建和移动单个球(不检查 canvas 的移动)。 FlipBell class 将每个 CoinToss 实例存储在一个列表中,并在每次创建球时为每个 class 调用 "flip" 函数。您还可以在 CoinToss class 中使用 "after" 让翻转函数重复调用自身。
from tkinter import *
from random import randint
class FlipBell(object):
"""
GUI to simulate cointoss.
"""
def __init__(self, wdw, dimension, delay):
"""
Determines the layout of the GUI.
wdw : top level widget, the main window,
dimension : determines the size of the canvas,
increment : step size for a billiard move,
delay : time between updates of canvas.
"""
wdw.title('Coin flips and Bell Curve')
self.cnv = Canvas(wdw, width=dimension,
height=dimension, bg='white')
self.cnv.grid()
self.ct_instances=[]
self.colors=["blue", "red", "yellow", "gray", "green"]
self.delay=delay
self.offset=0
self.create_next()
def create_next(self):
""" create one ball for each color in self.colors
and call each existing ball's flip function to
move it a random amount
"""
x=5
y=5
incr=10*self.offset
CT=CoinToss(self.cnv, x+incr, y+incr, self.colors[self.offset])
##save each CoinToss (ball) instance in a list
self.ct_instances.append(CT)
self.offset += 1
## call flip (move ball) for each instance
for instance in self.ct_instances:
instance.flip()
if self.offset < len(self.colors):
self.cnv.after(self.delay, self.create_next)
class CoinToss:
def __init__(self, canvas, start_x, start_y, color):
self.cnv=canvas
self.cointoss(start_x, start_y, color)
def cointoss(self, start_x, start_y, color):
self.this_ball=self.cnv.create_oval(start_x-5, start_y-5, start_x+5, start_y+5,
outline='black', fill=color, tag="dot")
def flip(self):
""" move the ball created for this class instance by a random amount
"""
rand_value = randint(10, 50)
self.cnv.move(self.this_ball, rand_value, rand_value)
if __name__ == "__main__":
top = Tk()
dimension = 400 # dimension of canvas
delay = 500 # how much sleep before update --> 1/2 second
num_flips = 3
FP=FlipBell(top, dimension, delay)
top.mainloop()