Home > Enterprise >  Python: parallel processing and tkinter
Python: parallel processing and tkinter

Time:03-13

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().

  • Related