winsound in a tkinter variable - python

I have made a hangman game in Tkinter with buttons for each letter, each button is linked to a function that will process the letter and tell me if it is correct or not. How can I make a function a correct sound and an incorrect sound, I do not want to say where I am getting my wav file from, because it will move (it will always be in the same file as my programme).i do not want to download another module and the answers from another question do not work.
i tried this but it did not make a sound:
def no():
lambda: PlaySound('wrong.wav', SND_FILENAME)
def yes():
lambda: PlaySound('right.wav', SND_FILENAME)

You seem unclear what lambda does:
Python supports the creation of anonymous functions (i.e. functions that are not bound to a name) at runtime, using a construct called "lambda".
In the example you have given you have not created any anonymous function so therefore they are totally unnecessary. Just remove them and everything should work.
def no():
PlaySound('wrong.wav', SND_FILENAME)
def yes():
PlaySound('right.wav', SND_FILENAME)
Remember however winsound is Windows only (so no cross platform development) and to use these functions you need from winsound import *

Related

Understanding import and scope

I am a noob to python and I am trying to understand how the import statement works.
I am running python 3.6.
I have IDLE working.
My test directory is setup like
directory:test_py
In the directory test_py I have files
test_calc.py,button.py,graphics.py
graphics.py is a graphics library.
My main program is in test_calc.py.
In test_calc.py I use from graphics import *. Its the first line
button.py is a module that contains a class definition, Button.
It also uses the graphics library graphics.py.
The main program in test_calc.py uses the Button class.
The Button class definition also calls Point, a function from the graphics library.
I thought the call to create a Button object would be able to use the graphcs
library from the statement from graphics import* in test_calc.py, but it cant.
button.py also needs a from graphics import * to access the graphics library functions.
Is there a way I can have just the **from graphics import* ** in test_calc.py
and have the Button class also see the graphics library
module and objects without have to also import it in button.py?
I have spent a easy 8hrs reading and trying to figure this out.
Any help is appreciated.
here is the complete test_calc.py file
from graphics import *
from button import *
def __createButtons(win):
#create a list of buttons
bSpecs = [(2,1,'0'), (3,1,'.'),
(1,2,'1'), (2,2,'2'), (3,2,'3'), (4,2,'+'), (5,2,'-'),
(1,3,'4'), (2,3,'5'), (3,3,'6'), (4,3,'*'), (5,3,'/'),
(1,4,'7'), (2,4,'8'), (3,4,'9'), (4,4,'<-'), (5,4,'C')]
buttons = []
for (cx,cy,label) in bSpecs:
buttons.append(Button(win, Point(cx,cy), .75,.75,label))
###create larger = button
buttons.append(Button(win, Point(4.5,1), 1.75, .75, "="))
#activate all buttons
for b in buttons:
b.activate()
def main():
#create the window for the calculator
win = GraphWin("Calculator")
win.setCoords(0,0,6,7)
win.setBackground("slategray")
win= win
__createButtons(win)
main()
here is the beginning of button.py
The Point function from graphics library is not available to Button without this statement
from graphics import *
class Button:
def __init__(self, win, center, width, height, label):
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
#Point function from graphics library
p1 = Point(self.xmin,self.ymin)
p2 = Point(self.xmax, self.ymax)
Short answer: No, there is no way to do that.
graphics.py is out of scope of button.py, because test_calc.py imported graphics and test_calc.py is in scope of button.py.
However, if you really want to save an extra line, you can just remove from graphics import * from test_calc.py because button.py already imported graphics.py.
No, you can't do this. If this worked the way you want, you'd break namespacing and scope implicitly within button, not just explicitly in test_calc.
The scope of a module (used by all things defined in that module) is defined by what it imports, and what it defines (and in poor design cases, by things monkey=patched in later). Adding in things from "stuff defined in the things that imported me" is a terrible idea.
The correct solution here is to just do:
# If a package, explicit relative imports avoid checking the whole of sys.path
from .graphics import *
# IF not a package, use plain names without leading .s
from graphics import *
at the top of your button.py file, so it actually specifies where it expects to get the APIs from (ideally, you explicitly list the names to use, not just *, which helps static checking code determine if all your names are defined, helps maintainers figure out where specific names came from, and avoids the risk of two modules providing the same name and stomping all over each other). Since you've already done this, you're golden; you can just omit the import of graphics stuff in test_calc.py (assuming test_calc.py itself doesn't use it).
If, for some insane reason, this really is necessary, the simplest way to do it is to bypass import semantics and explicitly eval the code for button in the same scope that includes the graphics stuff. In test_calc.py, after doing from graphics import *, you'd explicitly open and read the contents of button.py (into, for example, button_source) then do:
eval(compile(button_source, button_path, 'exec'), globals())
which would compile and evaluate the source code of button.py using test_calc's global scope (which includes graphics stuff now), so the newly defined stuff would be defined in test_calc's globals, and see all the stuff defined there as its "native scope" too.

How to bind Alt-F4 with Python tkinter?

I am building a GUI application using Python and Tkinter.
I want to control the behavior of the program when the user closes it.
I've installed a new WM_DELETE_WINDOW protocol using:
root = Tk()
root.protocol("WM_DELETE_WINDOW", lambda: closes_gracefully())
This indeed is working when the user clicks the X button on the titlebar, but it is NOT working when the user presses ALT+F4.
I tried binding the key sequence: root.bind("<Alt-F4>", lambda: closes_gracefully()) but it did not work.
How can I capture the ALT+F4 event?
For this purpose, you could use atexit.register.
It works like a stack that is executed when the program gets closed. Every time you do register(function), this function gets pushed on top. If you added a, b and c they get executed in the opposite order(c, b, a).
In your case, you should do:
register(closes_gracefully)
You should note that this works almost always, except with crashes(alt-f4 works too, just tested it).
You can even use register as a decorator when the function takes no parameters:
#register
def bye():
print("I'm out!")

Tkinter Keyboard Binds

I'm working on an interface using Tkinter and the canvas widget, and so far have found answers to issues I have had from others questions and the answers posted, but I am stumped on this one.
I have several keyboard binds in the class where my GUI elements are created, and they all work fine when the program is started. The binds looks something like this:
self.canvas.get_tk_widget().bind("<Control-o>",self.flash_open)
and are within the __init__ function of the class. As of yesterday, I initialized this class
to start the program, then waited for the user to select open from a menu, which then opened (among other things) a tkmessagebox
self.specfilename =askopenfilename(filetypes=[("spec", "")],initialdir= self.pathname)
With this filename I am able to retrieve my required variable names from a certain filetype (inconsequential to the problem). Today I modified the __init__ function to call the open function when the program starts. Since nothing else can be done until this file is opened, it would make sense to open it first thing. Once the file is selected and the Tkmessagebox is closed, the root window is active, but none of the keyboard binds work. My functions still work using the menu/buttons assigned to them, just not the binds. I have tried binding the shortcuts to the root, with the same result, and am now thinking it may be an issue with the order I am calling them
def __init__(self):
...
self.openfile() #calls the tkmessagebox
self.root.mainloop() #starts gui
I had actually run into this issue before, where a toplevel() instance was closed/destroyed and disabled the binds of the parent window. There isn't any error message to speak of, the binds just don't do anything. I should also mention I have tried to focus on the root window again using
self.openfile()
self.root.mainloop()
self.root.focus_set()
I got around it before by using the wm_withdraw() and wm_deiconify() functions to simply hide the child window, then close it after the program is complete. This fix is a little more difficult to apply in this case however. If anyone can shed some light on the cause of the problem I'd appreciate it.
Edit:
I've written up a runable code segment to show exactly what my issue is.
import os
from tkFileDialog import askopenfilename
from Tkinter import *
class Start:
def __init__(self):
self.root = Tk()
self.root.title('Binding Troubles')
menubar = Menu(self.root)
#add items and their commands to the menubar
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Do work", command=self.do_work)
filemenu.add_command(label="Open File",command=self.openfile)
menubar.add_cascade(label="File", menu=filemenu)
#bind control-o to perform the do work function
self.root.bind("<Control-o>",self.flash_do_work)
self.root.bind("<Control-O>",self.flash_do_work)
#add the menubar to the GUI
self.root.config(menu=menubar)
#initially open a tkdialog to open a file
self.openfile()#comment out this line to make the bind work
self.root.focus()#also tried self.root.focus_set()
self.root.mainloop()
def flash_do_work(self,event):
#indirect tie to the do_work() function, I'm don't know a
#proper way to make functions handle calls from both events and non-events
self.do_work()
def openfile(self):
#gets current path
self.pathname = os.getcwd()
#Requests filename using a tkdialog
self.filename =askopenfilename(initialdir= self.pathname)
print self.filename
def do_work(self):
#placeholder for actual function; shows whether the bind is working or not
print "work"
Start()
The bind will work if self.openfile() is removed from __init__, and used only from the menu
Another Edit: I've updated the example again, giving a menu option to run the openfile() function. I noticed that if openfile() is called in __init__, the bind will not work. But if next the openfile function is called again, this time manually from the menu, the bind will start working again. Not exactly sure what to take from this. Also, my apologies for the post getting so long.
Change
self.openfile()
to
self.root.after(1, self.openfile)
This moves the call to askopenfilename into the main event loop. Having it outside the main event loop is somehow clobbering your event bindings.
I had this kind of problem a couple of times and it took quite a while until I found a solution I was comfortable with. As #Steven Rumbalski suggests I tried with delaying the application, which works but seems shaky.
Then I found the functions for waiting until something is complete, in this case wait_visibility(widget). This will delay execution until the widget is visible, which seems to be the thing to be waiting for. Try this:
self.root.wait_visibility(self.root) # Wait for root to be displayed
self.openfile()
Now; I'm not sure why this is so, and it seems that there may be differences depending on platform: Tkinter window event . This has nevertheless worked for me on Windows10 and Python 3.10.5.

Why my button will not repeat command with tkinter GUI on python

I'm new to GUI and classes and I'm a just a bit confused, when I use a button in tkinter for python it's suppose to repeat it's command when pressed. but in my program it doesn't do that. is there something wrong with me codes that might counter it? I'm trying to make a simple program that echos whatever is typed.
-Thanks
from Tkinter import *
from PIL import Image, ImageTk
import tkMessageBox
class appsMain(Frame):
def __init__(self,parent):
Frame.__init__(self,parent)
self.parent=parent
self.initUI()
def initUI(self):
self.parent.title("OrganizedWindows")
self.send=Text(self,bg="white",height=3,width=35)
self.send.place(x=17,y=235)
self.msg=Text(self,width=35,height=12,state="disable")
self.msg.place(x=17,y=20)
sendbtn=Button(self,text=" Listen ",command=self.accept)
sendbtn.place(x=305,y=240)
self.pack(fill=BOTH, expand=1)
def accept(self,msg):
self.msg.configure(state="normal")
self.msg.insert(INSERT,msg+"\n")
self.msg.insert(INSERT,"BYE")
self.msg.configure(state="disable")
root=Tk()
root.geometry("350x300+300+300")
app=appsMain(root)
root.mainloop()
Your code has a few problems. The first is solved easily:
sendbtn=Button(self,text=" Listen ",command=self.accept)
doesn't work because when the button is clicked, self.accept is called with no additional arguments (accept expects 2 arguments, [self and msg], but it is only getting 1 [self]).
You can work around this with lambda:
sendbtn=Button(self,text=" Listen ",command=lambda : self.accept("some message here"))
(This is equivalent to):
def func:
self.accept("some message here")
sendbtn=Button(self,text=" Listen ",command=func)
But, I don't know if you want to constantly add different messages ... or where they come from, so it is difficult to give a general solution at this point.
Tkinter applications happily continue to run even after exceptions are raised. It is a good idea to watch the terminal for exceptions when you're developing a Tkinter application (In this case, it pointed me right to the source of the problem).
This is to better answer your Lambda comment question. Lambda is a quick, one-liner way to write a function. The variable you set it to is the same as the name of your function for def myFunction. Then you say the keyword lambda and the letter(s)/word(s) you put after the keyword lambda are just the parameters of your function. Next you put a colon (just like you would for a normal function-> def myFunction:). After that you write whatever you want the function to return. So if you wanted a function to square a given number, n, then you could write it normally like:
def square_num(n):
return n**2
OR as a cool Lambda:
square_num = lambda n: n**2
You can also have as many parameters as you wish, just like in a normal function, so for a given number raised to the x power you could write:
raise_num = lambda n, x: n**x

Seems like python is partial

The following is a function I created, and put it in a file called last_function.py
from tkinter import*
def new_gui(app,sound_file,mixer):
track=mixer.Sound(sound_file)
def track_toggle():
if ballCheckbutton.get()==1:
track.play(loops=-1)
else:
track.stop()
ballCheckbutton=IntVar()
c1=Checkbutton(app,text="check me out",command=track_toggle,variable=ballCheckbutton)
c1.pack(side=LEFT)
ballScale=DoubleVar()
def ScaleVolume(v):
track.set_volume(ballScale.get())
ballScale.set(track.get_volume())
s1=Scale(app,variable=ballScale,resolution=0.1,command=ScaleVolume,orient=HORIZONTAL,from_=0.0,to=1.0,label="volume")
s1.pack()
and this is the file i use.. to call the code and run it..
from tkinter import *
import pygame.mixer
from last_function import*
app=Tk()
mixer=pygame.mixer
mixer.init()
new_gui(app,"49119_M_RED_HardBouncer.wav",mixer)
def close():
mixer.stop()
app.destroy()
app.protocol("WM_DELETE_WINDOW",close)
app.mainloop()
Everything works fine.. but my query is...
1> Why can't I remove from tkinter import* from the last_function file.. cause anyway it's got that on the top of the file that's calling it right. Why do I get an error saying IntVar() not defined.
2> Why do I have to pass mixer as a parameter in the function? can the function not inherit it directly from import pygame.mixerthat's on top of the file calling it?
What I mean to say is. THERE ARE TKINTER COMPONENTS ALSO BEING USED, BUT I DON'T PASS TKINTER AS A PARAMETER.. Do I ! then why is there this... selective parameter assignment??
I'm really confused!!!
1> Why can't i remove from tkinter
import* from the last_function file..
cause anyway it's got that on the top
of the file that's calling it
right.Why do i get an error saying
IntVar() not defined
The Python "import" follows the same scoping rules as the rest of the Python language. By "import" at the top of your second files does not make the Tkinter namespace available to the last_function.py module. Tkinter also needs to be imported there.
2>why do i have to pass mixer as a
parameter in the function? can the
function not inherit it directly from
import pygame.mixerthat's on top of
the file calling it? WHAT I MEAN TO
SAY IS. THERE ARE TKINTER COMPONENTS
ALSO BEING USED,BUT I DON'T PASS
TKINTER AS A PARAMETER.. DO I!! then
why is there this.. selective
parameter assignment??
With the way you have this coded, you need to pass mixer because you are modifying it in your second file with:
mixer.init()
If you reimported mixer in your last_function.py, you would be getting another instance of mixer and not the one previously imported. There is nothing selective about this since both of your files have the Tkinter namespace imported.
You should try and re-factor this code to avoid having to import Tkinter into two modules and having to init mixer in one module and pass it to another.

Categories

Resources