Reputation: 123
Getting back into programming after a 20 years hiatus. Been reading that the use of global variables in python is a sign of bad design, but can't figure out a better way of doing it.
Below is a small program that utilizes a global variable 'paused' to determine the state of the music player. This variable is utilized by a couple of functions.
Is there a better way of doing this without utilizing a global variable?
# Global variable to access from multiple functions
paused = False
def play_music():
global paused
if not paused:
try:
mixer.music.load(filename)
mixer.music.play()
statusBar['text'] = 'Playing Music - ' + os.path.basename(filename)
except:
tkinter.messagebox.showerror('File not found',
'Melody could not find the file.')
else:
mixer.music.unpause()
paused = False
statusBar['text'] = 'Playing Music - ' + os.path.basename(filename)
def stop_music():
mixer.music.stop()
statusBar['text'] = 'Music stopped'
def pause_music():
global paused
if not paused:
mixer.music.pause()
paused = True
statusBar['text'] = 'Music paused'
else:
play_music()
Upvotes: 4
Views: 1907
Reputation: 123
In case anyone else is interested, below is the improved code where a Player class was created to encapsulate the pause variable:
import os
from tkinter import *
from tkinter import filedialog
import tkinter.messagebox
from pygame import mixer
# Global variable to access from multiple functions
# paused = False
class Player:
def __init__(self):
self.paused = False
self.filename = None
def play_music(self):
if not self.paused:
try:
mixer.music.load(self.filename)
mixer.music.play()
statusBar['text'] = 'Playing Music - ' + os.path.basename(self.filename)
except FileNotFoundError:
tkinter.messagebox.showerror('File not found',
'Melody could not find the file. Please choose a music file to play')
else:
mixer.music.unpause()
self.paused = False
statusBar['text'] = 'Playing Music - ' + os.path.basename(self.filename)
@staticmethod
def stop_music():
mixer.music.stop()
statusBar['text'] = 'Music stopped'
def pause_music(self):
if not self.paused:
mixer.music.pause()
self.paused = True
statusBar['text'] = 'Music paused'
else:
self.play_music()
def rewind_music(self):
self.play_music()
statusBar['text'] = 'Music rewound'
@staticmethod
def set_volume(val):
# val is set automatically by the any tkinter widget
volume = int(val)/100 # mixer only takes value between 0 and 1
mixer.music.set_volume(volume)
# Create about us Message Box
@staticmethod
def about_us():
tkinter.messagebox.showinfo('About Melody', 'This is a music player built using python and tkinter')
def browse_file(self):
self.filename = filedialog.askopenfilename()
print(self.filename)
# Create main window
root = Tk()
# Create window frames
middle_frame = Frame(root)
bottom_frame = Frame(root)
# Create Menu
menu_bar = Menu(root)
root.config(menu=menu_bar)
# Create Player object
player = Player()
subMenu = Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label="File", menu=subMenu)
subMenu.add_command(label="Open", command=player.browse_file)
subMenu.add_command(label="Exit", command=root.destroy)
# it appears we can re-use subMenu variable and re-assign it
subMenu = Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label="Help", menu=subMenu)
subMenu.add_command(label="About Us", command=player.about_us)
# Initialise Mixer
mixer.init()
# Create and set the main window
root.title("Melody")
root.wm_iconbitmap(r'favicon.ico')
# root.geometry('300x300')
# Create and arrange widgets
text = Label(root, text="Lets make some noise!")
text.pack(pady=10)
middle_frame.pack(pady=30, padx=30) # Place the middle and bottom frame below this text
bottom_frame.pack()
playPhoto = PhotoImage(file='play-button.png')
playBtn = Button(middle_frame, image=playPhoto, command=player.play_music)
playBtn.grid(row=0, column=0, padx=10)
stopPhoto = PhotoImage(file='stop-button.png')
stopBtn = Button(middle_frame, image=stopPhoto, command=player.stop_music)
stopBtn.grid(row=0, column=1, padx=10)
pausePhoto = PhotoImage(file='pause-button.png')
pauseBtn = Button(middle_frame, image=pausePhoto, command=player.pause_music)
pauseBtn.grid(row=0, column=2, padx=10)
rewindPhoto = PhotoImage(file='rewind-button.png')
rewindBtn = Button(bottom_frame, image=rewindPhoto, command=player.rewind_music)
rewindBtn.grid(row=0, column=0, padx=20)
# Create and set volume slider
scale = Scale(bottom_frame, from_=0, to=100, orient=HORIZONTAL, command=player.set_volume)
scale.set(70) # set default slider and and volume
player.set_volume(70)
scale.grid(row=0, column=1, padx=10)
statusBar = Label(root, text='Welcome to Melody', relief=SUNKEN, anchor=W)
statusBar.pack(side=BOTTOM, fill=X)
# Keep main window displayed
root.mainloop()
Upvotes: 1
Reputation: 17322
if you do not want to use classes, you could pass a settings dictionary that has a pause key, it will be mutated in all over your functions
def play_music(settings):
# some extra code
settings['pause'] = False
def stop_music(settings)
# some extra code
settings['pause'] = None
def pause_music(settings):
# some extra code
settings['pause'] = True
def main():
settings = {'pause': None}
play_music(settings)
.....
Upvotes: 0
Reputation: 353
Indeed, using global variable isn't recommended. You can have side effects that leads your program to have an unexpected behavior.
You have the possibility above (using class), but another solution is just to pass your variable as a parameter of your function.
def play_music(paused):
...
def stop_music(paused):
...
def pause_music(paused):
...
Upvotes: 0
Reputation: 236124
You could put all your functions inside a class, and make the "global" variable an attribute. In that way you can share it between methods:
class Player(object):
def __init__(self):
self.paused = False
def play_music(self):
if not self.paused:
# and so on
def pause_music(self):
if not self.paused:
# etc.
Upvotes: 9