Related
Hello i try to write a desktop app with tkinter for typing speed test i search but i cant find my answer is the any way to check the value of user entry in Textbox while app is running ? i need to compare user entry with test text i try to put the function before main loop when i init class but its just check the entry one time and also i try to use window.after()
this is my full code and problem is running last function
def run_test():
random_number = random.randint(1, 5)
test = session.query(data).filter(database.Database.id == random_number).first()
return test
class UserInterFace:
def __init__(self):
customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("green")
self.window = CTk()
self.window.title("Typing Speed Test Application")
self.window.resizable(False, False)
self.window.config(padx=50, pady=50)
self.window.geometry("1000x700")
self.test = run_test()
self.wrong_entry = 0
self.correct_entry = 0
self.test_running = True
self.start_count = 5
self.start_test_btn = None
self.timer = None
self.start_test_time = None
self.end_test_time = None
self.user_input = []
self.get_ready_label = CTkLabel(master=self.window,
font=timer_font,
text="Get ready Test will be start in :",
text_color="#609EA2")
self.count_down_label = CTkLabel(master=self.window,
font=count_down_font,
text=str(self.start_count),
text_color="#820000")
self.label_test = CTkLabel(master=self.window,
font=my_font2,
text="",
wraplength=900,
)
self.best_score_record = CTkLabel(master=self.window,
font=my_font1,
text_color="#609EA2",
text="")
self.type_area = CTkTextbox(master=self.window,
width=900,
height=200)
self.main()
self.tset_user_text(self.test.text, self.user_input)
self.window.mainloop()
def start_test(self, test):
self.start_test_time = time.time()
self.get_ready_label.grid_forget()
self.count_down_label.grid_forget()
test_id = test.id
test_text = test.text
test_record = test.record
self.label_test.configure(text=test_text)
if test_record == 0:
test_record = "No Record Yet"
else:
test_record = f"Best Record : {test_record}"
self.best_score_record.configure(text=test_record)
self.best_score_record.grid(row=0, column=0)
self.label_test.grid(row=1, column=0, pady=(20, 0))
self.type_area.grid(row=2, column=0, pady=(20, 0))
self.type_area.focus_set()
self.user_input = self.type_area.get("1.0", END)
def main(self):
self.start_test_btn = CTkButton(master=self.window,
text="Start Test",
command=lambda: self.start_time(),
height=100,
width=300,
font=my_font1)
self.start_test_btn.grid(row=0, column=0, padx=(300, 0), pady=(250, 0))
def start_time(self):
if self.start_count >= 0:
self.start_test_btn.grid_forget()
self.get_ready_label.grid(row=0, column=0, pady=(50, 100), padx=(200, 0))
self.count_down_label.grid(row=1, column=0, padx=(200, 0))
self.count_down_label.configure(text=self.start_count)
self.start_count -= 1
self.timer = self.window.after(1000, self.start_time)
else:
self.window.after_cancel(self.timer)
self.start_test(self.test)
def tset_user_text(self, test_text, user_text):
print("test")
test_text_list = list(test_text)
user_text_list = list(user_text)
if len(test_text_list) <= len(user_text_list):
self.test_running = False
self.end_test_time = time.time()
for n in range(len(test_text_list)):
if test_text_list[n] == user_text_list[n]:
self.correct_entry += 1
else:
self.wrong_entry += 1
Okay, so take this with a grain of salt because I can't test this on CustomTkinter, but you should be able to bind an event to your textbox like so:
# call a method called 'on_type' whenever a key is released in the text area
self.type_area.bind('<KeyRelease>', self.on_type)
You can define a callback method to handle this event. Thanks to the binding above, the event parameter is passed to your callback automatically when the event is triggered
def on_type(self, event):
print(event) # do whatever you need to do
FYI: binding to '<KeyRelease>' avoids (some) issues with key repeat caused by held keys
I am working on a Pomodoro application with Tkinter. I want to send the work/rest time to a function to countdown the time sent. These work/rest time should loop within the conditioned number of sessions.
My try was to send the values to the function and return after they are done with the time counter. the counter code worked for me when I separated from the code. but the problem I have is with how to return after It's done.
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.init__window()
self.remaining = 0
self.remainingSessions = 0
self.countdownLabel = Label(self, text="", width=10, height=5)
self.countdownLabel.pack()
self.work = 5
self.rest = 3
self.sessions = 2
self.startButton = Button(self, text="Start", command= partial(self.sessionsCountdown, self.work, self.rest, self.sessions))
self.startButton.pack()
def init__window(self):
self.master.title("Pomodoro")
self.pack(fill=BOTH, expand=1)
def sessionsCountdown(self, remainingWork = None, remainingRest = None, sessions = None):
self.remainingSessions = sessions
while self.remainingSessions >= 0:
print("session number = ", self.remainingSessions)
self.countdown(remainingWork)
print("done with work")
self.countdown(remainingRest)
print("done with rest")
self.remainingSessions = self.remainingSessions - 1
self.countdownLabel.configure(text="time's up!")
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
print(remaining)
if self.remaining <= 0:
return
else:
mins, secs = divmod(self.remaining, 60)
timer = '{:02}:{:02d}'.format(mins, secs)
self.countdownLabel.configure(text=timer)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
I wrote some prints for debugging and I keep getting the following output in the terminal
session number = 2
5
done with work
3
done with rest
session number = 1
5
done with work
3
done with rest
session number = 0
5
done with work
3
done with rest
None
None
None
None
None
None
None
None
No, you can't.
GUI's work with "event-driven programming", which is very different from the procedural programming that most people learn at first. When you use a function like self.after you are scheduling an event to happen later, but allowing other events to happen in the meantime. Important things like responding to clicks and updating the display, but also the little things like moving the window or changing the button color slightly when you hover over it.
To do what you want you'll need to set up some kind of variables to keep track of where you are in the series and use those. I think you know this because you already have those instance variables. All that's left is to trigger the right one in your if self.remaining <= 0: clause.
from tkinter import *
class Window(Frame):
def __init__(self, master = None):
super().__init__(master) # the modern way
# self.master = master # not needed
self.init__window()
self.remaining = 0
self.remainingSessions = 3
self.current = None
self.current_activity = Label(self, text="", width=10, height=5)
self.current_activity.pack()
self.countdownLabel = Label(self, text="", width=10, height=5)
self.countdownLabel.pack()
self.startButton = Button(self, text="Start", command= self.sessionsCountdown)
self.startButton.pack()
def init__window(self):
self.master.title("Pomodoro")
self.pack(fill=BOTH, expand=1) # this line is traditionally not in the class
def sessionsCountdown(self):
'''choose the right activity to trigger'''
if self.remainingSessions <= 0:
self.current_activity.config(text="Done!")
self.countdownLabel.configure(text="time's up!")
return
else:
self.remainingSessions -= 1
if self.current == "resting":
self.current = 'working'
self.current_activity.config(text="Working:")
self.remaining = 5
else:
self.current = 'resting'
self.current_activity.config(text="Resting:")
self.remaining = 3
self.countdown() # start the timer again.
def countdown(self, remaining=0):
if self.remaining <= 0:
self.sessionsCountdown()
else:
mins, secs = divmod(self.remaining, 60)
timer = '{:02}:{:02d}'.format(mins, secs)
self.countdownLabel.configure(text=timer)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
r = Tk()
Window(r)
r.mainloop()
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.
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!
I'm new here and new in PY, I'm trying to write a simple GUI with Tkinter (py 2.7.10) where there are two buttons: the first one starts printing stuff and the second one quits.
I'd like the first button to change the text after the first click from "START" to "STOP" and of course to stop the loop, so I can pause instead of closing and reopen every time.
Also feel free to give any advice to improve it :)
I hope it's clear, here is the code.
import random, time
from Tkinter import *
START, STOP = "start", "stop"
class AppBase:
def __init__(self, root):
self.myRoot = root
self.frame1 = Frame(root)
self.frame1["background"] = "LightSteelBlue"
self.frame1.pack()
self.delay = Scale(self.frame1, from_=100, to=0)
self.delay.pack(side = LEFT, padx=5, pady=15)
self.label0 = Label(self.frame1, text="Notes", background="LightSteelBlue", foreground="darkblue")
self.label0.pack(padx=5, pady=15)
self.label1 = Label(self.frame1, text="NOTE", background="LightSteelBlue", foreground="SteelBlue")
self.label1.pack(padx=30, pady=10)
self.label2 = Label(self.frame1, text="STRING", background="LightSteelBlue", foreground="SteelBlue")
self.label2.pack(padx=30, pady=7)
self.label3 = Label(self.frame1, text="FINGER", background="LightSteelBlue", foreground="SteelBlue")
self.label3.pack(padx=30, pady=7)
self.puls1 = Button(self.frame1)
self.puls1.configure(text = "Start", background = "CadetBlue", borderwidth = 3, command = self.generate_notes)
self.puls1.pack(side = LEFT, padx=5, pady=15)
self.puls2 = Button(self.frame1)
self.puls2.configure(text = "Exit", background = "CadetBlue", borderwidth = 3)
self.puls2.pack(side = LEFT, padx=5, pady=15)
self.puls2.bind("<Button-1>", self.close_all)
self.notes_process=1
def generate_notes(self):
self.notes = ['DO','DO#','RE','RE#','MI','MI#','FA','FA#','SOL','SOL#','LA','LA#','SI','SI#']
self.strings = ['1^ corda','2^ corda','3^ corda','4^ corda','5^ corda','6^ corda']
self.fingers = ['Indice','Medio','Anulare','Mignolo']
self.note = random.randrange(0, len(self.notes))
self.string = random.randrange(0, len(self.strings))
self.finger = random.randrange(0, len(self.fingers))
self.timer=self.delay.get()
if self.timer == '':
self.timer = 500
elif int(self.timer) < 1:
self.timer = 500
else:
self.timer=int(self.delay.get())*100
self.label1["text"] = self.notes[self.note]
self.label2["text"] = self.strings[self.string]
self.label3["text"] = self.fingers[self.finger]
self.myRoot.after(self.timer, self.generate_notes)
def close_all(self, evento):
self.myRoot.destroy()
self.myRoot.quit()
def main():
master = Tk()
master.title("Notes")
appBase = AppBase(master)
master.mainloop()
main()
I found a solution, thanks to everybody for their help and of course if you want to keep talking about different and (sure) better way to do, you are more than welcome!
Here my solution:
step 1, add this to the button:
self.puls1.bind("<Button-1>", self.puls1Press1)
step 2, add a new variable:
self.active = True
step 3, create a new function:
def puls1Press1(self, evento):
if self.puls1["text"] == "Start":
self.puls1["text"] = "Stop"
self.active = True
else:
self.puls1["text"] = "Start"
self.active = False
step 4, modify the function that I want to stop:
def generate_notes(self):
if self.active == True:
[some code]
[some code]
else:
return
You need to save the return value of the after, so that you can use it when you cancel the loop using after_cancel(the_return_value_of_after).
def generate_notes(self):
if self.puls1['text'] == 'Start':
# Change button text to Stop, and call the original loop function
self.puls1['text'] = 'Stop'
self._generate_notes()
else:
# cancel timer
self.puls1['text'] = 'Start'
self.myRoot.after_cancel(self.timer_handle)
def _generate_notes(self):
...(old codes)..
self.timer_handle = self.myRoot.after(self.timer, self._generate_notes)
StringVar() from variable class can be used to update text as well as a looping condition. Example code:
ButtonText=StringVar()
ButtonText.set("START")
button1 = tkinter.Button(self.frame1, text=ButtonText, width=25, command= lambda: do_something(ButtonText))
Elsewhere where you are looping check for the value of the variable:
My_Loop():
if(ButtonText.get()=="STOP"):
break
else:
#some print statements
Now in function do_something(ButtonText), which will be called after each button press, change the string to "STOP" or vice versa :
do_something(ButtonText):
if(ButtonText.get()=="START): #Change text
ButtonText.set("STOP")
else:
ButtonText.set("START") #Change text and call function to loop again
My_Loop()
Hope this helps.