qr / app.py
seawolf2357's picture
Update app.py
aa67d14 verified
import gradio as gr
import qrcode
import random
import os
from datetime import datetime
from PIL import Image, ImageDraw
from math import cos, sin, radians
def create_border_decoration(qr_image, decoration_style="Flowers"):
# Convert QR image to RGB mode first
qr_image = qr_image.convert('RGB')
# Get the size of the QR code image
width, height = qr_image.size
# ํŒจ๋”ฉ์„ ๋” ์ž‘๊ฒŒ ์กฐ์ •
padding = 20
new_width = width + (padding * 2)
new_height = height + (padding * 2)
# Create new image with white background
decorated_image = Image.new('RGB', (new_width, new_height), 'white')
# Paste QR code in center
decorated_image.paste(qr_image, (padding, padding))
# Get draw object
draw = ImageDraw.Draw(decorated_image)
# ์žฅ์‹ ํฌ๊ธฐ ์„ค์ •
deco_size = 8
gap = deco_size * 1.5
# ํ…Œ๋‘๋ฆฌ๋ฅผ ๋”ฐ๋ผ ์ ๋“ค์˜ ์œ„์น˜ ๊ณ„์‚ฐ
border_points = []
# ์ƒ๋‹จ ํ…Œ๋‘๋ฆฌ
for x in range(padding//2, new_width - padding//2, int(gap)):
border_points.append((x, padding//2))
# ์šฐ์ธก ํ…Œ๋‘๋ฆฌ
for y in range(padding//2, new_height - padding//2, int(gap)):
border_points.append((new_width - padding//2, y))
# ํ•˜๋‹จ ํ…Œ๋‘๋ฆฌ
for x in range(new_width - padding//2, padding//2, -int(gap)):
border_points.append((x, new_height - padding//2))
# ์ขŒ์ธก ํ…Œ๋‘๋ฆฌ
for y in range(new_height - padding//2, padding//2, -int(gap)):
border_points.append((padding//2, y))
# ๊ฐ ์Šคํƒ€์ผ์— ๋”ฐ๋ฅธ ์žฅ์‹ ๊ทธ๋ฆฌ๊ธฐ
for x, y in border_points:
if decoration_style == "Flowers":
for angle in range(0, 360, 45):
x1 = x + deco_size * cos(radians(angle))
y1 = y + deco_size * sin(radians(angle))
draw.ellipse([x1-4, y1-4, x1+4, y1+4], fill='pink')
draw.ellipse([x-3, y-3, x+3, y+3], fill='yellow')
elif decoration_style == "Hearts":
heart_size = 6
draw.ellipse([x-heart_size, y-heart_size, x, y], fill='red')
draw.ellipse([x, y-heart_size, x+heart_size, y], fill='red')
draw.polygon([(x-heart_size, y), (x+heart_size, y), (x, y+heart_size)], fill='red')
elif decoration_style == "Waves":
wave_size = 10
draw.arc([x-wave_size, y-wave_size//2, x+wave_size, y+wave_size//2],
0, 180, fill='lightblue', width=2)
draw.arc([x-wave_size, y, x+wave_size, y+wave_size],
0, 180, fill='blue', width=2)
elif decoration_style == "Leaves":
leaf_size = 8
draw.ellipse([x-leaf_size, y-leaf_size//2, x+leaf_size, y+leaf_size//2],
fill='lightgreen')
draw.ellipse([x-leaf_size//2, y-leaf_size, x+leaf_size//2, y+leaf_size],
fill='darkgreen')
elif decoration_style == "Stars":
star_size = 6
points = []
for i in range(5):
angle = i * 72
x1 = x + star_size * cos(radians(angle))
y1 = y + star_size * sin(radians(angle))
points.append((x1, y1))
x2 = x + (star_size/2) * cos(radians(angle + 36))
y2 = y + (star_size/2) * sin(radians(angle + 36))
points.append((x2, y2))
draw.polygon(points, fill='gold')
elif decoration_style == "Chains":
chain_size = 8
draw.ellipse([x-chain_size, y-chain_size//2, x+chain_size, y+chain_size//2],
outline='gray', width=2)
elif decoration_style == "Bubbles":
bubble_sizes = [6, 4, 2]
for size in bubble_sizes:
draw.ellipse([x-size, y-size, x+size, y+size],
outline='skyblue', width=1)
elif decoration_style == "Vines":
vine_size = 10
draw.arc([x-vine_size, y-vine_size, x+vine_size, y+vine_size],
0, 180, fill='green', width=2)
draw.ellipse([x-3, y-3, x+3, y+3], fill='lightgreen')
elif decoration_style == "Diamonds":
diamond_size = 6
points = [
(x, y-diamond_size),
(x+diamond_size, y),
(x, y+diamond_size),
(x-diamond_size, y)
]
draw.polygon(points, outline='purple', width=1)
elif decoration_style == "Lace":
lace_size = 8
draw.arc([x-lace_size, y-lace_size, x+lace_size, y+lace_size],
0, 180, fill='gray', width=1)
draw.arc([x-lace_size//2, y-lace_size//2, x+lace_size//2, y+lace_size//2],
180, 360, fill='gray', width=1)
return decorated_image
def rgba_to_rgb(rgba_color):
"""Convert RGBA color string to RGB hex color"""
if rgba_color.startswith('rgba'):
values = rgba_color.strip('rgba()').split(',')
r = int(float(values[0]))
g = int(float(values[1]))
b = int(float(values[2]))
return f'#{r:02x}{g:02x}{b:02x}'
return rgba_color
def create_qr(content, qr_type, fill_color, back_color, box_size, border_size, error_correction, border_decoration="No Decoration"):
# Convert RGBA colors to RGB
fill_color = rgba_to_rgb(fill_color)
back_color = rgba_to_rgb(back_color)
# QR ์ฝ”๋“œ ๋ฐ์ดํ„ฐ ํฌ๋งทํŒ…
formatted_data = format_data(content, qr_type)
# ์—๋Ÿฌ ์ˆ˜์ • ๋ ˆ๋ฒจ ์„ค์ •
error_levels = {
"Low (7%)": qrcode.constants.ERROR_CORRECT_L,
"Medium (15%)": qrcode.constants.ERROR_CORRECT_M,
"Quartile (25%)": qrcode.constants.ERROR_CORRECT_Q,
"High (30%)": qrcode.constants.ERROR_CORRECT_H
}
# QR ์ฝ”๋“œ ์ƒ์„ฑ
qr = qrcode.QRCode(
version=1,
error_correction=error_levels[error_correction],
box_size=box_size,
border=border_size,
)
qr.add_data(formatted_data)
qr.make(fit=True)
# QR ์ด๋ฏธ์ง€ ์ƒ์„ฑ
qr_img = qr.make_image(fill_color=fill_color, back_color=back_color)
# Add border decoration if specified
if border_decoration != "No Decoration":
qr_img = create_border_decoration(qr_img, border_decoration)
# ํŒŒ์ผ ์ €์žฅ
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
random_id = random.randint(1000, 9999)
filename = f"qrfile/qr_{timestamp}_{random_id}.png"
os.makedirs("qrfile", exist_ok=True)
qr_img.save(filename)
cleanup_old_files("qrfile/", max_files=100)
# ์ •๋ณด ๋กœ๊ทธ ์ƒ์„ฑ
info_log = f"""โœ… QR CODE GENERATED!
{'=' * 50}
๐Ÿ“‹ QR Code Info:
โ€ข Type: {qr_type}
โ€ข Content: {content[:40]}{'...' if len(content) > 40 else ''}
{'=' * 50}
๐ŸŽจ Style Settings:
โ€ข QR Color: {fill_color}
โ€ข Background: {back_color}
โ€ข Size: {box_size}
โ€ข Border: {border_size}
โ€ข Decoration: {border_decoration}
{'=' * 50}
๐Ÿ”ง Technical:
โ€ข Error Correction: {error_correction}
โ€ข Formatted Data: {formatted_data[:50]}{'...' if len(formatted_data) > 50 else ''}
{'=' * 50}
๐Ÿ’พ Ready to download!"""
return filename, formatted_data, info_log
def format_data(content, qr_type):
if not content:
return ""
format_rules = {
"URL": lambda x: f"https://{x}" if not x.startswith(('http://', 'https://')) else x,
"Email": lambda x: f"mailto:{x}",
"Phone": lambda x: f"tel:{x}",
"SMS": lambda x: f"sms:{x}",
"WhatsApp": lambda x: f"whatsapp://send?text={x}",
"Location": lambda x: f"geo:{x}",
"Wi-Fi": lambda x: f"WIFI:S:{x};;",
"Text": lambda x: x,
"vCard": lambda x: f"BEGIN:VCARD\nVERSION:3.0\n{x}\nEND:VCARD"
}
return format_rules[qr_type](content.strip())
def cleanup_old_files(directory, max_files):
files = [f for f in os.listdir(directory) if f.endswith('.png')]
if len(files) > max_files:
files.sort(key=lambda x: os.path.getctime(os.path.join(directory, x)))
for f in files[:-max_files]:
try:
os.remove(os.path.join(directory, f))
except:
continue
def format_example_text(qr_type):
examples = {
"URL": "โ€ข Direct URL: https://example.com\nโ€ข Without https: example.com",
"Email": "โ€ข Basic: example@email.com\nโ€ข With subject: example@email.com?subject=Hello",
"Phone": "โ€ข International: +1234567890\nโ€ข Local: 01012345678",
"SMS": "โ€ข Basic: +1234567890\nโ€ข With message: +1234567890?body=Hello",
"WhatsApp": "โ€ข Message: Hello World!\nโ€ข With number: +1234567890:Hello",
"Location": "โ€ข Coordinates: 37.7749,-122.4194\nโ€ข With zoom: 37.7749,-122.4194,15z",
"Wi-Fi": "โ€ข Network name only: MyWiFiNetwork\nโ€ข With password: WIFI:S:MyNetwork;P:password;;",
"Text": "โ€ข Simple text: Hello World!\nโ€ข Multiple lines: Line 1\\nLine 2",
"vCard": "โ€ข Basic:\nFN:John Doe\nTEL:+1234567890\nEMAIL:john@example.com"
}
return examples.get(qr_type, "Enter your content here...")
# ============================================
# ๐ŸŽจ Comic Classic Theme - Toon Playground
# ============================================
css = """
/* ===== ๐ŸŽจ Google Fonts Import ===== */
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
/* ===== ๐ŸŽจ Comic Classic ๋ฐฐ๊ฒฝ - ๋นˆํ‹ฐ์ง€ ํŽ˜์ดํผ + ๋„ํŠธ ํŒจํ„ด ===== */
.gradio-container {
background-color: #FEF9C3 !important;
background-image:
radial-gradient(#1F2937 1px, transparent 1px) !important;
background-size: 20px 20px !important;
min-height: 100vh !important;
font-family: 'Comic Neue', cursive, sans-serif !important;
}
/* ===== ํ—ˆ๊น…ํŽ˜์ด์Šค ์ƒ๋‹จ ์š”์†Œ ์ˆจ๊น€ ===== */
.huggingface-space-header,
#space-header,
.space-header,
[class*="space-header"],
.svelte-1ed2p3z,
.space-header-badge,
.header-badge,
[data-testid="space-header"],
.svelte-kqij2n,
.svelte-1ax1toq,
.embed-container > div:first-child {
display: none !important;
visibility: hidden !important;
height: 0 !important;
width: 0 !important;
overflow: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
}
/* ===== Footer ์™„์ „ ์ˆจ๊น€ ===== */
footer,
.footer,
.gradio-container footer,
.built-with,
[class*="footer"],
.gradio-footer,
.main-footer,
div[class*="footer"],
.show-api,
.built-with-gradio,
a[href*="gradio.app"],
a[href*="huggingface.co/spaces"] {
display: none !important;
visibility: hidden !important;
height: 0 !important;
padding: 0 !important;
margin: 0 !important;
}
/* ===== ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ===== */
#col-container {
max-width: 1200px;
margin: 0 auto;
}
/* ===== ๐ŸŽจ ํ—ค๋” ํƒ€์ดํ‹€ - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
.header-text h1 {
font-family: 'Bangers', cursive !important;
color: #1F2937 !important;
font-size: 3.5rem !important;
font-weight: 400 !important;
text-align: center !important;
margin-bottom: 0.5rem !important;
text-shadow:
4px 4px 0px #FACC15,
6px 6px 0px #1F2937 !important;
letter-spacing: 3px !important;
-webkit-text-stroke: 2px #1F2937 !important;
}
/* ===== ๐ŸŽจ ์„œ๋ธŒํƒ€์ดํ‹€ ===== */
.subtitle {
text-align: center !important;
font-family: 'Comic Neue', cursive !important;
font-size: 1.2rem !important;
color: #1F2937 !important;
margin-bottom: 1.5rem !important;
font-weight: 700 !important;
}
/* ===== ๐ŸŽจ ์นด๋“œ/ํŒจ๋„ - ๋งŒํ™” ํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ ===== */
.gr-panel,
.gr-box,
.gr-form,
.block,
.gr-group {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 6px 6px 0px #1F2937 !important;
transition: all 0.2s ease !important;
}
.gr-panel:hover,
.block:hover {
transform: translate(-2px, -2px) !important;
box-shadow: 8px 8px 0px #1F2937 !important;
}
/* ===== ๐ŸŽจ ์ž…๋ ฅ ํ•„๋“œ (Textbox) ===== */
textarea,
input[type="text"],
input[type="number"] {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-size: 1rem !important;
font-weight: 700 !important;
transition: all 0.2s ease !important;
}
textarea:focus,
input[type="text"]:focus,
input[type="number"]:focus {
border-color: #3B82F6 !important;
box-shadow: 4px 4px 0px #3B82F6 !important;
outline: none !important;
}
textarea::placeholder {
color: #9CA3AF !important;
font-weight: 400 !important;
}
/* ===== ๐ŸŽจ ๋“œ๋กญ๋‹ค์šด ์Šคํƒ€์ผ ===== */
.gr-dropdown {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 3px 3px 0px #1F2937 !important;
}
.gr-dropdown > div {
background: #FFFFFF !important;
border: none !important;
}
.gr-dropdown input {
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-weight: 700 !important;
}
.gr-dropdown ul {
background: #FFFFFF !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 4px 4px 0px #1F2937 !important;
}
.gr-dropdown ul li {
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-weight: 700 !important;
padding: 8px 12px !important;
}
.gr-dropdown ul li:hover {
background: #FACC15 !important;
color: #1F2937 !important;
}
.gr-dropdown ul li.selected {
background: #3B82F6 !important;
color: #FFFFFF !important;
}
/* ===== ๐ŸŽจ Primary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ธ”๋ฃจ ===== */
.gr-button-primary,
button.primary,
.gr-button.primary,
.generate-btn {
background: #3B82F6 !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #FFFFFF !important;
font-family: 'Bangers', cursive !important;
font-weight: 400 !important;
font-size: 1.3rem !important;
letter-spacing: 2px !important;
padding: 14px 28px !important;
box-shadow: 5px 5px 0px #1F2937 !important;
transition: all 0.1s ease !important;
text-shadow: 1px 1px 0px #1F2937 !important;
}
.gr-button-primary:hover,
button.primary:hover,
.gr-button.primary:hover,
.generate-btn:hover {
background: #2563EB !important;
transform: translate(-2px, -2px) !important;
box-shadow: 7px 7px 0px #1F2937 !important;
}
.gr-button-primary:active,
button.primary:active,
.gr-button.primary:active,
.generate-btn:active {
transform: translate(3px, 3px) !important;
box-shadow: 2px 2px 0px #1F2937 !important;
}
/* ===== ๐ŸŽจ Secondary ๋ฒ„ํŠผ - ์ฝ”๋ฏน ๋ ˆ๋“œ ===== */
.gr-button-secondary,
button.secondary {
background: #EF4444 !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
color: #FFFFFF !important;
font-family: 'Bangers', cursive !important;
font-weight: 400 !important;
font-size: 1.1rem !important;
letter-spacing: 1px !important;
box-shadow: 4px 4px 0px #1F2937 !important;
transition: all 0.1s ease !important;
text-shadow: 1px 1px 0px #1F2937 !important;
}
.gr-button-secondary:hover,
button.secondary:hover {
background: #DC2626 !important;
transform: translate(-2px, -2px) !important;
box-shadow: 6px 6px 0px #1F2937 !important;
}
/* ===== ๐ŸŽจ ๋กœ๊ทธ ์ถœ๋ ฅ ์˜์—ญ ===== */
.info-log textarea {
background: #1F2937 !important;
color: #10B981 !important;
font-family: 'Courier New', monospace !important;
font-size: 0.9rem !important;
font-weight: 400 !important;
border: 3px solid #10B981 !important;
border-radius: 8px !important;
box-shadow: 4px 4px 0px #10B981 !important;
}
/* ===== ๐ŸŽจ ์ปฌ๋Ÿฌ ํ”ผ์ปค ์Šคํƒ€์ผ ===== */
.gr-colorpicker,
input[type="color"] {
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 3px 3px 0px #1F2937 !important;
width: 60px !important;
height: 40px !important;
cursor: pointer !important;
}
.gr-colorpicker:hover,
input[type="color"]:hover {
transform: translate(-2px, -2px) !important;
box-shadow: 5px 5px 0px #1F2937 !important;
}
/* ===== ๐ŸŽจ ์Šฌ๋ผ์ด๋” ์Šคํƒ€์ผ ===== */
input[type="range"] {
accent-color: #3B82F6 !important;
height: 8px !important;
}
.gr-slider {
background: #FFFFFF !important;
}
/* ===== ๐ŸŽจ ์˜ˆ์‹œ ํ…์ŠคํŠธ ๋ฐ•์Šค ===== */
.example-box textarea {
background: #FEF3C7 !important;
border: 2px dashed #F59E0B !important;
border-radius: 8px !important;
color: #92400E !important;
font-family: 'Comic Neue', cursive !important;
font-size: 0.9rem !important;
}
/* ===== ๐ŸŽจ QR ์ฝ”๋“œ ์ถœ๋ ฅ ์˜์—ญ ===== */
.qr-output,
.gr-image {
border: 4px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 8px 8px 0px #1F2937 !important;
overflow: hidden !important;
background: #FFFFFF !important;
}
/* ===== ๐ŸŽจ ์•„์ฝ”๋””์–ธ - ๋งํ’์„  ์Šคํƒ€์ผ ===== */
.gr-accordion {
background: #FACC15 !important;
border: 3px solid #1F2937 !important;
border-radius: 8px !important;
box-shadow: 4px 4px 0px #1F2937 !important;
}
.gr-accordion-header {
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-weight: 700 !important;
font-size: 1.1rem !important;
}
/* ===== ๐ŸŽจ ๋ผ๋ฒจ ์Šคํƒ€์ผ ===== */
label,
.gr-input-label,
.gr-block-label {
color: #1F2937 !important;
font-family: 'Comic Neue', cursive !important;
font-weight: 700 !important;
font-size: 1rem !important;
}
span.gr-label {
color: #1F2937 !important;
}
/* ===== ๐ŸŽจ ์ •๋ณด ํ…์ŠคํŠธ ===== */
.gr-info,
.info {
color: #6B7280 !important;
font-family: 'Comic Neue', cursive !important;
font-size: 0.9rem !important;
}
/* ===== ๐ŸŽจ Instructions ์„น์…˜ ===== */
.instructions-box {
background: linear-gradient(135deg, #EFF6FF 0%, #DBEAFE 100%) !important;
border: 3px solid #3B82F6 !important;
border-radius: 12px !important;
padding: 1.5rem !important;
box-shadow: 6px 6px 0px #1F2937 !important;
margin-top: 1.5rem !important;
}
.instructions-box h3 {
font-family: 'Bangers', cursive !important;
color: #1F2937 !important;
font-size: 1.3rem !important;
margin-bottom: 0.5rem !important;
}
.instructions-box ul,
.instructions-box ol {
font-family: 'Comic Neue', cursive !important;
color: #1F2937 !important;
font-weight: 700 !important;
}
/* ===== ๐ŸŽจ Tips ์„น์…˜ ===== */
.tips-box {
background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%) !important;
border: 3px solid #F59E0B !important;
border-radius: 12px !important;
padding: 1.5rem !important;
box-shadow: 6px 6px 0px #1F2937 !important;
margin-top: 1rem !important;
}
/* ===== ๐ŸŽจ ์Šคํฌ๋กค๋ฐ” - ์ฝ”๋ฏน ์Šคํƒ€์ผ ===== */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: #FEF9C3;
border: 2px solid #1F2937;
}
::-webkit-scrollbar-thumb {
background: #3B82F6;
border: 2px solid #1F2937;
border-radius: 0px;
}
::-webkit-scrollbar-thumb:hover {
background: #EF4444;
}
/* ===== ๐ŸŽจ ์„ ํƒ ํ•˜์ด๋ผ์ดํŠธ ===== */
::selection {
background: #FACC15;
color: #1F2937;
}
/* ===== ๐ŸŽจ ๋งํฌ ์Šคํƒ€์ผ ===== */
a {
color: #3B82F6 !important;
text-decoration: none !important;
font-weight: 700 !important;
}
a:hover {
color: #EF4444 !important;
}
/* ===== ๐ŸŽจ Row/Column ๊ฐ„๊ฒฉ ===== */
.gr-row {
gap: 1.5rem !important;
}
.gr-column {
gap: 1rem !important;
}
/* ===== ๐ŸŽจ ์ปฌ๋Ÿฌ ํ”ผ์ปค Row ===== */
.color-picker-row {
display: flex !important;
gap: 1rem !important;
align-items: center !important;
}
/* ===== ๋ฐ˜์‘ํ˜• ์กฐ์ • ===== */
@media (max-width: 768px) {
.header-text h1 {
font-size: 2.2rem !important;
text-shadow:
3px 3px 0px #FACC15,
4px 4px 0px #1F2937 !important;
}
.gr-button-primary,
button.primary {
padding: 12px 20px !important;
font-size: 1.1rem !important;
}
.gr-panel,
.block {
box-shadow: 4px 4px 0px #1F2937 !important;
}
}
/* ===== ๐ŸŽจ ๋‹คํฌ๋ชจ๋“œ ๋น„ํ™œ์„ฑํ™” ===== */
@media (prefers-color-scheme: dark) {
.gradio-container {
background-color: #FEF9C3 !important;
}
}
"""
def create_interface():
with gr.Blocks(fill_height=True, css=css, title="QR Canvas+") as demo:
# HOME Badge
gr.HTML("""
<div style="text-align: center; margin: 20px 0 10px 0;">
<a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
<img src="https://img.shields.io/static/v1?label=๐Ÿ  HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
</a>
</div>
""")
# Header Title
gr.Markdown(
"""
# ๐ŸŽฏ QR CANVAS+ GENERATOR ๐Ÿ“ฑ
""",
elem_classes="header-text"
)
gr.Markdown(
"""
<p class="subtitle">๐ŸŽจ Create customized QR codes with professional styling & decorations! โœจ</p>
""",
)
with gr.Row(equal_height=False):
# Left column - Input Settings
with gr.Column(scale=2, min_width=450):
qr_type = gr.Dropdown(
choices=["URL", "Email", "Phone", "SMS", "WhatsApp", "Location", "Wi-Fi", "Text", "vCard"],
value="URL",
label="๐Ÿ“‹ QR Code Type"
)
content = gr.Textbox(
label="โœ๏ธ Content",
placeholder="Enter your content here...",
lines=3
)
example_format = gr.Textbox(
value=format_example_text("URL"),
label="๐Ÿ“ Format Examples",
interactive=False,
lines=4,
elem_classes="example-box"
)
with gr.Row(elem_classes="color-picker-row"):
fill_color = gr.ColorPicker(
label="๐ŸŽจ QR Code Color",
value="#000000"
)
back_color = gr.ColorPicker(
label="๐Ÿ–ผ๏ธ Background Color",
value="#FFFFFF"
)
with gr.Row():
box_size = gr.Slider(
minimum=1,
maximum=30,
value=15,
step=1,
label="๐Ÿ“ QR Code Size"
)
border_size = gr.Slider(
minimum=0,
maximum=5,
value=2,
step=1,
label="๐Ÿ”ฒ Border Size"
)
error_correction = gr.Dropdown(
choices=[
"Low (7%)",
"Medium (15%)",
"Quartile (25%)",
"High (30%)"
],
value="Medium (15%)",
label="๐Ÿ”ง Error Correction Level"
)
border_decoration = gr.Dropdown(
choices=[
"No Decoration",
"Flowers",
"Hearts",
"Waves",
"Leaves",
"Stars",
"Chains",
"Bubbles",
"Vines",
"Diamonds",
"Lace"
],
value="No Decoration",
label="๐ŸŒธ Border Decoration Style"
)
generate_btn = gr.Button(
"๐ŸŽฏ GENERATE QR CODE! ๐Ÿ“ฑ",
variant="primary",
size="lg",
elem_classes="generate-btn"
)
with gr.Accordion("๐Ÿ“œ Generation Log", open=True):
info_log = gr.Textbox(
label="",
placeholder="Enter content and click generate to see info...",
lines=14,
max_lines=20,
interactive=False,
elem_classes="info-log"
)
# Right column - Output
with gr.Column(scale=1, min_width=350):
output_image = gr.Image(
label="๐Ÿ–ผ๏ธ Generated QR Code",
type="filepath",
height=400,
elem_classes="qr-output"
)
output_data = gr.Textbox(
label="๐Ÿ“„ Formatted Data",
interactive=False,
lines=2
)
gr.Markdown(
"""
<p style="text-align: center; margin-top: 15px; font-weight: 700; color: #1F2937;">
๐Ÿ’ก Right-click on the QR code to save!
</p>
"""
)
# Instructions Section
gr.Markdown(
"""
<div class="instructions-box">
<h3>๐Ÿ“ INSTRUCTIONS</h3>
<ol>
<li>Select the QR code type from the dropdown menu</li>
<li>Enter your content following the format examples shown</li>
<li>Customize the appearance using the color pickers and sliders</li>
<li>Choose a border decoration style (optional)</li>
<li>Click 'GENERATE QR CODE' to create your custom QR code</li>
</ol>
</div>
<div class="tips-box">
<h3>๐Ÿ’ก TIPS</h3>
<ul>
<li>Use higher error correction levels for better scan reliability</li>
<li>Ensure sufficient contrast between QR code and background colors</li>
<li>Keep the content concise for better readability</li>
<li>Follow the format examples for best results</li>
</ul>
</div>
"""
)
# Event handlers
def update_example(qr_type):
return format_example_text(qr_type)
qr_type.change(
fn=update_example,
inputs=[qr_type],
outputs=example_format
)
generate_btn.click(
fn=create_qr,
inputs=[
content,
qr_type,
fill_color,
back_color,
box_size,
border_size,
error_correction,
border_decoration
],
outputs=[output_image, output_data, info_log]
)
return demo
if __name__ == "__main__":
try:
os.makedirs("qrfile", exist_ok=True)
demo = create_interface()
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
debug=True
)
except Exception as e:
print(f"Error starting the application: {e}")