How to refresh tkinter entry value? I get a 1-click lag - python

I built an interface where the user fills a hierarchical form. Past values are displayed in a ttk.Treeview.
I allow the user to edit previous values by clicking on the tree. The value gets filled on the form where it can be edited and overwriten.
The problem: the value I insert on the Entry widget is only displayed the next time the user clicks it, so that it is always 1 click lagging. Please run my sample code to get a better understanding. It gets confusing because if the user clicks a value and then another, it will display the previously clicked value.
It must have something to do with the event handling routine in tkinter, but I could not find and answer.
How can I get rid of this lag?
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
def cb_clique(event):
item = tree.selection()[0]
entry1.delete(0, "end")
entry1.insert(0, item)
entry1 = tk.Entry(root, width=15)
entry1.grid(row=1,column=1)
tree = ttk.Treeview(root)
tree.bind("<Button-1>", cb_clique)
tree["columns"]=("valor")
tree.column("valor", width=200 )
tree.heading("valor", text="Valor")
tree.grid(row=3, column = 1, columnspan = 4)
tree.insert("", "end", iid = "Will display position",text = "Click me",
values=("a","b"))
tree.insert("", "end", iid = "Use position to get info",
text = "Click me", values=("a","b"))
root.mainloop()

Looks like the <Button-1> event triggers before the window notices that the selection has changed, so selection() returns the thing that was selected before your click. Try changing the event binding to <<TreeViewSelect>>.
tree.bind("<<TreeviewSelect>>", cb_clique)

Related

Radio Button and Checkbox don't update variable value

I have a GUI that uses a class that calls a function that provides a popup window.
The popup box has a button that gets a directory from the user using askdirectory() from tkinter.filedialog, a pair of radio buttons(RadioG, RadioY), and checkbox(state_checkbox) that I want to update the two different variables(driveletter and statevalue) . I've set the drive letter to "G:" and that value is held correctly, but doesn't update if I select the "Y:" Radio button. I have the IntVar.set(1), but that checkbox does not populate as checked and checking/unchecking it keeps the value at 1 and does not update it to 0 when unchecked.
Any advice on what I'm missing to keep my values from updating is appreciated.
relevant section of code below:
class preferences(Preferences):
def save_preferences(self):
# Used to reset the folder location desired by the user
self.prefdialog = tk.Tk()
self.prefdialog.title('Set Preferences')
self.driveletter = tk.StringVar()
self.driveletter.set("G:")
self.statevalue = tk.IntVar()
self.statevalue.set(1)
self.fpath = '\\Desktop'
self.RadioG = tk.Radiobutton(self.prefdialog, text="G:", variable=self.driveletter,
value="G:", command=lambda: print(self.driveletter.get()))
self.RadioG.grid(row=3,column=0,sticky=tk.W,pady=4)
self.RadioY = tk.Radiobutton(self.prefdialog, text="Y:", variable=self.driveletter,
value="Y:", command=lambda: print(self.driveletter.get()))
self.RadioY.grid(row=3,column=1,sticky=tk.W,pady=4)
self.state_checkbox = tk.Checkbutton(
self.prefdialog, text="Check to use state folders",
variable=self.statevalue, command=lambda: print(self.statevalue.get()))
self.state_checkbox.grid(row=4,column=0,sticky=tk.W,pady=4)
self.prefdialog.mainloop()
For posterity: Looks like my issue was using Tk() twice. Per Bryan since I'm trying to create a popup window, I should be calling Toplevel(), not a second instance of Tk(). I also definitely called mainloop() twice, once for my main window and once for this popup window.

tkinter entry() not returning string

I have several entry boxes made with tk: Entry()
I need to put what the user enters into a variable, which I do as such (as I have found online):
window = Tk()
#make entry and turn it into stringvar
entry1string = tk.StringVar
entry_1 = Entry(window,textvariable=entry1string)
#retrieve it into a variable
retrieved = entry1string.get()
This gives the following error:
AttributeError: 'str' object has no attribute 'get'
How do I get the string/value entered into the entry box by the user into a variable? The code seems to be just how every example I've found is, I don't see why it's giving me that error.
Refer here to know about Entry widgets in Tkinter.
What you can do is create a button, on which upon clicking, the data entered in the Entry box will be retrieved.
...
entry1string = tk.StringVar()
entry_1 = Entry(window,textvariable=entry1string).pack()
def retrieveData():
#retrieve it into a variable
retrieved = entry1string.get()
#print the data
print(retrieved)
#Or output the data on the window in a Label :
Label(window, text=retrieved).pack()
button1 = Button(window, text="PRINT", command=retrieveData).pack()
There needs to be a function to call when the button is clicked. you can print the data in the command line or even output on the GUI window, that's your choice.
Read the documentation to know more.

OptionMenu widget not updating when StringVar is changed until hovered

I need a menu that can respond to items being clicked by running code then switch the text back to a default text.
Currently, my implementation works but the default text is only displayed when the cursor hovers over the menu after clicking.
I have searched but I could not find anything related to this problem, although maybe that is because I am unsure as to what exactly is causing this.
Here is the code to reproduce this behaviour:
from tkinter import *
root = Tk()
default_text = 'select an item'
def thing_selected(self, *args):
#other stuff happens here
var.set(default_text)
var = StringVar(root)
var.set(default_text)
var.trace('w', thing_selected)
menu = OptionMenu(root, var, *['Pizza','Lasagne','Fries','Fish'])
menu.pack()
root.mainloop()
Here is a gif representing the outcome:
I would expect the text at the top to be updated instantaneously, but it only updates when the cursor has hovered over the widget
I am looking for some way to trigger a hover event on the widget or I am open to suggestions for any other methods of accomplishing this.
You could take a different route and use the command attribute of the OptionMenu:
import tkinter as tk
root = tk.Tk()
default_text = 'select an item'
def thing_selected(selected):
#other stuff happens here
print(var.get())
var.set(default_text)
print(var.get())
var = tk.StringVar()
var.set(default_text)
options = ['Pizza','Lasagne','Fries','Fish']
menu = tk.OptionMenu(root, var, *options, command = thing_selected)
menu.pack()
root.mainloop()

Tkinter Button Function Control by MessageBox

The code below shows part of my program and the issue im facing.
def checkAnswer():
mainAnswer = answer01.get()
if len(mainAnswer) == 0:
messagebox.showwarning(message='Please answer the question!')
return
if int(mainAnswer) != answer:
messagebox.showwarning(message='Incorrect! The correct answer is: ' + str(answer))
else:
nxtquest.config(state=NORMAL)
messagebox.showinfo(message='Correct! :)')question01 = Label(easy)
question01.grid(row=2, column=0)
answer01 = Entry(easy)
answer01.grid(row=3, column=2)
answer01.bind('<Return>', func=lambda e:checkAnswer())
start = Button(easy, text = "Start!", command=ask, bg='green', fg='white')
start.grid(row=3, column=3)
nxtquest = Button(easy, text='Next Question', command=ask)
nxtquest.grid(row=5, column=2)
checkbut = Button(easy, text='Check', command=checkAnswer)
checkbut.grid(row=4, column=2)
#check button and answer01 enabled after start pressed
launch = 1
if launch == 1:
answer01.config(state=DISABLED)
checkbut.config(state=DISABLED)
nxtquest.config(state=DISABLED)
The issue which im struggling here is that whenever i run the program everything is okay. When the window is displayed checkbut, nxtquest and label answer01 are greyed out (disabled).
The start button enables only checkbut and answer01 and then is destroyed. (So far so good)
So nxtquest will enable once the input is correct as seen in the
else:
nxtquest.config(state=NORMAL)
But when I reach another question the nxtquest button is already enabled, this is the problem!
How could I make it so the button will enable itself only after the warning message box is displayed?
Could I ask for some help with this and possibly suggestions if you see any rookie mistakes ?
Whilst I don't know of any way you could do this with a messagebox widget (although I'm sure there's an event you could use as the trigger) you can most certainly do this by substituting the messagebox with a Toplevel widget and using .protocol("WM_DELETE_WINDOW", callback()) on the widget.
This would mean that whenever the Toplevel widget was "closed" we would actually be overwriting the action taken when the event was raised and would manually handle the closing of the widget as well as whatever else we wanted it to do.
This would look something like the below:
from tkinter import *
root = Tk()
button = Button(root, text="Ok", state="disabled")
button.pack()
top = Toplevel(root)
def close():
top.destroy()
button.configure(state="active")
top.protocol("WM_DELETE_WINDOW", close)
root.mainloop()
If you close the Toplevel widget you will see that the button is now active instead. This would equally work if we added a Button to the Toplevel widget which called the function close().

Python chat box whith Tkinter

I'm trying to do a chat box with Tkinter, but for send the text I want to press on Return key and not to click on a button. When Ï run this code, I can wrote in the Entry section, but when I press the Return key, nothing append. Thanks for your help.
(Sorry for bad English)
from tkinter import *
window = Tk()
input_user = StringVar()
input_field = Entry(window, text=input_user)
input_field.pack()
def Enter_pressed(event):
"""Took the current string in the Entry field."""
input_get = input_field.get()
print(input_get)
frame = Frame(window, width=100, height=100)
frame.bind("<Return>", Enter_pressed)
frame.pack()
window.mainloop()
you are binding to the wrong widget, when you press the return key that event is sent to the entry widget not the frame, so change
frame.bind("<Return>", Enter_pressed)
to:
input_field.bind("<Return>", Enter_pressed)
and if you want to prevent other bindings from firing you can add
return "break"
to the end of your function

Categories

Resources