Skip to content

Commit 5b0ac30

Browse files
authored
Create Phone-Number-Formatter.py
1 parent e910c3d commit 5b0ac30

1 file changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import sys
2+
import os
3+
import threading
4+
import tkinter as tk
5+
from tkinter import ttk, messagebox, filedialog
6+
import sv_ttk
7+
import phonenumbers
8+
from phonenumbers import NumberParseException, PhoneNumberFormat, PhoneNumberType
9+
import pycountry
10+
11+
# =========================
12+
# Helpers
13+
# =========================
14+
def resource_path(file_name):
15+
base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
16+
return os.path.join(base_path, file_name)
17+
18+
def set_status(msg):
19+
status_var.set(msg)
20+
root.update_idletasks()
21+
22+
def get_country_name(code):
23+
try:
24+
return pycountry.countries.get(alpha_2=code).name
25+
except:
26+
return code
27+
28+
def get_example_number(region_code):
29+
"""Returns an example national number for the region"""
30+
try:
31+
example = phonenumbers.example_number_for_type(region_code, PhoneNumberType.MOBILE)
32+
if example:
33+
return phonenumbers.format_number(example, PhoneNumberFormat.NATIONAL)
34+
return ""
35+
except:
36+
return ""
37+
38+
# =========================
39+
# App Setup
40+
# =========================
41+
root = tk.Tk()
42+
root.title("Universal Phone Formatter")
43+
root.geometry("780x750")
44+
sv_ttk.set_theme("light")
45+
46+
# =========================
47+
# Globals
48+
# =========================
49+
dark_mode_var = tk.BooleanVar(value=False)
50+
phone_var = tk.StringVar()
51+
formatted_result_var = tk.StringVar(value="Result: —")
52+
phone_history = []
53+
region_var = tk.StringVar(value="US")
54+
region_example_var = tk.StringVar(value="")
55+
56+
# =========================
57+
# Theme Toggle
58+
# =========================
59+
def toggle_theme():
60+
bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
61+
fg = "white" if dark_mode_var.get() else "black"
62+
root.configure(bg=bg)
63+
for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]:
64+
style.configure(w, background=bg, foreground=fg)
65+
phone_entry.configure(background=bg, foreground=fg)
66+
region_entry.configure(background=bg, foreground=fg)
67+
68+
# =========================
69+
# Update Example Number
70+
# =========================
71+
def update_region_example(*args):
72+
region = region_var.get().strip().upper()
73+
example = get_example_number(region)
74+
region_example_var.set(f"Example format for {region}: {example}" if example else "No example available")
75+
76+
region_var.trace_add("write", update_region_example)
77+
update_region_example() # Initial example
78+
79+
# =========================
80+
# Phone Formatting Logic
81+
# =========================
82+
def format_phone_number(number, region=None):
83+
try:
84+
parsed = phonenumbers.parse(number, region) if region else phonenumbers.parse(number, None)
85+
if not phonenumbers.is_possible_number(parsed):
86+
return None
87+
if not phonenumbers.is_valid_number(parsed):
88+
return None
89+
international = phonenumbers.format_number(parsed, PhoneNumberFormat.INTERNATIONAL)
90+
national = phonenumbers.format_number(parsed, PhoneNumberFormat.NATIONAL)
91+
country = get_country_name(phonenumbers.region_code_for_number(parsed))
92+
return f"{international} (National: {national}, Country: {country})"
93+
except NumberParseException:
94+
return None
95+
96+
def validate_phone():
97+
number = phone_var.get().strip()
98+
region = region_var.get().strip().upper()
99+
if not number:
100+
messagebox.showwarning("Error", "Please enter a phone number.")
101+
return
102+
103+
set_status("Formatting phone number...")
104+
threading.Thread(target=_validate_phone_thread, args=(number, region), daemon=True).start()
105+
106+
def _validate_phone_thread(number, region):
107+
formatted = format_phone_number(number, region)
108+
if formatted:
109+
formatted_result_var.set(f"Result: ✅ {formatted}")
110+
add_to_history(formatted)
111+
else:
112+
formatted_result_var.set("Result: ❌ Invalid phone number")
113+
set_status("Formatting complete")
114+
115+
# =========================
116+
# Phone History
117+
# =========================
118+
def add_to_history(formatted_number):
119+
phone_history.append(formatted_number)
120+
history_list.insert(tk.END, f"{len(phone_history)}{formatted_number}")
121+
122+
def export_history_txt():
123+
if not phone_history:
124+
messagebox.showinfo("Empty History", "No phone numbers to export.")
125+
return
126+
127+
file_path = filedialog.asksaveasfilename(
128+
defaultextension=".txt",
129+
filetypes=[("Text Files", "*.txt")],
130+
title="Export Phone History"
131+
)
132+
133+
if not file_path:
134+
return
135+
136+
try:
137+
with open(file_path, "w", encoding="utf-8") as f:
138+
f.write("Universal Phone Number History\n")
139+
f.write("=" * 50 + "\n\n")
140+
for i, number in enumerate(phone_history, 1):
141+
f.write(f"{i}. {number}\n")
142+
set_status("Phone history exported")
143+
messagebox.showinfo("Export Successful", "Phone history saved successfully.")
144+
except Exception as e:
145+
messagebox.showerror("Export Failed", str(e))
146+
147+
# =========================
148+
# Styles
149+
# =========================
150+
style = ttk.Style()
151+
style.theme_use("clam")
152+
style.configure("Action.TButton", font=("Segoe UI", 11, "bold"), padding=8)
153+
154+
# =========================
155+
# Status Bar
156+
# =========================
157+
status_var = tk.StringVar(value="Ready")
158+
ttk.Label(root, textvariable=status_var, anchor="w").pack(side=tk.BOTTOM, fill="x")
159+
160+
# =========================
161+
# UI Layout
162+
# =========================
163+
main = ttk.Frame(root, padding=20)
164+
main.pack(expand=True, fill="both")
165+
166+
ttk.Label(main, text="Universal Phone Formatter",
167+
font=("Segoe UI", 22, "bold")).pack()
168+
169+
ttk.Label(main, text="Enter Phone Number:", font=("Segoe UI", 12)).pack(pady=(10,0))
170+
phone_entry = ttk.Entry(main, textvariable=phone_var, font=("Segoe UI", 14), justify="center")
171+
phone_entry.pack(fill="x", pady=6)
172+
173+
ttk.Label(main, text="Default Region (for local numbers, e.g., US, GB, IN):", font=("Segoe UI", 10)).pack(pady=(4,0))
174+
region_entry = ttk.Entry(main, textvariable=region_var, font=("Segoe UI", 12), justify="center")
175+
region_entry.pack(fill="x", pady=4)
176+
177+
ttk.Label(main, textvariable=region_example_var, font=("Segoe UI", 10, "italic")).pack(pady=(2,6))
178+
179+
ttk.Label(main, textvariable=formatted_result_var, font=("Segoe UI", 12, "bold")).pack(pady=8)
180+
181+
# =========================
182+
# Controls
183+
# =========================
184+
controls = ttk.Frame(main)
185+
controls.pack(pady=8)
186+
187+
ttk.Button(controls, text="✅ Format", command=validate_phone, style="Action.TButton").pack(side="left", padx=4)
188+
ttk.Button(controls, text="📤 Export History", command=export_history_txt, style="Action.TButton").pack(side="left", padx=4)
189+
190+
# =========================
191+
# History Vault
192+
# =========================
193+
vault = ttk.LabelFrame(main, text="Phone History Vault", padding=10)
194+
vault.pack(fill="both", expand=True, pady=10)
195+
196+
history_list = tk.Listbox(vault, font=("Segoe UI", 10), height=12)
197+
history_list.pack(fill="both", expand=True)
198+
199+
# =========================
200+
# Options
201+
# =========================
202+
ttk.Checkbutton(main, text="Dark Mode", variable=dark_mode_var, command=toggle_theme).pack(pady=6)
203+
204+
# =========================
205+
# Run App
206+
# =========================
207+
root.mainloop()

0 commit comments

Comments
 (0)