DeadQR/qr_generator.py

234 lines
8.9 KiB
Python

#!/usr/bin/env python3
"""
DeadQR - A simple GUI tool to create and customize QR codes
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, colorchooser
from PIL import Image, ImageTk
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer, CircleModuleDrawer, SquareModuleDrawer
import io
class QRCodeGenerator:
def __init__(self, root):
self.root = root
self.root.title("DeadQR")
self.root.geometry("800x720")
self.root.resizable(False, False)
# QR code settings
self.qr_image = None
self.fg_color = "#000000"
self.bg_color = "#FFFFFF"
self.setup_ui()
def setup_ui(self):
# Main container
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Title
title_label = ttk.Label(main_frame, text="DeadQR",
font=("Arial", 24, "bold"))
title_label.grid(row=0, column=0, columnspan=2, pady=10)
subtitle_label = ttk.Label(main_frame, text="Advanced QR Code Generator",
font=("Arial", 10, "italic"))
subtitle_label.grid(row=1, column=0, columnspan=2, pady=(0, 10))
# Input section
input_frame = ttk.LabelFrame(main_frame, text="Content", padding="10")
input_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
ttk.Label(input_frame, text="Enter text or URL:").grid(row=0, column=0, sticky=tk.W)
self.text_input = tk.Text(input_frame, height=4, width=60, wrap=tk.WORD)
self.text_input.grid(row=1, column=0, pady=5)
self.text_input.bind('<KeyRelease>', lambda e: self.generate_qr())
# Settings section
settings_frame = ttk.LabelFrame(main_frame, text="Settings", padding="10")
settings_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)
# Size
ttk.Label(settings_frame, text="Size:").grid(row=0, column=0, sticky=tk.W, padx=5)
self.size_var = tk.IntVar(value=10)
size_slider = ttk.Scale(settings_frame, from_=5, to=20, orient=tk.HORIZONTAL,
variable=self.size_var, command=lambda e: self.generate_qr())
size_slider.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5)
self.size_label = ttk.Label(settings_frame, text="10")
self.size_label.grid(row=0, column=2, padx=5)
# Error correction
ttk.Label(settings_frame, text="Error Correction:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
self.error_var = tk.StringVar(value="M")
error_combo = ttk.Combobox(settings_frame, textvariable=self.error_var,
values=["L (7%)", "M (15%)", "Q (25%)", "H (30%)"],
state="readonly", width=15)
error_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)
error_combo.bind('<<ComboboxSelected>>', lambda e: self.generate_qr())
# Style
ttk.Label(settings_frame, text="Style:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=5)
self.style_var = tk.StringVar(value="Square")
style_combo = ttk.Combobox(settings_frame, textvariable=self.style_var,
values=["Square", "Rounded", "Circle"],
state="readonly", width=15)
style_combo.grid(row=2, column=1, sticky=tk.W, padx=5, pady=5)
style_combo.bind('<<ComboboxSelected>>', lambda e: self.generate_qr())
# Colors
color_frame = ttk.Frame(settings_frame)
color_frame.grid(row=3, column=0, columnspan=3, pady=10)
ttk.Label(color_frame, text="Foreground:").grid(row=0, column=0, padx=5)
self.fg_btn = tk.Button(color_frame, bg=self.fg_color, width=10,
command=lambda: self.choose_color('fg'))
self.fg_btn.grid(row=0, column=1, padx=5)
ttk.Label(color_frame, text="Background:").grid(row=0, column=2, padx=5)
self.bg_btn = tk.Button(color_frame, bg=self.bg_color, width=10,
command=lambda: self.choose_color('bg'))
self.bg_btn.grid(row=0, column=3, padx=5)
# Preview section
preview_frame = ttk.LabelFrame(main_frame, text="Preview", padding="10")
preview_frame.grid(row=4, column=0, columnspan=2, pady=10)
self.preview_label = ttk.Label(preview_frame, text="Enter text to generate QR code",
relief=tk.SUNKEN, width=50)
self.preview_label.grid(row=0, column=0, padx=10, pady=10)
# Buttons
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=5, column=0, columnspan=2, pady=10)
ttk.Button(button_frame, text="Generate QR Code",
command=self.generate_qr).grid(row=0, column=0, padx=5)
ttk.Button(button_frame, text="Save as PNG",
command=self.save_qr).grid(row=0, column=1, padx=5)
ttk.Button(button_frame, text="Clear",
command=self.clear_all).grid(row=0, column=2, padx=5)
# Update size label when slider moves
self.size_var.trace('w', self.update_size_label)
def update_size_label(self, *args):
self.size_label.config(text=str(self.size_var.get()))
def choose_color(self, color_type):
color = colorchooser.askcolor(title="Choose color")
if color[1]:
if color_type == 'fg':
self.fg_color = color[1]
self.fg_btn.config(bg=self.fg_color)
else:
self.bg_color = color[1]
self.bg_btn.config(bg=self.bg_color)
self.generate_qr()
def get_error_correction(self):
error_map = {
"L (7%)": qrcode.constants.ERROR_CORRECT_L,
"M (15%)": qrcode.constants.ERROR_CORRECT_M,
"Q (25%)": qrcode.constants.ERROR_CORRECT_Q,
"H (30%)": qrcode.constants.ERROR_CORRECT_H
}
return error_map.get(self.error_var.get(), qrcode.constants.ERROR_CORRECT_M)
def get_style_drawer(self):
style_map = {
"Square": SquareModuleDrawer(),
"Rounded": RoundedModuleDrawer(),
"Circle": CircleModuleDrawer()
}
return style_map.get(self.style_var.get(), SquareModuleDrawer())
def generate_qr(self):
text = self.text_input.get("1.0", tk.END).strip()
if not text:
self.preview_label.config(image='', text="Enter text to generate QR code")
self.qr_image = None
return
try:
# Create QR code
qr = qrcode.QRCode(
version=1,
error_correction=self.get_error_correction(),
box_size=self.size_var.get(),
border=4,
)
qr.add_data(text)
qr.make(fit=True)
# Generate image with style
self.qr_image = qr.make_image(
image_factory=StyledPilImage,
module_drawer=self.get_style_drawer(),
fill_color=self.fg_color,
back_color=self.bg_color
)
# Display preview
self.display_preview()
except Exception as e:
messagebox.showerror("Error", f"Failed to generate QR code: {str(e)}")
def display_preview(self):
if self.qr_image:
# Resize for preview
preview_size = (300, 300)
preview_img = self.qr_image.copy()
preview_img.thumbnail(preview_size, Image.Resampling.LANCZOS)
# Convert to PhotoImage
photo = ImageTk.PhotoImage(preview_img)
self.preview_label.config(image=photo, text="")
self.preview_label.image = photo # Keep reference
def save_qr(self):
if not self.qr_image:
messagebox.showwarning("No QR Code", "Please generate a QR code first!")
return
file_path = filedialog.asksaveasfilename(
defaultextension=".png",
filetypes=[("PNG files", "*.png"), ("All files", "*.*")]
)
if file_path:
try:
self.qr_image.save(file_path)
messagebox.showinfo("Success", f"QR code saved to:\n{file_path}")
except Exception as e:
messagebox.showerror("Error", f"Failed to save QR code: {str(e)}")
def clear_all(self):
self.text_input.delete("1.0", tk.END)
self.preview_label.config(image='', text="Enter text to generate QR code")
self.qr_image = None
self.size_var.set(10)
self.error_var.set("M")
self.style_var.set("Square")
self.fg_color = "#000000"
self.bg_color = "#FFFFFF"
self.fg_btn.config(bg=self.fg_color)
self.bg_btn.config(bg=self.bg_color)
def main():
root = tk.Tk()
app = QRCodeGenerator(root)
root.mainloop()
if __name__ == "__main__":
main()