I am trying to set the text of an Entry widget using a button in a GUI using the tkinter module.
This GUI is to help me classify thousands of words into five categories. Each of the categories has a button. I was hoping that using a button would significantly speed me up and I want to double check the words every time otherwise I would just use the button and have the GUI process the current word and bring the next word.
The command buttons for some reason are not behaving like I want them to. This is an example:
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
v = tk.StringVar()
def setText(word):
v.set(word)
a = ttk.Button(win, text="plant", command=setText("plant"))
a.pack()
b = ttk.Button(win, text="animal", command=setText("animal"))
b.pack()
c = ttk.Entry(win, textvariable=v)
c.pack()
win.mainloop()
So far, when I am able to compile, the click does nothing.
You might want to use insert method. You can find the documentation for the Tkinter Entry Widget here.
This script inserts a text into Entry. The inserted text can be changed in command parameter of the Button.
from tkinter import *
def set_text(text):
e.delete(0,END)
e.insert(0,text)
return
win = Tk()
e = Entry(win,width=10)
e.pack()
b1 = Button(win,text="animal",command=lambda:set_text("animal"))
b1.pack()
b2 = Button(win,text="plant",command=lambda:set_text("plant"))
b2.pack()
win.mainloop()
If you use a "text variable" tk.StringVar(), you can just set() that.
No need to use the Entry delete and insert. Moreover, those functions don't work when the Entry is disabled or readonly! The text variable method, however, does work under those conditions as well.
import Tkinter as tk
...
entry_text = tk.StringVar()
entry = tk.Entry( master, textvariable=entry_text )
entry_text.set( "Hello World" )
You can choose between the following two methods to set the text of an Entry widget. For the examples, assume imported library import tkinter as tk and root window root = tk.Tk().
Method A: Use delete and insert
Widget Entry provides methods delete and insert which can be used to set its text to a new value. First, you'll have to remove any former, old text from Entry with delete which needs the positions where to start and end the deletion. Since we want to remove the full old text, we start at 0 and end at wherever the end currently is. We can access that value via END. Afterwards the Entry is empty and we can insert new_text at position 0.
entry = tk.Entry(root)
new_text = "Example text"
entry.delete(0, tk.END)
entry.insert(0, new_text)
Method B: Use StringVar
You have to create a new StringVar object called entry_text in the example. Also, your Entry widget has to be created with keyword argument textvariable. Afterwards, every time you change entry_text with set, the text will automatically show up in the Entry widget.
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
new_text = "Example text"
entry_text.set(new_text)
Complete working example which contains both methods to set the text via Button:
This window
is generated by the following complete working example:
import tkinter as tk
def button_1_click():
# define new text (you can modify this to your needs!)
new_text = "Button 1 clicked!"
# delete content from position 0 to end
entry.delete(0, tk.END)
# insert new_text at position 0
entry.insert(0, new_text)
def button_2_click():
# define new text (you can modify this to your needs!)
new_text = "Button 2 clicked!"
# set connected text variable to new_text
entry_text.set(new_text)
root = tk.Tk()
entry_text = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_text)
button_1 = tk.Button(root, text="Button 1", command=button_1_click)
button_2 = tk.Button(root, text="Button 2", command=button_2_click)
entry.pack(side=tk.TOP)
button_1.pack(side=tk.LEFT)
button_2.pack(side=tk.LEFT)
root.mainloop()
Your problem is that when you do this:
a = Button(win, text="plant", command=setText("plant"))
it tries to evaluate what to set for the command. So when instantiating the Button object, it actually calls setText("plant"). This is wrong, because you don't want to call the setText method yet. Then it takes the return value of this call (which is None), and sets that to the command of the button. That's why clicking the button does nothing, because there is no command set for it.
If you do as Milan Skála suggested and use a lambda expression instead, then your code will work (assuming you fix the indentation and the parentheses).
Instead of command=setText("plant"), which actually calls the function, you can set command=lambda:setText("plant") which specifies something which will call the function later, when you want to call it.
If you don't like lambdas, another (slightly more cumbersome) way would be to define a pair of functions to do what you want:
def set_to_plant():
set_text("plant")
def set_to_animal():
set_text("animal")
and then you can use command=set_to_plant and command=set_to_animal - these will evaluate to the corresponding functions, but are definitely not the same as command=set_to_plant() which would of course evaluate to None again.
One way would be to inherit a new class,EntryWithSet, and defining set method that makes use of delete and insert methods of the Entry class objects:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
class EntryWithSet(tk.Entry):
"""
A subclass to Entry that has a set method for setting its text to
a given string, much like a Variable class.
"""
def __init__(self, master, *args, **kwargs):
tk.Entry.__init__(self, master, *args, **kwargs)
def set(self, text_string):
"""
Sets the object's text to text_string.
"""
self.delete('0', 'end')
self.insert('0', text_string)
def on_button_click():
import random, string
rand_str = ''.join(random.choice(string.ascii_letters) for _ in range(19))
entry.set(rand_str)
if __name__ == '__main__':
root = tk.Tk()
entry = EntryWithSet(root)
entry.pack()
tk.Button(root, text="Set", command=on_button_click).pack()
tk.mainloop()
e= StringVar()
def fileDialog():
filename = filedialog.askopenfilename(initialdir = "/",title = "Select A
File",filetype = (("jpeg","*.jpg"),("png","*.png"),("All Files","*.*")))
e.set(filename)
la = Entry(self,textvariable = e,width = 30).place(x=230,y=330)
butt=Button(self,text="Browse",width=7,command=fileDialog).place(x=430,y=328)
Related
I want to write a program where after a user enters text and clicks a button, the text becomes a label and the button text is changed. My code is:
# Imports
import os, sys
import tkinter
"""
Tkinter program 1
text box + button + label
"""
# Button Entry
def enter(inputtedinfo, randvar, EnterMessage):
randvar = inputtedinfo.get()
EnterMessage = "Submitted!"
# Main Function
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = ""
EnterMessage = "Enter"
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, text = randvar)
userEntry = tkinter.Entry(something, textvariable = inputtedinfo)
userButton = tkinter.Button(something, text = EnterMessage, command = enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()
sys.exit(0)
if(__name__ == "__main__"):
main()
The user input works, but clicking the button does nothing despite the fact that it is supposed to change the variables for the button and label displays. Did I mess up somewhere?
The command argument takes the name of a function. If you write the complete call with arguments, it's not the name of the function but whatever is returned by this exact function call. So, your button will not work. It will have the command None.
In order to do what you want to do, you have to make the StringVar()s accessible to the function you are calling. So, you can both get the contents of the entry and change the values of the button and the label. To do this, best add the string variables and the widgets as attributes to the toplevel you already created (something). So, they stay available to all functions and you can get and change information:
# Button Entry
def enter():
something.randvar.set(something.inputtedinfo.get())
something.userButton["text"] = "Submitted!"
# Main Function
def main():
global something
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
something.randvar = tkinter.StringVar()
something.randvar.set("")
EnterMessage = "Enter"
something.inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable = something.randvar)
something.userEntry = tkinter.Entry(something, textvariable = something.inputtedinfo)
something.userButton = tkinter.Button(something, text = EnterMessage, command = enter)
something.userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
something.userButton.grid(row=0,column=2)
something.mainloop()
if(__name__ == "__main__"):
main()
There are few issues in your code:
assign string to textvariable, should use StringVar instead
command=enter(...) will execute enter(...) immediately and then assign None to command option, should use lambda instead
updating strings inside enter() does not automatically update the label and the button, should use .set() on the StirngVar instead
Below is modified code:
def enter(inputtedinfo, randvar, EnterMessage):
# used .set() to update StringVar
randvar.set(inputtedinfo.get())
EnterMessage.set("Submitted!")
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = tkinter.StringVar() # changed to StringVar()
EnterMessage = tkinter.StringVar(value="Enter") # changed to StringVar()
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable=randvar) # used textvariable instead of text option
userEntry = tkinter.Entry(something, textvariable=inputtedinfo)
userButton = tkinter.Button(something, textvariable=EnterMessage, command=lambda: enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()
I'm trying to make a tkinter GUI with a certain amount of buttons and entry fields that is specified by the user, so in this code below for example if the user specifies the variable number to be 3 I want there to be 3 entry boxes and , and I want to set it up so that if I type a value into the first field and click the button next to it, I want that button to read the value from the entry field next to it. I also need to assign each entry field to a variable that will be created through the same iterative loop. However, I'm having difficulty especially in regards to mapping the buttons to the entry fields, as I always seem to run up against an error with the text "AttributeError: 'NoneType' object has no attribute 'get'". Would anyone be able to either fix my code or help me find an alternative solution to the problem? Sorry if the description's a bit confusing.
This is different from just a problem of just getting the contents of the entry widget as I need it to create a certain amount of entry widgets and buttons using iteration, and the question that my question has been marked a duplicate of doesn't explain how to iteratively map each entry field to each button. For example, if I enter 3 as the variable, I need the program to create entry field 1, entry field 2 and entry field 3 and button 1, button 2 and button 3 and then map each button to its respective entry field using iteration.
I've tried using dictionaries, but this doesn't seem to help much.
import tkinter as tk
root = tk.Tk()
number = 3
d={}
def callBack():
print(d["E{0}".format(i)].get())
return
for i in range(0,number):
d["E{0}".format(i)] = tk.Entry(root)
d["E{0}".format(i)].grid(row=i, column=0)
d["B{0}".format(i)] = tk.Button(root, text="test", command=callBack)
d["B{0}".format(i)].grid(row=i, column=1)
The solution to "'NoneType' object has no attribute 'get'" has been asked probably a hundred times on this site, and the answer is always the same.
In python, when you do x = y().z(), x will be given the value of z(). In the case of x=tk.Entry(...).grid(...), x will be None because grid(...) always returns None. The solution is to always call grid or pack or place separate from when you create a widget.
You also claim you are having problems with dictionaries, but I don't see any problem in your code other than you are making it more difficult than necessary. You can directly use i as an index without having to build up a string for the index. If you need to keep track of both buttons and entries, I recommend two variables rather than one.
Part of the problem may also have to do with the fact you're trying to do something very odd in your command. You're trying to call the get method of the entry, but that's pointless since it simply returns a value that gets thrown away. In almost all cases, the correct solution is to write a proper function rather than trying to squeeze functionality into a lambda.
Example:
def handle_click(i):
entry = entries[i]
print("the value is {}".format(entry.get()))
buttons = {}
entries = {}
for i in range(0,number):
entry = tk.Entry(root)
button = tk.Button(root, text="test", command=lambda i=i: handle_click(i))
buttons[i] = button
entries[i] = entry
entry.grid(row=i, column=0)
button.grid(row=i, column=1)
You need to save the Entry and Button before calling grid:
import tkinter as tk
number = 3
root = tk.Tk()
def get_on_click(widget_dict, entry_name):
def on_click():
result = widget_dict[entry_name].get()
print("%s = %s" % (entry_name, result))
return result
return on_click
d = dict()
for i in range(0, number):
entry_name = "E{0}".format(i)
button_name = "B{0}".format(i)
print(entry_name, button_name)
d[entry_name] = tk.Entry(root)
d[entry_name].grid(row=i, column=0)
d[button_name] = tk.Button(root, text="test", command=get_on_click(d, entry_name))
d[button_name].grid(row=i, column=1)
root.mainloop()
This should help you get started.
In your comment, you ask how to save the value in the Entry. I would create a class to handle everything:
import tkinter as tk
number = 3
root = tk.Tk()
class EntryButton(object):
def __init__(self, root, number):
self.number = number
self.entry = tk.Entry(root)
self.button = tk.Button(root, text="test", command=self.on_click)
self.entry.grid(row=number, column=0)
self.button.grid(row=number, column=1)
self.value = None
def on_click(self):
self.value = self.entry.get()
storage = dict()
for i in range(0, number):
storage[i] = EntryButton(root, i)
root.mainloop()
for i in range(0, number):
value = storage[i].value
print(f"storage[{i}] = {value}")
As you can see, this eliminates a lot of extra work.
For get text from entry
Entry.get("1.0", "end-1c")
# 1.0 for get first line.
# end-1c for if last letter space, this deletes it.
More info
I have created a textbox in a tkinter window which is called by a button.
I want to get the value of textbox in another window on clicking a button.
But when i print the value on console, it prints an empty string instead of the value entered in textbox.
from tkinter import *
def click():
print(id.get())
def submit():
win2=Tk()
Label1=Label(win2,text='Id').pack()
global id
id=StringVar()
textbox=Entry(win2,textvariable=id).pack()
btn2=Button(win2,text='Click',command=click).pack()
win1=Tk()
btn1=Button(win1,text='Submit',command=submit).pack()
Please check my code.
You might want to try something like this:
import tkinter as tk
class MainPage(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.btn1 = tk.Button(self, text='Submit', command=self.submit)
self.btn1.pack()
def submit(self):
self.win2 = tk.Tk()
self.Label1 = tk.Label(self.win2, text='Id')
self.Label1.pack()
self.id = tk.StringVar()
self.textbox = tk.Entry(self.win2, textvariable=self.id)
self.textbox.pack()
self.btn2 = tk.Button(self.win2, text='Click', command=self.click)
self.btn2.pack()
def click(self):
print(self.textbox.get())
interface = MainPage()
interface.mainloop()
Edit: The main difference with the original code is that the print calls the Entry widget, rather than the StringVar. If you would like to set the StringVar to the text inserted in the textbox, then you would have to call at some point
self.id.set(self.textbox.get())
then the self.id.get() would return what you expect.
From previous answer by #neko,i got an answer similar to my code:
from tkinter import *
def click():
print(textbox.get())
def submit():
win2=Tk()
Label1=Label(win2,text='Id').pack()
global textbox
id=StringVar()
textbox=Entry(win2,textvariable=id)
textbox.pack()
btn2=Button(win2,text='Click',command=click).pack()
win1=Tk()
btn1=Button(win1,text='Submit',command=submit).pack()
The problem was that i was using id.get(), which is not working in multiple tkinter window. So now i used textbox.get() which is working fine here.
Have you tried to do this? :
print(id.get("1.0",'end-1c'))
The '1.0' part tells the method to start reading at the first line (starting with the first character, because of the 0). The 'end-1c' argument tells the method to read until it reaches the end of the text. Note that 1c removes 1 character from the text, because if you used just 'end', it would add a new line character at the end of your text.
I would like to to use tkinter and checkboxes to make a selection of files in a directory and save those files names in a list when I press a button:
import speech_recognition as sr
import playsound
import os
import glob
import unidecode
import pickle
import random
import tkinter
from tkinter.constants import *
ldv = os.listdir("D:/FFOutput/")
i = 0
ldv1 = []
while i < len(ldv):
ldv1.append(unidecode.unidecode(ldv[i]))
i += 1
print(ldv1)
root = tkinter.Tk()
frame = tkinter.Frame(root, relief=RIDGE, borderwidth=10)
frame.pack(fill=BOTH, expand=1)
label = tkinter.Label(frame, text="choose file(s)")
label.pack(fill=X, expand=1)
a = 0
while a < len(ldv1):
bouton = tkinter.Checkbutton(root, text=ldv1[a], command=print(ldv1[a]))
a += 1
bouton.pack()
button = tkinter.Button(frame, text="Exit", command=root.destroy)
button.pack(side=BOTTOM)
lr = []
buttonregister = tkinter.Button(root, text="Register checked files names in list lr and close tk")
buttonregister.pack(side=BOTTOM)
print(lr)
root.mainloop()
When I click on buttonregister, I would like to append the files names into the list lr and close the frame. Example
In that example, I wish to print(lr) "['alors soyez pret.mp3','c'est bien.mp3']" in the shell when I click the button.
For a Checkbutton to hold a value, a BoolVar (or any other var) from tkinter must be used. This is usually quite tedious as you would have to make a variable for every Checkbutton. This can be avoided by sub-classing the Checkbutton and adding storage for a variable. Since you require the text as well, we can also use the class to store the text value.
Replacing the Checkbuttons with the class below will do this.
class CheckBox(tkinter.Checkbutton):
boxes = [] # Storage for all buttons
def __init__(self, master=None, **options):
tkinter.Checkbutton.__init__(self, master, options) # Subclass checkbutton to keep other methods
self.boxes.append(self)
self.var = tkinter.BooleanVar() # var used to store checkbox state (on/off)
self.text = self.cget('text') # store the text for later
self.configure(variable=self.var) # set the checkbox to use our var
We would then use the class to make the buttons as follows:
a=0
while a<len(ldv1):
bouton=CheckBox(tk, text=ldv1[a], command=print(ldv1[a])) # Replace Checkbutton
a=a+1
bouton.pack()
And finally, to get the values when the window is closed, you can loop through CheckBox.buttons for each button's value. You would need to add this after the mainloop or add it to a function.
for box in CheckBox.boxes:
if box.var.get(): # Checks if the button is ticked
lr.append(box.text)
print(lr)
I am trying to make two entry boxes and an 'OK' button that processes both entries:
I don't understand the init() function or the reason 'self' has to be include in doing this.
I want to access the entries outside this class and mainloop. The entries should be string type.
from tkinter import *
root = Tk()
root.geometry("550x145+500+300")
class myDose:
def __init__(self):
l1 = Label(text="Enter the prescription dose to three (3) decimal places [cGy]").pack()
e1 = Entry(root)
e1.pack()
l2 = Label(text="Save new excel file as...").pack()
e2 = Entry(root)
e2.pack()
l3 = Label(text="click 'OK', and then close window.").pack()
b = Button(root, text="OK", command=submit)
b.pack()
def submit(self):
print(e1.get())
print(e2.get())
D = myDose()
root.mainloop()
#I want to access the entries, e1 and e2, outside the mainloop
print(D.e1)
print(D.e2)
The problem is that mainloop doesn't exit until the root window is destroyed. Once the root window is destroyed you can no longer access the widgets inside the root window. You can, however, access non-widget attributes of the object.
If that is what you want -- to destroy the window and have access to the values in the widget -- you need to save the values before destroying the window.
For example, in your submit you could save the values like this:
def submit(self):
self.data = {"e1": self.e1.get(), "e2": self.e2.get()}
Once the window is destroyed, you still have a reference to the application object, so you can still access the non-widget attributes of the class:
...
D = myDose(root)
root.mainloop()
print D.data["e1"]
print D.data["e2"]
Based on comments to the original question, you mention that later in your code you'll need to use askopenfilename. if that is the case, you should reconsider the need to run code after mainloop. Tkinter is designed to have a root window created exactly once, and for the program to exit immediately after mainloop exits.
You can write programs however you want, but I think your code will be easier to maintain and modify over the long run if you stick to normal design patterns. Move your "real" code into a method of the app class and that will eliminate all of the problems associated with widgets being destroyed.
Your approach is fundamentally flawed. mainloop() does what it says: starts a loop. The lines that follow it will not be executed until that loop is over - meaning, until your application has closed. Since you already have a class with Tkinter stuff in it, just finish the move to an OO approach:
from tkinter import *
class myDose:
def __init__(self, root):
root.geometry("550x145+500+300")
self.l1 = Label(root, text="Enter the prescription dose to three (3) decimal places [cGy]")
self.l1.pack()
self.e1 = Entry(root)
self.e1.pack()
self.l2 = Label(root, text="Save new excel file as...")
self.l2.pack()
self.e2 = Entry(root)
self.e2.pack()
self.l3 = Label(root, text="click 'OK', and then close window.")
self.l3.pack()
self.b = Button(root, text="OK", command=self.submit)
self.b.pack()
def submit(self):
print(self.e1.get())
print(self.e2.get())
root = Tk()
D = myDose(root)
root.mainloop()
I recommend looking more thoroughly into how fundamental concepts like objects and classes work before creating a full GUI application.
Here's a quick fix.
The problem with both your version and TigerhawkT3's modified version is that when you close the window the Entry widgets are no longer valid, so you need to save the data before closing the window. One way to do that is to attach Tkinter StringVars to the Entry widgets. The values in the StringVars will be available after the window is closed.
import tkinter as tk
class myDose:
def __init__(self, root):
root.geometry("550x145+500+300")
l = tk.Label(root, text="Enter the prescription dose to three (3) decimal places [cGy]")
l.pack()
self.dosage = tk.StringVar()
e = tk.Entry(root, textvariable=self.dosage)
e.pack()
l = tk.Label(root, text="Save new excel file as...")
l.pack()
self.savename = tk.StringVar()
e = tk.Entry(root, textvariable=self.savename)
e.pack()
l = tk.Label(root, text="Enter the data and then close window.")
l.pack()
root.mainloop()
D = myDose(tk.Tk())
print(D.dosage.get())
print(D.savename.get())
Note that I've changed the import statement. It's much cleaner to not do
from tkinter import *
as that clutters your namespace with over 100 names, leading to potential name collisions.
The __init__() function is a method of the myDose class. A class's __init__() method is called when an instance of that class is created. In your code that happens when you do
D = myDose()
In my version it's when this line is executed:
D = myDose(tk.Tk())
which first creates the Tkinter root window which is then passed to myDose; that root window is then available to my __init__() as its root argument.
The name self is used inside the class to refer to the current class instance. Outside the class we need to use the actual name we assigned the instance to, which in this case is D.