Styled QR Code Generator

Author: @Steve T

Install Dependencies

pip install lona lona-picocss qrcode[pil]

Source Code

example_make_qr.py

"""
    Styled QR code generator

    Using Lona
"""

from lona_picocss.html import (
    InlineButton,
    TextInput,
    Label,
    HTML,
    Card,
    Img,
    H1,
)
from lona_picocss import install_picocss
import make_qr

from lona.html import Select2, Option2
from lona import View, App

app = App(__file__)

install_picocss(app, debug=True)


def select_add_options(select2_node, select_options):
    """
    helper function to populate a Select2 node with select_options

    must have:
        from lona.html import Select2, Option2

    option is the text that will used in the webpage
    value is the value returned when the user selects

    select_options = [ {"option": <option>, "value": <value> }, ... ]

    eg.
    select_options = [ {"option": "Option 1", "value": 1 }, ... ]
    """

    for item in select_options:
        select2_node.append(Option2(item["option"], value=item["value"]))


@app.route("/")
class Index(View):
    def generate_qr(self):
        """
        make a new qr code

        update img_qr src
        """

        self.img_qr.attributes["src"] = make_qr.image_src_str(
            self.textinput_url.value, self.select_qr_type.value
        )

    def handle_textinput_url_change(self, input_event):
        """
        textinput_url has changed
        generate qr code, update img_qr

        hide card_qr_img if no url
        """

        if not input_event.node.value:
            self.card_qr_img.hide()
        else:
            self.generate_qr()
            self.card_qr_img.show()

    def handle_select_qr_type_change(self, input_event):
        """
        qr_type has changed

        generate qr code, update img_qr
        """

        self.generate_qr()
        self.card_qr_img.show()

    def handle_request(self, request):
        # img (qr image)
        self.img_qr = Img(
            # center the qr image
            _style={"display": "block", "margin-left": "auto", "margin-right": "auto"}
        )

        # textinput (url)
        self.textinput_url = TextInput(
            placeholder="eg. https://example.com",
            handle_change=self.handle_textinput_url_change,
        )

        # select (qr_type)
        self.select_qr_type = Select2(handle_change=self.handle_select_qr_type_change)
        select_add_options(self.select_qr_type, make_qr.select_options())

        # card for qr code (for qr_img)
        self.card_qr_img = Card(self.img_qr)
        # initally, hide card_qr_img, show it when user clicks button to generate qr
        self.card_qr_img.hide()

        # card for qr code parameters (url, qr_type)
        self.card_qr_inputs = Card(
            Label("URL ", self.textinput_url),
            Label("QR Type ", self.select_qr_type),
        )

        return HTML(
            H1("Styled QR Code Generator"),
            self.card_qr_inputs,
            self.card_qr_img,
        )


if __name__ == '__main__':
    app.run()

make_qr.py

"""
    make styled QR code
"""

from io import BytesIO
import base64

from qrcode.image.styles.moduledrawers.pil import (
    GappedSquareModuleDrawer,
    HorizontalBarsDrawer,
    RoundedModuleDrawer,
    VerticalBarsDrawer,
    SquareModuleDrawer,
    CircleModuleDrawer,
)
from qrcode.image.styledpil import StyledPilImage
# https://pypi.org/project/qrcode/
import qrcode

MODULES = {
    "Square": SquareModuleDrawer(),
    "Gapped Square": GappedSquareModuleDrawer(),
    "Circle": CircleModuleDrawer(),
    "Rounded": RoundedModuleDrawer(),
    "Vertical Bars": VerticalBarsDrawer(),
    "Horizontal Bars": HorizontalBarsDrawer(),
}

IMG_FORMAT = "png"


def select_options():
    """
    return list of options
        [ {"option": <option>, "value": <value> }, ...  ]
    so that select (lona.html.Select2) can be built

    ie.
        [ {"option": "Square", "value": "Square" }, ... ]
    """

    return [{"option": k, "value": k} for k in MODULES.keys()]


def image_src_str(url, qr_type=None):
    """
    generate qr code of url

    qr_type:
        "Square"
        "Gapped Square"
        "Circle"
        "Rounded"
        "Vertical Bars"
        "Horizontal Bars"

    return string that can be used for src in <img> tag
    """

    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )

    qr.add_data(url)
    qr.make(fit=True)

    # set module_drawer (defaults to SquareModuleDrawer)
    module_drawer = SquareModuleDrawer()
    if qr_type in MODULES:
        module_drawer = MODULES[qr_type]

    img = qr.make_image(image_factory=StyledPilImage, module_drawer=module_drawer)

    img_bytes = BytesIO()
    img.save(img_bytes, format=IMG_FORMAT)
    img_encoded = base64.b64encode(img_bytes.getvalue()).decode("utf-8")

    return f"data:image/{IMG_FORMAT};base64," + img_encoded