问题
I am trying to bind the ENTER key to the email password entry box. That way when I have entered all three items I can press enter to call the callback()
function. The other issue I have is with the .get()
method. It does not seem to assign the values I typed into the entry boxes to the variables I defined in my code.
from Tkinter import *
root = Tk()
# grabs the values in the entry boxes and assigns them to variablse
def callback():
steamUser = steamUserW.get()
steamPass = steamPassW.get()
emailPass = emailPassW.get()
root.destroy()
# labels for each entry
Label(root, text="Steam Username").grid(row=0)
Label(root, text="Steam Password").grid(row=1)
Label(root, text="Email Password").grid(row=2)
# entry and button widgets
steamUserW = Entry(root)
steamPassW = Entry(root, show="*")
emailPassW = Entry(root, show="*")
submit = Button(root, text="Submit", command=callback)
# bind the ENTER key to callback function
emailPassW.bind("<Return>", callback)
# space out the widgets
steamUserW.grid(row=0, column=1)
steamPassW.grid(row=1, column=1)
emailPassW.grid(row=2, column=1)
submit.grid(row=3, column=1)
root.mainloop()
print steamUser
EDIT, my new code fixes the .get() issue but I still have a binding issue with the ENTER keys
from Tkinter import *
class gui:
def __init__(self, master):
self.master = master
# labels for each entry
Label(self.master, text="Steam Username").grid(row=0)
Label(self.master, text="Steam Password").grid(row=1)
Label(self.master, text="Email Password").grid(row=2)
# button widget
self.steamUserW = Entry(self.master)
self.steamPassW = Entry(self.master, show="*")
self.emailPassW = Entry(self.master, show="*")
self.submit = Button(self.master, text="Submit", command=self.assign)
# bind the ENTER key to callback function
self.emailPassW.bind("<Return>", self.assign)
self.emailPassW.bind("<KP_Enter>", self.assign)
# space out the widgets
self.steamUserW.grid(row=0, column=1)
self.steamPassW.grid(row=1, column=1)
self.emailPassW.grid(row=2, column=1)
self.submit.grid(row=3, column=1)
# grabs the values in the entry boxes and assigns them to variablse
def assign(self):
self.steamUser = self.steamUserW.get()
self.steamPass = self.steamPassW.get()
self.emailPass = self.emailPassW.get()
self.close()
# closes GUI window
def close(self):
self.master.destroy()
root = Tk()
userGui = gui(root)
root.mainloop()
print userGui.steamUser
回答1:
The original error with the assign not working was because you were assigning the values to local variables. It was working, but no function outside of the callback could see them. The fix is to either declare the variables as global, or re-architect your code to use classes, and make the variables attributes of the class.
The problem with the binding is that your callback needs to accept an argument. When you use bind
, the function that is called will always be passed an argument. This argument is an object representing the event. From this object you can get the widget that triggered the event, the x/y coordinates of the cursor, and several other bits of information.
For your original code, you can fix it like this:
def callback(event):
global steamUser, steamPass, emailPass
steamUser = steamUserW.get()
steamPass = steamPassW.get()
emailPass = emailPassW.get()
root.destroy()
Since you've switched to using a class, you have to have one argument for the event and one argument for self
:
def callback(self, event):
...
This behavior of passing in an argument is well documented. For example, the effbot page on Events and Bindings says:
"If an event matching the event description occurs in the widget, the given handler is called with an object describing the event."
回答2:
Assignment works, but only to a set of variables local to callback() scope
That means, you do not "see" these variables after the function terminates.
Bind each of the ENTER and NumPad-ENTER keys, otherwise just half-done
# COMMAND prepared as <lambda>-container
# to show both auto-injected
# and ad-hoc added variable <placeholder>-s
a_COMMAND2bind = ( lambda aTkInjectedBindEVENT = None, # .SET a <placeholder>
aParameter1 = None, # .SET a <placeholder>
aParameter2 = None, # .SET a <placeholder>
anotherPlaceholderVoidVAR = "?": # .SET another if needed
callback( aTkInjectedBindEVENT ) # .ACT on <<Event>>
or # trick to chain
print( "DEBUG:: aParameter1 == ", aParameter1 )
or
print( "DEBUG:: aParameter2 == ", aParameter2 )
or
call_other_Funct( "Log: " # .ACT on <<Event>>
+ time.ctime()
)
)
# --.|||||||||
root.event_add( "<<anEnterKeyVirtualEVENT>>", "<KeyPress-Return>", # setup as-<<aVirtualEVENT>>
"<KeyPress-KP_Enter>"
)
# --.||||
root.bind( "<<anEnterKey_VirtualEVENT>>", a_COMMAND2bind ) # .bind aCommandAsLAMBDA
Rather wrap into Class and process with smart context-handling
Your code invokes your def callback():
construct in two different contexts
- as a
Button( ... , command = callback )
- as an Entry widget
.bind( ..., callback )
as referred, Tkinter auto-injects anEventOBJECT upon an event occurence, to allow a context-full processing:
# <anEventOBJECT>.char on-{ <KeyPress> | <KeyRelease> }
# .height on-{ <Configure> }
# .width on-{ <Configure> }
# .keysym on-{ <KeyPress> | <KeyRelease> }
# .keysym_num on-{ <KeyPress> | <KeyRelease> }
# .num on-{ <Mouse-1> | <Mouse-2> | ... } 4,5 == <MouseWheel> <--test on target platform O/S
# .serial <-- system-assigned Integer
# .time <-- system-assigned Integer ( .inc each msec )
# .widget <-- system-assigned <widget>-instance
# .x <-- system-assigned <Event>-in-<widget>-mouse-location.x
# .y <-- system-assigned <Event>-in-<widget>-mouse-location.y
# .x_root <-- system-assigned <Event>-on-<Screen>-mouse-location.x
# .y_root <-- system-assigned <Event>-on-<Screen>-mouse-location.y
So the code, as originally proposed, would have hard time to handle each of the different event-context(s) inside the same function.
Better structure into different events/handlers, your event-handling will get simpler per each context.
回答3:
With the help of user3666197 I was able to figure some of it out and through a bit of googling I came up with a solution.
I had to create a gui class so I could easily store and access the three user inputs.
As for the bindings, the error I got was that the callback()
(which I renamed to assign()
was taking in too many arguments. The error is exactly TypeError: assign() takes exactly 1 argument (2 given)
. So I fixed this by allowing as many arguments to be passed into the assign() function, def assign(self, *args):
. I am actually not too sure why I got this error after pressing enter, if someone knows please let me know!
from Tkinter import *
class gui:
def __init__(self, master):
self.master = master
# labels for each entry
Label(self.master, text="Steam Username").grid(row=0)
Label(self.master, text="Steam Password").grid(row=1)
Label(self.master, text="Email Password").grid(row=2)
# button widget
self.steamUserW = Entry(self.master)
self.steamPassW = Entry(self.master, show="*")
self.emailPassW = Entry(self.master, show="*")
self.submit = Button(self.master, text="Submit", command=self.assign)
# bind the ENTER key to callback function
self.emailPassW.bind("<Return>", self.assign)
self.emailPassW.bind("<KP_Enter>", self.assign)
# space out the widgets
self.steamUserW.grid(row=0, column=1)
self.steamPassW.grid(row=1, column=1)
self.emailPassW.grid(row=2, column=1)
self.submit.grid(row=3, column=1)
# grabs the values in the entry boxes and assigns them to variablse
def assign(self, *args):
self.steamUser = self.steamUserW.get()
self.steamPass = self.steamPassW.get()
self.emailPass = self.emailPassW.get()
self.close()
# closes GUI window
def close(self):
self.master.destroy()
root = Tk()
userGui = gui(root)
root.mainloop()
print userGui.steamUser
print userGui.emailPass
来源:https://stackoverflow.com/questions/25579317/tkinter-binding-and-get-issues