|
| 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 | + |
| 8 | +# ========================= |
| 9 | +# Helpers |
| 10 | +# ========================= |
| 11 | +def resource_path(file_name): |
| 12 | + base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__))) |
| 13 | + return os.path.join(base_path, file_name) |
| 14 | + |
| 15 | +def set_status(msg): |
| 16 | + status_var.set(msg) |
| 17 | + root.update_idletasks() |
| 18 | + |
| 19 | +# ========================= |
| 20 | +# App Setup |
| 21 | +# ========================= |
| 22 | +root = tk.Tk() |
| 23 | +root.title("Binary → Decimal Converter Pro") |
| 24 | +root.geometry("1200x680") |
| 25 | +sv_ttk.set_theme("light") |
| 26 | + |
| 27 | +# ========================= |
| 28 | +# Globals |
| 29 | +# ========================= |
| 30 | +dark_mode_var = tk.BooleanVar(value=False) |
| 31 | +binary_var = tk.StringVar() |
| 32 | +decimal_var = tk.StringVar(value="0") |
| 33 | +batch_list = [] |
| 34 | +stats_vars = { |
| 35 | + "total_binaries": tk.StringVar(value="0"), |
| 36 | + "valid": tk.StringVar(value="0"), |
| 37 | + "invalid": tk.StringVar(value="0"), |
| 38 | + "sum": tk.StringVar(value="0"), |
| 39 | +} |
| 40 | + |
| 41 | +# ========================= |
| 42 | +# Theme Toggle |
| 43 | +# ========================= |
| 44 | +def toggle_theme(): |
| 45 | + style.theme_use("clam") |
| 46 | + bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF" |
| 47 | + fg = "white" if dark_mode_var.get() else "black" |
| 48 | + |
| 49 | + root.configure(bg=bg) |
| 50 | + for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]: |
| 51 | + style.configure(w, background=bg, foreground=fg) |
| 52 | + |
| 53 | + input_entry.configure( |
| 54 | + bg="#1e1e1e" if dark_mode_var.get() else "white", |
| 55 | + fg="white" if dark_mode_var.get() else "black", |
| 56 | + insertbackground="white" if dark_mode_var.get() else "black" |
| 57 | + ) |
| 58 | + |
| 59 | + batch_text.configure( |
| 60 | + bg="#1e1e1e" if dark_mode_var.get() else "white", |
| 61 | + fg="white" if dark_mode_var.get() else "black", |
| 62 | + insertbackground="white" if dark_mode_var.get() else "black" |
| 63 | + ) |
| 64 | + |
| 65 | + set_status(f"Theme switched to {'Dark' if dark_mode_var.get() else 'Light'} mode") |
| 66 | + |
| 67 | +# ========================= |
| 68 | +# Core Logic |
| 69 | +# ========================= |
| 70 | +def convert_single_binary(): |
| 71 | + bin_str = binary_var.get().strip() |
| 72 | + if not bin_str: |
| 73 | + decimal_var.set("0") |
| 74 | + set_status("Enter a binary number") |
| 75 | + return |
| 76 | + |
| 77 | + if not all(c in "01" for c in bin_str): |
| 78 | + decimal_var.set("Error") |
| 79 | + set_status("Invalid binary number") |
| 80 | + return |
| 81 | + |
| 82 | + decimal_var.set(str(int(bin_str, 2))) |
| 83 | + set_status(f"Converted {bin_str} → {decimal_var.get()}") |
| 84 | + |
| 85 | +def convert_batch(): |
| 86 | + content = batch_text.get("1.0", tk.END).strip().splitlines() |
| 87 | + batch_list.clear() |
| 88 | + valid_count = 0 |
| 89 | + invalid_count = 0 |
| 90 | + total_sum = 0 |
| 91 | + results = [] |
| 92 | + |
| 93 | + for line in content: |
| 94 | + line = line.strip() |
| 95 | + if not line: |
| 96 | + continue |
| 97 | + if all(c in "01" for c in line): |
| 98 | + dec = int(line, 2) |
| 99 | + results.append(f"{line} → {dec}") |
| 100 | + valid_count += 1 |
| 101 | + total_sum += dec |
| 102 | + batch_list.append((line, dec)) |
| 103 | + else: |
| 104 | + results.append(f"{line} → Invalid") |
| 105 | + invalid_count += 1 |
| 106 | + batch_list.append((line, "Invalid")) |
| 107 | + |
| 108 | + batch_text.delete("1.0", tk.END) |
| 109 | + batch_text.insert("1.0", "\n".join(results)) |
| 110 | + |
| 111 | + stats_vars["total_binaries"].set(len(content)) |
| 112 | + stats_vars["valid"].set(valid_count) |
| 113 | + stats_vars["invalid"].set(invalid_count) |
| 114 | + stats_vars["sum"].set(total_sum) |
| 115 | + |
| 116 | + set_status("Batch conversion completed") |
| 117 | + |
| 118 | +def delayed_single_convert(event=None): |
| 119 | + threading.Timer(0.1, convert_single_binary).start() |
| 120 | + |
| 121 | +def delayed_batch_convert(event=None): |
| 122 | + threading.Timer(0.2, convert_batch).start() |
| 123 | + |
| 124 | +# ========================= |
| 125 | +# File Operations |
| 126 | +# ========================= |
| 127 | +def open_file(): |
| 128 | + path = filedialog.askopenfilename( |
| 129 | + filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")] |
| 130 | + ) |
| 131 | + if not path: |
| 132 | + return |
| 133 | + try: |
| 134 | + with open(path, "r", encoding="utf-8") as f: |
| 135 | + batch_text.delete("1.0", tk.END) |
| 136 | + batch_text.insert("1.0", f.read()) |
| 137 | + convert_batch() |
| 138 | + set_status(f"Loaded: {os.path.basename(path)}") |
| 139 | + except Exception as e: |
| 140 | + messagebox.showerror("Error", str(e)) |
| 141 | + |
| 142 | +def save_file(): |
| 143 | + path = filedialog.asksaveasfilename( |
| 144 | + defaultextension=".txt", |
| 145 | + filetypes=[("Text Files", "*.txt")] |
| 146 | + ) |
| 147 | + if not path: |
| 148 | + return |
| 149 | + try: |
| 150 | + with open(path, "w", encoding="utf-8") as f: |
| 151 | + f.write(batch_text.get("1.0", tk.END)) |
| 152 | + set_status(f"Saved to {path}") |
| 153 | + except Exception as e: |
| 154 | + messagebox.showerror("Error", str(e)) |
| 155 | + |
| 156 | +def clear_all(): |
| 157 | + if messagebox.askyesno("Clear", "Clear all inputs and outputs?"): |
| 158 | + binary_var.set("") |
| 159 | + decimal_var.set("0") |
| 160 | + batch_text.delete("1.0", tk.END) |
| 161 | + for key in stats_vars: |
| 162 | + stats_vars[key].set("0") |
| 163 | + set_status("Cleared all inputs and outputs") |
| 164 | + |
| 165 | +# ========================= |
| 166 | +# Help Window |
| 167 | +# ========================= |
| 168 | +def show_help(): |
| 169 | + win = tk.Toplevel(root) |
| 170 | + win.title("Binary → Decimal Converter Pro - Help") |
| 171 | + win.geometry("500x400") |
| 172 | + win.configure(bg="#2e2e2e") |
| 173 | + win.resizable(False, False) |
| 174 | + win.transient(root) |
| 175 | + win.grab_set() |
| 176 | + |
| 177 | + frame = tk.Frame(win, bg="#2e2e2e") |
| 178 | + frame.pack(fill="both", expand=True, padx=12, pady=12) |
| 179 | + |
| 180 | + text = tk.Text( |
| 181 | + frame, |
| 182 | + bg="#2e2e2e", |
| 183 | + fg="#f0f0f0", |
| 184 | + font=("Segoe UI", 11), |
| 185 | + wrap="word", |
| 186 | + borderwidth=0 |
| 187 | + ) |
| 188 | + text.pack(fill="both", expand=True) |
| 189 | + |
| 190 | + help_text = """💻 Binary → Decimal Converter Pro |
| 191 | +
|
| 192 | +• Single binary input → decimal output |
| 193 | +• Batch conversion: multiple binaries, one per line |
| 194 | +• Automatic detection of invalid binaries |
| 195 | +• Live statistics: total, valid, invalid, sum of decimals |
| 196 | +• Open/save .txt files for batch processing |
| 197 | +• Clear all inputs and outputs with one click |
| 198 | +• Dark Mode toggle for comfortable viewing |
| 199 | +
|
| 200 | +Built with Python & Tkinter |
| 201 | +""" |
| 202 | + text.insert("1.0", help_text) |
| 203 | + text.config(state="disabled") |
| 204 | + |
| 205 | +# ========================= |
| 206 | +# Styles |
| 207 | +# ========================= |
| 208 | +style = ttk.Style() |
| 209 | +style.theme_use("clam") |
| 210 | +style.configure( |
| 211 | + "Action.TButton", |
| 212 | + font=("Segoe UI", 11, "bold"), |
| 213 | + foreground="white", |
| 214 | + background="#4CAF50", |
| 215 | + padding=8 |
| 216 | +) |
| 217 | +style.map("Action.TButton", background=[("active", "#45a049")]) |
| 218 | + |
| 219 | +# ========================= |
| 220 | +# Status Bar |
| 221 | +# ========================= |
| 222 | +status_var = tk.StringVar(value="Ready") |
| 223 | +ttk.Label(root, textvariable=status_var, anchor="w").pack(side=tk.BOTTOM, fill="x") |
| 224 | + |
| 225 | +# ========================= |
| 226 | +# Main UI |
| 227 | +# ========================= |
| 228 | +main = ttk.Frame(root, padding=20) |
| 229 | +main.pack(expand=True, fill="both") |
| 230 | + |
| 231 | +ttk.Label(main, text="Binary → Decimal Converter Pro", font=("Segoe UI", 20, "bold")).pack() |
| 232 | +ttk.Label(main, text="Single & Batch Conversion Tool", font=("Segoe UI", 11)).pack(pady=(0, 10)) |
| 233 | + |
| 234 | +# ========================= |
| 235 | +# Single Conversion Frame |
| 236 | +# ========================= |
| 237 | +single_frame = ttk.LabelFrame(main, text="Single Conversion", padding=12) |
| 238 | +single_frame.pack(fill="x", pady=6) |
| 239 | + |
| 240 | +ttk.Label(single_frame, text="Binary Input:", font=("Segoe UI", 10, "bold")).pack(anchor="w") |
| 241 | +input_entry = ttk.Entry(single_frame, textvariable=binary_var, font=("Segoe UI", 12)) |
| 242 | +input_entry.pack(fill="x", pady=4) |
| 243 | +input_entry.bind("<KeyRelease>", delayed_single_convert) |
| 244 | + |
| 245 | +ttk.Label(single_frame, text="Decimal Output:", font=("Segoe UI", 10, "bold")).pack(anchor="w", pady=(8,0)) |
| 246 | +ttk.Label(single_frame, textvariable=decimal_var, font=("Segoe UI", 12, "bold")).pack(anchor="w") |
| 247 | + |
| 248 | +# ========================= |
| 249 | +# Batch Conversion Frame |
| 250 | +# ========================= |
| 251 | +batch_frame = ttk.LabelFrame(main, text="Batch Conversion", padding=12) |
| 252 | +batch_frame.pack(fill="both", expand=True, pady=6) |
| 253 | + |
| 254 | +batch_text = tk.Text(batch_frame, font=("Segoe UI", 11), height=5, wrap="none") |
| 255 | +batch_text.pack(fill="both", expand=True) |
| 256 | +batch_text.bind("<KeyRelease>", delayed_batch_convert) |
| 257 | + |
| 258 | +# ========================= |
| 259 | +# Statistics Frame |
| 260 | +# ========================= |
| 261 | +stats_frame = ttk.LabelFrame(main, text="Statistics", padding=12) |
| 262 | +stats_frame.pack(fill="x", pady=6) |
| 263 | + |
| 264 | +for i, (label, var) in enumerate(stats_vars.items()): |
| 265 | + ttk.Label(stats_frame, text=label.replace("_", " ").title() + ":", |
| 266 | + font=("Segoe UI", 10, "bold")).grid(row=0, column=i * 2, padx=4, sticky="e") |
| 267 | + ttk.Label(stats_frame, textvariable=var, |
| 268 | + font=("Segoe UI", 10)).grid(row=0, column=i * 2 + 1, padx=6, sticky="w") |
| 269 | + |
| 270 | +# ========================= |
| 271 | +# Actions |
| 272 | +# ========================= |
| 273 | +actions = ttk.Frame(main) |
| 274 | +actions.pack(pady=12) |
| 275 | + |
| 276 | +ttk.Button(actions, text="📂 Open", command=open_file, |
| 277 | + style="Action.TButton").pack(side="left", padx=6) |
| 278 | +ttk.Button(actions, text="💾 Save", command=save_file, |
| 279 | + style="Action.TButton").pack(side="left", padx=6) |
| 280 | +ttk.Button(actions, text="🧹 Clear", command=clear_all, |
| 281 | + style="Action.TButton").pack(side="left", padx=6) |
| 282 | +ttk.Button(actions, text="❓ Help", command=show_help, |
| 283 | + style="Action.TButton").pack(side="left", padx=6) |
| 284 | + |
| 285 | +ttk.Checkbutton( |
| 286 | + actions, |
| 287 | + text="Dark Mode", |
| 288 | + variable=dark_mode_var, |
| 289 | + command=toggle_theme |
| 290 | +).pack(side="left", padx=14) |
| 291 | + |
| 292 | +# ========================= |
| 293 | +# Run App |
| 294 | +# ========================= |
| 295 | +root.mainloop() |
0 commit comments