Title: Reflect images to make little flags in Python
This is a silly example, or at least it has a silly origin. I wanted some little flags on toothpicks, a few each for six different countries. You can easily buy these online, but they tend to come in packs of 100 and I didn't want more than 500 leftover flags. So I decided to make my own by printing the flags, cutting them out, and gluing them to toothpicks. To make them come out correctly, I wanted to reflect them as shown in the picture. That's where this post comes in.
There are two main pieces to this example: the make_mirrored_image function that makes a mirror image and the main program's code that uses it.
Image Reflections
The idea is pretty simple:
- Load a flag image
- Make a new image that's twice as wide
- Draw the original flag on the right and a reflected version on the left
- Save the result into a file
Here's the code that does it.
def make_mirrored_image(image):
'''Make a mirrored flag image.'''
# Make the result image.
wid = image.width
hgt = image.height
new_image = Image.new(image.mode, (2 * wid, hgt))
# Draw the original image on the right.
new_image.paste(image, (wid, 0))
# Flip and draw the reflection on the left.
flpped_image = ImageOps.mirror(image)
new_image.paste(flpped_image, (0, 0))
# Return the result.
return new_image
This function first gets the image's dimensions and then creates a new image with the same mode (RGB, RGBA, etc.) as the original but twice as wide.
Next, the code draws the original image onto the right half of the new image. It then uses ImageOps.mirror to make a mirror image of the original, and draws it on the left side of the new image.
The function then returns the result. It's really not too hard.
Using Reflections
When you invoke the File menu's Open command, the following code executes.
def mnu_open(self):
'''Open a file.'''
filetypes = [
('image Files', '*.png *.jpg *.gif *.bmp'),
('All Files', '*,*')]
filename = askopenfilename(filetypes=filetypes)
if len(filename) == 0:
return
# Make the mirror image.
image = Image.open(filename)
mirrored_image = make_mirrored_image(image)
# Save the result in a file.
name, extension = os.path.splitext(filename)
filename = name + '_mirrored' + extension
mirrored_image.save(filename)
# Size the image to fit the canvas.
self.canvas.update() # Force update of the widget's geometry
hgt = mirrored_image.height
wid = mirrored_image.width
new_wid = self.canvas.winfo_width()
new_hgt = self.canvas.winfo_height()
if wid / hgt > new_wid / new_hgt:
# The target area is too tall and thin. Make it shorter.
new_hgt = int(new_wid / (wid / hgt))
else:
# The target area is too short and wide. Make it thinner.
new_wid = int(wid / hgt * new_hgt)
mirrored_image = mirrored_image.resize((new_wid, new_hgt),
Image.Resampling.LANCZOS)
# Display the result.
self.canvas.delete(tk.ALL)
x = (self.canvas.winfo_width() - new_wid) // 2
y = (self.canvas.winfo_height() - new_hgt) // 2
self.tk_image = ImageTk.PhotoImage(mirrored_image)
self.canvas.create_image(x, y, image=self.tk_image, anchor=tk.NW)
This code first uses an askopenfilename dialog to let you pick the flag's image file. It opens the file and calls make_mirrored_image to make the mirrored image.
Next, the code saves the result in a file with the same name as the original with "_mirrored" added. For example, if you open the file philippines.png, the program saves the result as philippines_mirrored.png. (I considered letting you pick where to save the result, but this is easier and seems simple enough.)
The rest of the method displays the mirrored result. It gets the dimensions of the Canvas widget where it will display the image and adjusts the image's aspect ratio so it fits without distortion.
The code then deletes any previous image on the canvas, converts the image into an ImageTk.PhotoImage, and displays it on the canvas. (Note how the code saves a reference to the PhotoImage in a non-volatile variable. You must do this because tkinter needs a copy of the image whenever it needs to display it. If you don't save the image somewhere safe, it may be garbage collected when tkinter needs it so it won't be able to show the image.)
Conclusion
This example is only useful under very specific conditions so you may never need it. It does, however, show how to create an image, flip an image, and draw one image on another. Download the example to experiment with it and to see additional details.
|