Python Serial Read Timer - python

So Im reading a serial device that sends data every couple of seconds, I have my timer running on the screen and the goal is that every time it updates the serial read it restarts the timer to zero.. This allows me to visually see if Ive lost contact with the device. Ive spent alot of time searching to no avail. Not real good with python so any help would be appreciated.
from tkinter import *
from serial import Serial
import time
from serial.threaded import ReaderThread, Protocol
import threading
import tkinter as tk
#Input ED Number
ed_num = 0
def tkinter_input(prompt=""):
global ed_num
root = tk.Tk()
root.wm_attributes('-fullscreen',1)
root.configure(background='black')
tk.Label(root, font=('Ariel', 100), foreground='Green',
background='Black', text=prompt).pack()
entry = tk.Entry(root, font=('Ariel', 100),justify='center', foreground='Black',
background='white')
entry.focus()
entry.pack()
result = None
def callback(event):
nonlocal result
result = entry.get()
root.destroy()
entry.bind("<Return>", callback)
root.mainloop()
return result
result = tkinter_input("Scan Dosimeter")
ed_num = result
print(ed_num)
#Start Loop
root = Tk()
#Sets Main Window
root.wm_attributes('-fullscreen',1)
root.configure(background='black')
'''Shuts Down App will need to remove'''
button_quit = Button(root, text = 'QUIT', command = root.destroy).pack(anchor=NE)
#Container Frame
mainframe= Frame(root, bd=4, bg='Black')
mainframe.pack(anchor=CENTER, expand=True, fill=BOTH, padx=(20,20), pady=(20,20))
mainframe.grid_columnconfigure(0, weight=1)
mainframe.grid_rowconfigure(0, weight=1)
mainframe.grid_rowconfigure(1, weight=1)
mainframe.grid_rowconfigure(2, weight=1)
#ID Label
ed_id_label = Label(mainframe,
font=('Ariel', 50), foreground='Green',
background='Black', anchor='w')
ed_id_label.grid(row=0, column=0, sticky='ew', padx=(100,100), pady=(100,100))
#Dose Label
doselabel = Label(mainframe,
font=('Ariel', 130), foreground='Green',
background='Black', anchor='w')
doselabel.grid(row=1, column=0, sticky='ew', padx=(100,100), pady=(100,100))
# Rate Label
ratelabel = Label(mainframe,
font=('Ariel', 130), foreground='Green',
background='Black', anchor='w')
ratelabel.grid(row=2, column=0, sticky='ew', padx=(100,100), pady=(100,100))
#Timer Label
timelabel = Label(mainframe,
font=('Ariel', 20), foreground='Green',
background='Black', anchor='w')
timelabel.grid(row=3, column=0, sticky='se')
#Data Timer
seconds = 0
def countup(seconds):
seconds += 1
timelabel['text'] = str(seconds)+"s"
root.after(1000, countup, seconds)
#Serial Reader Thread
class SerialReaderProtocolRaw(Protocol):
def data_received(self, data):
"""Called with snippets received from the serial port"""
updateLabelData(data)
def updateLabelData(data):
data = data.decode("utf-8")
if data[0:6] == ed_num:
ed_id_label['text']='Ed id: ' +data[0:6]
doselabel['text']='Dose: ' + data[11:14]+'.'+data[14:15]
if data[21:22] == '0':
ratelabel['text']='Rate: ' + data[18:19]
if data[21:22] == '1':
ratelabel['text']='Rate: ' + data[18:20]
if data[21:22] == '2':
ratelabel['text']='Rate: ' + data[18:21]
if data[21:22] == '3':
ratelabel['text']='Rate: ' + data[18:22]
if data[6:7] =='1':
doselabel.config(bg= "red")
if data[6:7] =='2':
ratelabel.config(bg= "red")
root.update_idletasks()
# Initiate serial port
serial_port = Serial('COM3', baudrate = 57600)
# Initiate ReaderThread
reader = ReaderThread(serial_port, SerialReaderProtocolRaw)
# Initiate Counter Thread
countupthread = threading.Thread(target=countup, args = (seconds,))
# Start reader/counter
countupthread.start()
reader.start()
# Join Threads
countupthread.join()
root.after(1, countup, seconds)
root.mainloop()

There are at least two solutions I can think of off the top of my head: cancel and restart the counter, or make seconds global.
Stop and restart the counter
When you schedule something with after, it returns an identifier. You can pass this identifier to after_cancel to prevent that function from running. Since you're not using classes, you can use a global variable to track this identifier.
Example:
def countup(seconds):
global after_id
seconds += 1
timelabel['text'] = str(seconds)+"s"
after_id = root.after(1000, countup, seconds)
Then, whenever you need to reset the timer you can use the following two lines to cancel and restart countup:
root.after_cancel(after_id)
countup(0)
Make seconds global
Instead of passing seconds to the countup function, have it use a global variable. You can then just reset this variable whenever you want.
seconds = 0
def countup():
global seconds
seconds += 1
timelabel['text'] = str(seconds)+"s"
after_id = root.after(1000, countup, seconds)
With that, whenever you want to reset the counter you simply need to reset seconds:
global seconds
seconds = 0

Related

Unable to label text using config in tkinter

So, I have been trying to create a simple stopwatch in tkinter in which I created a loop to update text to new time i.e., the next second in timer label as I click button_1. I tried to do this with StringVar() as well as .config method but none of them are updating the text in label. The code is below
from datetime import *
from time import *
init_time = datetime(100, 1, 1, 0, 0, 0)
running = True
def clock():
while running == True:
sleep(1)
global init_time
a = init_time.strftime("%H:%M:%S")
mtime.set(a)
init_time = init_time + timedelta(seconds=1)
def stop():
global running
running = False
main = Tk()
main.geometry("500x200")
mtime = StringVar()
timer = Label(main, textvariable = mtime, width=30, bg="black", fg="white", font=(25))
timer.place(x=90, y=20)
button_1 = Button(main, text = "Start", command = clock()).place(x=170, y=120)
button = Button(main, text = "Stop", command = stop).place(x=250, y=120)
main.mainloop()
I even tried to convert the init_time to a string because I thought maybe the updates of text work only for strings. The initial GUI window shows but as I click button_1 it doesn't work.
You did common mistake, look at these two lines
button_1 = Button(main, text = "Start", command = clock()).place(x=170, y=120)
button = Button(main, text = "Stop", command = stop).place(x=250, y=120)
Note that you have clock() and stop. First is function invocation, second is function. You should provide function as command. Replace clock() using clock.
Also if you are interested in executing function every n miliseconds, please take look at .after, consider following simple timer
import tkinter as tk
elapsed = 0
def update_timer():
global elapsed
elapsed += 1
timer['text'] = str(elapsed)
root.after(1000, update_timer) # 1000 ms = 1 second
root = tk.Tk()
timer = tk.Label(root, text="0")
btn = tk.Button(root, text="Go", command=update_timer)
timer.pack()
btn.pack()
root.mainloop()

how to cancel the after method for an off delay timer in tkinter using python 3.8

I have a off delay program in which when I select the input checkbutton, the output is 1. When I deselect the input checkbutton, the output goes back to 0 after a timer (set up in a scale). For that I use the after method. That part works. My problem is that I want to reset the timer if the checkbutton is selected again before the output went to 0; but once the checkbutton is selected the first time, the after method get triggered and it doesn't stop. I'm trying to use after_cancel, but I can't get it to work. Any solution?
from tkinter import *
root = Tk()
t1= IntVar()
out = Label(root, text="0")
remain_time = IntVar()
grab_time = 1000
def start_timer(set_time):
global grab_time
grab_time = int(set_time) * 1000
def play():
if t1.get() == 1:
button1.configure(bg='red')
out.configure(bg="red", text="1")
else:
button1.configure(bg='green')
def result():
out.configure(bg="green", text="0")
out.after(grab_time,result)
button1 = Checkbutton(root,variable=t1, textvariable=t1, command=play)
time = Scale(root, from_=1, to=10, command=start_timer)
button1.pack()
time.pack()
out.pack()
root.mainloop()
Expected: when press the checkbutton before the output went to 0, reset the counter.
So you could use the .after_cencel when the value of checkbutton is 1:
from tkinter import *
root = Tk()
t1= IntVar()
out = Label(root, text="0")
remain_time = IntVar()
grab_time = 1000
def start_timer(set_time):
global grab_time
grab_time = int(set_time) * 1000
def play():
if t1.get() == 1:
button1.configure(bg='red')
out.configure(bg="red", text="1")
try: # when the first time you start the counter, root.counter didn't exist, use a try..except to catch it.
root.after_cancel(root.counter)
except :
pass
else:
button1.configure(bg='green')
def result():
out.configure(bg="green", text="0")
root.counter = out.after(grab_time,result)
button1 = Checkbutton(root,variable=t1, textvariable=t1, command=play)
time = Scale(root, from_=1, to=10, command=start_timer)
button1.pack()
time.pack()
out.pack()
root.mainloop()

Tkinter-python button not responding when in a loop of another function

I have a GUI for the purpose of recording audio. There are two buttons, start and stop recording.
There is a loop inside the function for start recording which i cannot remove. When the start recording button is pressed, the stop button doesn't respond (because of the loop in start which I cannot remove for a few reasons). I would like to know if there is a way to solve this issue and get both buttons to respond even when the program is in the loop of start recording which is an infinite loop.I'm using python2. The code looks something like the following,
class RecAUD:
def __init__(self, chunk=4000, frmat=pyaudio.paInt16, channels=1, rate=44100, py=pyaudio.PyAudio()):
# Start Tkinter and set Title
self.main = tk.Tk()
self.collections = []
self.var = StringVar()
self.var.set('READY')
self.main.geometry('1200x500')
self.main.title('Demo')
self.CHUNK = chunk
self.FORMAT = frmat
self.CHANNELS = channels
self.RATE = rate
self.p = py
self.st = 1
print("----------------------record device list---------------------")
info = self.p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
print("Input Device id ", i, " - ", self.p.get_device_info_by_host_api_device_index(0, i).get('name'))
print("-------------------------------------------------------------")
self.index = int(input())
print("recording via index "+str(self.index))
self.stream = self.p.open(format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, input=True,input_device_index = self.index, frames_per_buffer=self.CHUNK)
self.buttons = tkinter.Frame(self.main, padx=1, pady=50)
self.buttons.pack(fill=tk.BOTH)
photo = PhotoImage(file = r"stt.png")
photoimage = photo.subsample(5, 5)
self.strt_rec = tkinter.Button(self.buttons, width=100, padx=8, pady=25, text='\n\n\n\nStart Recording', command=lambda: self.start_record(), bg='white', image = photoimage, compound = CENTER)
self.strt_rec.grid(row=0, column=0, columnspan=2, padx=50, pady=5)
self.stop_rec = tkinter.Button(self.buttons, width=100, padx=8, pady=25, text='\n\n\n\nStop Recording', command=lambda: self.stop_record(), bg='white', image = photoimage, compound = CENTER)
self.stop_rec.grid(row=0, column=1, columnspan=2, padx=450, pady=5)
self.op_text = Label(self.main,textvariable = self.var,foreground="green",font = "Times 30 bold italic")
self.op_text.place(x=350,y=100,anchor=NW)
self.op_text.pack()
tkinter.mainloop()
def start_record(self):
WAVE_OUTPUT_FILENAME = "recordedFile.wav"
while True:
data_frame = self.stream.read(self.CHUNK)
data_int16_frame = list(struct.unpack(str(self.CHUNK) + 'h', data_frame))
...
...
def stop_record(self):
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
guiAUD = RecAUD()
How do I go about solving this issue, what should I add to the above code to make the buttons responsive at anytime? Is multithreading required? If yes, how can I introduce it for the buttons in the above code? Any suggestions are much appreciated! Thanks in advance!
You can use threading a module used to make threads
import threading
class cls():
def __init__():
self.thrun = True
def start_record(self):
WAVE_OUTPUT_FILENAME = "recordedFile.wav"
while self.thrun:
data_frame = self.stream.read(self.CHUNK)
data_int16_frame = list(struct.unpack(str(self.CHUNK) + 'h', data_frame))
def click(self):
self.th = threading.Thread(target=start_record)
def stop(self):
self.thrun = False
self.th.join()
For example, you can add a update in a while True to kke the GUI "alive".
In the code below, press first OK, it enters in a while True loop but with update, GUI reacts when you press KO:
import tkinter as tk
a = 0
def action():
global a
while True:
if a == 0:
b.configure(bg="red")
else:
b.configure(bg="blue")
w.update()
def reaction():
global a
a = 1
w = tk.Tk()
b = tk.Button(w, text="OK", command=action)
c = tk.Button(w, text="K0", command=reaction)
b.pack()
c.pack()
w.mainloop()
The danger is to enter in multiple calls to action and worse in recursive calls... you, in these cases, should manage this in your start callback.

Trouble stopping a timer program in Python and Tkinter

I have this timer program in python with a Tkinter GUI. I am able to run the timer user inputs, but I am having trouble pausing or stopping the timer from a tkinter button. I tried creating a variable that changes when the stop button press to initiate time.sleep() in my if statement, but no luck. Any ideas?
from tkinter import Tk, Label, StringVar
from tkinter.ttk import Button, Entry
class Timer:
def __init__(self, master):
self.seconds = 0
self.f = False
self.time = StringVar()
self.time_label = Label(master, relief='flat', font=("Cambria", 20),
textvariable=self.time)
self.hr = StringVar()
self.hr_label = Label(master, text='Hours:').grid(row=1, column=1, padx=5, pady=1)
self.entry_hr = Entry(master, textvariable=self.hr, width=4)
self.entry_hr.grid(row=1, column=2)
self.mins = StringVar()
self.min_label = Label(master, text='Minutes:').grid(row=2, column=1, padx=5, pady=1)
self.entry_min = Entry(master, textvariable=self.mins, width=4)
self.entry_min.grid(row=2, column=2)
self.secs = StringVar()
self.secs_label = Label(master, text='Seconds:').grid(row=3, column=1, padx=5, pady=1)
self.entry_sec = Entry(master, textvariable=self.secs, width=4)
self.entry_sec.grid(row=3, column=2)
self.time.set('00:00:00')
self.start_button = Button(master, text='Start')
self.start_button.bind('<Button-1>', self.start_countdown)
self.stop_button = Button(master, text='Stop')
self.time_label.grid(row=0, columnspan=4, padx=30, pady=10)
self.start_button.grid(row=4, columnspan=4, pady=(10,20))
self.stop_button.grid(row=5, columnspan=4, pady=(10,20))
def set_time(self, hours, minutes, seconds):
self.seconds = hours * 3600 + minutes * 60 + seconds
self.time.set(self.format_time(self.seconds))
def start_countdown(self, event):
f = False
h = self.entry_hr.get()
m = self.entry_min.get()
s = self.entry_sec.get()
h,m,s = map(lambda x: int(x) if x else 0, (h,m,s))
self.set_time(h,m,s)
self.countdown()
def stop(self):
global f
f= True
def countdown(self):
if self.seconds <= 0:
return
else:
self.end_label = Label( text ="Time's up")
if self.f == True:
time.sleep()
self.time.set(self.format_time(self.seconds))
self.seconds -= 1
self.time_label.after(1000, self.countdown)
def format_time(self, seconds):
h = seconds // 3600
m = (seconds - h*3600) // 60
s = seconds - h*3600 - m*60
return '{:0>2}:{:0>2}:{:0>2}'.format(h,m,s)
if __name__ == '__main__':
root = Tk()
timer = Timer(root)
root.mainloop()
First of all your Button widget self.stop_button doesn't have a command or binding associated with it, so it doesn't actually do anything.
Secondly, even if it did call the function you're changing the value of the global variable f but in your if statement you're referencing the local variable self.f (Timer.f).
Thirdly, you're not calling time.sleep() correctly, it requires a value in seconds to sleep for.
And fourthly, time.sleep() would only delay the function from running for the period of time you specify and would eventually cause the timer to start ticking down again.
Instead, you should do something like the below (after associating the Button widget to the function self.stop()):
def stop(self):
self.f = True
def countdown(self):
if self.f == False:
print(self.f)
if self.seconds <= 0:
return
else:
self.end_label = Label( text ="Time's up")
self.time.set(self.format_time(self.seconds))
self.seconds -= 1
self.time_label.after(1000, self.countdown)
This will mean that every time we cycle through the function the first thing we do is check whether self.f is False, if it isn't then the timer stops where it is.

Pygame / Tkinter music player: Time slider causes choppy audio

I'm building a music player with Pygame & Tkinter and currently trying to add a working time slider that allows you to jump to a specific point in a song by dragging the slider.
I set the value of the end of the slider ('to' in config) to the length of the current song then update the slider with 'after' as shown below:
def update_timeslider(self, _ = None):
time = (pygame.mixer.music.get_pos()/1000)
timeslider.set(time)
self.after(1000, self.update_timeslider)
This works in moving the slider along with the time of the song but now I'm trying to implement the cue function and having issues. I try to update song position to the value of the slider with
def cue(self, _ = None):
pygame.mixer.music.set_pos(timeslider.get())
Now when I play the song, its very choppy. When I move the slider, it works for a split second before the 'after' function updates but then it jumps back to the position it was before being moved. I tried increasing the refresh rate but it just causes it to jump back faster and remains just as choppy.
Is there a better way to do this?
Full code:
import os
import pygame
import tkinter
from tkinter.filedialog import askdirectory
from tkinter import *
from tkinter import ttk
playlist = []
index = 0
paused = False
class Application(tkinter.Tk):
def __init__(self, parent):
tkinter.Tk.__init__(self, parent)
self.minsize(400,400)
self.parent = parent
self.main()
def main(self):
global v
global songlabel
global listbox
global volumeslider
global timeslider
global time_elapsed
global songlength
self.configure(background='grey')
self.grid()
self.listbox = Listbox(self, width=20, height=25, relief='ridge', bd=3)
self.listbox.grid(padx=30, pady=15, row=1, columnspan=11, sticky='NSEW')
v = StringVar()
songlabel = tkinter.Label(self, textvariable=v, width=30, anchor="n")
rewbtn = PhotoImage(file="rew.gif")
stopbtn = PhotoImage(file="stop.gif")
playbtn = PhotoImage(file="play.gif")
pausebtn = PhotoImage(file="pause.gif")
ffbtn = PhotoImage(file="ff.gif")
prevbutton = Button(self, width=30, height=30, image=rewbtn, anchor='w')
prevbutton.image = rewbtn
prevbutton.bind("<Button-1>", self.prevsong)
prevbutton.grid(row=10, column=0, padx=(30,0), sticky='w')
playbutton = Button(self, width=30, height=30, image=playbtn, anchor='w')
playbutton.image = playbtn
playbutton.bind("<Button-1>", self.play)
playbutton.grid(row=10, column=1, sticky='w')
pausebutton = Button(self, width=30, height=30, image=pausebtn, anchor='w')
pausebutton.image = pausebtn
pausebutton.bind("<Button-1>", self.pause)
pausebutton.grid(row=10, column=2, sticky='w')
stopbutton = Button(self, width=30, height=30, image=stopbtn, anchor='w')
stopbutton.image = stopbtn
stopbutton.bind("<Button-1>", self.stop)
stopbutton.grid(row=10, column=3, sticky='w')
nextbutton = Button(self, width=30, height=30, image=ffbtn, anchor='w')
nextbutton.image = ffbtn
nextbutton.bind("<Button-1>", self.nextsong)
nextbutton.grid(row=10, column=4, sticky='w')
volumeslider = Scale(self, from_=0, to = 1, resolution = 0.01, orient = HORIZONTAL, showvalue = 'yes', command = self.change_vol)
volumeslider.grid(row=10, column=8, columnspan=3, padx=30, pady=(0,10), sticky='wse')
volumeslider.set(50)
timeslider = Scale(self, from_=0, to=100, resolution=1, orient=HORIZONTAL, showvalue = 'no', command=self.cue)
timeslider.grid(row=12, column=0, columnspan=11, padx = 30, sticky='wse')
timeslider.set(0)
time_elapsed = Label(text="0:00:00")
time_elapsed.grid(row=13, columnspan=11, padx=(30,0), pady=(0,30), sticky='ws')
# time_remaining = Label(text="0:00:00")
# time_remaining.grid(row=13, column = 7, columnspan=5, padx=(0,30), pady=(0,30), sticky='se')
# FILE OPEN
self.directorychooser()
playlist.reverse()
for items in playlist:
self.listbox.insert(0, items)
playlist.reverse()
self.listbox.bind("<Double-Button-1>", self.selectsong)
self.listbox.bind("<Return>", self.selectsong)
songlabel.grid(row = 0, column = 0, columnspan = 10, padx = 55, pady=(10,0), sticky=W+N+E)
# GRID WEIGHT
self.grid_columnconfigure(5,weight=1)
self.grid_columnconfigure(7,weight=1)
self.grid_rowconfigure(1,weight=1)
def prevsong(self, event):
global index
if index > 0:
index-=1
print(index)
elif index == 0:
index = len(playlist)-1
pygame.mixer.music.load(playlist[index])
self.set_timescale()
pygame.mixer.music.play()
self.get_time_elapsed()
self.update_timeslider()
self.update_currentsong()
def play(self, event):
self.set_timescale()
pygame.mixer.music.play()
self.get_time_elapsed()
self.update_timeslider()
self.update_currentsong()
def pause(self, event):
global paused
if paused == True:
pygame.mixer.music.unpause()
paused = False
elif paused == False:
pygame.mixer.music.pause()
paused = True
def nextsong(self, event):
global index
if index < len(playlist)-1:
index+=1
elif index == (len(playlist)-1):
index = 0
pygame.mixer.music.load(playlist[index])
self.set_timescale()
pygame.mixer.music.play()
self.get_time_elapsed()
self.update_timeslider()
self.update_currentsong()
def stop(self, event):
pygame.mixer.music.stop()
v.set("")
return songlabel
def selectsong(self, event):
global index
global songtime
global songlength
idx = self.listbox.curselection()
index = idx[0]
pygame.mixer.music.load(playlist[index])
self.set_timescale()
pygame.mixer.music.play()
self.get_time_elapsed()
# self.get_time_remaining()
self.update_timeslider()
self.update_currentsong()
def change_vol(self, _ = None):
pygame.mixer.music.set_volume(volumeslider.get())
def cue(self, _ = None):
pygame.mixer.music.set_pos(timeslider.get())
def getsonglen(self):
s = pygame.mixer.Sound(playlist[index])
songlength = s.get_length()
return songlength
def set_timescale(self):
songlength = self.getsonglen()
timeslider.config(to=songlength)
def get_time_elapsed(self):
global time_elapsed
time = int(pygame.mixer.music.get_pos()/1000)
m, s = divmod(time, 60)
h, m = divmod(m, 60)
clock = "%d:%02d:%02d" % (h, m, s)
time_elapsed.configure(text=clock)
self.after(100, self.get_time_elapsed)
# def get_time_remaining(self):
# global time_remaining
# time = int(pygame.mixer.music.get_pos()/1000)
# songlen = int(self.getsonglen())
# rem = songlen - time
# m, s = divmod(rem, 60)
# h, m = divmod(m, 60)
# clock2 = "%d:%02d:%02d" % (h, m, s)
# time_remaining.configure(text=clock2)
# self.after(100, self.get_time_remaining)
def update_timeslider(self, _ = None):
time = (pygame.mixer.music.get_pos()/1000)
timeslider.set(time)
self.after(10, self.update_timeslider)
def update_currentsong(self):
global index
global songlabel
v.set(playlist[index])
return songlabel
def directorychooser(self):
directory = askdirectory()
os.chdir(directory)
for files in os.listdir(directory):
if files.endswith(".flac"):
realdir = os.path.realpath(files)
playlist.append(files)
print(files)
pygame.mixer.init()
pygame.mixer.music.load(playlist[0])
self.update_currentsong()
app = Application(None)
app.mainloop()
The problem appears to be that update_timeslider is being called way more times than you think it does.
When you do this:
self.update_timeslider()
... it causes self.update_timeslider to be called 100 times per second.
The problem is that you call that from multiple places in the code, which mean you may ultimately be calling that function 500-1000 times per second or even more. If each call takes a large fraction of a second, you'll end up spending more CPU time updating the slider than you are playing the sound.
When you call after it will return an id. You can use this id to later cancel any existing call before starting a new loop. For example, you might want to do something like this:
class Application(tkinter.Tk):
def __init__(self, parent):
...
self.after_id = None
def update_timeslider(self, _ = None):
if self.after_id is not None:
self.after_cancel(self.after_id)
self.after_id = None
time = (pygame.mixer.music.get_pos()/1000)
timeslider.set(time)
self.after_id = self.after(1000, self.update_timeslider)
Instead of using pygame.mixer.Sound() in your getsonglen() function, use Mutagen for getting the song length.
pygame.mixer.Sound() at first converts the song to a Sound, I exactly don't know what happens, but it probably causes it to use more CPU and that's why the audio gets choppy.
I also faced the same problem, so I used Mutagen for getting the song's length. It worked fine for me.
from mutagen.mp3 import MP3
song = 'your song path'
total_length = MP3(song).info.length
Hope it helps!

Categories