Python Tkinter 刷新画布
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21357178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python Tkinter refresh canvas
提问by JmRag
Hello I have a tuple in python with colours that are related to squares that are drawn in the canvas by the following dictionary:
您好,我在 python 中有一个元组,其颜色与以下字典在画布中绘制的方块相关:
colour_mapping = {0: "red", 1: "green", 2: "blue" , 3:"purple"}
To be more specific for example a node at the tuple is:
更具体地说,例如元组中的节点是:
((2, 3), (3, 3))
This means that 4 squares should be drawn this way:
这意味着应该以这种方式绘制 4 个正方形:
blue square purple square
purple square purple square
and then their colours should be changed accordingly to the next node in my tuple
然后它们的颜色应该相应地更改为我的元组中的下一个节点
To do this I iterate the tuple and for each element I draw a new rectangle at the canvas and then I call the time.sleep()function in order to give time to the user to see the differences to the previous state.
My problem is that only the last node is rendered correctly while all the others aren't shown. Can you help me?
为此,我迭代元组,并为每个元素在画布上绘制一个新矩形,然后调用该time.sleep()函数,以便让用户有时间查看与之前状态的差异。我的问题是只有最后一个节点被正确渲染,而所有其他节点都没有显示。你能帮助我吗?
Here is my code so far:
到目前为止,这是我的代码:
self.parent.title("AlienTiles")
self.style = Style()
self.style.theme_use("default")
self.frame = Frame(self, relief=RAISED, borderwidth=1)
self.frame.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self.frame)
self.canvas.pack(fill=BOTH, expand=1)
self.pack(fill=BOTH, expand=1)
for i in range(len(path)) : #the tuple is path
state = path[i].state
print state
time.sleep(1)
y_offset=10
for x in state:
start_x=40
start_y=10
i=1
x_offset=0
for y in x:
x0=(start_x*i)+x_offset
y0=(start_y*i)+y_offset
x1=x0+size
y1=y0+size
colour=colour_mapping[y]
print colour
self.canvas.create_rectangle(x0, y0, x1, y1, fill=colour)
x_offset=x_offset+size+10
y_offset=y_offset+size+10
All in all, I try to make an animation described above. Is there anything I don't think correctly or something to refresh the canvas at each loop?
总而言之,我尝试制作上述动画。有什么我认为不正确的东西或在每个循环中刷新画布的东西吗?
采纳答案by Bryan Oakley
The only way for the canvas to refresh is for the event loop to service "redraw" events. In your loop you're never giving the event loop a chance to update, so you don't see any changes.
画布刷新的唯一方法是让事件循环为“重绘”事件提供服务。在您的循环中,您永远不会给事件循环更新的机会,因此您看不到任何更改。
The quick fix is to call self.canvas.update_idletasks, but that's just a hack and not a proper solution.
快速解决方法是调用self.canvas.update_idletasks,但这只是一个黑客而不是正确的解决方案。
The proper way to do animation is to use the event loop to do the iterations. You do this by placing work to be done on a queue -- in this case, the idle event queue. You can place things on this queue with the aftercommand.
做动画的正确方法是使用事件循环来进行迭代。为此,您可以将要完成的工作放在一个队列中——在本例中为空闲事件队列。您可以使用该after命令将内容放置在此队列中。
What you should do is write a function that does one iteration of your animation. Essentially, take everything in your while loop and move it to a function. Then, arrange for that function to be continually be called as long as there is work to do. You can either place the call to afterin that function, or have a separate function controlling the animation.
您应该做的是编写一个函数来执行一次动画迭代。本质上,将 while 循环中的所有内容都移到一个函数中。然后,只要有工作要做,就安排不断调用该函数。您可以after在该函数中调用,也可以使用单独的函数来控制动画。
Roughly speaking, the solution looks like this:
粗略地说,解决方案如下所示:
def do_one_frame(self, ...):
# do whatever you need to draw one frame
if (there_is_more_work_to_be_done):
self.after(10, do_one_frame)
This will draw one frame of your animation, check to see if there are any new frames to be drawn, and then arranges for the next frame to be drawn in 10ms. Of course, you can set that value to whatever you want in order to control the speed of the animation.
这将绘制您的动画的一帧,检查是否有要绘制的新帧,然后安排在 10 毫秒内绘制下一帧。当然,您可以将该值设置为任何您想要的值以控制动画的速度。
There are working examples of this technique on this website. For example, see https://stackoverflow.com/a/25431690/7432
本网站上有此技术的工作示例。例如,参见https://stackoverflow.com/a/25431690/7432
回答by Rooks103
First, I'm a bit confused about why ((2,3)(3,3)) would get you green and blue squares. Your color coding seems to indicate they would be blue and purple, would it not?
首先,我有点困惑为什么 ((2,3)(3,3)) 会让你得到绿色和蓝色方块。你的颜色编码似乎表明它们是蓝色和紫色,不是吗?
Second, I'm not fully sure I understand the statement "and then their colours should be changed accordingly to the next node in my tuple". Does that mean at one point you are going to pass in ((2,3)(3,3)) and expect to get 4 squares, then the next time pass in ((2,3)(3,3)(1,2)) and you would expect 6 squares to be drawn in blue?
其次,我不完全确定我理解“然后它们的颜色应该相应地更改为我的元组中的下一个节点”的说法。这是否意味着您将在某一时刻传入 ((2,3)(3,3)) 并期望获得 4 个方格,然后下一次传入 ((2,3)(3,3)(1 ,2)) 并且您希望用蓝色绘制 6 个正方形?
Third, what is the output of your program? It seems as though you have enough print statements where you should be able to figure out where the problem lies.
第三,你的程序的输出是什么?似乎您有足够的打印语句,您应该能够找出问题所在。
Taking a guess without fully understanding the program, I would guess the problem is with one of your for loops not iterating over the proper value, which is causing not all your squares to be drawn. My guess is the first one:
在没有完全理解程序的情况下进行猜测,我猜想问题在于您的一个 for 循环没有迭代正确的值,这导致并非所有的方块都被绘制。我的猜测是第一个:
for i in range(len(path)) :
But that is really a guess since like I said, I don't fully understand what is happening. I'll do my best to help if you can answer some of my questions. Sorry I'm not more help.
但这真的是一个猜测,因为就像我说的那样,我不完全明白发生了什么。如果您能回答我的一些问题,我会尽力提供帮助。对不起,我没有更多的帮助。

