What is qrgen.py?

qrgen.py is a tiny but flexible Python script that lets you generate QR codes right from your terminal. No web apps, no clipboard shenanigans—just run, encode, and use. Pipe in any text, or save as an image (.png,.jpg,.gif—based on the filename you give). Works on Linux, Mac, and Windows (with Python 3 and pip).

Why is this cool?

QR codes are everywhere these days and there are all sorts of websites and apps to create them but it’s not a casually trivial thing. It requires steps that take time and effort. This script makes it ridiculously easy to create your own scannable QR codes, openening up all sorts of creative possibilities:

  • Share WiFi passwords with houseguests in a flash
  • Put URLs, emails, or TOTP keys on your phone with zero friction
  • Encode one-time codes, contacts, server hostnames, whatever
  • Use ASCII QR for terminal-based setup or fun demos
  • Easily create codes for printable signs, stickers or labels.

an awesome youtube clip Scan me for another exciting demo!

Installation

Download or clone the project from GitHub

🔗 View qrgen on GitHub

Quickstart

# Clone and enter directory
git clone https://github.com/forfaxx/qrgen.git
cd qrgen

# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Run the script!
python qrgen.py "https://adminjitsu.com/qrgen"

Usage Examples

Make sure you have followed the steps above and have activated the venv for the script.

# Encode a string, open the QR image viewer
./qrgen.py "https://adminjitsu.com"

# Save as PNG
./qrgen.py "https://adminjitsu.com" --output adminjitsu.png

# Save as high-quality JPEG
./qrgen.py "https://adminjitsu.com" --output ../adminjitsu.jpg

# Save to a directory (saves as output.png there)
./qrgen.py "https://adminjitsu.com" --output ~/Pictures/qr/

# Show as ASCII in terminal
./qrgen.py --ascii "shell magic"

# Show version/help
./qrgen.py --version
./qrgen.py --help
  • Tip If you want to make the output png larger in size, simply increase the value of box_size in the generate_qr function.

Saving and Output Location

By default, using –output saves the file to your current directory as the format you specify (.png, .jpg, or .gif).

If you provide a directory for –output, qrgen.py will save the file as output.png in that directory.

Examples:

# Save as a PNG file in the current directory
./qrgen.py "root password: pencil" --output root-password.png

# Save as a JPEG file in Pictures
./qrgen.py "http://example.com" --output ~/Pictures/qr/wifi.jpg

# Save as PNG in the specified directory
./qrgen.py "note to self" --output ~/qr-codes/

Tip: To always save to a specific place, give an explicit path, e.g. --output ~/Pictures/wifi-qr.png

Managing Python virtual environments

On the official README I detail how to use the venv-launch.sh to create simple wrappers for your python tools. This is easily as cool as qrgen itself and deserves a full post. However, you can make it even easier to run this script if you take a moment to set it up. On my system I have a bootstrap.sh script that does the following for each python tool folder it finds in my tools folder. I’ll include this here for the curious who might want to automate this process as well.

# --- 4. link python tools from tools/*/script.py ---------------------------
declare -A GENERATED_WRAPPERS=()

for tool in "${DOTROOT}/tools/"*/; do
  [[ -d "$tool" ]] || continue

  tool_name="$(basename "$tool")"
  main_script="${tool}/${tool_name}.py"
  wrapper="${BIN_DEST}/${tool_name}"

if [[ -f "$main_script" ]]; then
  # Extract header comment block from main script (skip shebang, keep contiguous comments)
  head_comment=$(awk '
    NR == 1 && /^#!/ { next }     # skip shebang
    /^#/ { print; next }          # include comment lines
    { exit }                      # stop at first non-comment
  ' "$main_script")

  # Fallback comment if nothing found
  [[ -z "$head_comment" ]] && head_comment="# ${tool_name} — Python CLI tool"

  # Generate wrapper with docblock and marker
  cat > "$wrapper" <<EOF
#!/usr/bin/env bash
# dotfiles/bootstrap.sh generated wrapper
$head_comment
# wrapper: $main_script

exec "${DOTROOT}/lib/venv-launch.sh" "$main_script" "\$@"
EOF

  act "chmod +x '$wrapper'"
  GENERATED_WRAPPERS["$wrapper"]=1
  log "Linked tool launcher: $tool_name"

  else
    log "⚠️  No main script found for tool: $tool_name"
  fi
done

Luckily the manual steps are not that difficult. You just need to put venv-launch.sh somewhere in your PATH (e.g., ~/bin) and then you can create simple wrappers for all of your Python tools, like qrgen. Simply do the following:

  1. Put venv-launch.sh somewhere in your PATH (e.g., ~/bin):
cp venv-launch.sh ~/bin/
chmod +x ~/bin/venv-launch.sh
  1. Create a wrapper script for qrgen: In ~/bin (or another place in your PATH), add this:
#!/usr/bin/env bash
# qrgen global launcher

SCRIPT_DIR="$HOME/tools/qrgen"   # <--- change this to wherever you cloned the repo

exec ~/bin/venv-launch.sh "$SCRIPT_DIR/qrgen.py" "$@"
  1. Make the launcher executable:
chmod +x ~/bin/qrgen
  1. Now you can run:
qrgen "https://adminjitsu.com" 
echo "my wifi password" | qrgen --ascii

venv-launch.sh

The magic behind this is the venv-launch.sh script. On my system, I have dozens of python tools—managing them all would be enough of an annoyance that I would likely rarely use them otherwise. With venv-launch, you can write a simple wrapper for your tools that automates the fiddly bits.

This is what the script looks like:

#!/usr/bin/env bash
# venv-launch.sh — Run a Python script inside its local virtualenv, if one exists.
#
# Usage:
#   venv-launch.sh /absolute/path/to/script.py [args...]
#
# If a `venv/` directory is found next to the script, its Python interpreter
# is used. Otherwise, falls back to system Python (python3).
#
# This is used by launcher scripts auto-generated by bootstrap.sh. and allows for simple stub scripts to create a named command in ~/bin

set -euo pipefail

if [[ $# -lt 1 ]]; then
  echo "Usage: $0 path/to/script.py [args...]" >&2
  exit 1
fi


SCRIPT_PATH="$(realpath "$1")"
shift

SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
VENV_PYTHON="$SCRIPT_DIR/venv/bin/python"

if [[ -x "$VENV_PYTHON" ]]; then
  exec "$VENV_PYTHON" "$SCRIPT_PATH" "$@"
else
  echo "⚠️  No venv found next to script. Falling back to system Python." >&2
  exec python3 "$SCRIPT_PATH" "$@"
fi

Once that script is in your PATH, you can create a wrapper like this (also in your PATH)

#!/usr/bin/env bash
# dotfiles/bootstrap.sh generated wrapper
# qrgen — Python CLI tool
# wrapper: /home/grumble/codelab/dotfiles/tools/qrgen//qrgen.py

exec "/home/grumble/bin/venv-launch.sh" "/home/grumble/codelab/tools/qrgen//qrgen.py" "$@"

After that is in place, you can simply run qrgen and it will handle activating and deactivating the venv automatically

Conclusion

That’s it—a dead-simple way to generate QR codes from anywhere, anytime, no questions asked. Perfect for WiFi, links, OTPs, passwords, notes, and anything else you don’t want to type out twice. I hope you find this as handy and delightful as I do!

Have feedback, feature requests, or a cool use case? PRs welcome! or Email me: feedback@adminjitsu.com