I've got my home made music player up and running. Now it is time to add some album artwork extracted from currently playing song with ffmpeg
. I've done lots of searching but am not sure the most efficient way of setting this up in tkinter. A rough window mock-up:
+================+
| | Current Artist: Best Band Ever
| (No Artwork) | Current Album: Greatest Album Ever
| | Current Song: Irresistible Tune
| | Play Progress: 99.9 seconds of: 999
+================+
At the bottom of the window are buttons; Close, Pause, Previous, Next.
When grabbing window border to enlarge, I'd like the picture to resize the most as initial size is 200x200 which is hard to see.
My question is: How do I define the image variable in the TK label field?
Temporarily I have setup the image as a TK string var:
''' Artwork image spanning 4 rows '''
self.current_song_artwork = tk.StringVar()
tk.Label(master2, text="(No Artwork)", font=(None, MON_FONTSIZE)) \
.grid(row=0, rowspan=4, column=0, sticky=tk.W)
NOTE 1: I've seen this Q&A: How to update the image of a Tkinter Label widget? which uses:
img2 = ImageTk.PhotoImage(Image.open(path2))
panel.configure(image=img2)
panel.image = img2
and this seems like the only option?
NOTE 2: When the toplevel window is first opened there is no song playing and no image to display for artwork. Some sort of placeholder will be required?
NOTE 3: Down the road I'd like options to replace artwork with music visualizer (scaled down), scrolling lyrics, or scrolling biography from wikipedia. So future-proofing with a robust design makes sense today.
NOTE 4: This is only my second Python/Tkinter program and much of the code below was copied and pasted from the first. The first in turn was mainly code copied and pasted from Stack Overflow. Any tips on reducing code lines or improving efficiency would be greatly appreciated!
TL;DR
The complete (not very exciting code) is:
def play_items(self):
''' Play 1 or more songs in listbox.selection()
"Close", "Next", "Prev" and "Pause" buttons
Selection may be artist, album or random songs. Get item tag:
Auto-starts playing first song with:
ffplay -nodisp -autoexit "/path/to/song.ext"
If Pause button pressed change text to "Play" and issue:
PID with pgrep ffplay then use kill -s STOP <PID> to suspend
If Play button pressed change text to "Pause" and issue:
PID with pgrep ffplay then use kill -s CONT <PID> to resume
'''
if len(self.listbox.selection()) == 0 :
# TODO: Dialog "Select one or more songs to play."
return
''' Make parent buttons invisible so user doesn't try to click '''
self.clear_buttons()
self.top2 = tk.Toplevel()
self.top2_is_active = True
''' Place Window top-left of parent window with PANEL_HGT padding '''
xy = (self.toplevel.winfo_x() + PANEL_HGT, \
self.toplevel.winfo_y() + PANEL_HGT)
self.top2.minsize(width=BTN_WID * 10, height=PANEL_HGT * 4)
self.top2.geometry('+%d+%d'%(xy[0], xy[1]))
self.top2.title("Playing Selected Songs")
self.top2.configure(background="Gray")
self.top2.columnconfigure(0, weight=1)
self.top2.rowconfigure(0, weight=1)
''' Create master frame '''
master2 = tk.Frame(self.top2, borderwidth=BTN_BRD_WID, relief=tk.RIDGE)
master2.grid(sticky=tk.NSEW)
''' Artwork image spanning 4 rows '''
self.current_song_artwork = tk.StringVar()
tk.Label(master2, text="(No Artwork)", font=(None, MON_FONTSIZE)) \
.grid(row=0, rowspan=4, column=0, sticky=tk.W)
''' Current artist '''
self.current_song_artist = tk.StringVar()
tk.Label(master2, text="Current Artist:", font=(None, MON_FONTSIZE)) \
.grid(row=0, column=1, sticky=tk.W)
tk.Label(master2, text="", textvariable=self.current_song_artist, \
font=(None, MON_FONTSIZE)).grid(row=0, column=2, \
sticky=tk.W)
''' Current album '''
self.current_song_album = tk.StringVar()
tk.Label(master2, text="Current Album:", font=(None, MON_FONTSIZE)) \
.grid(row=1, column=1, sticky=tk.W)
tk.Label(master2, text="", textvariable=self.current_song_album, \
font=(None, MON_FONTSIZE)).grid(row=1, column=2, \
sticky=tk.W)
''' Current song '''
self.current_song_path = ""
self.current_song_name = tk.StringVar()
tk.Label(master2, text="Current Song:", font=(None, MON_FONTSIZE)) \
.grid(row=2, column=1, sticky=tk.W)
tk.Label(master2, text="", textvariable=self.current_song_name, \
font=(None, MON_FONTSIZE)).grid(row=2, column=2, \
sticky=tk.W)
''' Progress of play '''
self.current_progress = tk.StringVar()
tk.Label(master2, text="Play Progress:", font=(None, MON_FONTSIZE)) \
.grid(row=3, column=1, sticky=tk.W)
tk.Label(master2, text="", textvariable=self.current_progress, \
font=(None, MON_FONTSIZE)).grid(row=3, column=2, \
sticky=tk.W)
''' Frame for Buttons '''
frame3 = tk.Frame(self.top2, bg="Blue", borderwidth=BTN_BRD_WID, \
relief=tk.GROOVE)
frame3.grid(row=2, column=0, sticky=tk.NSEW)
frame3.grid_rowconfigure(0, weight=1)
frame3.grid_columnconfigure(0, weight=0)
button = tk.Button(frame3, text="✘ Close", width=BTN_WID, \
command=self.play_close)
button.grid(row=0, column=0, padx=2, sticky=tk.W)
''' Close Button '''
self.top2.bind("<Escape>", self.play_close)
self.top2.protocol("WM_DELETE_WINDOW", self.play_close)
''' Pause/Play Button '''
self.pp_state = "Playing"
self.pp_play_text = "▶ Play"
self.pp_pause_text = "❚❚ Pause"
self.pp_button_text = self.pp_pause_text
self.pp_button = tk.Button(frame3, text=self.pp_button_text, \
width=BTN_WID, command=self.pp_toggle)
self.pp_button.grid(row=0, column=1, padx=2)
''' Next/Prev Button '''
# U+1f844 U+1f846
# U_1f808 I+1f80a
prevbutton = tk.Button(frame3, text=" Previous", width=BTN_WID, \
command=lambda s=self: s.get_setting('prev'))
prevbutton.grid(row=0, column=2, padx=2, sticky=tk.W)
nextbutton = tk.Button(frame3, text="Next ", width=BTN_WID, \
command=lambda s=self: s.get_setting('next'))
nextbutton.grid(row=0, column=3, padx=2, sticky=tk.W)
''' Start at first listbox entry seleected '''
self.get_setting('ZERO') # Set self.ndx to 0
self.listbox.update_idletasks()
self.play_forever()