[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Let the user draw circles in Python and tkinter

[This program lets the user draw circles in Python and tkinter.]

This example uses mouse down, mouse move, and mouse up events to let the user draw circles in Python and tkinter. When it starts, the program initializes several variables that it will later use to draw circles.

# Initially we are not drawing a circle. self.start_point = None self.end_point = None self.current_circle = None

When the users presses the left mouse button down, the following code prepares to draw a circle.

def mouse_down(self, event): '''Record the circle's starting point.''' self.start_point = (event.x, event.y)

This code simply saves the mouse's position.

When the user moves the mouse with the left button down, the following code executes.

def mouse_move(self, event): # Ignore extraneous events. if self.start_point is None: return # If there is a current new circle, remove it. if self.current_circle is not None: self.canvas.delete(self.current_circle) # Create the new temporary circle. self.end_point = self.get_circle_endpoint(self.start_point, (event.x, event.y)) self.current_circle = self.canvas.create_oval( self.start_point, self.end_point, outline='red')

If self.start_point is None, then the user is not currently drawing a circle so the method returns. This should not be the case (this event handler should not be called unless the mouse button is down), but I was getting spurious mouse move events so this lets the code ignore them.

If the user is drawing a circle, the code deletes the previously drawn temporary circle if there is one. It then calls get_circle_endpoint (described shortly) to get a point that it can use with the start point to define the circle. The code then uses the canvas's create_oval method to make the new temporary circle, saving that object in self.current_circle.

When the user releases the mouse button, the following code executes.

def mouse_up(self, event): # Ignore extraneous events. if self.start_point is None: return # If there is a current new circle, remove it. if self.current_circle is not None: self.canvas.delete(self.current_circle) # Create the permanent circle. self.end_point = self.get_circle_endpoint(self.start_point, (event.x, event.y)) self.current_circle = self.canvas.create_oval( self.start_point, self.end_point, outline='green') # We are no longer creating a circle. self.start_point = None self.end_point = None self.current_circle = None

Like the mouse_move method, this code ignores extraneous events and removes the current temporary circle if there is one. It also calls get circle_endpoint as before but this time it doesn't save the new oval. The method finishes by setting its circle-drawing variables to None so later mouse move and up events know that the user is no longer drawing a circle.

The following code shows the circle_endpoint method.

def get_circle_endpoint(self, start_point, end_point): '''Return the other corner for the circle.''' # Get the width and height. dx = end_point[0] - self.start_point[0] dy = end_point[1] - self.start_point[1] # Use the larger of the width and height. if abs(dx) < abs(dy): # Make dx have the size of dy but in the direction of dx. dx = math.copysign(dy, dx) else: # Make dy have the size of dx but in the direction of dy. dy = math.copysign(dx, dy) return (start_point[0] + dx, start_point[1] + dy)

This method returns a point that the program can use, together with self.start_point, to define the circle's bounding box. It first calculates the distance the mouse has moved from the start point.

Next, the code makes dx and dy the same length as the larger of those two values. For example, if dx is 100 and dy is 200, then the program makes the circle 200 pixels wide and tall.

The code uses math.copysign to make the smaller distance (dx or dy) the same length as the other distance but pointed in its original direction. For example, is dx is 100 and dy is -200, then the code sets dx (which is smaller in length) to 200 (the length of dy but in the negative direction).

The method then uses the adjusted distances to calculate the circle's other corner and returns it.

Download the example to see all of the details.

© 2024 Rocky Mountain Computer Consulting, Inc. All rights reserved.