TL;DR Full Python notebook and images in my GitHub repo here.

Coming up in the text below: 1. Create a simple QR code without styling. 2. QR code data: URL, VCard, iCal 3. Styling — Colors and sizes — Module styling — Eye styling — Adding logos

Creating a simple QR Code:

import qrcode
img = qrcode.make('Some data here')
type(img) 
img.save("qrcode1.png")

vCard

The data provided in the QR code is usually a URL but it can be anything including a VCard which is useful for sharing contact details on mobile devices and an example of that data is shown below. For more info on how to add other data fields to VCards checkout the article on them on Wikipedia.

import qrcode
img = qrcode.make('''
BEGIN:VCARD
VERSION:3.0
N:Gump;Forrest;;Mr.;
FN:Forrest Gump
ORG:Bubba Gump Shrimp Co.
TEL;TYPE=Work:011 555-1212
TEL;TYPE=mobile:072 434-1202
ADR;TYPE=Work,PREF:;;100 Waters Edge;Baytown;LA;30314;United States of America
LABEL;TYPE=Work,PREF:100 Waters Edge\nBaytown\, LA 30314\nUnited States of America
EMAIL;TYPE=Work:[email protected]
END:VCARD
''')
type(img)
img.save("vcard.png")

iCalendar

Or by creating a iCal booking for others to add to their calendars. For more info on iCal or .ics files, checkout this resource.

import qrcode
img = qrcode.make('''
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
SUMMARY:Lunchtime meeting
DTSTART;TZID=America/New_York:20230420T120000
DURATION:PT1H
LOCATION:Meeting Room 1
END:VEVENT
END:VCALENDAR
''')
type(img)
img.save("vcal.png")

Styling

There are many ways to style a QR code especially using the qrcode python package. First, each styling parameter will be explained that is built into the package and following that I'll explain some custom styling features I have added.

Included parameters are: Version, Error correction, Box size, Border, Fill color, Background color, Module drawer, Embedded image, Color mask, Eye drawers.

Version

The size of the QR code is set by an parameter called the version. This parameter sets how many modules (black or white boxes) there are per side. The smallest version, version 1, has 21 modules per size which means the entire image is 21x21 modules. The size increases by 4 modules per side per version up to the maximum which is version 40 and is 177x177 in size.

None
Various QR Code version sizes
None
Version sizes from version 1 to 40

Error correction

This parameter looks at how many "junk" or unreadable modules are allowed in the image. Based on a mathematical error correction algorithm the short description is that the higher the error correction then the more physical damage the QR code can take and still be readable. This is great for codes to be used on flyers or outdoors that can still be readable even after a bit of weathering. For the most damage resistant QR code, set the error correction to H.

None
Error correction levels in a QR code

Box size

The box size determines the physical size of the QR code by setting how many pixels each box, or module, is.

Border

This is another simple setting which allows you to set the size of the boarder surrounding your QR code. It is measured in modules and helps add some extra whitespace to pad your QR code when generating the .png file.

Fill color

This is the color of the modules. This parameter can accept color strings i.e. "pink" or "red" as well as RGB color tuples.

Background color

This is the color of the background. This parameter can also accept color strings i.e. "pink" or "red" as well as RGB color tuples.

Module drawer

It is possible to style to modules themselves. This parameter allows you to do this in the various forms shown below.

None

Embedded image

It is also possible to embed an image to the center of the QR code such as a logo. This parameter allows you to do that with any .png file. It is preferred to start with a square image but if you do decide to use one that is not square then the qrcode package will compress the longest side of the image to match the shortest one and make it square.

None
Qrcode generated with an embedded image.

Color mask

The color mask allows you to add more complex color patterns to your qrcode such as gradients or even an image. This colors the modules of your qrcode and leaves the background.

None
Color Masking options from the qrcode package.

Eye drawers

The last piece of customization is the eyes. The three corner squares that are seen on the qrcodes serve a vital function of helping the reading device understand the orientation of the qrcode. The qrcode package has a built-in function to allow you to customize the eyes with various module drawers. In order to customize them further I added my own styling function which uses masking to allow you to decide not only the module drawer to use for your eye but also change the colors of the inner and outer eye.

None
Composition of the various images and masks to create the eye customization.

The code for the custom styling can be seen below.

import PIL
from PIL import Image, ImageDraw

#Custom function for eye styling. These create the eye masks

def style_inner_eyes(img):
  img_size = img.size[0]
  eye_size = 70 #default
  quiet_zone = 40 #default
  mask = Image.new('L', img.size, 0)
  draw = ImageDraw.Draw(mask)
  draw.rectangle((60, 60, 90, 90), fill=255) #top left eye
  draw.rectangle((img_size-90, 60, img_size-60, 90), fill=255) #top right eye
  draw.rectangle((60, img_size-90, 90, img_size-60), fill=255) #bottom left eye
  return mask

def style_outer_eyes(img):
  img_size = img.size[0]
  eye_size = 70 #default
  quiet_zone = 40 #default
  mask = Image.new('L', img.size, 0)
  draw = ImageDraw.Draw(mask)
  draw.rectangle((40, 40, 110, 110), fill=255) #top left eye
  draw.rectangle((img_size-110, 40, img_size-40, 110), fill=255) #top right eye
  draw.rectangle((40, img_size-110, 110, img_size-40), fill=255) #bottom left eye
  draw.rectangle((60, 60, 90, 90), fill=0) #top left eye
  draw.rectangle((img_size-90, 60, img_size-60, 90), fill=0) #top right eye
  draw.rectangle((60, img_size-90, 90, img_size-60), fill=0) #bottom left eye  
  return mask  

# Useage of the custom functions
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer,VerticalBarsDrawer,SquareModuleDrawer
from  qrcode.image.styles.colormasks import SolidFillColorMask

if not hasattr(PIL.Image, 'Resampling'):
  PIL.Image.Resampling = PIL.Image
# Now PIL.Image.Resampling.BICUBIC is always recognized.


qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)

qr.add_data('http://www.medium.com')

qr_inner_eyes_img = qr.make_image(image_factory=StyledPilImage,
                            eye_drawer=RoundedModuleDrawer(radius_ratio=1.2),
                            color_mask=SolidFillColorMask(back_color=(255, 255, 255), front_color=(63, 42, 86)))

qr_outer_eyes_img = qr.make_image(image_factory=StyledPilImage,
                            eye_drawer=VerticalBarsDrawer(),
                            color_mask=SolidFillColorMask(front_color=(255, 128, 0)))                            

qr_img = qr.make_image(image_factory=StyledPilImage,
                       module_drawer=SquareModuleDrawer())

inner_eye_mask = style_inner_eyes(qr_img)
outer_eye_mask = style_outer_eyes(qr_img)
intermediate_img = Image.composite(qr_inner_eyes_img, qr_img, inner_eye_mask)
final_image = Image.composite(qr_outer_eyes_img, intermediate_img, outer_eye_mask)
final_image.save('final_image.png')

Conclusion

There are plenty ways to style qrcodes and make them interesting for your target audience and Python is a great language to do it with! You can check out all the code on my GitHub repo here.

Loved this and want to say thanks? ☕ Buy me a coffee: https://www.buymeacoffee.com/reegan_anne