简体   繁体   中英

Python Tkinter refresh canvas

Hello I have a tuple in python with colours that are related to squares that are drawn in the canvas by the following dictionary:

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:

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?

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?

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.

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 after command.

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 after in that function, or have a separate function controlling the animation.

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.

There are working examples of this technique on this website. For example, see https://stackoverflow.com/a/25431690/7432

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?

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?

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 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM