MorseCode / simulator.py
nightfury's picture
Update simulator.py
a04cd9f verified
raw
history blame
12.3 kB
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 = """
<div style="width: 50px; height: 50px; border-radius: 50%; background-color: {color};"></div>
"""
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()
"""