betterqr 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: betterqr
3
+ Version: 0.1.0
4
+ Author: DevX-Dragon
5
+ Requires-Dist: pillow>=10.0.0
@@ -0,0 +1,4 @@
1
+ ![image](https://cdn.hackclub.com/019ead26-8c58-711a-bd9b-c3dcc19c367f/image.png)
2
+
3
+ # BetterQR
4
+ The upgraded qrcode library for Python. Features an CLI and normal code support as well!. Comes packed with many features.
@@ -0,0 +1,14 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "betterqr"
7
+ version = "0.1.0"
8
+ authors = [{ name = "DevX-Dragon" }]
9
+ dependencies = [
10
+ "pillow>=10.0.0"
11
+ ]
12
+
13
+ [project.scripts]
14
+ betterqr = "betterqr.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .core import (
2
+ QR, WiFi, VCard, MeCard, GeoLocation,
3
+ Event, SMS, Email, Phone, Crypto, batch
4
+ )
5
+ __version__ = "2.0.0"
6
+ __all__ = ["QR","WiFi","VCard","MeCard","GeoLocation","Event","SMS","Email","Phone","Crypto","batch"]
@@ -0,0 +1,307 @@
1
+ """
2
+ betterqr CLI
3
+ ============
4
+ The cleanest QR code generator in your terminal.
5
+
6
+ BASIC USAGE
7
+ betterqr "https://example.com" → qr.png
8
+ betterqr "Hello World" out.png
9
+ betterqr "https://example.com" out.svg
10
+
11
+ SHAPES
12
+ betterqr "text" -s circle
13
+ betterqr "text" -s rounded
14
+ betterqr "text" -s diamond / star / gapped
15
+
16
+ COLORS
17
+ betterqr "text" --fill "#6C3082" --back "#F3E8FF"
18
+ betterqr "text" --fill "#000" --back transparent
19
+
20
+ GRADIENTS
21
+ betterqr "text" --gradient "#FF6B6B" "#4ECDC4"
22
+ betterqr "text" --gradient "#1d4ed8" "#7c3aed" --gradient-dir radial
23
+
24
+ LOGO
25
+ betterqr "https://mysite.com" --logo logo.png
26
+ betterqr "https://mysite.com" --logo logo.png --logo-ratio 0.3 -e H
27
+
28
+ FRAME & LABEL
29
+ betterqr "Scan Me!" --frame rounded --label "Visit our site"
30
+ betterqr "Scan Me!" --frame fancy --label "WiFi Password: abc123"
31
+
32
+ ANIMATION (saves .gif)
33
+ betterqr "Hello" out.gif --effect shimmer
34
+ betterqr "Hello" out.gif --effect matrix --fps 12
35
+ betterqr "Hello" out.gif --effect wave --frames 30
36
+
37
+ DATA TYPES
38
+ betterqr --wifi MyNet MyPassword
39
+ betterqr --wifi MyNet MyPassword --security WEP
40
+ betterqr --contact "Jane Doe" --phone "+1-555-0199" --email "jane@example.com"
41
+ betterqr --geo 51.5074 -0.1278
42
+ betterqr --sms "+1-555-0199" "Hello!"
43
+ betterqr --email "hi@example.com" "Subject" "Body text"
44
+ betterqr --phone "+1-800-555-0199"
45
+
46
+ TERMINAL PREVIEW
47
+ betterqr "Hello" --print
48
+ betterqr "Hello" --print --invert
49
+ Note: Add stuff here as well if you contribute lol
50
+ """
51
+ import argparse
52
+ import sys
53
+ import os
54
+
55
+
56
+ def _color(s):
57
+ """Validate a color argument."""
58
+ if s.lower() == 'transparent': return s
59
+ if not s.startswith('#'): s = '#' + s
60
+ s = s.lstrip('#')
61
+ if len(s) not in (3, 6):
62
+ raise argparse.ArgumentTypeError(f"Invalid color '{s}'. Use hex like #FF0000 or #F00")
63
+ return '#' + s
64
+
65
+
66
+ def main():
67
+ p = argparse.ArgumentParser(
68
+ prog="betterqr",
69
+ description="BetterQR — Beautiful, scannable QR codes. Zero external QR dependencies.",
70
+ formatter_class=argparse.RawDescriptionHelpFormatter,
71
+ epilog=__doc__,
72
+ add_help=True,
73
+ )
74
+
75
+ # ── Core ──────────────────────────────────────────────────────────
76
+ p.add_argument("data", nargs="?", help="Data to encode (text, URL, etc.)")
77
+ p.add_argument("output", nargs="?", default="qr.png",
78
+ help="Output file (.png .jpg .svg .gif) [default: qr.png]")
79
+
80
+ # ── QR settings ───────────────────────────────────────────────────
81
+ qr = p.add_argument_group("QR settings")
82
+ qr.add_argument("-e", "--ecc", default="M", choices=["L","M","Q","H"],
83
+ metavar="L|M|Q|H",
84
+ help="Error correction: L=7%% M=15%% Q=25%% H=30%% [default: M]. Use H with logos.")
85
+ qr.add_argument("-v", "--version", type=int, metavar="1-40",
86
+ help="Force QR version 1-40 (auto-selected by default)")
87
+ qr.add_argument("--box-size", type=int, default=10, metavar="PX",
88
+ help="Pixels per module [default: 10]")
89
+ qr.add_argument("--border", type=int, default=4, metavar="N",
90
+ help="Quiet zone width in modules [default: 4]")
91
+
92
+ # ── Style ─────────────────────────────────────────────────────────
93
+ st = p.add_argument_group("Style")
94
+ st.add_argument("-s", "--shape", default="square",
95
+ choices=["square","circle","rounded","diamond","star","gapped",
96
+ "vertical_bar","horizontal_bar"],
97
+ metavar="SHAPE",
98
+ help="Module shape [default: square]\n"
99
+ " square | circle | rounded | diamond | star | gapped | vertical_bar | horizontal_bar")
100
+ st.add_argument("--fill", default="#000000", type=_color, metavar="COLOR",
101
+ help="Dark module color [default: #000000]")
102
+ st.add_argument("--back", default="#FFFFFF", type=_color, metavar="COLOR",
103
+ help="Background color [default: #FFFFFF] (use 'transparent' for PNG alpha)")
104
+ st.add_argument("--finder", type=_color, metavar="COLOR",
105
+ help="Separate color for the 3 finder squares")
106
+
107
+ # ── Gradient ──────────────────────────────────────────────────────
108
+ gr = p.add_argument_group("Gradient")
109
+ gr.add_argument("--gradient", nargs=2, metavar=("START","END"),
110
+ help="Two colors for a gradient fill. Example: --gradient '#FF6B6B' '#4ECDC4'")
111
+ gr.add_argument("--gradient-dir", default="diagonal",
112
+ choices=["horizontal","vertical","diagonal","radial"],
113
+ metavar="DIR",
114
+ help="Gradient direction [default: diagonal]\n"
115
+ " horizontal | vertical | diagonal | radial")
116
+
117
+ # ── Logo ──────────────────────────────────────────────────────────
118
+ lo = p.add_argument_group("Logo / image")
119
+ lo.add_argument("--logo", metavar="PATH",
120
+ help="Embed a logo/image in the center of the QR code (PNG/JPG)")
121
+ lo.add_argument("--logo-ratio", type=float, default=0.25, metavar="0.1-0.35",
122
+ help="Logo size as fraction of QR total size [default: 0.25]")
123
+ lo.add_argument("--logo-shape", default="square",
124
+ choices=["square","circle","rounded"],
125
+ help="Logo background shape [default: square]")
126
+
127
+ # ── Frame / label ─────────────────────────────────────────────────
128
+ fr = p.add_argument_group("Frame & label")
129
+ fr.add_argument("--frame", metavar="STYLE",
130
+ choices=["simple","rounded","double","shadow","fancy"],
131
+ help="Add a decorative frame: simple | rounded | double | shadow | fancy")
132
+ fr.add_argument("--frame-color", type=_color, default="#000000", metavar="COLOR",
133
+ help="Frame color [default: #000000]")
134
+ fr.add_argument("--label", metavar="TEXT",
135
+ help="Text label to add below (or above) the QR code")
136
+ fr.add_argument("--label-above", action="store_true",
137
+ help="Place label above the QR instead of below")
138
+ fr.add_argument("--label-color", type=_color, default="#000000", metavar="COLOR")
139
+ fr.add_argument("--label-size", type=int, default=14, metavar="PX")
140
+
141
+ # ── Animation ─────────────────────────────────────────────────────
142
+ an = p.add_argument_group("Animation (saves .gif)")
143
+ an.add_argument("--effect", metavar="EFFECT",
144
+ choices=["shimmer","fade","scan","pulse","build","matrix",
145
+ "wave","blink","typewriter","rotate"],
146
+ help="Animation effect:\n"
147
+ " shimmer | fade | scan | pulse | build |\n"
148
+ " matrix | wave | blink | typewriter | rotate")
149
+ an.add_argument("--frames", type=int, default=20, metavar="N",
150
+ help="Number of animation frames [default: 20]")
151
+ an.add_argument("--fps", type=int, default=15, metavar="N",
152
+ help="Animation playback speed [default: 15]")
153
+ an.add_argument("--accent", type=_color, metavar="COLOR",
154
+ help="Accent color for some animation effects")
155
+
156
+ # ── Data type shortcuts ───────────────────────────────────────────
157
+ dt = p.add_argument_group("Data type shortcuts")
158
+ dt.add_argument("--wifi", nargs="+", metavar=("SSID", "PASS"),
159
+ help="Wi-Fi QR: --wifi SSID [PASSWORD]")
160
+ dt.add_argument("--security", default="WPA", choices=["WPA","WEP","nopass"],
161
+ help="Wi-Fi security type [default: WPA]")
162
+ dt.add_argument("--contact", metavar="NAME",
163
+ help="vCard contact QR (use with --phone --email --org --url)")
164
+ dt.add_argument("--phone", metavar="NUMBER",
165
+ help="Phone number (for --contact or standalone tel: QR)")
166
+ dt.add_argument("--email", nargs="+", metavar=("ADDRESS", "SUBJECT"),
167
+ help="Email QR: --email ADDRESS [SUBJECT] [BODY]")
168
+ dt.add_argument("--org", metavar="NAME", help="Organization (for --contact)")
169
+ dt.add_argument("--url", metavar="URL", help="URL (for --contact)")
170
+ dt.add_argument("--geo", nargs=2, metavar=("LAT","LON"),
171
+ help="Location QR: --geo 51.5074 -0.1278")
172
+ dt.add_argument("--sms", nargs=2, metavar=("PHONE","MSG"),
173
+ help="SMS QR: --sms PHONE 'Message body'")
174
+
175
+ # ── Output ────────────────────────────────────────────────────────
176
+ ou = p.add_argument_group("Output")
177
+ ou.add_argument("--print", action="store_true",
178
+ help="Print QR to terminal (works alongside file output)")
179
+ ou.add_argument("--invert", action="store_true",
180
+ help="Invert terminal colors (for dark-background terminals)")
181
+ ou.add_argument("--terminal-style", default="block",
182
+ choices=["block","ascii","compact"],
183
+ help="Terminal render style [default: block]")
184
+ ou.add_argument("--info", action="store_true",
185
+ help="Print QR code metadata (version, size, mode, etc.)")
186
+
187
+ args = p.parse_args()
188
+
189
+ # ── Build data payload ────────────────────────────────────────────
190
+ from .core import QR, WiFi, VCard, GeoLocation, SMS, Email, Phone
191
+
192
+ data = None
193
+
194
+ if args.wifi:
195
+ ssid = args.wifi[0]
196
+ pw = args.wifi[1] if len(args.wifi) > 1 else ""
197
+ data = WiFi(ssid, pw, args.security)
198
+
199
+ elif args.contact:
200
+ data = VCard(
201
+ name = args.contact,
202
+ phone = args.phone or "",
203
+ email = args.email[0] if args.email else "",
204
+ org = args.org or "",
205
+ url = args.url or "",
206
+ )
207
+
208
+ elif args.geo:
209
+ data = GeoLocation(float(args.geo[0]), float(args.geo[1]))
210
+
211
+ elif args.sms:
212
+ data = SMS(args.sms[0], args.sms[1])
213
+
214
+ elif args.email and not args.contact:
215
+ addr = args.email[0]
216
+ subject = args.email[1] if len(args.email) > 1 else ""
217
+ body = args.email[2] if len(args.email) > 2 else ""
218
+ data = Email(addr, subject, body)
219
+
220
+ elif args.phone and not args.contact:
221
+ data = Phone(args.phone)
222
+
223
+ elif args.data:
224
+ data = args.data
225
+
226
+ else:
227
+ p.print_help()
228
+ sys.exit(1)
229
+
230
+ # ── Build QR ─────────────────────────────────────────────────────
231
+ try:
232
+ qr = QR(data, ecc=args.ecc, version=args.version)
233
+ except ValueError as e:
234
+ print(f"✗ Error: {e}", file=sys.stderr)
235
+ sys.exit(1)
236
+
237
+ # ── Apply style ───────────────────────────────────────────────────
238
+ fill = args.fill
239
+ if args.gradient:
240
+ qr.gradient(args.gradient[0], args.gradient[1], args.gradient_dir)
241
+ else:
242
+ qr.style(fill=fill, back=args.back, shape=args.shape,
243
+ finder=args.finder, box_size=args.box_size, border=args.border)
244
+
245
+ # Gradient + shape/back
246
+ if args.gradient:
247
+ qr._shape = args.shape
248
+ qr._back = args.back
249
+ qr._box_size = args.box_size
250
+ qr._border = args.border
251
+ if args.finder: qr._finder_color = args.finder
252
+
253
+ # Logo
254
+ if args.logo:
255
+ qr.logo(args.logo, ratio=args.logo_ratio, shape=args.logo_shape)
256
+
257
+ # Frame / label
258
+ if args.frame or args.label:
259
+ frame_style = args.frame or "simple"
260
+ qr.frame(
261
+ style = frame_style,
262
+ color = args.frame_color,
263
+ label = args.label,
264
+ label_color = args.label_color,
265
+ label_size = args.label_size,
266
+ label_position = "top" if args.label_above else "bottom",
267
+ )
268
+
269
+ # Animation
270
+ if args.effect:
271
+ qr.animate(
272
+ effect = args.effect,
273
+ frames = args.frames,
274
+ fps = args.fps,
275
+ accent = args.accent,
276
+ )
277
+ # Force .gif output if not already
278
+ if not args.output.lower().endswith(".gif"):
279
+ args.output = args.output.rsplit(".", 1)[0] + ".gif"
280
+
281
+ # ── Info ─────────────────────────────────────────────────────────
282
+ if args.info:
283
+ info = qr.info()
284
+ print("QR Code Info")
285
+ print("─" * 30)
286
+ for k, v in info.items():
287
+ print(f" {k:<14} {v}")
288
+ print()
289
+
290
+ # ── Terminal print ────────────────────────────────────────────────
291
+ if getattr(args, 'print'):
292
+ print(qr.to_terminal(style=args.terminal_style, invert=args.invert))
293
+
294
+ # ── Save file ─────────────────────────────────────────────────────
295
+ if args.output:
296
+ try:
297
+ qr.save(args.output)
298
+ ext = args.output.rsplit('.',1)[-1].upper()
299
+ print(f"✓ Saved {args.output} "
300
+ f"[v{qr.version} ECC-{qr.ecc_level} {qr.module_count}×{qr.module_count} modules]")
301
+ except Exception as e:
302
+ print(f"✗ Failed to save: {e}", file=sys.stderr)
303
+ sys.exit(1)
304
+
305
+
306
+ if __name__ == "__main__":
307
+ main()