Toknn
Toknn

Reputation: 85

Change variable when TK Button clicked?

I am creating a music library for my first school project in Python. My plan in this scenario is that when the 'PLAY' button (a triangle) is clicked, the path of the image / the variable for it changes and converts it to a 'PAUSE' image (two parallel vertical lines). I can imagine it being something to do with .set() but I'm not sure how to work with that since this is my first week in python.

Here's the full code:

#     GUI     #
import tkinter as tk
from tkinter import *
from tkinter import filedialog


#     AUDIO   #
from pygame import mixer

#     DIRECTORY NAVIGATION   #
from os import walk

#     EXCEPTION HANDLER   #
import pygame

#     VOLUME CONTROL   #
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume, ISimpleAudioVolume
from ctypes import cast, POINTER
# YOU MIGHT NEED TO PIP INSTALL THESE
# IF THOSE DONT WORK TRY
# py -m pip install [library]
# pip install pycaw
# pip install comtypes
# pip install psutil

from PIL import Image, ImageTk




class MP:
    
    def __init__(self, win):
        # Create Tkinter window
        win.geometry('600x300')
        win.title('Jared AIT Music Player')
        win.resizable(0, 0)
        win.iconbitmap('icon.ico')




        


        








        

        # StringVar to change button text later
        self.play_restart = tk.StringVar()
        self.pause_resume = tk.StringVar()
        self.play_restart.set('Play')
        self.pause_resume.set('Pause')

        # The buttons and their positions

        self.loadpngPATH = tk.StringVar()
        self.loadpngPATH.set('load.png')
        self.load_image = Image.open(self.loadpngPATH)

        resized = self.load_image.resize((50,50))

        self.login_btn = ImageTk.PhotoImage(resized)

        self.background_image = Image.open('background.png')
        back_res = self.background_image.resize((600,300))
        self.backphoto = ImageTk.PhotoImage(back_res)

        back_image = Label(image = self.backphoto)
        back_image.image = self.backphoto
        back_image.place(x = 300, y = 150, anchor = 'center')

        self.play_image = Image.open('play.png')
        play_res = self.play_image.resize((50,50))
        self.playphoto = ImageTk.PhotoImage(play_res)

        self.pause_image = Image.open('pause.png')
        pause_res = self.pause_image.resize((50,50))
        self.pausephoto = ImageTk.PhotoImage(pause_res)

        self.stop_image = Image.open('stop.png')
        stop_res = self.stop_image.resize((50,50))
        self.stop_photo = ImageTk.PhotoImage(stop_res)

        self.forward_image = Image.open('forward.png')
        forward_res = self.forward_image.resize((50,50))
        self.forward_photo = ImageTk.PhotoImage(forward_res)

        self.back_image = Image.open('back.png')
        back_res = self.back_image.resize((50,50))
        self.back_photo = ImageTk.PhotoImage(back_res)
 



        load_button = Button(win, text='Load', width=55, height = 55, font=("Arial", 10), command=self.load, image = self.login_btn, bg = 'white', borderwidth = 0, highlightthickness=0)
        load_button.place(x=50,y=250, anchor='center')

        play_button = Button(win, textvariable=self.play_restart, width=55, height = 55, font=("Arial", 10), command=self.play, image = self.playphoto, borderwidth = 0, bg = 'white', highlightthickness=0)
        play_button.place(x=150,y=250, anchor='center')

        pause_button = Button(win, textvariable=self.pause_resume, width=55, height=55, font=("Arial", 10), command=self.pause, image = pself.ausephoto, bg = 'white', borderwidth = 0, highlightthickness=0)
        pause_button.place(x=250,y=250, anchor='center')

        stop_button = Button(win,width=55, height=55, font=("Arial", 10), command=self.stop, image = self.stop_photo, borderwidth = 0, highlightthickness=0, bg="white")
        stop_button.place(x=350,y=250, anchor='center')

        next_button = Button(win ,width=55, height=55, font = ('Arial', 10), command = self.next, image = self.forward_photo, borderwidth = 0, highlightthickness=0, bg="white")
        next_button.place(x=550,y=250, anchor='center')

        back_button = Button(win ,width=55, height=55, font = ('Arial', 10), command = self.back, image = self.back_photo, borderwidth = 0, highlightthickness=0, bg="white")
        back_button.place(x=450,y=250, anchor='center')


        #SLIDERS
        volume_slider = Scale(win, from_=100, to=0, orient=VERTICAL, command=self.volume, length=125)
        volume_slider.grid(row=0, column=1)



 
 

        self.music_file = False
        self.playing_state = False
        mainloop()

        
#IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE



#IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE IMAGE


        
        
    def volume(self,volume_level):
        #       THIS INITIALISES THE VOLUME CONTROL     #
        devices = AudioUtilities.GetSpeakers()
        interface = devices.Activate(
        IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
        volume = cast(interface, POINTER(IAudioEndpointVolume))

        #       THIS SETS THE VOLUME    #
        volume.SetMasterVolumeLevelScalar(int(volume_level)/100, None)

        

    def load(self):
        self.music_file = filedialog.askopenfilename(initialdir="/AIT Python 1/Assets", title="Select a song", filetypes=(("wav files", "*.wav"),("all files", "*.*"),("mp3 files", "*.mp3")))
        print("Loaded:", self.music_file)
        self.play_restart.set('Play')

    


    def play(self):
        if self.music_file:
            mixer.init()
            mixer.music.load(self.music_file)
            mixer.music.play()
            self.playing_state = False
            self.play_restart.set('Restart')
            self.pause_resume.set('Pause')


    def pause(self):
        if not self.playing_state:
            mixer.music.pause()
            self.playing_state = True
            self.pause_resume.set('Resume')

        else:
            mixer.music.unpause()
            self.playing_state = False
            self.pause_resume.set('Pause')

    def stop(self):
        mixer.music.stop()

    ########################################################################################################       
    def next(self):
        self.file_path = (self.music_file.rsplit("/",1))[0].replace("/","\\")
        if "/" in self.music_file:
            self.file_name = self.music_file.rsplit("/",1)[1]
        else:
            self.file_name = self.music_file

        self.filenames = next(walk(self.file_path), (None, None, []))[2]
        self.file_count = 0

        for i in self.filenames:
            if i == self.file_name:
                break
            self.file_count += 1

        self.next_file = self.file_count + 1
        self.directory_limit = len(self.filenames)
        if self.next_file == self.directory_limit:
            self.next_file = 0
        self.music_file = self.file_path + "/" + self.filenames[self.next_file]
        self.file_count = 0
        mixer.init()
        try:
            mixer.music.load(self.music_file)
        except pygame.error as message:
            while True:
                self.next_file += 1
                if self.next_file == self.directory_limit:
                    self.next_file = 0
                self.music_file = self.file_path + "/" + self.filenames[self.next_file]
                self.file_extension = self.music_file.rsplit(".",1)[1]
                if (".wav") or (".mp3") in self.file_extension:
                    mixer.music.load(self.music_file)
                    break
        
        mixer.music.play()



    def back(self):
        self.file_path = (self.music_file.rsplit("/",1))[0].replace("/","\\")
        if "/" in self.music_file:
            self.file_name = self.music_file.rsplit("/",1)[1]
        else:
            self.file_name = self.music_file

        self.filenames = next(walk(self.file_path), (None, None, []))[2]
        self.file_count = 0

        for i in self.filenames:
            if i == self.file_name:
                break
            self.file_count += 1

        self.back_file = self.file_count - 1
        self.directory_limit = len(self.filenames)
        if self.back_file == self.directory_limit:
            self.back_file = 0
        self.music_file = self.file_path + "/" + self.filenames[self.back_file]
        self.file_count = 0
        mixer.init()
        try:
            mixer.music.load(self.music_file)
        except pygame.error as message:
            while True:
                self.back_file += 1
                if self.back_file == self.directory_limit:
                    self.back_file = 0
                self.music_file = self.file_path + "/" + self.filenames[self.back_file]
                self.file_extension = self.music_file.rsplit(".",1)[1]
                if (".wav") or (".mp3") in self.file_extension:
                    mixer.music.load(self.music_file)
                    break
        
        mixer.music.play()







        ########################################################################################################      




root = tk.Tk()
MP(root)
root.mainloop()

Here's the specific part of my code that sets the images:

# The buttons and their positions


self.load_image = Image.open('load.png')
resized = self.load_image.resize((50,50))

self.login_btn = ImageTk.PhotoImage(resized)

self.background_image = Image.open('background.png')
back_res = self.background_image.resize((600,300))
self.backphoto = ImageTk.PhotoImage(back_res)

back_image = Label(image = self.backphoto)
back_image.image = self.backphoto
back_image.place(x = 300, y = 150, anchor = 'center')

self.play_image = Image.open('play.png')
play_res = self.play_image.resize((50,50))
self.playphoto = ImageTk.PhotoImage(play_res)

self.pause_image = Image.open('pause.png')
pause_res = self.pause_image.resize((50,50))
self.pausephoto = ImageTk.PhotoImage(pause_res)

self.stop_image = Image.open('stop.png')
stop_res = self.stop_image.resize((50,50))
self.stop_photo = ImageTk.PhotoImage(stop_res)

self.forward_image = Image.open('forward.png')
forward_res = self.forward_image.resize((50,50))
self.forward_photo = ImageTk.PhotoImage(forward_res)

self.back_image = Image.open('back.png')
back_res = self.back_image.resize((50,50))
self.back_photo = ImageTk.PhotoImage(back_res)




load_button = Button(win, text='Load', width=55, height = 55, font=("Arial", 10), command=self.load, image = self.login_btn, bg = 'white', borderwidth = 0, highlightthickness=0)
load_button.place(x=50,y=250, anchor='center')

play_button = Button(win, textvariable=self.play_restart, width=55, height = 55, font=("Arial", 10), command=self.play, image = self.playphoto, borderwidth = 0, bg = 'white', highlightthickness=0)
play_button.place(x=150,y=250, anchor='center')

pause_button = Button(win, textvariable=self.pause_resume, width=55, height=55, font=("Arial", 10), command=self.pause, image = self.pausephoto, bg = 'white', borderwidth = 0, highlightthickness=0)
pause_button.place(x=250,y=250, anchor='center')

stop_button = Button(win,width=55, height=55, font=("Arial", 10), command=self.stop, image = self.stop_photo, borderwidth = 0, highlightthickness=0, bg="white")
stop_button.place(x=350,y=250, anchor='center')

next_button = Button(win ,width=55, height=55, font = ('Arial', 10), command = self.next, image = self.forward_photo, borderwidth = 0, highlightthickness=0, bg="white")
next_button.place(x=550,y=250, anchor='center')

back_button = Button(win ,width=55, height=55, font = ('Arial', 10), command = self.back, image = self.back_photo, borderwidth = 0, highlightthickness=0, bg="white")
back_button.place(x=450,y=250, anchor='center')

This is what the overall GUI Looks like (but I want some removed) enter image description here

Upvotes: 0

Views: 115

Answers (3)

Daggerpov
Daggerpov

Reputation: 404

You could pass in the play_button Button itself into your function (which would be located inside of where you defined the play_button) instead of command = self.play:

command = lambda: self.play(play_button)

Then this would give you full access to modifying this button and you could then set the image to your pause image as such:

def play(self, play_button):
    if self.music_file:
        #whatever other code you want to keep in this
        play_button['image'] = self.pause_image

Also, I'd suggest that you use a main() function like this one to run your script with (before everything else):

root = tk.Tk() #you could either keep this outside of the main() function or put it inside and set it as a global variable
def main():
    MP(root)
    root.mainloop()

Then you could call it in the same place as you currently have these commands by using this:

if __name__ == "__main__":
    main()

Upvotes: 0

Matiiss
Matiiss

Reputation: 6176

Here is my take on the issue (explanation in code, you can also simply copy-paste and run it (don't remember if requests module was built-in but in case you don't have it simply pip install requests) to test it because it gets images from internet but as I have explained in the code, you can also simply load images from the path):

from tkinter import Tk, Button
from PIL import Image, ImageTk

""" I suggest preloading all of the images (if using PIL then `.open()` before `Tk` and after `Tk` create
PhotoImages (using ImageTk)) """

# play_btn_image = Image.open('play_btn.png')
# pause_btn_image = Image.open('pause_btn.png')

""" since you probably don't have the above mentioned files the below script will get them from
the internet just so you can easily test this code, but otherwise just load them using the path
basically use the above method, just change the path, also all of this is just an example """

# NOT NECESSARY (can load from path) ---
import requests
url_play = 'https://image.flaticon.com/icons/png/512/149/149125.png'
url_pause = 'https://image.flaticon.com/icons/png/512/813/813677.png'

# for reference: https://pillow.readthedocs.io/en/stable/releasenotes/2.8.0.html?highlight=open#open-http-response-objects-with-image-open
play_btn_image = Image.open(requests.get(url_play, stream=True).raw).resize((100, 100), Image.ANTIALIAS)
pause_btn_image = Image.open(requests.get(url_pause, stream=True).raw).resize((100, 100), Image.ANTIALIAS)
# ---

""" I also like to define all the functions before initialising `Tk` so here I define the function
to change the image on the button (this is a universal function that can be used anywhere)
also this function can be used in another function for example you would have the main `play()` function
and inside of it you would have this function """


def change_image(widget, new_image):
    widget.config(image=new_image)


""" I will also mimic the play_pause function, meaning that you can adjust it as you need, it will also
in this case at least, use a global variable to make things simpler """
play = False


def play_pause():
    global play
    if play:
        change_image(play_pause_btn, images['play'])
        # code to pause the audio
        play = False
    elif not play:
        change_image(play_pause_btn, images['pause'])
        # code to start playing the audio
        play = True


root = Tk()

# initialise ImageTk.PhotoImages here because it has to be done after initialising `Tk`
# also I suggest using a dictionary to keep it all a bit more organized
images = {'play': ImageTk.PhotoImage(play_btn_image), 'pause': ImageTk.PhotoImage(pause_btn_image)}

# create the play/pause button
# also I will already assign the base image to it
play_pause_btn = Button(root, image=images['play'], command=play_pause)
play_pause_btn.pack()

root.mainloop()

If you have any questions, be sure to ask, I will try my best to answer them!

EDIT 1: I noticed that the change_image() function is quite useless in this state since you might as well just use play_pause_btn.config(image=images['play']) and the other image in the play_pause() function, the main takeaway is how the play_pause() function works

EDIT 2: I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.

Upvotes: 1

The Parallax
The Parallax

Reputation: 78

you can create a variable for the image path.

self.load_image = Image.open('load.png')

so basically instead of 'load.png' make a variable. for example:

self.image_path = 'load.png'
self.load_image = Image.open(self.image_path)

then create an if-else ladder in which u basically tell the program that when the play button is clicked, set the value of self.load_image to the new value.

Upvotes: 0

Related Questions