I have a following peice of code where I want query button to disappear when entry box is empty. But that is not happening.When I tried to print the entry value on pressing any key, it shows the last value, not the current value.
eg. if "Test" is previous value in pin, than on hitting backspace pin.get() shows Test instead of "Tes". I am new to python and Tkinter, so don't know what silly thing I am doing.
def send_button ():
print pin.get()
if pin.get() == "":
print "in if"
query.place_forget()
else:
query.place(relx=0.0, rely=0.75, relwidth=0.2, relheight=0.15)
return TRUE
#Entry box
pin = StringVar()
ent1 = Entry(top, textvariable = pin, validate = 'key', vcmd = send_button)
#Query button
query = Button (top, command = get_temp, text = 'Query')
top.mainloop()
validation happens before the character is inserted in the widget. That's it's whole purpose: to decide if the edit should be allowed or not. It can't make that decision of the text has already been entered (or deleted).
Entry validation has a way to pass in the value of the text before it is entered, after it is entered, what is being inserted or deleted, and several other pieces of information.
See this answer for a comprehensive example:
https://stackoverflow.com/a/4140988/7432
Related
I'm trying to get the user input from an entry box and once a button is pressed, display that in a tk.Text(). The reason I am not doing it in a label is because I'd like the gui looking something like this:
User: Hey
Response: What's up
User: Nothing..
I had a look at this documentation :http://effbot.org/tkinterbook/text.htm
And example uses here but can't get mine to work.
result = None
window = Tk()
def Response():
global result
result = myText.get()
#The below print displays result in console, I'd like that in GUI instead.
#print "User: ", result
#Creating the GUI
myText = tk.StringVar()
window.resizable(False, False)
window.title("Chatbot")
window.geometry('400x400')
User_Input = tk.Entry(window, textvariable=myText, width=50).place(x=20, y=350)
subButton = tk.Button(window, text="Send", command=Response).place(x =350, y=350)
displayText = Text(window, height=20, width=40)
displayText.pack()
displayText.configure(state='disabled')
scroll = Scrollbar(window, command=displayText).pack(side=RIGHT)
window.mainloop()
I have tried variations of;
displayText.insert(window,result) and displayText.insert(End, result)
But still get nothing when I submit the text. The key thing is to obviously keep the last stored text of the User, rather than overwriting it, simply displaying each input underneath each other, I have been advised Text is the best method for this.
UPDATE
Thanks to the comments and answer by Kevin, the user text is now showing in the gui, but when I enter something and click send again, it goes to the side, like this:
HeyHey
rather than :
Hey
Hey
My chatbot is linked to Dialogflow and so in between each user input the chatbot will respond.
as jasonharper indicated in the comments, you need to un-disable your text box before you can add text to it. Additionally, displayText.insert(window,result) is not the correct way to call it. insert's first argument should be an index, not the window object.
Try:
def Response():
#no need to use global here
result = myText.get()
displayText.configure(state='normal')
displayText.insert(END, result)
displayText.configure(state='disabled')
(You may need to do tk.END or tkinter.END instead of just END, depending on how you originally imported tkinter. It's hard to tell since you didn't provide that part of your code)
I have A Note Taking Program and currently I can type in a Keyword in TextBox-1 and hit Enter to get my notes displayed in a TextBox-2.
The only way I can find how to bind Enter to a button is for it to always be bound to that button. Or I can bind Enter to a function. I would rather have it bound to the button/function only if I am currently inside textBox-1.
I don't even know if it is possible because I can not find any references to something similar to my needs.
Currently I have my Enter key bound like this:
root.bind('<Return>', kw_entry)
This calls the function kw_entry when I hit Enter.
def kw_entry(event=None):
e1Current = keywordEntry.get().lower()
if e1Current in notes: # e1Corrent is just the current text in TextBox-1
root.text.delete(1.0, END)
root.text.insert(tkinter.END, notes[e1Current])
root.text.see(tkinter.END)
else:
root.text.delete(1.0, END)
root.text.insert(tkinter.END, "Not a Keyword")
root.text.see(tkinter.END)
For the most part this works fine however I also want to edit the notes being displayed and the problem is I can not hit Enter while in TextBox-2 because Enter is bound to call the function kw_entry. This is a problem because it resets everything in TextBox-2.
Can anyone point me in the right direction?
If you only want the binding to apply when focus is on a specific widget, put the binding on that widget.
In the following example, if you press return while in the text widget then a message will be printed on the console. If you are in the entry widget, that won't happen.
import tkinter as tk
def foo(event):
print("you pressed return")
# the following prevents the enter key from inserting
# a newline. If you remove the line, the newline will
# be entered after this function runs
return "break"
root = tk.Tk()
entry = tk.Entry(root)
text = tk.Text(root)
entry.pack(side="top", fill="x")
text.pack(side="bottom", fill="both", expand=True)
text.bind("<Return>", foo)
root.mainloop()
I have an application that, for a part of it, will take a user's input and format it into a standardized time format. To do this, i have a time input that has a focusout event tied to it that calls the time parser/replacer method. However, while testing it and just setting a message label to output some stuff, i noticed that it only triggers once...
Below is some sample code to show the problem.
from Tkinter import *
root=Tk()
message_var = StringVar()
message = Label(root, textvariable=message_var, height=2, width=35, bg="light grey")
time_var = StringVar()
time = Entry(root, textvariable=time_var, validate="focusout", validatecommand=time_update)
lose_focus_var = StringVar()
lose_focus_textbox = Entry(root, textvariable=lose_focus_var)
message_var.set("Enter a time below.")
lose_focus_var.set("Click here to lose focus.")
def time_update():
"""
Updates the time field to show standardized times.
"""
cur_entry = time_var.get()
if len(cur_entry) == 0:
message_var.set("0!")
elif len(cur_entry) == 1:
message_var.set("1!")
elif len(cur_entry) == 2:
message_var.set("2!")
elif len(cur_entry) == 3:
message_var.set("3!")
elif len(cur_entry) == 4:
message_var.set("4!")
elif len(cur_entry) == 5:
message_var.set("5!")
else:
message_var.set("TOO MANY!")
time_var.set("")
message.pack()
time.pack()
lose_focus_textbox.pack()
To reproduce my issue, run the code above. In the window that appears, click into the blank textbox, enter any number of characters, then click into the textbox that says "Click here to lose focus." You'll see that the message widget updates correctly! Yay!
However, if you click into the first textbox again, change the number of characters, then click the Lose Focus box again, the message will not update again. You will need to kill the window and re-run the code for the messages widget to update again.
If i add the time_update call to the other textbox (and refactor time_update to figure out which textbox called it), the message update will happen once for each text box. But only once.
Is there a way for me to re-trigger the <FocusOut> event, other than destroying the Entry widget and recreating it? Why doesn't it trigger every time?
One problem, as you mention in a comment to your own question, is that you're not returning True or False. The whole point of the validation is to let tkinter know if it should allow the edit or not, and it does this based on the return value.
Beyond that, however, the main problem is that you're changing the value of the widget within the validation function. When you do that, tkinter will automatically disable the validation. There are workarounds, but you really shouldn't be changing the value in the validation function. If I were a user, I would find this behavior to be extremely frustrating.
If you want to change the value when it loses focus, consider adding a binding to <FocusOut> rather than using the validation functions.
I'm using Tkinter to create a GUI for a simple geometry calculator I'm creating.
Basically, what I have is an Entry box. What I want is for the program/GUI/system to detect when the user of the program hits the 'Enter' or 'return' key WHILE they are in the Entry box. When this is detected, I want the contents of the Entry box to be appended to a list I have defined earlier. I also want a simple label to be created on the GUI that displays the contents of the list (including the appended item(s)). Note that the list begins with nothing in it.
Here is my code so far:
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint).grid(row=0, column=0)
app.bind('<Return>', list_add)
mainloop()
I don't really know the proper way to check for 'Return' and then use it in an if statement.
I hope you understand what I'm trying to get help with, and I've looked all around for an explanation that I could understand with no success.
Instead of binding with the app just bind it with the Entry widget object,i.e,e1
from tkinter import *
#Window setup(ignore this)
app = Tk()
app.title('Geometry Calculator')
app.geometry('384x192+491+216')
app.iconbitmap('Geo.ico')
app.minsize(width=256, height=96)
app.maxsize(width=384, height=192)
app.configure(bg='WhiteSmoke')
#This is the emtry list...
PointList = []
#Here is where I define the variable that I will be appending to the list (which is the object of the Entry box below)
StrPoint = StringVar()
def list_add(event):
print ("hello")
#I don't really know how the bind-checking works and how I would implement it; I want to check if the user hits enter while in the Entry box here
if event.char == '':
PointList.append(StrPoint)
e1 = Entry(textvariable=StrPoint)
e1.grid(row=0, column=0)#use grid in next line,else it would return None
e1.bind('<Return>', list_add)# bind Entry
mainloop()
The solution is to set the binding on the widget itself. That way, the binding will only apply while focus is on that widget. And since you're binding on a specific key, you don't need to check for the value later. You know the user pressed return, because that's the only thing that will cause the binding to fire.
...
e1.bind('<Return>', list_add)
...
You have another problem in that your list_add function needs to call the get method of the variable rather than accessing the variable directly. However, since you aren't using any of the special features of a StringVar, you really don't need it -- it's just one more thing you have to manage.
Here's how to do it without the StringVar:
def list_add(event):
PointLit.append(e1.get())
...
e1 = Entry(app)
e1.grid(row=0, column=0)
e1.bind('<Return>', list_add)
Note that you need to create the widget and lay out the widget in two steps. Doing it the way you did it (e1=Entry(...).grid(...) will cause e1 to be None since that is what .grid(...) returns.
Question
Why is my random ascii character selector function outputting fours, and what is the significance of the number four in this context? Why am I not recieving an error message?
Remember, the question is not about how to solve the issue, it is about why that particular number was output.
Background and Code
I am trying to creating a basic email client. I thought that it would be cool for my password box to show random characters instead of the obvious *. So, I created a function which chose a random ascii letter.
import random
import string
def random_char():
char_select = random.randrange(52)
char_choice = string.ascii_letters[char_select]
return char_choice
When I run this in an interactive terminal, it spits out a random letter. But, when I run it through my widget
self.Password = Entry (self, show = lambda: random_char())
I am met with a bunch of fours.
Extra Credit
If you have the time, please visit my related question, How to have a Tkinter Entry box repeat a function each time a character is inputted?
The show parameter accepts a value not a callback. Tkinter is taking your callback object and trying to convert it to a string and that is what you get when you type in the Entry box.
Instead you can re-configure your Entry after you type by using binding:
def key(event):
entry.configure(show = random_char())
entry = tk.Entry (self)
entry.pack()
entry.bind("<Key>", key)
EDIT
Bryan Oakley is correct in that this will change all the characters to the same single random character as you type. Showing different random characters as you type is not the way you are supposed to use the Entry widget. You can try something like:
def key(event):
global real_password
global garbage
current_len = len(v.get())
if event.char and event.char in string.ascii_letters:
real_password += event.char
garbage += random_char()
garbage = garbage[:current_len]
v.set(garbage)
v = tk.StringVar()
real_password = ""
garbage = ""
entry = tk.Entry (self, textvariable = v)
entry.pack()
entry.bind("<KeyRelease>", key)
Of course there are lots of limitations, the last character typed is changed when the key is released not when is pressed, so you have to type fast :) , there is not control over the cursor movement keys etc. But anyway it was fun trying.