Spaces:
Sleeping
Sleeping
File size: 12,330 Bytes
2a39f76 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
import streamlit as st
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import (
RoundedModuleDrawer, CircleModuleDrawer,
GappedSquareModuleDrawer, SquareModuleDrawer
)
from qrcode.image.styles.colormasks import SolidFillColorMask
from PIL import Image
import io
import re
# Helper function to convert hex color to RGB tuple
def hex_to_rgb(hex_color):
"""Convert hex color string to RGB tuple"""
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
# Page configuration
st.set_page_config(
page_title="QR Code Generator",
page_icon="π±",
layout="centered",
initial_sidebar_state="collapsed"
)
# Custom CSS for better UX
st.markdown("""
<style>
.big-input textarea {
font-size: 18px !important;
}
.template-button {
text-align: center;
padding: 10px;
border-radius: 8px;
cursor: pointer;
}
.stDownloadButton button {
width: 100%;
background-color: #4CAF50;
color: white;
font-size: 18px;
padding: 12px;
border-radius: 8px;
}
</style>
""", unsafe_allow_html=True)
# Initialize session state
if 'qr_data' not in st.session_state:
st.session_state.qr_data = "https://streamlit.io"
if 'template_type' not in st.session_state:
st.session_state.template_type = "url"
if 'show_advanced' not in st.session_state:
st.session_state.show_advanced = False
# Header
st.title("π± QR Code Generator")
st.markdown("**Free & Open Source** β’ No Ads β’ Privacy-Friendly")
# ============ TEMPLATES (QUICK START) ============
st.markdown("### π Quick Start Templates")
templates = [
("URL", "π", "url", "https://example.com"),
("WiFi", "πΆ", "wifi", ""),
("Email", "π§", "email", "mailto:name@example.com"),
("Phone", "π", "phone", "tel:+1234567890"),
("Text", "π¬", "text", "Hello World!")
]
# Create responsive grid layout (3 columns on first row, 2 on second)
col1, col2, col3 = st.columns(3)
cols = [col1, col2, col3]
for idx, (name, icon, template_id, placeholder) in enumerate(templates[:3]):
with cols[idx]:
if st.button(f"{icon}\n{name}", use_container_width=True, key=f"btn_{template_id}"):
st.session_state.template_type = template_id
if template_id == "wifi":
st.session_state.qr_data = ""
else:
st.session_state.qr_data = placeholder
st.rerun()
col4, col5, col6 = st.columns(3)
cols2 = [col4, col5, col6]
for idx, (name, icon, template_id, placeholder) in enumerate(templates[3:]):
with cols2[idx]:
if st.button(f"{icon}\n{name}", use_container_width=True, key=f"btn_{template_id}"):
st.session_state.template_type = template_id
if template_id == "wifi":
st.session_state.qr_data = ""
else:
st.session_state.qr_data = placeholder
st.rerun()
# ============ SMART INPUT AREA ============
st.markdown("---")
# Template-specific input
if st.session_state.template_type == "wifi":
st.markdown("### πΆ WiFi Configuration")
col1, col2 = st.columns(2)
with col1:
wifi_ssid = st.text_input("Network Name (SSID)", placeholder="MyWiFi")
wifi_password = st.text_input("Password", type="password", placeholder="password123")
with col2:
wifi_security = st.selectbox("Security Type", ["WPA", "WEP", "nopass"], index=0)
wifi_hidden = st.checkbox("Hidden Network", value=False)
# Generate WiFi QR format
if wifi_ssid:
hidden_str = "true" if wifi_hidden else "false"
if wifi_security == "nopass":
qr_data = f"WIFI:T:{wifi_security};S:{wifi_ssid};H:{hidden_str};;"
else:
qr_data = f"WIFI:T:{wifi_security};S:{wifi_ssid};P:{wifi_password};H:{hidden_str};;"
else:
qr_data = ""
else:
# Standard input for all other types
input_labels = {
"url": "π Enter your URL or website",
"email": "π§ Enter email address or mailto link",
"phone": "π Enter phone number",
"text": "π¬ Enter any text"
}
label = input_labels.get(st.session_state.template_type, "Enter your content")
qr_data = st.text_area(
label,
value=st.session_state.qr_data,
height=100,
placeholder="Type or paste your content here...",
key=f"input_{st.session_state.template_type}"
)
st.session_state.qr_data = qr_data
# Input validation and helpful feedback
validation_message = ""
validation_type = "info"
if qr_data:
# URL validation
if st.session_state.template_type == "url":
url_pattern = re.compile(r'^https?://')
if not url_pattern.match(qr_data):
validation_message = "π‘ Tip: URLs should start with http:// or https://"
validation_type = "warning"
# Character count
char_count = len(qr_data)
if char_count > 2000:
validation_message = f"β οΈ Warning: {char_count} characters may result in a complex QR code"
validation_type = "warning"
elif char_count > 500:
validation_message = f"βΉοΈ {char_count} characters - consider using a URL shortener for easier scanning"
validation_type = "info"
if validation_message:
if validation_type == "warning":
st.warning(validation_message)
else:
st.info(validation_message)
# ============ CUSTOMIZATION (COLLAPSED BY DEFAULT) ============
with st.expander("π¨ Customize Appearance (Optional)", expanded=False):
tab1, tab2, tab3 = st.tabs(["Colors & Style", "Size", "Logo"])
with tab1:
col1, col2 = st.columns(2)
with col1:
fill_color = st.color_picker("QR Code Color", "#000000")
module_style = st.selectbox(
"Shape Style",
options=["Square", "Rounded", "Circle", "Gapped Square"],
help="Shape of the QR code modules"
)
with col2:
back_color = st.color_picker("Background Color", "#FFFFFF")
error_correction = st.select_slider(
"Error Correction",
options=["Low", "Medium", "High", "Very High"],
value="Medium",
help="Higher levels make QR code more readable if damaged"
)
with tab2:
box_size = st.slider(
"Resolution (pixels per module)",
min_value=5,
max_value=30,
value=10,
help="Higher = larger file size but better quality"
)
border_size = st.slider(
"Border Thickness",
min_value=1,
max_value=10,
value=4
)
with tab3:
st.markdown("Add a logo or image to the center of your QR code")
logo_file = st.file_uploader(
"Upload logo",
type=["png", "jpg", "jpeg"],
help="Best results with square, transparent PNG images"
)
if logo_file:
logo_size_percent = st.slider(
"Logo size (%)",
min_value=10,
max_value=40,
value=20,
help="Logo shouldn't exceed 30% for reliable scanning"
)
else:
logo_size_percent = 20
st.info("π‘ Tip: Use high error correction with logos to maintain scannability")
# ============ QR CODE GENERATION ============
# Maps
error_correction_map = {
"Low": qrcode.constants.ERROR_CORRECT_L,
"Medium": qrcode.constants.ERROR_CORRECT_M,
"High": qrcode.constants.ERROR_CORRECT_Q,
"Very High": qrcode.constants.ERROR_CORRECT_H
}
module_drawer_map = {
"Square": SquareModuleDrawer(),
"Rounded": RoundedModuleDrawer(),
"Circle": CircleModuleDrawer(),
"Gapped Square": GappedSquareModuleDrawer()
}
def generate_qr_code(data, fill, back, style, error_lvl, box, border, logo, logo_size):
"""Generate QR code with given parameters"""
try:
# Convert hex colors to RGB tuples
fill_rgb = hex_to_rgb(fill)
back_rgb = hex_to_rgb(back)
qr = qrcode.QRCode(
version=1,
error_correction=error_correction_map[error_lvl],
box_size=box,
border=border,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=module_drawer_map[style],
color_mask=SolidFillColorMask(
back_color=back_rgb,
front_color=fill_rgb
)
)
# Convert to standard PIL Image for Streamlit compatibility
img = img.convert('RGB')
# Add logo if provided
if logo is not None:
logo_img = Image.open(logo)
# Calculate logo size
qr_width, qr_height = img.size
logo_max_size = min(qr_width, qr_height) * logo_size // 100
# Resize logo maintaining aspect ratio
logo_img.thumbnail((logo_max_size, logo_max_size), Image.Resampling.LANCZOS)
# Create a white background for logo if it doesn't have transparency
if logo_img.mode != 'RGBA':
logo_bg = Image.new('RGB', logo_img.size, 'white')
logo_bg.paste(logo_img, (0, 0))
logo_img = logo_bg
# Calculate position to center logo
logo_pos = (
(qr_width - logo_img.width) // 2,
(qr_height - logo_img.height) // 2
)
# Paste logo
img.paste(logo_img, logo_pos, logo_img if logo_img.mode == 'RGBA' else None)
return img, qr
except Exception as e:
st.error(f"Error generating QR code: {str(e)}")
return None, None
# ============ DISPLAY QR CODE ============
st.markdown("---")
if qr_data:
img, qr = generate_qr_code(
qr_data, fill_color, back_color, module_style,
error_correction, box_size, border_size, logo_file, logo_size_percent
)
if img:
# Display QR code
col1, col2, col3 = st.columns([1, 3, 1])
with col2:
st.image(img, use_container_width=True)
# Download button (prominent)
buf = io.BytesIO()
img.save(buf, format="PNG")
byte_im = buf.getvalue()
st.download_button(
label="β¬οΈ Download QR Code",
data=byte_im,
file_name="qr_code.png",
mime="image/png",
use_container_width=True
)
# QR Code info
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Size", f"{img.size[0]}Γ{img.size[1]}px")
with col2:
st.metric("Version", qr.version)
with col3:
st.metric("Characters", len(qr_data))
else:
# Show helpful message when empty
st.info("π Enter content above or select a template to generate your QR code")
# Show example QR code
example_img, _ = generate_qr_code(
"https://streamlit.io",
"#000000", "#FFFFFF", "Square", "Medium", 10, 4, None, 20
)
if example_img:
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.image(example_img, caption="Example QR Code", use_container_width=True)
# ============ HELPFUL TIPS ============
with st.expander("βΉοΈ Tips & Examples"):
st.markdown("""
### π Format Examples:
**Website/URL:**
- `https://www.example.com`
- `https://mysite.com/page?id=123`
**Email:**
- `mailto:name@example.com`
- `mailto:name@example.com?subject=Hello&body=Message`
**Phone:**
- `tel:+1234567890`
**SMS:**
- `sms:+1234567890`
- `sms:+1234567890?body=Hello there`
**WiFi** (use WiFi template):
- Automatically formatted when you fill the form
**Plain Text:**
- Any text you want to encode
### π‘ Best Practices:
- Keep content short for easier scanning
- Use URL shorteners for long links
- Test QR codes before printing
- Use high error correction if adding logos
- Ensure good contrast (dark on light background)
- Maintain minimum size (2Γ2 cm for print)
""")
|