import platform import customtkinter as ctk from PIL import Image, ImageTk import time import threading import tkinter as tk import dobluetooth # If on Windows, allow STA for Bleak if platform.system() == "Windows": try: from bleak.backends.winrt.util import allow_sta allow_sta() except ImportError: pass # UI setup ctk.set_appearance_mode("System") ctk.set_default_color_theme("blue") app = ctk.CTk() app.geometry("1440x1024") canvas = ctk.CTkCanvas(app, width=250, height=250, highlightthickness=0) canvas.config(background="white") canvas.place(x=0, y=0) # Score variables currentScore = tk.DoubleVar() currentScore.set(0) names = [] scores = [] highestScore = 100 highScoreName = "" punch_on = False # Punch control def startPunch(): global punch_on punch_on = True print("STARTED PUNCH") def stopPunch(): global punch_on, highestScore punch_on = False print("STOPPED PUNCH") if currentScore.get() > highestScore: highestScore = currentScore.get() try: with open("scores.txt", "w") as f: # Fixed mode from "rw" to "w" f.write(f"{highestScore} {highScoreName}") except Exception as e: print("Error writing to file:", e) scores.append(highestScore) names.append(highScoreName) def resetPunch(): global currentScore currentScore.set(0) print("reset punch") dinger = ctk.CTkLabel(width=40, height=30, text="", fg_color="gray77", master=app) dinger.place(x=1405, y=900) # Leaderboard & UI setup def placeLeaderboard(): canvas.create_rectangle(0, 0, 500, 500, fill="grey20", width=2) intx = 0 inty = 60 for i in range(len(names)): label1 = ctk.CTkLabel(width=124, height=39, text_color="white", text=names[i], font=("Arial", 15), master=app) label1.place(x=intx, y=inty) label2 = ctk.CTkLabel(width=124, height=39, text_color="white", text=scores[i], font=("Arial", 15), master=app) label2.place(x=intx + 125, y=inty) inty += 40 ctk.CTkLabel(width=249, height=39, text_color="white", text="Leaderboard", font=("Arial", 50), master=app).place(x=0, y=0) ctk.CTkButton(width=0, height=150, text="START", fg_color="green", corner_radius=50, font=("Arial", 50), text_color="gray99", master=app, command=startPunch).place(x=50, y=300) ctk.CTkButton(width=0, height=150, text="STOP", fg_color="orange2", corner_radius=50, font=("Arial", 50), text_color="gray99", master=app, command=stopPunch).place(x=50, y=500) ctk.CTkButton(width=0, height=150, text="RESET", fg_color="red1", corner_radius=50, font=("Arial", 50), text_color="gray99", master=app, command=resetPunch).place(x=50, y=700) ctk.CTkLabel(width=50, height=20, text_color="white", text="Current Score", font=("Arial", 50), master=app).place(x=350, y=20) ctk.CTkLabel(width=50, height=20, text_color="white", textvariable=currentScore, font=("Arial", 50), master=app).place(x=500, y=100) nameEntry = ctk.CTkEntry(width=300, height=50, master=app, font=("Arial", 50)) nameEntry.place(x=370, y=200) ctk.CTkButton(width=50, height=50, text="ENTER", fg_color="green", font=("Arial", 20), text_color="gray99", master=app).place(x=670, y=205) image = Image.open("./BellDrawing.png") photo = ImageTk.PhotoImage(image) ctk.CTkLabel(image=photo, text="", width=10, master=app).place(x=800, y=0) # BLE thread setup def ble_thread_func(): import asyncio loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(dobluetooth.getData()) def start_ble_thread(): thread = threading.Thread(target=ble_thread_func, daemon=True) thread.start() def update_dinger_position(): global dinger score = currentScore.get() * 0.01 # Example logic: map score (0–100) to y-position (900 to 300) # You can adjust the ranges to suit your needs max_score = 100 min_y = 300 max_y = 900 # Clamp score between 0 and max_score score = min(max(score, 0), max_score) # Calculate the new y position new_y = max_y - int((score / max_score) * (max_y - min_y)) # Move the label dinger.place_configure(y=new_y) # Call this again after a short delay app.after(100, update_dinger_position) # Data processing loop in background thread def data_loop(): while True: bledata = dobluetooth.getDataArr() if punch_on: accel_mag: float = bledata[6] app.after(0, lambda val=accel_mag: currentScore.set(currentScore.get() + val)) app.after(0, update_dinger_position) time.sleep(0.1) def start_data_loop_thread(): thread = threading.Thread(target=data_loop, daemon=True) thread.start() # Run UI setup placeLeaderboard() # Start threads after UI initializes app.after(500, start_ble_thread) app.after(1000, start_data_loop_thread) app.mainloop()