Alexandre Moreira
Alexandre Moreira

Reputation: 19

How to delete and recreate a canvas ? (TKINTER / CANVAS )

I have search for hours on the net how to delete and next recreate a "canvas" on Tkinter.

So first my code :

import tkinter as tk
import time
import numpy as np
m = 100
matrice = np.zeros((m,m))
matrice[10][10] = 1

root = tk.Tk()
root.title("Test")
root.geometry('1200x800')
canvas = tk.Canvas(width = 700 , height = 700)

def trace(m) :
    global canvas
    m2 = int(700/m)
    canvas.destroy()
    canvas = tk.Canvas(width = 700 , height = 700)
    for i in range(0,m) :
        for j in range(0,m) : 
            x = m2*i
            y = m2*j
            canvas.create_rectangle(x , y , m2+x , m2+y,outline = 'grey' , width = 1)

def affiche_points(m,matrice) :
    global canvas
    m2 = int(700/m)
    for i in range(m):
        for j in range(m):
            if matrice[i][j] == 1 :
                x = m2*i
                y = m2*j
                canvas.create_rectangle(x , y , m2+x , m2+y , fill = 'black')


def actualisation(m,matrice) :
    global canvas
    trace(m)
    affiche_points(m,matrice)
    canvas.pack()


actualisation(m,matrice)

for i in range(0,20) :

    matrice = np.zeros((m,m)) 
    matrice[i][10] = 1
    actualisation(m,matrice)
    time.sleep(0.5) 



root.mainloop()

I just want to create a grid and when an element in the matrix changes the grid changes too (0 = white and 1 = black) In my case i use

canvas.destroy()

to clear the window

and i wait 0.5 seconds with time.sleep() until i show the new image from the matrix

My problem is that i can't see the actualization :(

I just see the last frame ! I don't know if it is the canvas.pack() or something like that which can not be refreshed.

NB : This is a simplification for a bigger plan !

NB 2 : Sorry for my weird english i'm french you know...

THANK YOU

Upvotes: 1

Views: 3605

Answers (1)

Minion Jim
Minion Jim

Reputation: 1279

The Explanation

There are obviously two parts to this question:

  1. How to get a savable structure of details for every item currently on the canvas (manually keeping a log of what items we add and remove is a real pain, so we will ignore this as a solution)
  2. How to reconstruct the items from this saved state

For the first part of the problem, I believe there are a further three parts that need to be considered*, which are as follows:

  1. How to get the type of canvas object (line, rectangle, etc.). This turns out to be fairly easy as there is a Canvas.type method which we can use.
  2. How to get the coords of the item. This is not so obvious, and requires a little knowledge of Tcl to be able to get the solution. The solution is to use Canvas.coords and what the tkinter documentation doesn't explain is that if this method is called without any coords (only the item is provided), it will return the current coords of the given item (rather than changing them).
  3. How to get the options and their values for the item. Like above, the solution for this requires a little understanding of Tcl. The solution is to use Canvas.itemconfig without any arguments other than the item (like above), and this will return a dictionary containing all valid options for that type of canvas object and (among other things) the current value for that option.

The second part of the problem turned out to be much simpler as each of the Canvas.create_* methods simply calls the hidden method Canvas._create with the coords, kwargs and most interestingly, the object type as a string. For example, the call from Canvas.create_arc(*args, **kw) is Canvas._create('arc', args, kw), as easily seen in the tkinter source (the link is to what was the latest blob at the time of writing). Therefore, if we have done the retrieval part correctly, this part should be very simple to implement.

*If I have overlooked something, please leave a comment I will try to address it.

The Code

(for those of us too lazy to read the explanation!)

Here is my implementation of the above explanation:

import tkinter as tk

root = tk.Tk()
root.title("Canvas resume demo")
c = tk.Canvas(root, width=400, height=400)

# Create your canvas objects with some random coords and kwargs
c.create_rectangle(0, 0, 100, 100, fill="red", width=3)
c.create_line(0, 0, 100, 100)
c.create_polygon(90, 100, 100, 90, 100, 100, fill="blue")
# Items can also be deleted
c.delete(c.create_line(100, 100, 200, 200))

# Save the details into 'objs' (which can be pickle or json dumped)
objs = [(c.type(item),
            c.coords(item),
            {i: j[-1] for i, j in c.itemconfig(item).items()}) \
        for item in c.find_all()]

# Create a completely new canvas
c.destroy()
c = tk.Canvas(root, width=400, height=400)
c.pack()

# Resume from 'objs'
for item in objs:
    c._create(*item)

root.mainloop()

EDIT

Canvas objects which rely on another tkinter object (Canvas.create_image and Canvas.create_window) will not resume correctly if run in different instance (i.e. details saved and then resumed) as these objects will need to be reinitialised with the same Tcl name. For this, would would probably need to read the value for the relevant kwargs and then use a combination of Misc.nametowidget and Misc.config to be able to get required information.

Upvotes: 1

Related Questions