doglas doglas - 21 days ago 5
Python Question

python + tkinter: How to use Canvas for the main function and for a subfunction both?

It should be a easy question but I just didnot find the answer.

I have made a main function, in which a button and a line are drawn. I have also a subfunction, which connects to the button in the main function.

By clicking the button I want to have an another line drawn into the same window, and the original line should be erased.

Here is the code I wrote. I really dont know how to finish it. Any kind of help is highly appreciated. Thanks.

import Tkinter as tk

def DrawFunc():
x1 = 20 ; y1 = 20
x2 = 60 ; y2 = 80

S_Canvas = tk.Canvas(root)
S_Canvas.pack()

S_Canvas.create_line(x1, y1, x2, y2, fill="black") # When the button is clicked, this line should be shown. And the old line should be deleted.


def Window():
global root
root = tk.Tk()
root.geometry("400x400")

S_Canvas = tk.Canvas(root)
S_Canvas.pack()
S_Canvas.create_line(50, 250, 250, 70, fill="red") # When the program is run, this line is shown.

Frame_1 = tk.Frame(root)
Frame_1.place(width=120, height=80, x=0, y=0)
Button_1 = tk.Button(Frame_1, text = u"Draw", width = 20, height=10, bg= "green" ,command = DrawFunc())
Button_1.pack()

root.mainloop()
Window()

Answer

To draw in the same canvas, your DrawFunc function must know that canvas.

The quickest solution is to add a targetCanvas parameter to your DrawFunc function, and to pass it S_Canvas.

I might remember bad, but I think that tkinter does not support directly passing arguments to callback functions. As a workaround, you can use a lambda function:

Button_1 = tk.Button(Frame_1,
                     text = u"Draw",
                     width = 20,
                     height=10,
                     bg= "green",
                     command = lambda: DrawFunc(targetCanvas))

If however you want to reuse this command, you must define a draw_func_command function, that takes no argument:

def draw_func_command():
    global S_Canvas
    DrawFunc(S_Canvas)

But then, you need to declare S_Canvas as global...

To avoid this, the general way to implement UI elements is to declare them as classes. I am not going to develop this here though, because it's a broad topic and it's a bit beyond the question.

To delete the first line, it's a bit tricky. Actually, the Canvas.create_line returns an object, that is the line drawn. To delete it, you must store it in a variable, then call the delete function on it.

The problem with your code is that the function are not in the same class, so the variables must be passed from each other...

As for the erasing canvas problem, add a oldLine argument to your DrawFunc function, then call oldLine.delete() in its body.

currentLine = S_Canvas.create_line(50, 250, 250, 70, fill="red")
Button1 = Button(..., command = lambda: DrawFunc(targetCanvas, currentLine))
Comments