PIL Tutorial: How to Create a Button Generator
Python Imaging Library (PIL) is an external library for Python. It supports opening, manipulating, and saving different image file formats. PIL is well documented and easy to install.
In this tutorial I'm focusing on the paste function. At the end of the tutorial, I'll explain a useful script that converts any icon into a web 2.0 style button, just like these:
The paste function
im.paste(im, box=None, mask=None)
The first argument can either be an Image object or a color. If it is an Image, the image will be pasted into this image. If it is a color, the region specified by the box argument will be filled with a single color.
The box argument defines the size and position of the pasting region. It can be a 2-tuple that defines the left upper corner, or a 4-tuple that defines the left, upper, right and lower pixel coordinate. It defaults to None, which translates to (0, 0, self.width, self.height)
When providing a mask argument, it only pastes the regions indicated by the mask. You can either use "1", "L" or "RGBA" images. In the latter case, the alpha band is used as mask. Where the mask is 255, the given source is copied as is. Where the mask is 0, the current value is preserved. Intermediate values can be used for transparency effects.
Examples
as a background for illustrative purposes only; it's not actually a part of the image itself.We have two images, a flower and a butterfly.

We want to paste the butterfly image on top of the flower. Since the butterfly image is smaller in size, we need to set the box argument. Otherwise, PIL will complain saying "Images do not match". I will set the box to (40, 10), which is roughly the top edge of the flower.
flower = Image.open('flower.png') butterfly = Image.open('butterfly.png') flower.paste(butterfly, (40, 10))
Now let's see how the result looks:

Oops, transparency was pasted on the flower along with the butterfly's image, definitely not what we want!
But that is to be expected; we asked to paste the image as it is. We should have specified a mask argument to tell PIL what to paste and what to leave.
If we specify a mask that is the same as the source then we will get the effect we want.
flower = Image.open('flower.png') butterfly = Image.open('butterfly.png') flower.paste(butterfly, (40, 10), butterfly)

Where the mask's alpha is 255 (opaque), the source is copied as it is. Where the alpha is 0 (transparent), the source is not copied.
Very simple button effect
Now let's say we want to create a very simple button effect. For this purpose we need an image with a transparent border effect.

border = Image.open('border.png') flower.paste(border, mask=border)

No, this is not what we want. Part of the transparency is being copied along with the border, but why?
Because as we learned in the previous example, transparency is not ignored by the paste function. In fact transparency in the source image is being treated as any other color band.
border = Image.open('border.png') source = border.convert('RGB') flower.paste(source, mask=border)

Now this worked!
Putting it all together
We'll put everything we learned together to create a web 2.0 style button.
First we need a mask image to crop the icon in the shape and size we want. Then, we need a highlight image to create the button effect.

The mask and highlight images need to be of the same size for our example to work.
Let's convert this Blogger icon into a button

highlight = Image.open('round.png') mask = Image.open('round-mask.png') # Read the icon and convert it into 'RGBA' in case it wasn't icon = Image.open('blogger.png').convert('RGBA') button = Image.new('RGBA', mask.size) # Resize Icon icon = ImageOps.fit( icon, highlight.size, method=Image.ANTIALIAS, centering=(0.5, 0.5) ) # Create a helper image that will hold the icon after the reshape helper = button.copy() # Cut the icon by the shape of the mask helper.paste(icon, mask=mask) # Fill with a solid color by the mask's shape button.paste((255, 255, 255), mask=mask) # Get rid of the icon's alpha band icon = icon.convert('RGB') # Paste the icon on the solid background # Note we are using the reshaped icon as a mask button.paste(icon, mask=helper) # Get a copy of the highlight image without the alpha band overlay = highlight.copy().convert('RGB') button.paste(overlay, mask=highlight) button.save('button.png')
And the result is:

You may wonder why ImageOps.fit was used instead of Image.resize. Image.resize will work perfectly fine with images that have the same aspect ratio as the new size, but will distort the image otherwise. ImageOps.fit will resize and crop the image to match the requested aspect ratio and size. You have the option to control the cropping position, which is the center (0.5, 0.5) in our case.
You have to keep in mind that if we want our script to handle icons with transparent backgrounds, we need to specify a background color that fills the transparent spaces, or we'll end up with buttons that look like this:

This is why I fill the button's background with a solid color before pasting the icon.
Web 2.0 style buttons generator
Now here is the complete script with command line arguments support using getopt module
Example of usage:
./make-button.py feed.png ./make-button.py delicious.gif blogger.png butterfly.png --shape round ./make-button.py butterfly.png --shape heart --color 200 120 22
I've also attached the full script with helper images in a zip file for convenience. The helper images include round, heart and square shaped button effects.
#!/usr/bin/python # Button generator script # Author: Nadia Alramli http://nadiana.com from PIL import Image import ImageOps from optparse import OptionParser import os.path def create_button(iconPath, shape, bgColor): highlight = Image.open('helper/%s.png' % shape) mask = Image.open('helper/%s-mask.png' % shape) # Read the icon and convert it into 'RGBA' in case it wasn't icon = Image.open(iconPath).convert('RGBA') button = Image.new('RGBA', mask.size) # Resize Icon icon = ImageOps.fit( icon, highlight.size, method=Image.ANTIALIAS, centering=(0.5, 0.5) ) # Create a helper image that will hold the icon after the reshape helper = button.copy() # Cut the icon by the shape of the mask helper.paste(icon, mask=mask) # Fill with a solid color by the mask's shape button.paste((255, 255, 255), mask=mask) # Get rid of the icon's alpha band icon = icon.convert('RGB') # Paste the icon on the solid background # Note we are using the reshaped icon as a mask button.paste(icon, mask=helper) # Get a copy of the highlight image without the alpha band overlay = highlight.copy().convert('RGB') button.paste(overlay, mask=highlight) iconPath, ext = os.path.splitext(iconPath) button.save('%s-%s.png' % (iconPath, shape)) def main(): usage = 'usage: %prog file [file] [option]' parser = OptionParser(usage) parser.add_option('-s', '--shape', dest='shape', help='button shape') parser.add_option( '-c', '--color', dest='color', help='background color', nargs=3, type= int ) parser.set_defaults(shape='square', color=(255, 255, 255)) options, args = parser.parse_args() if len(args) < 1: parser.error('Missing file operand') shape = options.shape bgColor = options.color for arg in args: iconPath = arg create_button(iconPath, shape, bgColor) if __name__ == '__main__': main()
With this script you can add as many button effects as you want. All you need is two images: a mask and a highlight.
Let's say the style you want is a "star" then name the highlight image as star.png and the mask image as star-mask.png and place both of them in the helper folder.
Then run:
./make-button.py icon.png --shape star
| Attachment | Size |
|---|---|
| button_generator.zip | 60.1 KB |
- 12715 reads



Comments
Great posts!
Thanks a lot for making these. Discovering ImageOps.fit by reading this post has sure made my life a lot easier. I've just hacked about 10 lines of code out of several scripts!
very informative
excel tutorial
was looking for a function that returns the picture taken date, can you help?
thanks
Thanks
Thank you, Nadia.
I've been having some trouble with the alpha layer being copied along until I read your explanation. Thanks =)
Thank you, Nadia.
Thank you, Nadia.
Awesome would be great to
Awesome
would be great to extend this to create the needed image files from code, instead of needing an editor :)
Nadia, The last bit of the
Nadia,
The last bit of the script looks like an typo to me:
"if name == 'main':"
Very interesting tutorial overall, though.
Ah, that's because a newly
Ah, that's because a newly installed filter is breaking up existing code. I'll remove the filter. Thanks for the note!
hi! i would like to use this
hi! i would like to use this script, with some modifications, for a desktop widget.
can i change it? Obviously I will write your name as the original author, with a link to your site. :)
Sure, you can change it. No
Sure, you can change it. No problem :)
Post new comment