import gradio as gr import time import threading # Morse code dictionary MORSE_CODE_DICT = { 'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----', ', ': '--..--', '.': '.-.-.-', '?': '..--..', '/': '-..-.', '-': '-....-', '(': '-.--.', ')': '-.--.-' } dot_duration = 0.2 # seconds dash_duration = dot_duration * 3 space_duration = dot_duration * 7 letter_space_duration = dot_duration * 3 led_html = """
""" def morse_to_text(morse_code): morse_code = morse_code.replace("/", " / ") # Add space for word separator words = morse_code.split(" / ") text = "" for word in words: letters = word.split() #Splits into individual Morse codes for letter in letters: try: found = False for key, value in MORSE_CODE_DICT.items(): if value == letter: text += key found = True break if not found: raise ValueError(f"Invalid Morse code: {letter}") except ValueError as e: raise e text += " " return text.strip() def display_led(morse_code, callback): """Simulates the LED display and updates the Gradio LED component.""" try: for symbol in morse_code: if symbol == '.': callback(led_html.format(color="yellow")) time.sleep(dot_duration) callback(led_html.format(color="gray")) time.sleep(dot_duration) elif symbol == '-': callback(led_html.format(color="yellow")) time.sleep(dash_duration) callback(led_html.format(color="gray")) time.sleep(dot_duration) elif symbol == ' ': time.sleep(letter_space_duration) elif symbol == '/': time.sleep(space_duration) except Exception as e: return f"Error during LED display: {e}" # Return error message return led_html.format(color="gray") # Ensure led is turned off after the process def decode_morse(morse_code): try: decoded_text = morse_to_text(morse_code.upper()) except ValueError as e: return str(e), led_html.format(color="gray") # Return error message # Use a GradioState to keep track of the LED display def update_led(led_status): return led_status # Call the display_led function in a separate thread to keep the UI responsive threading.Thread(target=display_led, args=(morse_code, update_led)).start() return decoded_text, led_html.format(color="gray") # Gradio Interface with gr.Blocks() as iface: morse_input = gr.Textbox(label="Morse Code Input", placeholder="Enter Morse Code Here") decoded_output = gr.Textbox(label="Decoded Text") led_display = gr.HTML(led_html.format(color="gray"), label="LED Output") # Initialize with gray morse_input.change(fn=decode_morse, inputs=morse_input, outputs=[decoded_output, led_display]) # To run it locally iface.launch() # To run it on Hugging Face Spaces """ import tkinter as tk from tkinter import ttk import time import threading # Morse code dictionary MORSE_CODE_DICT = { 'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----', ', ': '--..--', '.': '.-.-.-', '?': '..--..', '/': '-..-.', '-': '-....-', '(': '-.--.', ')': '-.--.-' } class MorseDecoderApp: def __init__(self, root): self.root = root self.root.title("Morse Code Decoder") self.root.geometry("600x400") # Set initial window size self.dot_duration = 200 # milliseconds self.dash_duration = self.dot_duration * 3 self.space_duration = self.dot_duration * 7 # Between words self.letter_space_duration = self.dot_duration * 3 # Between letters # Input Frame self.input_frame = ttk.Frame(root, padding=(10, 10, 10, 10)) self.input_frame.pack(fill=tk.BOTH, expand=True) self.input_label = ttk.Label(self.input_frame, text="Morse Code Input:") self.input_label.grid(row=0, column=0, sticky=tk.W) self.morse_input = tk.Text(self.input_frame, height=5, width=50) self.morse_input.grid(row=1, column=0, columnspan=2, sticky=tk.EW) self.decode_button = ttk.Button(self.input_frame, text="Decode", command=self.decode_morse) self.decode_button.grid(row=2, column=0, columnspan=2, pady=5) # Output Frame self.output_frame = ttk.Frame(root, padding=(10, 0, 10, 10)) # Less top padding self.output_frame.pack(fill=tk.BOTH, expand=True) self.output_label = ttk.Label(self.output_frame, text="Decoded Text:") self.output_label.grid(row=0, column=0, sticky=tk.W) self.decoded_text = tk.Text(self.output_frame, height=5, width=50, state=tk.DISABLED) # Start disabled self.decoded_text.grid(row=1, column=0, columnspan=2, sticky=tk.EW) # LED Light Simulation (Basic) self.led_frame = ttk.Frame(root, padding=(10, 0, 10, 10)) # Less top padding self.led_frame.pack(fill=tk.BOTH, expand=True) self.led_label = ttk.Label(self.led_frame, text="LED Output:") self.led_label.grid(row=0, column=0, sticky=tk.W) self.led = tk.Canvas(self.led_frame, width=50, height=50, bg="white", highlightthickness=0) # highlightthickness=0 removes border self.led.grid(row=1, column=0, columnspan=2, pady=5) # pady for spacing self.led_circle = self.led.create_oval(10, 10, 40, 40, fill="gray", outline="") # outline="" removes the default black outline #Status bar at the bottom self.status_bar = ttk.Label(root, text="Ready", relief=tk.SUNKEN, anchor=tk.W) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) # Configure grid weights for resizing self.input_frame.columnconfigure(0, weight=1) self.input_frame.columnconfigure(1, weight=1) self.input_frame.rowconfigure(1, weight=1) # Morse Text area expands vertically self.output_frame.columnconfigure(0, weight=1) self.output_frame.columnconfigure(1, weight=1) self.output_frame.rowconfigure(1, weight=1) # Decoded text area expands vertically self.led_frame.columnconfigure(0, weight=1) self.led_frame.columnconfigure(1, weight=1) def decode_morse(self): morse_code = self.morse_input.get("1.0", tk.END).strip().upper() # Get text, remove whitespace, and convert to uppercase if not morse_code: self.status_bar.config(text="Error: Please enter Morse code.") return try: decoded_text = self.morse_to_text(morse_code) self.status_bar.config(text="Decoding...") #Update Status bar self.display_led(morse_code) except ValueError as e: self.decoded_text.config(state=tk.NORMAL) self.decoded_text.delete("1.0", tk.END) self.decoded_text.insert("1.0", str(e)) #Display error in output text area self.decoded_text.config(state=tk.DISABLED) self.status_bar.config(text=f"Error: {str(e)}") # Show error in status bar except Exception as e: # Catch any other unexpected errors self.decoded_text.config(state=tk.NORMAL) self.decoded_text.delete("1.0", tk.END) self.decoded_text.insert("1.0", "An unexpected error occurred.") self.decoded_text.config(state=tk.DISABLED) self.status_bar.config(text="An unexpected error occurred.") def morse_to_text(self, morse_code): # Basic morse to text conversion (doesn't handle invalid codes well) morse_code = morse_code.replace("/", " / ") # Add space for word separator words = morse_code.split(" / ") text = "" for word in words: letters = word.split() #Splits into individual Morse codes for letter in letters: try: # Reverse lookup from the dictionary (inefficient for large dictionaries, but simple) found = False for key, value in MORSE_CODE_DICT.items(): if value == letter: text += key found = True break # Stop search once a match is found if not found: raise ValueError(f"Invalid Morse code: {letter}") # Custom Error except ValueError as e: raise e # Re-raise the exception to be caught outside text += " " return text.strip() # Remove trailing space def display_led(self, morse_code): #Displays the Morse code using the LED simulation. Runs in a separate thread to avoid freezing the GUI. self.decode_button.config(state=tk.DISABLED) # Disable button during processing self.morse_input.config(state=tk.DISABLED) # Disable input field during processing def led_thread(): try: for symbol in morse_code: if symbol == '.': self.turn_on_led() time.sleep(self.dot_duration / 1000) self.turn_off_led() time.sleep(self.dot_duration / 1000) # Intra-character space elif symbol == '-': self.turn_on_led() time.sleep(self.dash_duration / 1000) self.turn_off_led() time.sleep(self.dot_duration / 1000) # Intra-character space elif symbol == ' ': # Space between letters time.sleep(self.letter_space_duration / 1000) # Longer pause between letters elif symbol == '/': time.sleep(self.space_duration/1000) self.root.update() # Update the GUI decoded_text = self.morse_to_text(morse_code) #Decode here, after animation is complete self.decoded_text.config(state=tk.NORMAL) self.decoded_text.delete("1.0", tk.END) self.decoded_text.insert("1.0", decoded_text) self.decoded_text.config(state=tk.DISABLED) self.status_bar.config(text="Decoding complete.") #Update Status bar except Exception as e: self.status_bar.config(text=f"Error during LED display: {e}") #Status bar print(f"Error during LED display: {e}") finally: # Make sure to re-enable button/text, even if there's an error self.decode_button.config(state=tk.NORMAL) self.morse_input.config(state=tk.NORMAL) thread = threading.Thread(target=led_thread) thread.start() def turn_on_led(self): self.led.itemconfig(self.led_circle, fill="yellow") # Simulate LED turning on self.root.update() def turn_off_led(self): self.led.itemconfig(self.led_circle, fill="gray") # Simulate LED turning off self.root.update() if __name__ == "__main__": root = tk.Tk() app = MorseDecoderApp(root) root.mainloop() """