In the code below, I tried to develop an audio player that plays .wav files but unfortunately, the pause and play buttons don't work as intended. The pause button shows but it doesn't automatically start. The play button doesn't change even after it is clicked and it also does not play. The rewind and fast-forward button work by moving the slider. Unfortunately, after the initial period, it starts to malfunction. Affecting the volume_slider and giving me OSError: [Errno -9988] Stream closed. I'm still a beginner so please make your answers to be as simple as possible. Thank you.
import pyaudio
import tkinter as tk
from tkinter import Scale
from tkinter import PhotoImage
from tkinter import filedialog
file_path = filedialog.askopenfilename(filetypes=[("WAV files", "*.wav")])
CHUNK = 1024
p = pyaudio.PyAudio()
wf = wave.open(file_path, 'rb')
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
value = 0
def on_fast_forward(event):
global value, timer
seek_slider.set(value + 5)
timer = root.after(100, on_fast_forward, event)
print("Fast forward button clicked!")
def stop_fast_forward(event):
root.after_cancel(timer)
def on_rewind(event):
global value, timer
print("Rewind button clicked!")
seek_slider.set(value - 5)
timer = root.after(100, on_rewind, event)
def stop_rewind(self, event):
root.after_cancel(timer)
def on_play():
global data, value, play_button, pause_button
play_button.destroy()
if "!button3" not in root.children:
pause_button = tk.Button(root, image=play_img, command=on_pause)
pause_button.place(x=320, y=400)
stream.start_stream()
data = wf.readframes(CHUNK)
stream.write(data)
def on_pause():
global data, value, play_button, pause_button
pause_button.destroy()
if "!button2" not in root.children:
play_button = tk.Button(root, image=play_img, command=on_play)
play_button.place(x=320, y=400)
stream.stop_stream()
def on_volume_change(val):
global value
value = val
print("Seeked to:", value)
wf.setpos(int((value / wf.getnframes() * wf.getframerate()) * wf.getnframes())) # seek to the desired position in the audio file
data = wf.readframes(CHUNK) # read the next chunk of data
stream.write(data) # play the data
root = tk.Tk() # create the main window
root.title("Audio Transmitter") # set the title of the window
root.geometry("700x500") # set the window size to 700x500 pixels
# Create a scale widget
seek_slider = Scale(root, from_=0, to=int(wf.getnframes()/wf.getframerate()), orient='horizontal', command=lambda value: on_volume_change(int(value)))
seek_slider.place(x=45, y=350)
seek_slider.config(length=600)
seek_slider.config(showvalue=False)
seek_slider.set(0) # set the initial value of the slider to 100
# create rewind button
rewind_img = PhotoImage(file="Rewind_button.png")
rewind_button = tk.Button(root, image=rewind_img, command=on_rewind)
rewind_button.place(x=270, y=400)
rewind_button.bind("<Button-1>", on_rewind)
rewind_button.bind("<ButtonRelease-1>", stop_rewind)
# create play button
play_img = PhotoImage(file="Play_button.png")
play_button = tk.Button(root, image=play_img, command=on_play)
# create pause button
pause_img = PhotoImage(file="Pause_button.png")
pause_button = tk.Button(root, image=pause_img, command=on_pause)
pause_button.place(x=320, y=400)
# create fast forward button
fast_forward_img = PhotoImage(file="FastForward_button.png")
fast_forward_button = tk.Button(root, image=fast_forward_img, command=on_fast_forward)
fast_forward_button.place(x=370, y=400)
fast_forward_button.bind("<Button-1>", on_fast_forward)
fast_forward_button.bind("<ButtonRelease-1>", stop_fast_forward)
root.mainloop() # start the event loop
# create pause button
pause_img = PhotoImage(file="Pause_button.png")
pause_button = tk.Button(root, image=pause_img, command=on_pause)
pause_button.place(x=320, y=400)
# create fast forward button0
fast_forward_img = PhotoImage(file="FastForward_button.png")
fast_forward_button = tk.Button(root, image=fast_forward_img, command=on_fast_forward)
fast_forward_button.bind("<ButtonPress-1>", on_fast_forward)
fast_forward_button.bind("<ButtonRelease-1>", lambda event: setattr(fast_forward_flag, False))
fast_forward_button.place(x=370, y=400)
root.mainloop() # start the event loop
Related
I made a GUI Python program with Tkinter.
I made a progressbar using "from tkinter.ttk import Progressbar".
When I press a button, I apply a function that do something and return a Boolean value.
I need that the progressbar will run until that function ends the process, and after that it will stop.
from tkinter.ttk import Progressbar
import time
import threading
wheel_flag = False
root = tk.Tk()
wheel = Progressbar(row,orient=HORIZONTAL,length=100,mode="indeterminate")
def btn_function()
loading_function = threading.Thread(target=start_loading)
loading_function.start()
boolean_wheel = threading.Thread(target=some_function, args = (x,y)) #"some_function" returns a boolean value
boolean_wheel.start()
while True:
if not boolean_wheel.is_alive():
break
wheel_flag = True
def start_loading():
while True:
global wheel_flag
wheel['value'] += 10
root.update_idletasks()
time.sleep(0.5)
if wheel_flag:
break
Here I don't get the boolean_wheel value, but I want to check if it true or false and send to the user message if function succeeded or not.
I want that when "btn_function" is applied, the progressbarwill start to load until "some_function" will finish run.
Now the result I get is that the loading start only after "some_function" finished, and runs without stopping until I close the program.
I would use root.after(millisecond, function) instead of while True-loop to run function which updates progressbar every few seconds. And I would use directly boolean_wheel.is_alive() in this function to stop updateing.
import tkinter as tk
from tkinter import ttk
import time
import threading
# --- functions ---
def some_function(x, y):
time.sleep(10)
def btn_function():
x = 100
y = 100
boolean_wheel = threading.Thread(target=some_function, args=(x,y))
boolean_wheel.start()
# block button
button['state'] = 'disabled'
# show progressbar
wheel.grid(row=0, column=1)
# start updating progressbar
update_progressbar(boolean_wheel) # send thread as parameter - so it doesn't need `global`
def update_progressbar(thread):
if thread.is_alive():
# update progressbar
wheel['value'] += 10
# check again after 250ms (0.25s)
root.after(250, update_progressbar, thread)
else:
# hide progressbar
wheel.grid_forget()
# unblock button
button['state'] = 'normal'
# --- main ---
root = tk.Tk()
wheel = ttk.Progressbar(root, orient='horizontal', length=100, mode="indeterminate")
#wheel.grid(row=0, column=1) # don't show
button = tk.Button(root, text='Start', command=btn_function)
button.grid(row=0, column=0)
root.mainloop()
EDIT:
If you would send widgets to functions as parameters then you could run it for many progressbars
import tkinter as tk
from tkinter import ttk
import time
import threading
# --- functions ---
def some_function(x, y):
time.sleep(10)
def btn_function(row, button, progressbar):
x = 100
y = 100
boolean_wheel = threading.Thread(target=some_function, args=(x,y))
boolean_wheel.start()
start_loading(row, button, progressbar, boolean_wheel)
def start_loading(row, button, progressbar, thread):
# block button
button['state'] = 'disabled'
# show progressbar
progressbar.grid(row=row, column=1)
# start updating progressbar
update_progressbar(button, progressbar, thread) # send thread as parameter - so it doesn't need `global`
def update_progressbar(button, progressbar, thread):
if thread.is_alive():
# update progressbar
progressbar['value'] += 10
# check again after 250ms (0.25s)
root.after(250, update_progressbar, button, progressbar, thread)
else:
end_loading(button, progressbar)
def end_loading(button, progressbar):
# hide progressbar
progressbar.grid_forget()
# unblock button
button['state'] = 'normal'
# --- main ---
root = tk.Tk()
# ---
wheel1 = ttk.Progressbar(root, orient='horizontal', length=100, mode="indeterminate")
#wheel.grid(row=1, column=1) # don't show
button1 = tk.Button(root, text='Start')
button1.grid(row=1, column=0)
button1['command'] = lambda:btn_function(1, button1, wheel1) # need to assign after creating button - to send button to function
# ---
wheel2 = ttk.Progressbar(root, orient='horizontal', length=100, mode="indeterminate")
#wheel2.grid(row=2, column=1) # don't show
button2 = tk.Button(root, text='Start')
button2.grid(row=2, column=0)
button2['command'] = lambda:btn_function(2, button2, wheel2) # need to assign after creating button - to send button to function
# ---
root.mainloop()
I would even put it in Frame and create own widget class MyProgress(tk.Frame) to create many widgets using for-loop
import tkinter as tk
from tkinter import ttk
import time
import threading
# --- classes ---
class MyProgrees(tk.Frame):
def __init__(self, master, target_function, target_args, *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.target_function = target_function
self.target_args = target_args
self.progressbar = ttk.Progressbar(self, orient='horizontal', length=100, mode="indeterminate")
#self.progressbar.grid(row=0, column=1) # don't show
self.button = tk.Button(self, text='Start', command=self.on_press_button)
self.button.grid(row=0, column=0)
def on_press_button(self):
self.thread = threading.Thread(target=self.target_function, args=self.target_args)
self.thread.start()
self.start_loading()
def start_loading(self):
# block button
self.button['state'] = 'disabled'
# show progressbar
self.progressbar.grid(row=0, column=1)
# start updating progressbar
self.update_progressbar()
def update_progressbar(self):
if self.thread.is_alive():
# update progressbar
self.progressbar['value'] += 10
# check again after 250ms (0.25s)
self.after(250, self.update_progressbar)
else:
self.end_loading()
def end_loading(self):
# hide progressbar
self.progressbar.grid_forget()
# unblock button
self.button['state'] = 'normal'
# --- functions ---
def some_function(x, y):
time.sleep(10)
# --- main ---
root = tk.Tk()
# ---
x = 100
y = 100
for _ in range(10):
widget = MyProgrees(root, some_function, (x, y))
widget.pack(fill='x', expand=True)
# ---
root.mainloop()
the proper solution is to remove the loop in btn_function and instead have some_function set wheel_flag after it is done.
def some_function_wrapper(*args,**kwargs):
global wheel_flag
some_function(*args,**kwargs)
wheel_flag = True
and call this instead of some_function inside your thread.
now another solution is to just run the entire btn_function inside another thread, by adding
command= lambda : threading.Thread(target=btn_function).start()
inside your button definition, but nesting threads sometimes becomes hard to track.
Edited:
It works.
I checked the function value inside that function
def some_function_wrapper(*args,**kwargs):
global wheel_flag
check = some_function(*args,**kwargs)
if check:
print("works")
else:
print("don't work")
wheel_flag = True
For one of the final 100 Days of Python, I need to build a PDF to audiobook program. The requirements aren't very clear on whether it needs to be an app or just write a script, so I decided to build it in Tkinter. In addition to just reading the text, I also want the user to be able to do things like adjust the volume, rate, and gender of the voice while the text is being read. However, when I press the play button, the GUI freezes until it's finished reading, and pressing the volume/etc. buttons has no effect.
Is this something that is feasible in Tkinter? I read some about threading and how Tkinter doesn't do very well with running multiple processes at a time. I tried putting the play button in a separate thread. When I did this, I could press the volume/etc. buttons, but they did not change the audio at all. Something about how the main loop can't change other threads?
Should I abandon doing this in Tkinter and try something else, like a Flask app, or is it possible to do this and I'm just missing something?
from tkinter import *
import pyttsx3
class App(Tk):
def __init__(self):
super().__init__()
self.engine = pyttsx3.init()
self.rate = 150
self.volume = 0.5
self.voices = self.engine.getProperty("voices")
self.title("DIY Audiobooks")
self.background_color = "#A7C4BC"
self.config(padx=20, pady=20, bg=self.background_color)
self.heading_font = "Cambria"
self.body_font = "Times New Roman"
self.title_label = Label(text="DIY Audiobooks",
font=(self.heading_font, 40, "bold"),
bg=self.background_color)
self.title_label.grid(row=0, column=0, columnspan=3)
## Volume Buttons ##
self.volume_up_img = PhotoImage(file="volume-up.png")
self.volume_down_img = PhotoImage(file="volume-down.png")
self.mute_image = PhotoImage(file="silent.png")
self.volume_up_button = Button(image=self.volume_up_img,
command=self.increase_volume)
self.volume_down_button = Button(image=self.volume_down_img,
command=self.decrease_volume)
self.mute_button = Button(image=self.mute_image,
command=self.mute_volume)
self.volume_up_button.grid(row=1, column=0)
self.volume_down_button.grid(row=1, column=1)
self.mute_button.grid(row=1, column=2)
## Rate Buttons ##
self.faster_img = PhotoImage(file="fast-forward-button.png")
self.slower_img = PhotoImage(file="fast-backward.png")
self.rate_up_button = Button(image=self.faster_img,
command=self.increase_rate)
self.rate_down_button = Button(image=self.slower_img,
command=self.decrease_rate)
self.rate_up_button.grid(row=2, column=0)
self.rate_down_button.grid(row=2, column=1)
## Voice Buttons ##
self.male = PhotoImage(file="male.png")
self.female = PhotoImage(file="woman.png")
self.select_voice_label = Label(text="Choose a male or female voice.",
font=(self.body_font, 14),
bg=self.background_color)
self.select_voice_label.grid(row=3, column=0, columnspan=2)
self.male_voice = Button(image=self.male,
command=self.change_to_male)
self.female_voice = Button(image=self.female,
command=self.change_to_female)
self.male_voice.grid(row=4, column=0)
self.female_voice.grid(row=4, column=1)
## Play and Pause Buttons ##
self.play_img = PhotoImage(file="play-button.png")
self.play_button = Button(image=self.play_img,
command=self.play)
self.play_button.grid(row=5, column=0)
self.run_app()
## Volume Controls
def volume_up_status(self):
if self.volume < 0.99:
self.volume_up_button["state"] = ACTIVE
else:
self.volume_up_button["state"] = DISABLED
self.after(1, self.volume_up_status)
def volume_down_status(self):
if self.volume > 0.01:
self.volume_down_button["state"] = ACTIVE
else:
self.volume_down_button["state"] = DISABLED
self.after(1, self.volume_down_status)
def increase_volume(self):
self.volume += 0.10
self.engine.setProperty("volume", self.volume)
print(self.volume)
def decrease_volume(self):
self.volume -= 0.10
self.engine.setProperty("volume", self.volume)
print(self.volume)
def mute_volume(self):
self.volume = 0
self.engine.setProperty("volume", self.volume)
## Speed Controls ##
def increase_rate(self):
self.rate += 25
self.engine.setProperty("rate", self.rate)
print(self.rate)
def decrease_rate(self):
self.rate -= 25
self.engine.setProperty("rate", self.rate)
print(self.rate)
## Voice Controls ##
def change_to_male(self):
self.engine.setProperty("voice", self.voices[0].id)
def change_to_female(self):
self.engine.setProperty("voice", self.voices[1].id)
## Play and Pause ##
def play(self):
self.engine.say("Hello and welcome to the DIY Audiobook Program.")
self.engine.runAndWait()
def run_app(self):
self.volume_up_status()
self.volume_down_status()
app = App()
app.mainloop()
I am trying to create a battlemap for dnd (picture) with adjustable grid and movable enemy/creature tokens. The idea is to drag one of the token from the right onto the map on the left.
The window is made of 3 frames. The frame for the map, the frame for the "new map" button and slider. And then frame for the tokens, which are buttons tiled using button.grid()
I found a drag and drop system here that I'm using to drag the tokens. However, when I bring them over the map, they go behind it and you can't see them (I know they go behind because they can be partially visible between the two frames). Is there any way to bring them to the front?
import tkinter as tk
class DragManager():
def add_dragable(self, widget):
widget.bind("<ButtonPress-1>", self.on_start)
widget.bind("<B1-Motion>", self.on_drag)
widget.bind("<ButtonRelease-1>", self.on_drop)
widget.configure(cursor="hand1")
def on_start(self, event):
# you could use this method to create a floating window
# that represents what is being dragged.
pass
def on_drag(self, event):
# you could use this method to move a floating window that
# represents what you're dragging
event.widget.place(x=event.x_root + event.x, y= event.y_root + event.y)
#when button is dropped, create a new one where this one originally was
def on_drop(self, event):
# find the widget under the cursor
x,y = event.widget.winfo_pointerxy()
target = event.widget.winfo_containing(x,y)
try:
target.configure(image=event.widget.cget("image"))
except:
pass
if x > window.winfo_screenwidth() - 200:
del event.widget
return
if not event.widget.pure:
return
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1, compound="top")
#avoiding garbage collection
button.gridx = event.widget.gridx
button.gridy = event.widget.gridy
button.grid(row = event.widget.gridx, column = event.widget.gridy)
button.grid()
button.pure = True
dnd.add_dragable(button)
window = tk.Tk()
window.geometry("1000x800")
map_frame = tk.Frame()
controls_frame = tk.Frame(width=200, borderwidth=1, relief=tk.RAISED)
tk.Label(master=controls_frame, text="controls here").pack()
entity_select_frame = tk.Frame(width=200, relief=tk.RAISED, borderwidth=1)
dnd = DragManager()
button = tk.Button(master=entity_select_frame, text = "dragable", borderwidth=1)
button.gridx = 0
button.gridy = 0
button.grid(row = 0, column = 0)
button.pure = True
dnd.add_dragable(button)
map_frame.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
controls_frame.pack(fill=tk.BOTH)
entity_select_frame.pack(fill=tk.BOTH)
window.mainloop()
I played around a little bit and used stuff from this post. I did not structure it as a class and I used the picture frame as my root-frame and put the control-frame inside that. I'm not sure how this would be best combined with your "draw-grid", "token" functionalities etc., however I hope it helps. I did not find a way to drag widgets across frames though (tried to set a new master for the button, recreate it after dropping it etc.). Get the image used in my code from here.
from tkinter import Tk, Frame, Label, Button, Canvas, font
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
""" ####################### Configuration parameters ###################### """
image_file_path = "Island_AngelaMaps-1024x768.jpg"
resize_img = False # set to True if you want to resize the image > window size
resize_to = (600, 600) # resolution to rescale image to
""" ####################### Drag and drop functionality ################### """
def make_draggable(widget):
widget.bind("<Button-1>", on_drag_start)
widget.bind("<B1-Motion>", on_drag_motion)
def on_drag_start(event):
widget = event.widget
widget._drag_start_x = event.x
widget._drag_start_y = event.y
def on_drag_motion(event):
widget = event.widget
x = widget.winfo_x() - widget._drag_start_x + event.x
y = widget.winfo_y() - widget._drag_start_y + event.y
widget.place(x=x, y=y)
""" ################################# Layout ############################## """
# picture frame with picture as background
picture_frame = Frame(root)
picture_frame.pack(side="left", anchor="w", fill="both", expand=True)
# load the image
if resize_img:
img = ImageTk.PhotoImage(Image.open(image_file_path).resize(resize_to, Image.ANTIALIAS))
else:
img = ImageTk.PhotoImage(Image.open(image_file_path))
# create canvas, set canvas background to the image
canvas = Canvas(picture_frame, width=img.width(), height=img.height())
canvas.pack(side="left")
canvas.background = img # Keep a reference in case this code is put in a function.
bg = canvas.create_image(0, 0, anchor="nw", image=img)
# subframe inside picture frame for controls
ctrl_subframe = Frame(picture_frame)
ctrl_subframe.pack(side="right", anchor="n")
# separator between picture and controls, inside picture frame
ttk.Separator(picture_frame, orient="vertical").pack(side="right", fill="y")
# underlined label 'Controls' in subframe
ctrl_header = Label(ctrl_subframe, text="Controls", font=("Arial", 10, "bold"))
f = font.Font(ctrl_header, ctrl_header.cget("font"))
f.configure(underline=True)
ctrl_header.configure(font=f)
ctrl_header.pack(side="top", pady=2)
# update window to get proper sizes from widgets
root.update()
# a draggable button, placed below ctrl_header
# (based on X of ctrl_subframe and height of ctrl_header, plus padding)
drag_button = Button(picture_frame, text="Drag me", bg="green", width=6)
drag_button.place(x=ctrl_subframe.winfo_x()+2, y=ctrl_header.winfo_height()+10)
make_draggable(drag_button)
""" ################################ Mainloop ############################# """
root.mainloop()
I have created few windows using Tkinter. I need help in the implementation of switching from one window to another when the button has been clicked.
All windows that are created should have the same size.
And also I want to clear existing window data and show next window data.
If you want to have multiple windows opened and want to switch between each window with all of their widgets intact then I don't think destroying a window each time you switch is a good idea instead you can try to withdraw and deiconify the windows.
I've created something like this which can switch between windows and maintain the same geometry of the previous window as you said.
import tkinter as tk
class Window(tk.Toplevel):
# List to keep the reference of all the toplevel windows
_info_pages = []
def __init__(self, master=None, cnf={}, **kw):
kw = tk._cnfmerge( (cnf,kw) )
width = kw.pop('width', master.winfo_width()) # 250x250 will be the standard size of the window
height = kw.pop('height', master.winfo_height())
title = kw.pop('title', 'Win %s' %(len(self._info_pages)+1) )
super(Window, self).__init__(master=master, cnf=cnf, **kw)
for i in self._info_pages: i.wm_withdraw() # Hide the previous windows
if self._info_pages and width == master.winfo_width():
self.wm_geometry(self._info_pages[-1].winfo_geometry())
else:
self.wm_geometry("%dx%d+%d+%d" % (width, height,
master.winfo_rootx()+master.winfo_width(), master.winfo_rooty()))
self._info_pages.append(self)
self.title(title)
self.B1 = tk.Button(self, text='◀ Prev', padx=5, command=self.switch_to_prev)
self.B1.place(relx=0, rely=1, anchor='sw')
self.B2 = tk.Button(self, text='Next ▶', padx=5, command=self.switch_to_next)
self.B2.place(relx=1, rely=1, anchor='se')
self.enable_disable_button()
def enable_disable_button(self):
"""Enable and disable the buttons accordingly if there is no window."""
for i in self._info_pages:
if i == self._info_pages[0]: i.B1['state'] = 'disabled'
else: i.B1['state'] = 'normal'
if i == self._info_pages[-1]: i.B2['state'] = 'disabled'
else: i.B2['state'] = 'normal'
def switch_to_prev(self):
"""Switch to the previous window"""
index = self._info_pages.index(self)
if index != 0:
for i in self._info_pages:
i.wm_withdraw()
self._info_pages[index-1].geometry(self.winfo_geometry())
self._info_pages[index-1].wm_deiconify()
def switch_to_next(self):
"""Switch to the next window"""
index = self._info_pages.index(self)
if index+1 != len(self._info_pages):
for i in self._info_pages:
i.wm_withdraw()
self._info_pages[index+1].geometry(self.winfo_geometry())
self._info_pages[index+1].wm_deiconify()
def destroy(self):
"""if a window is destroyed this will open the last window in the list"""
self._info_pages.remove(self)
if self._info_pages:
self._info_pages[-1].geometry(self.winfo_geometry())
self._info_pages[-1].wm_deiconify()
self.enable_disable_button()
return super().destroy()
# This is just a demo
if __name__ == '__main__':
import random as rnd
root = tk.Tk()
root.geometry('250x250')
root.title("I'm the main window")
colorlist = ['beige','bisque','black','blanchedalmond','blue','blueviolet',
'burlywood', 'cadetblue','chartreuse','chocolate' ]
def create_window():
Window(root, bg=rnd.choice(colorlist))
tk.Button(root, text='Create Window', command=create_window).pack()
root.mainloop()
So I'm trying to make a little battle scene using Tkinter, the code is supposed to change the image, wait a couple of seconds, then exit the Tkinter window. The code I have just makes a little pause when the button to change images is pressed. I'm still a beginner and some concepts are hard for me to grasp.
Here is the code:
from tkinter import *
import time
class MainWindow():
def __init__(self, main):
# canvas for image
self.canvas = Canvas(main, width=660, height=440)
self.canvas.grid(row=0, column=0)
# images
self.my_images = []
self.my_images.append(PhotoImage(file = "att1.gif"))
self.my_images.append(PhotoImage(file = "att2.gif"))
self.my_image_number = 0
# set first image on canvas
self.image_on_canvas = self.canvas.create_image(0, 0, anchor = NW, image = self.my_images[self.my_image_number])
# button to change image
self.button = Button(main, text="FIGHT", command=self.onButton)
self.button.grid(row=1, column=0)
#----------------
def onButton(self):
# next image
self.my_image_number = 1
if self.my_image_number == 1:
time.sleep(2)
root.quit()
# change image
self.canvas.itemconfig(self.image_on_canvas, image = self.my_images[self.my_image_number])
root = Tk()
MainWindow(root)
root.mainloop()
some of this code is borrowed, I tried to alter it to fit my purpose
The image is not changed because time.sleep(2) blocks tkinter update. After the sleep, tkinter quit and so the image is not updated.
Since you have only 2 images and you want to exit the tkinter window 2 seconds after the change of image, try:
def onButton(self):
self.canvas.itemconfig(self.image_on_canvas, image=self.my_images[1])
root.after(2000, root.destroy) # close root window after 2 seconds