Snakey/Souce code/Snakey.py
2024-12-14 23:14:38 +00:00

313 lines
11 KiB
Python

import tkinter as tk
from tkinter import filedialog, simpledialog, messagebox
import pyttsx3
import json
import os
import pygame
import webbrowser
import random
import logging
# Setup logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Initialize settings file
SETTINGS_FILE = "SnakeyData.json"
DEFAULT_SETTINGS = {
"image": None,
"image_moving": None,
"speech_enabled": True,
"user_data": {},
"PLAYED_BEFORE": "no",
}
def load_settings():
if not os.path.exists(SETTINGS_FILE):
save_settings(DEFAULT_SETTINGS)
try:
with open(SETTINGS_FILE, "r") as f:
data = json.load(f)
except json.JSONDecodeError as e:
logging.error(f"Failed to load settings: {e}")
data = DEFAULT_SETTINGS
for key, value in DEFAULT_SETTINGS.items():
if key not in data:
data[key] = value
return data
def save_settings(data):
try:
with open(SETTINGS_FILE, "w") as f:
json.dump(data, f, indent=4)
except Exception as e:
logging.error(f"Failed to save settings: {e}")
# Load initial settings
settings = load_settings()
# Speech Engine
engine = pyttsx3.init()
def speak(text):
if settings.get("speech_enabled", True):
try:
engine.say(text)
engine.runAndWait()
except Exception as e:
logging.error(f"Speech engine error: {e}")
# Initialize pygame for sound
try:
pygame.mixer.init()
except pygame.error as e:
logging.warning(f"Pygame mixer initialization failed: {e}")
def play_intro_music():
try:
music_path = os.path.join(os.path.dirname(__file__), "Snakey_intro.wav")
if not os.path.exists(music_path):
raise FileNotFoundError(f"No file '{music_path}' found.")
pygame.mixer.music.load(music_path)
pygame.mixer.music.play(-1) # Loop the intro music
except Exception as e:
logging.error(f"Failed to play intro music: {e}")
def stop_intro_music():
try:
pygame.mixer.music.stop()
except Exception as e:
logging.error(f"Failed to stop music: {e}")
class SnakeyApp:
def __init__(self, root):
self.root = root
self.root.overrideredirect(True)
self.root.geometry("200x200+500+300")
self.root.attributes("-topmost", True) # Always on top
try:
self.root.attributes("-transparentcolor", "white")
except tk.TclError:
logging.warning("Transparent color not supported on this system.")
self.canvas = tk.Canvas(root, width=200, height=200, bg='white', highlightthickness=0)
self.canvas.pack(expand=True)
self.snake_id = None
self.update_image(settings.get("image"))
self.schedule_random_movement()
self.schedule_talk()
self.canvas.bind("<Button-1>", self.start_drag)
self.canvas.bind("<B1-Motion>", self.drag)
self.menu = tk.Menu(root, tearoff=0)
self.menu.add_command(label="Settings", command=self.open_settings)
self.menu.add_command(label="Surf the Web!", command=self.surf_web)
self.menu.add_command(label="Quit", command=root.destroy)
self.menu.add_command(label="Tell me a joke!", command=self.tell_joke)
self.menu.add_command(label="Talk!", command=self.open_tts_window) # TTS button
self.canvas.bind("<Button-3>", self.show_menu)
self.greet_user()
def schedule_random_movement(self):
# Move more often (every 5-15 seconds)
delay = random.randint(5000, 15000)
self.root.after(delay, self.random_move)
def random_move(self):
# Move up to 1000 pixels away from current position
current_x = self.root.winfo_x()
current_y = self.root.winfo_y()
move_x = random.randint(-1000, 1000)
move_y = random.randint(-1000, 1000)
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
new_x = min(max(0, current_x + move_x), screen_width - 200)
new_y = min(max(0, current_y + move_y), screen_height - 200)
self.glide_to_new_position(current_x, current_y, new_x, new_y)
# Show the moving image for 5 seconds
self.update_image(settings.get("image_moving"))
# Switch back to "not moving" image after 5 seconds
self.root.after(5000, self.update_image, settings.get("image"))
self.schedule_random_movement()
def glide_to_new_position(self, start_x, start_y, end_x, end_y, duration=5000, steps=100):
# Calculate the total number of steps based on the duration
step_delay = duration // steps
step_x = (end_x - start_x) / steps
step_y = (end_y - start_y) / steps
def move_step(i):
if i < steps:
current_x = start_x + i * step_x
current_y = start_y + i * step_y
self.root.geometry(f"+{int(current_x)}+{int(current_y)}")
self.root.after(step_delay, move_step, i + 1)
move_step(0)
def schedule_talk(self):
# Talk every 10 seconds to 1 minute
delay = random.randint(10000, 60000)
self.root.after(delay, self.speak_random_phrase)
def speak_random_phrase(self):
phrases = [
"nice computer you got here! can i have it?",
"haha im digging into your files!",
"An SSD? let me see!",
"La la la la la",
"haha im a little snakey guy"
]
phrase = random.choice(phrases)
speak(phrase)
self.schedule_talk()
def start_drag(self, event):
self.start_x = event.x
self.start_y = event.y
def drag(self, event):
x_offset = event.x - self.start_x
y_offset = event.y - self.start_y
new_x = self.root.winfo_x() + x_offset
new_y = self.root.winfo_y() + y_offset
self.root.geometry(f"+{new_x}+{new_y}")
def show_menu(self, event):
self.menu.post(event.x_root, event.y_root)
def greet_user(self):
if settings["PLAYED_BEFORE"] == "no":
speak("Thanks for waking me up! My name's Snakey, and how about yours?")
self.show_name_input()
play_intro_music() # Play intro music on first run
else:
name = settings["user_data"].get("name", "there")
greeting = f"Hello there, {name}. It's nice to see you!"
self.show_notification(greeting)
speak(greeting)
stop_intro_music() # Stop the intro music if user has played before
def show_name_input(self):
name_input_window = tk.Toplevel(self.root)
name_input_window.title("BFDC - Best Friend Data Collector")
name_input_window.geometry("300x150")
label = tk.Label(name_input_window, text="What's your name?")
label.pack(pady=10)
name_entry = tk.Entry(name_input_window)
name_entry.pack(pady=5)
def save_name():
name = name_entry.get()
if name:
settings["user_data"]["name"] = name
settings["PLAYED_BEFORE"] = "yes"
save_settings(settings)
speak(f"Nice to meet you, {name}!")
name_input_window.after(2000, name_input_window.destroy)
stop_intro_music() # Stop the music once the name is entered
tk.Button(name_input_window, text="Submit", command=save_name).pack(pady=10)
def show_notification(self, text):
notif = tk.Toplevel(self.root)
notif.geometry("200x100+500+300")
tk.Label(notif, text=text, wraplength=180).pack(expand=True)
self.root.after(2000, notif.destroy)
def open_settings(self):
settings_window = tk.Toplevel(self.root)
settings_window.title("Settings")
settings_window.geometry("300x200")
def select_image():
file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")])
if file_path:
settings["image"] = file_path
save_settings(settings)
self.update_image(file_path)
tk.Button(settings_window, text="Select Image When Not Moving", command=select_image).pack(pady=5)
def select_moving_image():
file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg")])
if file_path:
settings["image_moving"] = file_path
save_settings(settings)
tk.Button(settings_window, text="Select Image When Moving", command=select_moving_image).pack(pady=5)
def clear_data():
response = messagebox.askyesno("Clear Data", "Are you sure you want to clear all collected data?")
if response:
save_settings(DEFAULT_SETTINGS)
self.root.quit() # Close the app
tk.Button(settings_window, text="Clear Collected Data", command=clear_data).pack(pady=5)
def update_image(self, image_path):
self.canvas.delete("all")
if image_path:
try:
self.image = tk.PhotoImage(file=image_path)
self.canvas.create_image(100, 100, image=self.image)
except Exception as e:
logging.error(f"Failed to load image: {e}")
else:
self.snake_id = self.canvas.create_oval(40, 40, 160, 160, fill="green")
def surf_web(self):
webbrowser.open("https://www.google.com")
def tell_joke(self):
jokes = [
"Why don't skeletons fight each other? They don't have the guts.",
"Why can't your nose be 12 inches long? Because then it would be a foot!",
"I'm reading a book on anti-gravity. It's impossible to put down!"
]
joke = random.choice(jokes)
speak(joke)
self.show_notification(joke)
def open_tts_window(self):
tts_window = tk.Toplevel(self.root)
tts_window.title("Text-to-Speech")
label = tk.Label(tts_window, text="Type something to say:")
label.pack(pady=10)
text_entry = tk.Entry(tts_window)
text_entry.pack(pady=5)
def speak_text():
text = text_entry.get()
if text:
speak(text)
tk.Button(tts_window, text="Speak", command=speak_text).pack(pady=10)
def run():
root = tk.Tk()
app = SnakeyApp(root)
root.mainloop()
if __name__ == "__main__":
run()