Title: Let the user scribble to make a drawing in Python and tkinter
When the program loads, it creates its widgets including a Canvas named canvas. That's where the user will draw.
The Canvas widget can display a Line object that represents a polyline (a sequence of connected line segments). Unfortuately, there doesn't seem to be a way to modify a Line after we create it, so we can't just add points to it. Instead we need to delete it and replace it with a new one.
After it creates its Canvas, the program uses the following code to prepare to draw.
# Initially we are not drawing a line.
self.line_points = None
self.current_line = None
self.num_lines = 0
self.line_colors = ['red', 'green', 'blue', 'black', 'purple', 'orange']
# Track Button-1 down, Button-1 motion, and Button-1 up.
self.canvas.bind("<Button-1>", self.mouse_down)
self.canvas.bind("<B1-Motion>", self.mouse_move)
self.canvas.bind("<ButtonRelease-1>", self.mouse_up)
This code sets line_points and current_line to None to indicate that we're not currently drawing anything. It sets num_lines to 0 and defines the colors that it will use to draw.
The code then binds the left mouse events to the three methods mouse_down, mouse_move, and mouse_up. That's where the most interesting work occurs.
Here's the mouse_down method.
def mouse_down(self, event):
'''Record the line's starting point.'''
self.line_points = [(event.x, event.y)]
This method sets line_points to a new list containing the mouse's current position. The tkinter Line object can't draw a single point, so the program doesn't draw anything yet.
The following code shows the mouse_move event handler.
def mouse_move(self, event):
# Ignore extraneous events.
if self.line_points is None: return
# If there is a current line, remove it.
if self.current_line is not None:
self.canvas.delete(self.current_line)
# Create a line.
self.line_points.append((event.x, event.y))
color = self.line_colors[self.num_lines % len(self.line_colors)]
self.current_line = self.canvas.create_line(self.line_points, fill=color)
This method first checks line_points and returns if no line drawing is in progress. That lets it ignore spurious mouse up events that I was seeing occasionally.
Next, the code checks current_line to see if we have already created a Line obect for this scribble. The first time the mouse moves with the left button down, we have not created that object yet so the program doesn't need to remove it. If current_line is not None, the progam removes it from the canvas.
The method then appends the mouse's current position to the line_points list. It picks the next color from the line_colors list and then creates the new Line object. Note that it saves the new object so it can remove it later.
The following code shows the last part of the scribble actio: the mouse_up event handler.
def mouse_up(self, event):
# Ignore extraneous events.
if self.line_points is None: return
# We are no longer creating a line.
self.line_points = None
self.current_line = None
self.num_lines += 1
Like the mouse_move method, this method first checks whether line_points is None and returns if it is.
The code then ends the current scribble by setting line_points and current_line to None. It also increments num_lines so we use the next color in the line_colors list for the next scribble.
Download the example to see all of the details.
|