I am developing an app to select and play a mp3 clip from a list. It will be run on a Pi4 with 7” touch display and piAMP+ hat. The GUI is in tkinter with pygame for the sound plus a pair of GPIO pins for volume up/down. The user uses the arrow up/down buttons on the GUI to select the clip; play starts the selected clip and stop halts playing.
The full script (up/down, start/stop and pygame functionality) works on a PC with PyCharm for debugging. For the PC I commented-out the GPIO related commands.
When ported to the Pi with Thonny for debug, the GUI will load but the GUI and GPIO buttons are unresponsive. A copy of the minimal script with 1 button and no GPIO or pygame references is attached. I’ve added debug print statements just before root.run() and inside the button logic to denote when reached.
I’ve observed:
Executing from Thonny or command line, the GUI is displayed but the buttons are unresponsive.
From Thonny debug, if I insert a break-point at
root.run(), the script will stop at the break-point and, when resumed, the previous/next, play/stop buttons and corresponding code will function.Adding a
timer.sleep(5)betweenroot.update()androot.mainloop()doesn’t make a difference. I also tried root.after(2500).
I’ve added the next 3 items to explain my code decisions.
My original design called for a list box to display the songs but I couldn’t find a way to highlight the currently selected item from the list. The result is the “locate songs on grid” code block with
label_listand current variables.label_listis a dict containing the grid location and tkinter.ttk.label object. When passed toprevious, the foreground and background colors are changed and current (selected song) value updated.When playing the selected song (code not shown), I get the mp3 filename from songs, not
label_list. Even iflabel_listis the culprit, I can’t activate play without the breakpoint.
I don’t understand why the Pi “needs” a breakpoint to make the buttons operational.
# Import tkinter (GUI), pygame (Audio) and csv (to parse file into a list of dict
import tkinter as tk # Display library, using grid placement
from tkinter import ttk # This includes newer methods for labels
# Variable declarations and defaults
label_list = {} # Initialize the list of song labels (in grid)
set_volume = 0.1 # Default volume, 10%
current = 0 # Index corresponding the current (highlighted) song
songs = [{'Index': 0, 'Program': "Song1", 'File': 'song1.mp3'},
{'Index': 1, 'Program': 'Song2', 'File': 'song2.mp3'},
{'Index': 2, 'Program': 'Song3', 'File': 'song3.mp3'},
{'Index': 3, 'Program': 'Song4', 'File': 'song4.mp3'},
{'Index': 4, 'Program': 'Song5', 'File': 'song5.mp3'}]
def previous():
# Decrement the current index (song), restore the old, highlight the new
global label_list # This is a list of dictionaries
global current # This is the index corresponding to the current song
# Revert the current song to default colors
print("Got to previous")
label_list[(0, current)].config(background='White', foreground='Black')
if current == 0:
current = 4 # Last song in the queue, ie wrap-around
else:
current -= 1 # Decrement by 1
# Reverse-video the new current song
label_list[(0, current)].config(background='Black', foreground='White')
def main():
# Create the root window
global songs
root = tk.Tk()
root.title("Help Me")
root.geometry('600x400')
root.config(bg='White') # Default to using white, not system background
# Create the GUI as a 6 row x 3 column Grid
for grid_row in range(6):
root.rowconfigure(grid_row, weight=1)
root.columnconfigure(0, weight=3) # The intent was to make this column wider
root.columnconfigure(1, weight=1) # This is actually a spacer between song and arrow
root.columnconfigure(2, weight=1) # These are the arrow buttons
# Locate songs on grid, column 1
for song_row in songs:
if song_row['Index'] == 0:
# Start with making this row reverse-video
song_label = ttk.Label(root, text=song_row['Program'], background='Black', foreground='White')
else:
song_label = ttk.Label(root, text=song_row['Program'], background='White', foreground='Black')
# Assign the song_label to a list which can later be manipulated
label_list[(0, song_row['Index'])] = song_label
# Assign the song_row contents to the grid at the row/column
song_label.grid(column=0, row=song_row['Index'], sticky=tk.EW, padx=5, pady=5)
# locate arrow up/down buttons on column 2 - spanning 2 rows down, u2191 is ascii up arrow
song_prev = ttk.Button(root, text='\u2191', command=previous)
song_prev.grid(column=2, row=1, columnspan=2, sticky=tk.EW, padx=5, pady=5)
# This sets the program to run continuously
root.update()
print("Got to mainloop")
root.mainloop()
if __name__ == '__main__':
main()
Thonnyis created withtkinterand maybe this can make some conflict with your code also created withtkinter