The Magic of Python – qrcodes.py

Sicherlich gibt es viele Wege QR-Codes zu erzeugen, allerdings ist Python für mich ein sehr angenehmer. Wenn ihr einen anderen bevorzugt, ist das natürlich vollkommen ok und ihr müsst dieses Tutorial vermutlich sowieso nicht lesen.

Wir gebrauchen hier die geradezu unendliche Macht zweiter Pakete, nämlich Pillow und natürlich qr-code, die wir beispielsweise folgendermaßen installieren (das kann je nach Entwicklungsumgebung ein bisschen anders sein, aber das schafft ihr schon)

pip install pillow
pip install qrcode

Nun müssen wir uns ein paar Dateien und ein Verzeichnis in unserem Arbeitverzeichnis erstellen, um die ganze Magie auch Realität werden zu lassen. Wir benötigen:

qrcodes – in diesem Verzeichnis landen später die erzeugten QR-Codes
index.php – hier wird später ein Script zur Weiterleitung zu finden sein
songs.json – die vorhin erzeugte JSON-Datei mit unseren handverlesenen Songs
qrcodes.py – das Script zur Erzeugung der QR-Codes
Raleway-ExtraBold.ttf – sucht euch eine hübsche Schriftart aus, beispielsweise auf 1001freefonts.com. Ich entschied mich für Raleway, aber das hat keinen besonderen Grund.

Nun aber endlich wirklich zu dem guten Zeug, den QR-Codes. Ich werde euch mein Script Schritt für Schritt erklären und hier und da ein paar Tweaks aufzeigen. Aber keine Sorge, auf der nächsten Seite bekommt ihr mein komplettes Script zum einfachen Kopieren, Einfügen, Ausführen und Wohlfühlen.

import qrcode
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import json

## Variablen
songs = json.load(open("songs.json"))
strFont = "Raleway-ExtraBold.ttf"
strBGcolor = "#ffffff"
strFGcolor = "#1a1a1a"
Code language: Python (python)

Wir importieren uns hier die eben installierten Pakete (PIL ist Pillow) und dazu noch json, weil wir uns ja extra die (na ja) Mühe machten, unsere Tabelle umzuwandeln. In Zeile 8 des Snippets seht ihr, warum wir das gemacht haben. Hier laden wir nämlich einfach die Datei und machen daraus ein JSON, das wir gleich einfach durch iterieren können. Hätten wir die ursprüngliche Tabelle benutzt, wäre das alles wesentlich aufwändiger und unschöner gewesen.
Außerdem sehen wir hier in Zeile 9 die gewählte Schriftart. Die beiden darauf folgenden Zeilen behandeln die Farbe der Codes. Ich entschied mich für einen weißen Hintergrund (BG) und einen dunkelgrauen und nicht so langweilig-schwarzen Vordergrund (FG).

for song in songs:
    ## Noch mehr Variablen!
    strGuid = song["Guid"]
    strDay = song["Day"]
    strClaim = song["Claim"]
Code language: Python (python)

Apropos durch iterieren, das machen wir doch direkt und holen uns aus dem JSON die Felder, die wir benötigen. An der Stelle haben wir ein bisschen Glück, dass alle Felder des JSONs Strings sind. Daher sind wir mit der Variablen strDay safe. Sollte es doch wider erwarten eine Zahl sein, schreibt für die Zuweisung einfach str(song[“Day”]) oder setzt halt im JSON Anführungszeichen um die Zahlen für den Tag.

    ## QR-Code erstelen
    qr = qrcode.QRCode(
        version=4,
        error_correction=qrcode.constants.ERROR_CORRECT_Q,
        box_size=20,
        border=4,
    )

    qr.add_data('https://www.friedersdorf.de/adventskalender/index.php?tag='+strGuid)
    qr.make(fit=True)
    img = qr.make_image(fill_color = strFGcolor, back_color=strBGcolor).convert('RGB')

Code language: Python (python)

Das ist der Moment, auf den ihr alle gewartet habt – die QR-Codes werden erzeugt! Yeah! Für die Variable qr greifen wir auf den Constructor des Pakets qrcode zu. Falls ihr euch in die Parameter einlesen wollt, dann sehr gerne, aber im Grunde beschreiben wir hier, wie der Code am Ende aussehen soll. Beachtenswert ist hier vor allem der Parameter error_correction. Statt des Q könnten wir hier auch ein L (für 7% Fehlerkorrektur), M (15%) oder H (30%) wählen. Vermutlich hätte eine geringere Fehlerkorrektur gereicht, aber mit den 25% vom Q sieht es gut aus und funktioniert vor allem, wenn wir gleich in den QR-Code malen.

Nun sagen wir der Variablen qr mittels add_data, was wir eigentlich auf ihr speichern möchten und das ist natürlich der Link zu unserer index.php mitsamt eines Parameters, den wir dann später mit PHP auswerten möchten. Das hier ist nur ein Beispiel und ihr könnt den Parameter nennen, wir ihr möchtet. Ihr könntet im Grunde auch direkt auf den Spotify-Link verweisen, allerdings sollten wir für die neugierigen eurer liebsten Personen natürlich auch noch ein paar Prüfungen einbauen, dass sie nicht “versehentlich” zu früh Türchen öffnen.
Am Ende erzeugen wir mit der Funktion make_image aus unserem qr-Objekt ein Bildobjekt und geben dafür noch die vorher definierten Farben mit. Würden wir nun jetzt schon mit imgNew.save(“qrcodes/” + strGuid + “.png”) das Bild abspeichern und die Schleife beenden, würde das Ergebnis beispielsweise so aussehen:

Ein einfacher QR-Code – yeah!

Das ist uns aber natürlich nicht gut genug und sollte es auch nicht sein. Außerdem ist bei einem Mixtape natürlich auch immer die Reihenfolge wichtig und woher sollten wir wissen, welcher Song das gerade ist? Lasst uns doch also noch die Nummern der Tage rein schreiben. Ja, direkt hinein!

Was man nämlich über QR-Codes wissen muss, ist Folgendes: Die vier großen Quadrate müssen sichtbar sein, damit das lesende Gerät weiß, dass es ein QR-Code ist, wobei das kleinere unten rechts die Ausrichtung angibt. Außerdem haben wir eine recht hohe Fehlertoleranz gewählt, was eigentlich bedeutet, dass so ein Knick oder ein Kratzer auf dem Code durchaus verkraftbar ist. Vor allem bedeutet das aber, dass wir etwas hinein zeichnen können, weil die Daten redundant im Code gespeichert sind. Sicherlich kann man noch mehr über QR-Codes wissen, aber dafür verweise ich gerne auf die Wikipedia.

imgW, imgH = img.size
## Nummer zeichnen
    font = ImageFont.truetype(strFont, 70, encoding="unic")
    W, H = (200,200)
    draw = ImageDraw.Draw(img)
    draw.ellipse((imgW/2-W/2, imgH/2-H/2, imgW/2+W/2, imgH/2+H/2), fill=strBGcolor, outline=strFGcolor, width=10)
    w, h = draw.textsize(strDay, font=font)
    draw.text(((imgW-w)/2,(imgH-h)/2), strDay, fill=strFGcolor, font=font)
Code language: Python (python)

In Zeile 3 begegnet uns wieder die Schriftart, die wir uns zuvor ausgewählt haben. Wir geben ihr die Schriftgröße 70 und definieren anschließend in Zeile 4 die Ausmaße der Ellipse, die wir zeichnen wollen.
Der Variablen draw sagen wir, dass wir sie zum Zeichnen auf die Variable img, die ja unser QR-Code ist, benutzen wollen, um das dann in Zeile 6 zu tun.

Hier wird es etwas tricky und wir müssen tatsächlich nachdenken. Der erste Parameter der Funktion ellipse, die wir auf das Objekt draw anwenden, ist ein Tupel mit 4 Ausdehnungen (links, unten, rechts und oben), die die Position und damit auch die Größe der Box beschreiben, in die wir zeichnen. Die Dokumentation hilft euch da weiter, aber im Grunde ermitteln wir lediglich die Mitte des Bildes (die Dimensionen ermitteln wir in Zeile 1) und ziehen die Hälfte unserer Ausmaße (Zeile 4) ab bzw. addieren sie hinzu.
Konkret für die linke Ausdehnung der Box imgW/2-W/2 sagen wir also: Nimm die Hälfte der Breite des Bildes und ziehe die Hälfte der Breite der Ellipse ab. Das ist deine linke Ausdehnung.
Mit dem zweiten Wert imgH/2-H/2 sagen wir: Nimm die Hälfte der Höhe des Bildes und ziehe die Hälfte der Höhe unserer Ellipse ab. Das ist deine untere Ausdehnung.
Für die rechte und die obere Ausdehnung addieren wir die Hälfte unserer entsprechenden Ellipsenausdehnung hinzu.
Wenn das schief geht, ist das kein Problem, es sieht lediglich etwas schräg aus. Ändert einfach die Werte, bis es ungefähr passt, solange ihr halbwegs die Logik dahinter verstanden habt, wie man ein Bild auf ein anderes Bild zentriert. Und vielleicht gefällt euch ja sogar euer schräges Ergebnis?

Nun möchten wir aber eben noch die Tageszahl in unsere Ellipse schreiben. Dazu messen wir (natürlich automatisch) aus, wie groß unser Text eigentlich ist. Das machen wir mit der Funktion textsize, der wir den Text (eben den Tag. bzw eine Zahl, die durchaus auch mal ein- oder zweistellig sein kann) und die Schriftart mit Größe mitgeben. Wir bekommen dann auch dafür die Ausdehnung und können sie ganz ähnlich der Logik von eben mittig positionieren. Auch hier sehen wir wieder unsere gewählte Vordergrundfarbe.
Et voila – wir haben einen Kreis in ein Bild und eine Zahl in diesen Kreis gezeichnet! Das ist der Moment, in dem wir uns ruhig mal auf die Schulter klopfen können, denn würden wir das ausgeben, würde es so aussehen:

Ein QR-Code mit Kreis und Zahl – woah!

Wenn ihr möchtet, wären wir an dieser Stelle schon fertig, allerdings hatten wir uns ja zuvor noch einen Claim (oder ein Zitat) für jeden Song überlegt, den wir natürlich auch noch gerne nutzen möchten. Einfach, weil es ein bisschen cool ist, das ganze Konstrukt etwas auflockert und dem Code noch eine kleine persönliche Note gibt. Dazu müssen wir aber erstmal das Bild vergrößern, denn nach unten hin ist da gerade nicht so viel Platz.

    ## Neues Bild erstellen, aber groeßer
    imgNew = Image.new("RGBA", (imgW, imgH+150), strBGcolor)
    imgNew.paste(img, (0,0))
Code language: Python (python)

Im Grunde steht da: Mach mir ein neues Bild in den Ausmaßen meines alten, aber 150 Pixel höher, und fülle es weiß. Anschließend fügst du das Alte Bild an den Koordinaten 0/0 (oben links), ein. Bestimmt geht das eleganter, aber wir wollen nicht elegant, wir wollen fertig werden.

## Claim reinschreiben
    draw = ImageDraw.Draw(imgNew)
    font = ImageFont.truetype(strFont, 50, encoding="unic")
    w, h = draw.textsize(strClaim, font=font)
    draw.text(((imgW-w)/2, 1000), strClaim, fill=strFGcolor, font=font)

    imgNew.save("qrcodes/" + strGuid + ".png")
Code language: Python (python)

Vielleicht klang das jetzt nach viel Aufwand, aber an und für sich sind das jetzt alles Dinge, die wir schon mal gemacht haben. Wir überschreiben die Variable draw, aber nun mit dem neuen Bild, wir sagen der Schriftart, dass sie bitte eine Größe von 50 haben soll und fragen dann ab, welche Ausmaße der Text des Claims haben wird.
Nun wollen wir ihn wieder horizontal mittig positionieren, vertikal aber bei 1000 Pixel. Das war so ein pi-mal-Daumen-Versuch von mir und hat ganz gut funktioniert, also habe ich das so gelassen.

Und nun können wir uns endlich unser Meisterwerk betrachten und so richtig stolz sein.

Seid ihr stolz auf euch? Ich bin stolz auf euch!

Es bietet sich nicht unbedingt an, die QR-Codes auf voller Größe auf A4 auszudrucken, weil das einfach Overkill wäre. Ich fand 3×3 pro A4 Blatt angemessen, aber noch kleiner wäre auch absolut in Ordnung.

Auf der folgenden Seite findet ihr nun jedenfalls den kompletten Code ohne Zwischenkommentare zum einfachen Kopieren und Benutzen.

2 Comments Add New Comment

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Hallo! Mein Name ist Marco Friedersdorf und bin Freelancer im Bereich der Software-Entwicklung und IT-Beratung. Mit Artikeln wie diesem möchte ich einen kleinen Einblick in meine Arbeit geben und über die Dinge sprechen, die mich als Informatiker beschäftigen.
tripadvisor flickr americanexpress bandcamp basecamp behance bigcartel bitbucket blogger codepen compropago digg dribbble dropbox ello etsy eventbrite evernote facebook feedly github gitlab goodreads googleplus instagram kickstarter lastfm line linkedin mailchimp mastercard medium meetup messenger mixcloud paypal periscope pinterest quora reddit rss runkeeper shopify signal sinaweibo skype slack snapchat soundcloud sourceforge spotify stackoverflow stripe stumbleupon trello tumblr twitch twitter uber vimeo vine visa vsco wechat whatsapp wheniwork wordpress xero xing yelp youtube zerply zillow px aboutme airbnb amazon pencil envelope bubble magnifier cross menu arrow-up arrow-down arrow-left arrow-right envelope-o caret-down caret-up caret-left caret-right