0

Everything works fine until a .mp3 file is loaded with mixer.music.load(). After that, there are buttons on the window that I have created are still responsive, but the window itself is unresponsive. I cannot drag the window around, and I cannot use the x button to close the window. They don't highlight as if you could press them at all after the music is loaded. I know the main loop is still running because my buttons highlight when the mouse is colliding with them as they should. I just don't know why the window itself is not responding.

What is weird is I have a load button that opens a tkinter filedialog to get a directory, if I click on that and just close the filedialog, the pygame main window becomes responsive again like normal.

import os
import pygame as pg
import tkinter.filedialog
import tkinter
from pygame import mixer

pg.init()
WIDTH, HEIGHT = 500, 800
HW = WIDTH // 2
HH = HEIGHT // 2
win = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Music Player")
CLOCK = pg.time.Clock()

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)


def play_song(directory, song_list):
    mixer.init()
    mixer.music.load(os.path.join(directory, song_list[0]))
    pg.mixer.music.play()


def get_files(directory):
    file_list = []
    for entry in os.listdir(directory):
        file = os.path.join(directory, entry)
        ext = os.path.splitext(entry)[-1].lower()
        if os.path.isfile(file) and ext == '.mp3':  # adds only files, that are .mp3
            file_list.append(entry)
    return file_list


def get_directory():
    tkinter.Tk().withdraw()  # Without this a blank tkinter window will appear.
    try:
        directory = tkinter.filedialog.askdirectory()  # This opens the file browser.
        files_list = get_files(directory)
        return directory, files_list
    except FileNotFoundError:
        return 0, []


class Button(object):
    def __init__(self, pic, hover, click, x, y):
        self.pic = pic
        self.hover = hover
        self.click = click
        self.x = x
        self.y = y
        self.w = self.pic.get_width()
        self.h = self.pic.get_height()
        self.hw = self.w // 2
        self.hh = self.h // 2

    def collide(self, mouse_x, mouse_y):
        if mouse_x > self.x and mouse_x < self.x + self.w:
            if mouse_y > self.y and mouse_y < self.y + self.h:
                return True

    def draw(self, win1, clicked=False):
        mouse_pos = pg.mouse.get_pos()
        if clicked:
            win1.blit(self.click, (self.x, self.y))
        elif self.collide(mouse_pos[0], mouse_pos[1]):
            win1.blit(self.hover, (self.x, self.y))
        else:
            win1.blit(self.pic, (self.x, self.y))


play_idle = pg.image.load('play.png')
play_hover = pg.image.load('play_hover.png')
play_click = pg.image.load('play_click.png')
play = Button(play_idle, play_hover, play_click, HW - 52, HEIGHT - 152)

pause_idle = pg.image.load('pause.png')
pause_hover = pg.image.load('pause_hover.png')
pause_click = pg.image.load('pause_click.png')
pause = Button(pause_idle, pause_hover, pause_click, HW - 52, HEIGHT - 152)

skip_idle = pg.image.load('skip.png')
skip_hover = pg.image.load('skip_hover.png')
skip_click = pg.image.load('skip_click.png')
skip = Button(skip_idle, skip_hover, skip_click, WIDTH - 152, HEIGHT - 152)

load_idle = pg.image.load('load.png')
load_hover = pg.image.load('load_hover.png')
load_click = pg.image.load('load_click.png')
load = Button(load_idle, load_hover, load_click, 50, 50)


def draw(win, clicked_play, clicked_load, playing):
    win.fill(WHITE)
    if playing:
        pause.draw(win, clicked_play)
    else:
        play.draw(win, clicked_play)
    skip.draw(win)
    load.draw(win, clicked_load)
    pg.display.update()


def main():
    directory, song_list = '', []
    CLOCK.tick(60)
    run = True
    while run:
        clicked_play = False
        clicked_load = False
        loaded = False
        playing = mixer.music.get_busy()
        events = pg.event.get()
        for event in events:
            if event.type == pg.QUIT:
                run = False
            if event.type == pg.MOUSEBUTTONDOWN:
                print('button down')
                if event.button == 1:
                    mouse_pos = pg.mouse.get_pos()
                    if load.collide(mouse_pos[0], mouse_pos[1]):
                        # Pause music
                        clicked_load = True
                        draw(win, clicked_play, clicked_load, playing)
                        directory, song_list = get_directory()
                    if play.collide(mouse_pos[0], mouse_pos[1]) and not playing:
                        clicked_play = True
                        if len(song_list) > 0:
                            play_song(directory, song_list)
                            print('playing')
                    if pause.collide(mouse_pos[0], mouse_pos[1]) and playing:
                        clicked_play = False
                        pg.mixer.music.stop()
                        print('pause')

        draw(win, clicked_play, clicked_load, playing)

    pg.display.quit()
    pg.quit()


main()
shadow
  • 11
  • 2
  • You seem to have posted more code than what would be reasonable for your issue. Please read [ask] and how to make a [mre]; providing a MRE helps users answer your question and future users relate to your issue. – rizerphe May 12 '20 at 18:24
  • 1
    This, ***`.music.play()`***, is blocking. Read [use threads to preventing main event loop from “freezing”](https://stackoverflow.com/a/16747734/7414759) – stovfl May 12 '20 at 18:39
  • @stovfi - are you sure `music.play()` blocks? It's not blocking for me. – Kingsley May 12 '20 at 23:35
  • Are you sure your `.mp3` file is not corrupt (or suchlike)? – Kingsley May 12 '20 at 23:38
  • I found a solution. It was a threading issue. Thank you to @stovfl for pointing me in the right direction. with concurrent.futures.ThreadPoolExecutor() as executor: future = executor.submit(get_directory) return_value = future.result() – shadow May 13 '20 at 00:42

1 Answers1

0

I found a solution. It was a threading issue. Thank you to @stovfl for pointing me in the right direction.

with concurrent.futures.ThreadPoolExecutor() as executor:
    future = executor.submit(get_directory)
    return_value = future.result()

get_directory is a function using tkinter.

shadow
  • 11
  • 2