I am trying to implement a piano game. The rules are simple: a note is played and after 5 seconds the answer is shown. The problem is that I want the user to be able to play the notes while the program waits for those 5 seconds. Right now the program creates a new GUI window when the the gamemode's process starts.
Gamemode class:
from multiprocessing import Process
import random
import time
class Practice(Process):
def __init__(self, keys, rounds = 5):
super(Process, self).__init__()
self.rounds = rounds
self.keys = keys
def run(self):
for i in range(3):
answer = self.__random_key()
answer.play()
time.sleep(3)
print("woke up")
self.show_answer()
def show_answer(self, answer):
print(f"Answer is: {answer.name}")
def __random_key(self):
return self.keys[random.randint(0, len(self.keys) - 1)]
piano key class:
import asyncio
import copy
import time
from tkinter import Button
from pygame import mixer
class PianoKey():
def __init__(self, master, note = None, octave = None, color = None):
self.master = master
self.note = note
self.color = color
self.octave = octave
self.name = f"{note}{octave}"
self.button = Button(self.master, bg = color, command = lambda : self.play())
def get_piano_button(self):
return self.button
def empty_copy(self):
button = self.button
self.button = None
result = copy.copy(self)
self.button = button
def change_color(self, color):
self.button.config(bg = color)
def play(self):
path = "PATH_OF_NOTES_FOLDER"
print(f"played {self.name}")
# mixer.music.load("B3.mp3")
# mixer.music.play()
Piano class and subclasses:
from core.GUI.gui_piece import GUIPiece, GUITypes
from core.GUI.piano_key import PianoKey
from core.game_modes.Practice import Practice
class Piano(GUIPiece):
def __init__(self, master, relx, rely, relwidth, relheight, background = "White", octaves = 3):
super().__init__(master, relx, rely, relwidth, relheight, GUITypes.FRAME, background)
self.keys = []
self.initiate_keys(octaves)
self.mode = Practice(self.keys)
def get_piano(self):
return self.gui
def initiate_keys(self, octaves):
white_width = 1/(octaves * 7)
self.initiate_key(octaves=octaves, keys=["C", "D", "E", "F", "G", "A", "B"], color="White", positions=[0,1,2,3,4,5,6], width = white_width, relxwidth = white_width, minus_starting = 0, relh = 1)
black_width = white_width * 0.7
self.initiate_key(octaves=octaves, keys=["Cb", "Db", "Fb", "Gb", "Ab"], color="Black", positions=[1,2,4,5,6], width = black_width, relxwidth = white_width, minus_starting= black_width/2, relh = 0.6)
def start_practice(self):
self.mode.start()
# t = threading.Thread(target=self.mode.play())
# print("now going in for loop")
# t.start()
def empty_keys_copy(self):
return [key.empty_copy() for key in self.keys]
def initiate_key(self, octaves, keys, color, positions, width, relxwidth, minus_starting, relh):
for o in range(octaves):
octave = o * 7
for index in range(len(positions)):
relx = relxwidth * (octave positions[index])- minus_starting
key = PianoKey(self.get_piano(), keys[index], o, color)
self.keys.append(key)
key.get_piano_button().place(relx=relx, rely=0, relwidth= width, relheight = relh)
def show_answer(self):
print(f"Answer is: {self.answer.name}")
self.answer.change_color('Red')
class GUITypes(Enum):
LABEL = 0,
FRAME = 1,
CHECKBUTTON = 2,
BUTTON = 3
class GUIPiece():
def __init__(self, master, relx, rely, relwidth, relheight, type, background = 'Grey', parent = None, text = ""):
self.master = master
self.relx = relx
self.rely = rely
self.relwidth = relwidth
self.relheight = relheight
if type == GUITypes.LABEL:
self.gui = Label(bg = background)
elif type == GUITypes.FRAME:
self.gui = Frame(bg=background)
elif type == GUITypes.CHECKBUTTON:
self.gui = Checkbutton(bg=background)
elif type == GUITypes.BUTTON:
self.gui = Button(bg=background)
else:
raise AttributeError("Please select a GUIType.")
if text != "":
self.set_text(text)
#adapt the relative sizes
if parent != None:
self.relx *= parent.relwidth
self.rely *= parent.relheight
self.relwidth = relwidth * parent.relwidth
self.relheight = relheight * parent.relheight
def get_gui(self):
return self.gui
def set_text(self, text):
self.get_gui().config(text = text)
def set_gui(self, gui):
self.gui = gui
def place(self):
self.gui.place(relx = self.relx, rely = self.rely, relwidth = self.relwidth, relheight = self.relheight)
def get_placements(self):
return self.relx, self.rely, self.relwidth, self.relheight
class GUIButton(GUIPiece):
def __init__(self, master, relx, rely, relwidth, relheight, background = "White", parent = None, text = ""):
super().__init__(master, relx, rely, relwidth, relheight, GUITypes.BUTTON, background, parent = parent, text = text)
main:
from tkinterdnd2 import *
from tkinter import *
from core.GUI.pian import Piano
from core.GUI.top_menu import TopMenu
from core.GUI.top_menu_parts.top_menu_pieces import GUIButton
from core.configurations import PIANO_RELX, PIANO_RELY, PIANO_RELWIDTH, PIANO_RELHEIGHT, TOPMENU_RELHEIGHT
from core.game_modes.Practice import Practice
root = Tk()
# set window title
root.wm_title("Tkinter window")
root.geometry("1600x700")
p = Piano(root, relx=PIANO_RELX, rely=PIANO_RELY, relwidth=PIANO_RELWIDTH, relheight=PIANO_RELHEIGHT, background="White")
p.place()
# t = TopMenu(root, relx=0, rely=0, relwidth=1, relheight=TOPMENU_RELHEIGHT, background="Pink")
start = GUIButton(root, relx=0, rely=0, relwidth=1, relheight=TOPMENU_RELHEIGHT, text="practice")
start.place()
# t.place()
mode = Practice(p.keys)
start.get_gui().config(command = lambda: p.start_practice())
root.mainloop()
CodePudding user response:
Use the root.after()
method. It can delay like time.sleep()
but can also call a function when the timeout finishes. In this way, the user can play the piano notes while the program is waiting. Here is a tutorial regarding root.after()
.