4

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) between root.update() and root.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_list and current variables.

  • label_list is a dict containing the grid location and tkinter.ttk.label object. When passed to previous, 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 if label_list is 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()
7
  • Thonny is created with tkinter and maybe this can make some conflict with your code also created with tkinter Commented Oct 13 at 18:56
  • did you test this code without Thonny? Commented Oct 13 at 18:58
  • if problem exists only on PI then you may have to add more details about system - system version, Python version, etc. And put this in question, not in comments. More people may see it. But problem is that this need to run code on PI to test what can makes problem. Commented Oct 13 at 19:02
  • Yes, I've tested the code from a command prompt, no Thonny, same result. System configuration: Pi4 running Bookworm, updated regularly Python 3.11.2 From pip list: types-ttkthemes 3.2. Commented Oct 14 at 1:13
  • read my previous comment: And put this in question, not in comments. More people may see it. Commented Oct 14 at 11:06

1 Answer 1

1

I was able to get the program to work, at least via command line.  The comments were helpful, especially stripping the program down to the minimum needed to show the issue.  An explanation is below.

Root cause:  I believe the culprit was a missing tk-tools library.  Tk, included by default, allowed the GUI to be built and loaded but not executed.  You don't have to import tk-tools and I never received an error message that the functionality was missing. The "local" or default Python instance does not include tk-tools.

Method:  Retracing my steps using the history command I noticed while I entered the python -m venv command, I didn’t follow up with source activate.  This meant I was still using the “local” Python instance, and libraries.  It became apparent when I added-back some code deleted for this question but received an error that the referenced library was missing.  The original script was created inside the venv and included tk-tools and other libraries but most of my subsequent development didn't activate the environment. Some libraries were installed on both.

Thonny:  Like the command line, Thonny defaulted to the “local” python instance.  Dummy me, I assumed since I had a venv structure, Thonny would have used it to execute the script.  There are internet pages providing instruction to configure Thonny to use a venv but those instructions didn’t match the screens of my version.  Given I had the command line working I didn’t pursue further.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.