GUI.py 6.0 KB

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