GUI.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import platform
  2. import customtkinter as ctk
  3. from PIL import Image, ImageTk
  4. import time
  5. import threading
  6. import tkinter as tk
  7. import dobluetooth
  8. # If on Windows, allow STA for Bleak
  9. if platform.system() == "Windows":
  10. try:
  11. from bleak.backends.winrt.util import allow_sta
  12. allow_sta()
  13. except ImportError:
  14. pass
  15. # UI setup
  16. ctk.set_appearance_mode("System")
  17. ctk.set_default_color_theme("blue")
  18. app = ctk.CTk()
  19. app.geometry("1440x1024")
  20. canvas = ctk.CTkCanvas(app, width=250, height=250, highlightthickness=0)
  21. canvas.config(background="white")
  22. canvas.place(x=0, y=0)
  23. # Score variables
  24. currentScore = tk.DoubleVar()
  25. currentScore.set(0)
  26. names = []
  27. scores = []
  28. highestScore = 100
  29. highScoreName = ""
  30. punch_on = False
  31. # Punch control
  32. def startPunch():
  33. global punch_on
  34. punch_on = True
  35. print("STARTED PUNCH")
  36. def stopPunch():
  37. global punch_on, highestScore
  38. punch_on = False
  39. print("STOPPED PUNCH")
  40. if currentScore.get() > highestScore:
  41. highestScore = currentScore.get()
  42. try:
  43. with open("scores.txt", "w") as f: # Fixed mode from "rw" to "w"
  44. f.write(f"{highestScore} {highScoreName}")
  45. except Exception as e:
  46. print("Error writing to file:", e)
  47. scores.append(highestScore)
  48. names.append(highScoreName)
  49. def resetPunch():
  50. global currentScore
  51. currentScore.set(0)
  52. print("reset punch")
  53. dinger = ctk.CTkLabel(width=40, height=30, text="", fg_color="gray77", master=app)
  54. dinger.place(x=1405, y=900)
  55. # Leaderboard & UI setup
  56. def placeLeaderboard():
  57. canvas.create_rectangle(0, 0, 500, 500, fill="grey20", width=2)
  58. intx = 0
  59. inty = 60
  60. for i in range(len(names)):
  61. label1 = ctk.CTkLabel(width=124, height=39, text_color="white", text=names[i], font=("Arial", 15), master=app)
  62. label1.place(x=intx, y=inty)
  63. label2 = ctk.CTkLabel(width=124, height=39, text_color="white", text=scores[i], font=("Arial", 15), master=app)
  64. label2.place(x=intx + 125, y=inty)
  65. inty += 40
  66. ctk.CTkLabel(width=249, height=39, text_color="white", text="Leaderboard", font=("Arial", 50), master=app).place(x=0, y=0)
  67. ctk.CTkButton(width=0, height=150, text="START", fg_color="green", corner_radius=50, font=("Arial", 50),
  68. text_color="gray99", master=app, command=startPunch).place(x=50, y=300)
  69. ctk.CTkButton(width=0, height=150, text="STOP", fg_color="orange2", corner_radius=50, font=("Arial", 50),
  70. text_color="gray99", master=app, command=stopPunch).place(x=50, y=500)
  71. ctk.CTkButton(width=0, height=150, text="RESET", fg_color="red1", corner_radius=50, font=("Arial", 50),
  72. text_color="gray99", master=app, command=resetPunch).place(x=50, y=700)
  73. ctk.CTkLabel(width=50, height=20, text_color="white", text="Current Score", font=("Arial", 50), master=app).place(x=350, y=20)
  74. ctk.CTkLabel(width=50, height=20, text_color="white", textvariable=currentScore, font=("Arial", 50), master=app).place(x=500, y=100)
  75. nameEntry = ctk.CTkEntry(width=300, height=50, master=app, font=("Arial", 50))
  76. nameEntry.place(x=370, y=200)
  77. ctk.CTkButton(width=50, height=50, text="ENTER", fg_color="green", font=("Arial", 20),
  78. text_color="gray99", master=app).place(x=670, y=205)
  79. image = Image.open("./BellDrawing.png")
  80. photo = ImageTk.PhotoImage(image)
  81. ctk.CTkLabel(image=photo, text="", width=10, master=app).place(x=800, y=0)
  82. # BLE thread setup
  83. def ble_thread_func():
  84. import asyncio
  85. loop = asyncio.new_event_loop()
  86. asyncio.set_event_loop(loop)
  87. loop.run_until_complete(dobluetooth.getData())
  88. def start_ble_thread():
  89. thread = threading.Thread(target=ble_thread_func, daemon=True)
  90. thread.start()
  91. def update_dinger_position():
  92. global dinger
  93. score = currentScore.get() * 0.01
  94. # Example logic: map score (0–100) to y-position (900 to 300)
  95. # You can adjust the ranges to suit your needs
  96. max_score = 100
  97. min_y = 300
  98. max_y = 900
  99. # Clamp score between 0 and max_score
  100. score = min(max(score, 0), max_score)
  101. # Calculate the new y position
  102. new_y = max_y - int((score / max_score) * (max_y - min_y))
  103. # Move the label
  104. dinger.place_configure(y=new_y)
  105. # Call this again after a short delay
  106. app.after(100, update_dinger_position)
  107. # Data processing loop in background thread
  108. def data_loop():
  109. while True:
  110. bledata = dobluetooth.getDataArr()
  111. if punch_on:
  112. accel_mag: float = bledata[6]
  113. app.after(0, lambda val=accel_mag: currentScore.set(currentScore.get() + val))
  114. app.after(0, update_dinger_position)
  115. time.sleep(0.1)
  116. def start_data_loop_thread():
  117. thread = threading.Thread(target=data_loop, daemon=True)
  118. thread.start()
  119. # Run UI setup
  120. placeLeaderboard()
  121. # Start threads after UI initializes
  122. app.after(500, start_ble_thread)
  123. app.after(1000, start_data_loop_thread)
  124. app.mainloop()