Daniel R
Daniel R

Reputation: 109

Reset a python thread on click of a new tkinter listbox item

I have a mini local music playing program which successfully increments the seek scale each second in addition to it's corresponding sofar label, and it also resets to 0 when the song restarts.

The function for the progress of the song is below, as well as select() function is called by the listBox via .bind() and rootpath is just a string containing the path to the folder with all the music. I used threading so the entire program doesn't break waiting for the loop to finish, though I am completely new at threading and async functions and the like.

import eyed3 as d3
import tkinter as tk
from pygame import mixer
import threading
canvas = tk.Tk()
canvas.title("Music Player")
canvas.geometry("1200x700")
canvas.config(bg = "#2C2F33")

rootpath = "C:\\Users\drp76\Music\CDs\\"
pattern = "*.mp3"

def progress():
    if listBox.curselection():
        audio = d3.load(rootpath + "\\" + listBox.get("anchor") + ".mp3")
        mixer.music.load(rootpath + "\\" + listBox.get("anchor") + ".mp3")
        current_value = 0
        while True:
            time.sleep(1)
            current_value += 1
            if current_value > round(audio.info.time_secs):
                current_value = 0
            sofar.config(text = str(datetime.timedelta(seconds=current_value))[2:] + " /")
            seek.set(current_value)
    else:
        pass
def select(song):
    label.config(text = listBox.get("anchor"))
    mixer.music.load(rootpath + "\\" + listBox.get("anchor") + ".mp3")
    audio = d3.load(rootpath + "\\" + listBox.get("anchor") + ".mp3")

    seek.config(to = audio.info.time_secs)
    m, s = divmod(round(audio.info.time_secs), 60) #audio.info.time_secs returns a float
    h, m = divmod(m, 60)
    duration.config(text = f'{m:02d}:{s:02d}')

    thread = threading.Thread(target=progress)
    thread.daemon = True
    thread.start()

    mixer.music.play(-1)

These are the relevant tkinter widgets with the for loop that adds all the mp3 files from that folder with the music into the listbox.

listBox = tk.Listbox(canvas, fg = "white", bg = "#2C2F33", width = 100, font = ('Segoe UI Light', 14))
listBox.pack(padx = 15, pady = 15)
listBox.bind('<<ListboxSelect>>', select)

seek = tk.Scale(canvas, from_=0, to=100, orient="horizontal", length = 550, bg = "#2C2F33", fg = "white", borderwidth = 0, command=seeking)
seek.pack(pady=5)

timeof = tk.Frame(canvas, bg = "#2C2F33")
timeof.pack(padx = 10, pady = 5, anchor = 'center')

duration = tk.Label(canvas, text='', bg = "#2C2F33", fg = 'white', font = ('Segoe UI Light', 10))
duration.pack(in_ = timeof, side="right")

sofar = tk.Label(canvas, text='', bg = "#2C2F33", fg = 'white', font = ('Segoe UI Light', 10))
sofar.pack(in_ = timeof, side="right")

for root, dirs, files in os.walk(rootpath):
    for filename in fnmatch.filter(files, pattern):
        listBox.insert('end', filename[:-4])

canvas.mainloop()

The problem is that when a new song is selected, the seek scale and the sofar label seems to jump back and forth between the newly selected song's position and the previously selected song's position. The seek scale is also not able to be used for it's intended purpose, as when you try to manually move the scale, it snaps back to where you started. Removing the progress function and threading in the select function allows the scale to be used for seeking, but of course, it does not move as the song progresses.

Also, worth noting, when a song is skipped using the skip() function (which simply skips the song and resets the max for the scale to the song duration), the seek scale and the sofar label both do not reset to 0 either, and the song that is skipped to starts at the position where the previous song left off.

I've also heard using asyncio may be the way to go, but as a novice, I'm not entirely sure how to implement it into this example. I imagine resetting or restarting the thread would fix the issues described.

Upvotes: 0

Views: 65

Answers (0)

Related Questions