Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 802
JmRag Avatar asked Jan 25 '14 22:01

JmRag


People also ask

How do I refresh tkinter?

Go to app/extension store (Chrome Web Store, Firefox Add-Ons, Microsoft Edge Add-ons Store, etc.). Enter “auto-refresh” in the search bar. Choose an extension. Follow the prompts to download and install the extension onto your browser toolbar.

How do I redraw a canvas in Python?

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.

How to update image on canvas Tkinter?

To change a particular image, we can configure the canvas by using the itemconfig() constructor. It takes image files which need to be updated and displayed them on the window. Use three images of your choice and save them in the same project directory.

What does update () do in tkinter?

Update method processes all the pending idle tasks, unvisited events, calling functions, and callbacks. The method is applicable for updating and processing all the events or tasks such as redrawing widgets, geometry management, configuring the widget property, etc.


1 Answers

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

like image 80
Bryan Oakley Avatar answered Oct 10 '22 06:10

Bryan Oakley