I found this neat code here that uses Tkinter to display a series of images. I extended the code to use the 'z' and 'x' keys to browse through the images and 'q' to quit. Also, I would like to be able to click on the individual frames and obtain the image coordinates of where I clicked. While the keyboard interaction works just fine, the mouse click event does not get triggered. I wonder why that is, since the key strokes are triggered just fine.
This is the code I have:
#!/usr/bin/env python
from Tkinter import *
import Image, ImageTk
import os, sys
class Clicker:
def __init__(self, master, filelist):
self.top = master
self.files = filelist
self.index = 0
#display first image
filename = filelist[0]
if not os.path.exists(filename):
print "Unable to find %s" % filename
self.top.quit()
self.title = Label(text=os.path.basename(filename))
self.title.pack()
im = Image.open(filename)
self.tkimage = ImageTk.PhotoImage(im, palette=256)
self.lbl = Label(master, image=self.tkimage)
self.lbl.pack(side='top')
# the button frame
fr = Frame(master)
fr.pack(side='top', expand=1, fill='both')
back = Button(fr, text="back", command=lambda : self.nextframe(-1))
back.grid(row=0, column=0, sticky="w", padx=4, pady=4)
self.ilabel = Label(fr, text="image number: %d/%d" %
(self.index+1, len(self.files)))
self.ilabel.grid(row=0, column=1, sticky="e", pady=4)
self.evar = IntVar()
self.evar.set(1)
next = Button(fr, text="next", command=lambda : self.nextframe(1))
next.grid(row=0, column=3, sticky="e", padx=4, pady=4)
# events
fr.focus_set()
fr.bind("<Key>", self.key)
fr.bind("<Button 1>", self.left_click)
def left_click(self, event):
print (event.x,event.y)
def key(self, event):
if event.char == 'z':
# previous frame
self.nextframe(-1)
elif event.char == 'x':
# next frame
self.nextframe(1)
elif event.char == 'q':
# quit
self.top.quit()
def getImage(self, filename):
im = Image.open(filename)
return im
def nextframe(self,i=1, imgnum=-1):
if imgnum == -1:
self.index += i
else:
self.index = imgnum - 1
if self.index >= len(self.files):
self.index = 0
elif self.index < 0:
self.index = len(self.files) - 1
filename = self.files[self.index]
if not os.path.exists(filename):
print "Unable to find %s" % filename
self.top.quit()
self.title.configure(text=os.path.basename(filename))
self.evar.set(self.index+1)
self.ilabel.configure(text="image number: %d/%d" %
(self.index+1, len(self.files)))
im = self.getImage(filename)
self.tkimage.paste(im)
def getimgnum(self, event=None):
self.nextframe(imgnum=self.evar.get())
# --------------------------------------------------------------------
if __name__ == "__main__":
if not sys.argv[1:]:
print "Usage: click.py images*"
sys.exit()
filelist = sys.argv[1:]
root = Tk()
app = Clicker(root, filelist)
root.mainloop()
The code should work with any set of images, all of which have to have the same dimensions.
Edit: Interestingly, I can get the cursor position on a key stroke but not on a mouse click.
It seems like I found the answer myself:
If I replace the Frame with a Canvas, I am able to trigger a mouse click event. I am not sure why that is the case, but it works.
Related
Problem:
I set up a image downloading GUI-application(tkinter) through unsplash API.
Every thing is working on. Not any error but when I run application so It's shows 'Not responding' cause Image downloading in background. How can I create Two threads. one will handle tkinter GUI and second will handle background image downloading process.
My code:
from tkinter import *
import requests
import os, pprint
from time import sleep
from threading import *
from urllib.request import urlretrieve
class Gui(Tk):
def __init__(self):
self.do_splash_call = False
Tk.__init__(self)
self.title('unsplash image download')
self.geometry('900x500')
# main canvas
main = Canvas(self, bg='#0099e6')
main.place(relx=0, rely=0, relwidth=1, relheight=1)
# frame1 for upper left
name_frame = Frame(main)
name_frame.place(relx=.1, rely=.1)
# name input and label
name_label = Label(name_frame, text='Image Name:')
name_label.pack(side=LEFT, pady=3)
self.name = Entry(name_frame, bd=0, font=2, width=23)
self.name.pack(side=RIGHT, pady=3)
# frame2 for upper right
page_frame = Frame(main)
page_frame.place(relx=.5, rely=.1, relwidth=.3)
# landing page
Label(page_frame, text='Landing page:').pack(side=LEFT, pady=3)
self.page = Spinbox(page_frame, from_=1, to=50, width=30)
self.page.pack(side=RIGHT, pady=3)
# frame3 for down left
size_frame = Frame(main)
size_frame.place(relx=.1, rely=.2)
# name input and label
size_label = Label(size_frame, text='Android, pc, tablet:')
size_label.pack(side=LEFT, pady=3)
self.size = Entry(size_frame, bd=0, font=2)
self.size.pack(side=RIGHT, pady=3)
# button
download = Button(main, bd=1, font=4, text='Download', width=10, command=lambda :self.splash())
download.place(relx=.6, rely=.2)
# output
self.output = Text(main, bd=2)
self.output.place(relx=0, rely=.5, relheight=1, relwidth=1)
# path to save images
save_frame = Frame(main)
save_frame.place(relx=.1, rely=.3, relwidth=.7)
Label(save_frame, text='Save:').pack(side=LEFT, padx=5)
self.save = Entry(save_frame, bd=1)
self.save.place(relx=.1, relwidth=1)
# insert path for save images
self.save.insert(0, str('c:\\users\\abc\\Desktop\\photos'))
def path(self):
if os.path.exists(self.save.get()):
os.chdir(self.save.get())
else:
os.mkdir(self.save.get())
os.chdir(self.save.get())
def splash(self):
self.do_splash_call = True
self.call_splash()
def call_splash(self):
if self.do_splash_call:
# getting input from user
image_name = self.name.get()
pages = self.page.get()
size = self.size.get()
# set the size of photos
if size == 'pc' or 'desktop':
size = 1080
orientation = 'landscape'
elif size == 'hd' or 'full hd' or 'clear' or 'normal' or 'HD':
size = 1500
orientation = 'landscape'
elif size == 'android' or 'mini' or 'mobile':
size = 400
orientation = 'portrait'
else:
size = 1500
orientation = 'landscape'
try:
api = f'''https://api.unsplash.com/photos/search?query=
{image_name}&resolution={size}&orientation=
{orientation}&client_id=API Key
&page={pages}&w=1500&dpi=2'''
res = requests.get(api).json()
for i in range(2):
url = res[i]['links']['download']
name_of_image = str(res[i]['alt_description'])
img_name = '_'.join(name_of_image[:40].split(' '))
# create folder for download images
self.path()
# print current images downloading
self.output.insert(INSERT, '\nDownloading img.... %s.png' % img_name)
self.output.tag_add('fine', '1.0', '3.0')
self.output.tag_config('fine', background='lightGreen', foreground='#196619')
# Downloading image in pc
urlretrieve(url, '%s.png' % img_name)
except Exception as e:
self.output.insert(INSERT, e)
self.output.tag_add('error', '1.0', '3.0')
self.output.tag_config('error', background='#ff4d4d', foreground='black')
print(e)
if __name__ == '__main__':
app = Gui()
app.mainloop()
I except:
There is a class and class inside there is method called splash() so How can I create Thread, one for constructor and second for splash() method ?
Example with thread and other changes or comments in code because there are mistakes.
I didn't change from tkinter import * because it would need more work. On some systems thread may not have access to GUI so self.output in call_spash may have problem but I didn't change it - too many works.
By the way: PEP 8 -- Style Guide for Python Code
from tkinter import * # PEP8 - don't use *
#import tkinter as tk
import requests
import os
import pprint # PEP8 - imports in separated lines
from time import sleep
# from threading import * # PEP8 - don't use *
import threading
from urllib.request import urlretrieve
class Gui(Tk):
def __init__(self):
#Tk.__init__(self)
super().__init__() # instead of Tk.__init__(self)
self.title('unsplash image download')
self.geometry('900x500')
self.do_splash_call = False
self.thread = None
# main canvas
main = Canvas(self, bg='#0099e6')
main.place(relx=0, rely=0, relwidth=1, relheight=1)
# frame1 for upper left
name_frame = Frame(main)
name_frame.place(relx=.1, rely=.1)
# name input and label
name_label = Label(name_frame, text='Image Name:')
name_label.pack(side=LEFT, pady=3)
self.name = Entry(name_frame, bd=0, font=2, width=23)
self.name.pack(side=RIGHT, pady=3)
# frame2 for upper right
page_frame = Frame(main)
page_frame.place(relx=.5, rely=.1, relwidth=.3)
# landing page
Label(page_frame, text='Landing page:').pack(side=LEFT, pady=3)
self.page = Spinbox(page_frame, from_=1, to=50, width=30)
self.page.pack(side=RIGHT, pady=3)
# frame3 for down left
size_frame = Frame(main)
size_frame.place(relx=.1, rely=.2)
# name input and label
size_label = Label(size_frame, text='Android, pc, tablet:')
size_label.pack(side=LEFT, pady=3)
self.size = Entry(size_frame, bd=0, font=2)
self.size.pack(side=RIGHT, pady=3)
# button
#download = Button(main, bd=1, font=4, text='Download', width=10, command=lambda :self.splash())
download = Button(main, bd=1, font=4, text='Download', width=10, command=self.splash) # no need lambda
download.place(relx=.6, rely=.2)
# output
self.output = Text(main, bd=2)
self.output.place(relx=0, rely=.5, relheight=1, relwidth=1)
# path to save images
save_frame = Frame(main)
save_frame.place(relx=.1, rely=.3, relwidth=.7)
Label(save_frame, text='Save:').pack(side=LEFT, padx=5)
self.save = Entry(save_frame, bd=1)
self.save.place(relx=.1, relwidth=1)
# insert path for save images
#self.save.insert(0, str('c:\\users\\abc\\Desktop\\photos'))
self.save.insert(0, 'c:\\users\\abc\\Desktop\\photos') # it is already string
def path(self):
# you could use `val = self.save.get()` once and later use `val`
#if os.path.exists(self.save.get()):
# os.chdir(self.save.get())
#else:
# os.mkdir(self.save.get())
# os.chdir(self.save.get())
val = self.save.get()
if not os.path.exists(val):
#os.mkdir(val) # it makes only first dir in path `dir1/dir2/dir3`
os.makedirs(val) # it makes all dirs in path `dir1/dir2/dir3`
#os.makedirs(val, exist_ok=True) # Python 3.7+, it doesn't need `os.path.exists`
os.chdir(val)
def splash(self):
#if not self.thread or not self.thread.is_alive(): # method without `self.do_splash_call`
if not self.do_splash_call:
print('[DEBUG] run splash')
self.do_splash_call = True
# getting input from user
image_name = self.name.get()
pages = self.page.get()
size = self.size.get()
self.thread = threading.Thread(target=self.call_splash, args=(image_name, pages, size) )
self.thread.start()
def call_splash(self, image_name, pages, size):
# set the size of photos
#if size == 'pc' or 'desktop': # wrong
#if size == 'pc' or size = 'desktop': # good
if size in ('pc', 'desktop'): # good
size = 1080
orientation = 'landscape'
#elif size == 'hd' or 'full hd' or 'clear' or 'normal' or 'HD': # wrong
elif size in ('hd', 'full hd', 'clear', 'normal', 'HD'):
size = 1500
orientation = 'landscape'
#elif size == 'android' or 'mini' or 'mobile': # wrong
elif size in ('android', 'mini', 'mobile'):
size = 400
orientation = 'portrait'
else:
size = 1500
orientation = 'landscape'
try:
api = f'''https://api.unsplash.com/photos/search?query=
{image_name}&resolution={size}&orientation=
{orientation}&client_id=API Key
&page={pages}&w=1500&dpi=2'''
res = requests.get(api).json()
print('[DEBUG] res:', res)
for i in range(2):
url = res[i]['links']['download']
name_of_image = str(res[i]['alt_description'])
img_name = '_'.join(name_of_image[:40].split(' '))
# create folder for download images
self.path()
# print current images downloading
self.output.insert(INSERT, '\nDownloading img.... %s.png' % img_name)
self.output.tag_add('fine', '1.0', '3.0')
self.output.tag_config('fine', background='lightGreen', foreground='#196619')
# Downloading image in pc
urlretrieve(url, '%s.png' % img_name)
except Exception as e:
self.output.insert('end', e)
self.output.tag_add('error', '1.0', '3.0')
self.output.tag_config('error', background='#ff4d4d', foreground='black') # you could define it once
print('[DEBUG] e:', e)
self.do_splash_call = False
if __name__ == '__main__':
app = Gui()
app.mainloop()
I I have 2 functions and the second function should run after the button in the first one has been clicked. This works fine, however I need the number that has been entered to go into a variable and so far the .get() function is not working and im not sure what to do.
I have looked at a lot of different examples, including login and sign up gui's however none of them have been able to help.
from tkinter import *
import tkinter.messagebox as box
def enter_rle_1():
enter_rle = Tk() #do not remove
enter_rle.title('Enter RLE') #do not remove
frame = Frame(enter_rle) #do not remove
label_linecount = Label(enter_rle,text = 'Linecount:')
label_linecount.pack(padx=15,pady= 5)
linecount = Entry(enter_rle,bd =5)
linecount.pack(padx=15, pady=5)
ok_button = Button(enter_rle, text="Next", command = linecount_button_clicked)
ok_button.pack(side = RIGHT , padx =5)
frame.pack(padx=100,pady = 19)
enter_rle.mainloop()
def linecount_button_clicked():
Linecount = linecount.get()
if int(Linecount) < 3:
tm.showinfo("Error", "Enter a number over 3")
elif int(Linecount) > 1000000000:
tm.showinfo("Error", "Enter a number under 1,000,000,000")
else:
print("fdsfd")
enter_rle_1()
I expect there to be a popup telling me the number is too big or too small, depending on wether the number is or not, and if it is a good number, i just have it set as a print as some code to test to see if it works before i move on.
Define a String variable for the Entry widget (Make sure it is global defined):
global linecount_STR
linecount_STR = StringVar()
linecount_STR.set('Enter value here')
linecount = Entry(enter_rle,bd =5,textvariable=linecount_STR)
linecount.pack(padx=15, pady=5)
The number entered there can then be read with int(linecount_STR.get()).
i suggest an OO approach, look this code
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')
self.linecount = tk.IntVar()
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Linecount").pack()
self.txTest = ttk.Entry(f,
validate='key',
validatecommand=self.vcmd,
textvariable=self.linecount).pack()
w = ttk.Frame()
ttk.Button(w, text="Next", command=self.on_callback).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
#print ("self.text = {}".format(self.linecount.get()))
x = self.linecount.get()
if x < 3:
msg = "Enter a number over 3"
elif x > 1000000000:
msg = "Enter a number under 1,000,000,000"
else:
msg = "You ahve enter {0}".format(x)
messagebox.showinfo(self.parent.title(), msg, parent=self)
def on_close(self):
self.parent.on_exit()
def validate(self, action, value, text,):
# action=1 -> insert
if action == '1':
if text in '0123456789':
try:
int(value)
return True
except ValueError:
return False
else:
return False
else:
return True
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Enter RLE')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel(self.title(), "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()
notice this line at the begin....
self.linecount = tk.IntVar()# declare an integer variable
self.vcmd = (self.register(self.validate), '%d', '%P', '%S')#register a function to allow only integer in your text widget.
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 having a peculiar problem when displaying a widget containing a tkinter.StringVar variable.
This happens whether or not I use frames to locate the variable.
In brief, I have three non-variable labels, one variable label, a progressbar and two buttons all gridded vertically (the button are side by side though (irrelevant but supplied for completeness).
When the variable is changed programmatically, concatenating a new character ('\n) and more text, the variable shows the extra lines, but duplicate progressbars and buttons are displayed:
(Separate images here).
Interesting note: This does not happen if the '\n' is not added.
import os
import sys
import time
import tkinter as tk
import tkinter.ttk as ttk
class rtGetPaths(tk.Frame):
"""
"""
def __init__(self, root):
tk.Frame.__init__(self, root)
self.root = root
# Get the system separator
self.path_sep = os.pathsep
# Find the root on the drive
self.root_id = os.path.abspath(os.sep)
self.data = [(os.path.join('C:', 'Program Files', 'App_1.exe')),
(os.path.join('C:', 'Program Files', 'App_2.exe')),
(os.path.join('C:', 'Program Files', 'App_3.exe')),
(os.path.join('C:', 'Program Files', 'App_4.exe'))
]
self.locns = []
self.root.title('Get Paths')
self.gpw_l1 = tk.Label(self.root, text='Searching for apps')
self.gpw_l2 = tk.Label(self.root, text='This may take some time')
self.gpw_l3 = tk.Label(self.root, text='Please be patient')
self.gpw_found_l_svar = tk.StringVar()
self.gpw_found_l_svar.set('')
self.gpw_found_l = tk.Label(self.root, textvariable=self.gpw_found_l_svar)
self.gpw_pb_ivar = tk.IntVar()
self.gpw_pb_ivar.set(0)
self.gpw_pb_length = 350
self.gpw_pb_max = 5
self.gpw_pb = ttk.Progressbar(self.root,
mode='determinate',
maximum=self.gpw_pb_max,
length=self.gpw_pb_length,
variable=self.gpw_pb_ivar)
self.gpw_exit_b = tk.Button(self.root,
text='Exit',
command=sys.exit)
self.gpw_continue_b = tk.Button(self.root,
text='Continue',
command=self.gpw_action_continue_b)
row = 0
self.gpw_l1.grid(row=row, columnspan=2)
row += 1
self.gpw_l2.grid(row=row, columnspan=2)
row += 1
self.gpw_l3.grid(row=row, columnspan=2)
row += 1
self.gpw_found_l.grid(row=row, columnspan=2)
row += 1
self.gpw_pb.grid(row=row, columnspan=2)
row += 1
self.gpw_exit_b.grid(row=row, column=0, sticky='ew')
self.gpw_continue_b.grid(row=row, column=1, sticky='ew')
self.gpw_found_l.grid_remove()
self.root.geometry('+100+200')
def gpw_action_continue_b(self):
"""
:return:
"""
for file in self.data:
lookfor = ''
if 'App_1.exe' in file:
lookfor = file
elif 'App_2.exe' in file:
lookfor = file
elif 'App_3.exe' in file:
lookfor = file
elif 'App_4.exe' in file:
lookfor = file
if lookfor != '':
self.locns.append(lookfor)
label = self.gpw_found_l_svar.get()
if 0 < self.gpw_pb_ivar.get() < 5:
label = label + '\n'
label = label + os.path.join(lookfor)
self.gpw_found_l_svar.set(label)
self.gpw_pb_ivar.set(self.gpw_pb_ivar.get() + 1)
if not self.gpw_found_l.winfo_viewable():
self.gpw_found_l.grid()
self.root.update_idletasks()
time.sleep(1)
self.gpw_continue_b['state'] = 'disabled'
self.gpw_pb_ivar.set(self.gpw_pb_max)
self.root.update_idletasks()
return
#==============================================================================
# MAIN (MAIN)
#==============================================================================
def main():
""" Run the app
"""
# # Create the screen instance and name it
root = tk.Tk()
app = rtGetPaths(root)
root.mainloop()
root.quit()
#==============================================================================
# MAIN (STARTUP)
#==============================================================================
if __name__ == '__main__':
# Run the function name main()
main()
Why is it doing this, and how do I stop it?
I'm not sure why this problem is occurring, but you can fix it by updating the progress bar each time you change the lable
The code:
def gpw_action_continue_b(self):
for file in self.data:
...
self.gpw_pb.update() # this fixes the problem
self.root.update_idletasks()
time.sleep(1)
Proof it works:
I have to make a Photo Browser GUI, and I need to be able to browse through the photos, how would I Go about making a 'next' button and a 'previous'. I am a complete beginner so any help is appreciated
As simple as it can be:
1 - Create a master window, usually name root:
root = Tk()
2 - Add a Main Frame for Displaying your Picture (current photo):
framePhoto = Frame(root, bg='gray50',relief = RAISED, width=800, height=600, bd=4)
3 - Add two buttons, Next & Prev:
prevBtn = Button(self.framePhoto, text='Previous', command=lambda s=self: s.getImgOpen('prev'),
bg='blue', fg='red').place(relx=0.85, rely=0.99, anchor=SE)
nextBtn = Button(self.framePhoto, text='Next', command=lambda s=self: s.getImgOpen('next'),
bg='green', fg='black').place(relx=0.90, rely=0.99, anchor=SE)
4 - You need to add method to handle listing all pictures in your current directory or directory that you input to the application, example:
def getImgList(self, path, ext):
imgList = [os.path.normcase(f) for f in os.listdir(path)]
imgList = [os.path.join(path, f) for f in imgList if os.path.splitext(f)[1] == ext]
self.images.extend(imgList)
5 - Another method to open and display the image:
def getImgOpen(self,seq):
print 'Opening %s' % seq
if seq=='ZERO':
self.imgIndex = 0
elif (seq == 'prev'):
if (self.imgIndex == 0):
self.imgIndex = len(self.images)-1
else:
self.imgIndex -= 1
elif(seq == 'next'):
if(self.imgIndex == len(self.images)-1):
self.imgIndex = 0
else:
self.imgIndex += 1
self.masterImg = Image.open(self.images[self.imgIndex])
self.master.title(self.images[self.imgIndex])
self.masterImg.thumbnail((400,400))
self.img = ImageTk.PhotoImage(self.masterImg)
self.lbl['image'] = self.img
return
This is as simple as I can explain to you and the above mentioned piece of codes are for clarification.
Here is the complete code, as explained by Iron Fist answer. I assembled it, so I it might be useful.
from Tkinter import *
import os
from PIL import ImageTk,Image
class Display(object):
def __init__(self):
self.images = [];
self.imgIndex = 0;
self.master= Tk()
self.framePhoto = Frame(self.master, bg='gray50',relief = RAISED, width=800, height=600, bd=4)
self.framePhoto.pack();
prevBtn = Button(self.framePhoto, text='Previous', command=lambda s=self: s.getImgOpen('prev')).place(relx=0.85, rely=0.99, anchor=SE)
nextBtn = Button(self.framePhoto, text='Next', command=lambda s=self: s.getImgOpen('next')).place(relx=0.90, rely=0.99, anchor=SE)
#prevBtn.pack();
#nextBtn.pack();
self.getImgList('test_2/test_2','.bmp')
mainloop()
def getImgList(self, path, ext):
imgList = [os.path.normcase(f) for f in os.listdir(path)]
imgList = [os.path.join(path, f) for f in imgList if os.path.splitext(f)[1] == ext]
self.images.extend(imgList)
#print self.images
def getImgOpen(self,seq):
print 'Opening %s' % seq
if seq=='ZERO':
self.imgIndex = 0
elif (seq == 'prev'):
if (self.imgIndex == 0):
self.imgIndex = len(self.images)-1
else:
self.imgIndex -= 1
elif(seq == 'next'):
if(self.imgIndex == len(self.images)-1):
self.imgIndex = 0
else:
self.imgIndex += 1
self.masterImg = Image.open(self.images[self.imgIndex])
self.master.title(self.images[self.imgIndex])
self.masterImg.thumbnail((400,400))
self.img = ImageTk.PhotoImage(self.masterImg)
label = Label(image=self.img)
label.image = self.img # keep a reference!
label.pack()
label.place(x=100,y=100)
#self.lbl['image'] = self.img
return
d = Display();
d.getImgOpen('next')