Python tkinter delete event.widget.create_line

I have written a simple paint program and I'm having trouble with a task. I want to be able to delete/remove lines that have been drawn. When ButtonPress-1 is down is creates a line until ButtonRelease-1 is triggered (ButtonPress-1 is up). When pressing the 'Reset' button I want the line to be deleted/removed. I currently have it so when I press 'Reset' the program restarts itself, which is a bad solution.

from Tkinter import *
import tkMessageBox
import sys
import os
import subprocess

b1 = "up"
xold, yold = None, None
color= "black"
linesize = 2

def main():
    global root
    root = Tk()
    global drawing_area
    drawing_area = Canvas(root, width=1050, height=1200, background="white")
    drawing_area.pack()
    drawing_area.bind("<Motion>", motion)
    drawing_area.bind("<ButtonPress-1>", b1down)
    drawing_area.bind("<ButtonRelease-1>", b1up)

    button1 = Button(root, text = "Reset", command = restart_program, anchor = N)
    button1.configure(width = 3, background = "#FFFFFF", relief = FLAT)
    button1_window = drawing_area.create_window(640, 0, anchor=N, window=button1)

    root.geometry('1050x1200')
    root.geometry('+0+720')
    root.mainloop()

def restart_program():
    python = sys.executable
    os.execl(python, python, * sys.argv)

def b1down(event):
    global b1
    b1 = "down"

def b1up(event):
    global b1, xold, yold
    b1 = "up"
    xold = None
    yold = None

def motion(event):
    if b1 == "down":
        global xold, yold
        if xold is not None and yold is not None:
            event.widget.create_line(xold,yold,event.x,event.y,smooth=TRUE,fill = color, width=linesize)
        xold = event.x
        yold = event.y

if __name__ == "__main__":
    main()

Upvotes: 0

Views: 3328

Answers (1)

James Kent
James Kent

Reputation: 5933

if you want it to delete all items on the canvas you can call canvas.delete("all")
Or you can create the lines with a tag, and then delete by tag

so you could change:

event.widget.create_line(xold,yold,event.x,event.y,smooth=TRUE,fill = color, width=linesize)

to:

event.widget.create_line(xold,yold,event.x,event.y,smooth=TRUE,fill = color, width=linesize, tag="line")

and then call: canvas.delete("line")

this will delete all items with that tag.

EDIT: further to the origional request here is a code sample that allows simple undo/redo functions, note the following drawbacks:

  1. as you undo more items the stored list will grow continually, it is not limited to any set size
  2. if you undo an item and then draw a new item manually, redo will still put back the removed line

    import Tkinter as tk
    import tkMessageBox
    import sys
    import os
    
    class App(tk.Tk):
        b1 = "up"
        xold, yold = None, None
        color= "black"
        linesize = 2
        counter = 1 #keeps track of the current working line, is incremented as soon as line is finished
        undone = [] #keeps a list of coordinate lists on undone items
    
        def __init__(self):
            tk.Tk.__init__(self)
            self.drawing_area = tk.Canvas(self, width=600, height=600, background="white")
            self.drawing_area.pack()
            self.drawing_area.bind("<Motion>", self.motion)
            self.drawing_area.bind("<ButtonPress-1>", self.b1down)
            self.drawing_area.bind("<ButtonRelease-1>", self.b1up)
    
            self.button1 = tk.Button(self, text = "Reset", command = self.blank_canvas, anchor = tk.N)
            self.button1.configure(width = 3, background = "#FFFFFF", relief = tk.FLAT)
            self.button1.pack(side="left")
    
            self.button2 = tk.Button(self, text = "Undo", command = self.undo, anchor = tk.N)
            self.button2.configure(width = 3, background = "#FFFFFF", relief = tk.FLAT)
            self.button2.pack(side="left")
    
            self.button3 = tk.Button(self, text = "Redo", command = self.redo, anchor = tk.N)
            self.button3.configure(width = 3, background = "#FFFFFF", relief = tk.FLAT)
            self.button3.pack(side="left")
    
        def blank_canvas(self):
            self.drawing_area.delete("line")
    
        def undo(self):
            self.counter -= 1 #decrements the counter to look at the previous item
            currentlist = [] #creates a list to store the coordinates in
            for item in self.drawing_area.find_withtag("line"+str(self.counter)): #find all sub lines from the previous line
                currentlist.append(self.drawing_area.coords(item)) #get and add the coordinates to the working list
            self.drawing_area.delete("line"+str(self.counter)) #delete all items of the current line
            self.undone.append(currentlist) #add the working list to the stored list
    
        def redo(self):
            try:
                currentlist = self.undone.pop() #fetch and remove last set of coordinates
                for coords in currentlist: #for set of coordinates redraw subline
                    self.drawing_area.create_line(coords,smooth=tk.TRUE,fill = self.color, width=self.linesize, tags=["line", "line"+str(self.counter)])
                self.counter += 1 #re increment counter
            except IndexError:
                pass #occurs if list is empty
    
        def b1down(self, event):
            self.b1 = "down"
    
        def b1up(self, event):
            self.b1 = "up"
            self.xold = None
            self.yold = None
            self.counter += 1
    
        def motion(self, event):
            if self.b1 == "down":
                if self.xold is not None and self.yold is not None:
                    event.widget.create_line(self.xold,self.yold,event.x,event.y,smooth=tk.TRUE,fill = self.color, width=self.linesize, tags=["line", "line"+str(self.counter)])
                self.xold = event.x
                self.yold = event.y
    
    if __name__ == "__main__":
        app = App()
        app.mainloop()
    

Upvotes: 4

Related Questions