I am learning to do a GUI using tkinter and I create an integer entry which is working fine except that whenever I run my program the number 0 is already put in the entry, is there anyway to remove it and just have nothing instead? It doesn't do that with strings
I checked answers like this one: https://stackoverflow.com/a/39879154/13061992
but I didn't work (the user already has said that is only for strings but I gave it a shot anyway)
To explain more, I am creating a text box using the following:
tries_var = tk.IntVar()
tries_label = tk.Label(root, text='Number Of Tries', font=('calibre', 10, 'bold'))
tries_entry = tk.Entry(root, textvariable=tries_var, font=('calibre', 10, 'normal'))
tries_label.grid(row=2, column=0)
tries_entry.grid(row=2, column=1)
When I run the program I have 0 written by default, like this:
I want to get rid of this and instead have the box to be empty, any help would be appreciated.
The reason for this is, you are using tk.IntVar() which by default will put a 0 onto the Entry widget. To get rid of this, change tk.IntVar() to tk.StringVar().
tries_var = tk.StringVar()
Though keep in mind, tk.IntVar.get() will return int, now that you are using tk.StringVar, you might need int(tk.StringVar.get()), given that your input is completely numbers.
print(int(tries_var.get()) # Given that, your input is entirely numbers
Try this:
import tkinter as tk
def only_allow_numbers(event):
char = event.char.lower()
if (event.state & 4) >> 2:
# If ctrl is also pressed:
# Check and handle Ctrl+c, Ctrl+v, Ctrl+x
# For now I will just allow it
return None
if char.isprintable() and (not event.char.isdigit()):
return "break"
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
entry.bind("<Key>", only_allow_numbers)
root.mainloop()
It uses bindings. When you return "break" from a binding that key is ignored (isn't inserted in the entry.)
Related
I am struggling to find the correct search terms to find an answer to my question. I have a program that is working, but I am trying to minimize the number of clicks required to make it work. I have a tkinter interface that takes a 7 digit input and looks it up in a spreadsheet and then displays some relevant information. Right now, the user scans a barcode which puts 7 digits in an entry box like this:
workorder = tk.Entry(root)
workorder.focus()
canvas1.create_window(175, 800, window=workorder)
Then, the user has to press the enter key to start the function that uses those 7 digits:
def f1(event):
getPartNumber()
root.bind('<Return>', f1)
I am trying to find a way to start the function automatically so a keyboard is not required. I was trying to do something like:
if len(str(workorder)) == 7:
getPartNumber()
However, it seems like this only works to check the length of an entry after the entry has been made. Is it possible to check on the state of the entry box before the entry is made?
You can associate a variable with the entry widget, and then set a trace on the variable. The trace will call a function whenever the data in the widget changes. Inside the trace you can examine the length of the data and call a function when the length is 7.
Here's an example that updates a label when you enter 7 characters:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar()
entry = tk.Entry(root, textvariable=var)
label = tk.Label(root, text="", anchor="w")
entry.pack(side="top", fill="x")
label.pack(side="top", fill="x")
def boom(*args):
if len(var.get()) == 7:
label.configure(text="Boom!")
var.trace("w", boom)
root.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
How could I pad the entry widget so it does not start writing right at the border of the widget? Visually, having a little space from the entry widget border.
My progress:
entry_widget.bind('<FocusIn>', lambda f: entry_widget.insert(0, ' '))
That adds an empty space when the user clicks the widget but there are multiple issue with this solution:
When clicking out and clicking back in, it will add another space on top of the empty space or any text the user had filled in. I considered clearing the entry widget on FocusOut but that would also clear all the text that the user might have written.
The user can delete the inserted space
when getting the content, there is the additional space at the beginning. Though this is a small problem which can be solved by removing the first character of the content.
And there might be more issues which I did not account for.
I think the way where my code is heading is bad, therefore I am asking if anyone has any idea how to 'properly' pad the entry widget?
I'm not aware a native way of adjusting the Entry's padding, but here's one way to get something like it. Make the entry's border invisible by giving it a FLAT style, and embed the entry in a Frame that acts as the entry's border. Then you can specify the padding by adjusting the entry's borderwidth. Example:
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, borderwidth=5, relief=tk.SUNKEN)
frame.pack()
entry = tk.Entry(frame, borderwidth=15, relief=tk.FLAT)
entry.pack()
root.mainloop()
Result:
I had this problem, and after a bit of digging, I found out you can internally pad ttk's Entry widget:
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
ttk.Style().configure('pad.TEntry', padding='5 1 1 1')
ent = ttk.Entry(root, style='pad.TEntry')
ent.pack()
root.mainloop()
Where '5 1 1 1' is 'ipad_left ipad_top ipad_right ipad_bottom' in pixels (default is '1 1 1 1'). Using the 'TEntry' argument does change the style of every ttk.Entry however, so if you wanted to avoid that you could create a new style:
ttk.Style().configure('pad.TEntry', padding='5 1 1 1')
ent1 = ttk.Entry(root, style='pad.TEntry')
ent2 = ttk.Entry(root)
Where ent1 would have the extra padding and ent2 would not.
I figured out the 'padding' option existed by running print(Style().configure('TEntry')) which returns all the style options for TEntry (in this case it's just 'padding'). This brings up a problem though; ttk's widgets often don't have a lot of customization options readily available (TEntry is missing background, borderwidth, relief, etc) meaning you'd have to 'create' them. See these links for more information: ttk widgets || Using and customizing ttk styles || ttk.Entry information || (example) adding fieldbackground to ttk.Entry
using tkinter 8.6U
entry.config(padx = 15)
Here is a simple example of how you can force a space to start with and then with a bind keep the space while the user types.
import tkinter as tk
root = tk.Tk()
def check_for_space(event):
if entry.get() == "":
entry.insert(0, " ")
elif entry.get()[0] != " ":
entry.insert(0, " ")
else:
pass
entry = tk.Entry(root)
entry.insert(0, " ")
entry.pack()
entry.bind("<Key>", check_for_space)
root.mainloop()
Try this Custom made entry
from tkinter import Entry,Tk
class MyEntry(Entry):
def __init__(self,root,placeholder,*args,**kw):
self.root = root
self.placeholder = placeholder
Entry.__init__(self,self.root,*args,**kw)
self.add()
self.bind("<FocusIn>",self.focusin)
self.bind("<FocusOut>",self.focusout)
def add(self):
if self.get()=='':
self.insert(0,self.placeholder)
def focusin(self,event=None):
if self.get()=='' or self.get()==self.placeholder:
self.delete(0,END)
def focusout(self,event=None):
if self.get()=='':
self.add()
root = Tk()
a = MyEntry(root,placeholder='<Name>')
a.pack()
b = MyEntry(root,placeholder='<Contact>')
b.pack()
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)
Here is the below code and I am having issues surrounding retrieving input from multiple values. I have been working on this issue for some time now without any suggest. I do appreciate the insight; however, I am at wits end. If someone could please provide a fix to my code I would be ever so grateful.
#!C:/Python27/python.exe
from Tkinter import *
import ImageTk, Image
root = Tk()
root.title('HADOUKEN!')
def retrieve_input(text,chkvar,v):
textarea_result = text.get()
checkbox_result = chkvar.get()
radiobutton_result = v.get()
root.destroy()
text = Text(root, height=16, width=40)
scroll = Scrollbar(root, command=text.yview)
text.configure(yscrollcommand=scroll.set)
text.grid(sticky=E)
scroll.grid(row=0,column=1,sticky='ns')
text.focus()
chkvar = IntVar()
chkvar.set(0)
c = Checkbutton(root, text="CaseIt", variable=chkvar)
c.grid(row=1,column=0,sticky=W)
v = ""
radio1 = Radiobutton(root, text="Src", variable=v, value=1)
radio1.grid(row=1,column=0)
radio1.focus()
radio2 = Radiobutton(root, text="Dst", variable=v, value=2)
radio2.grid(row=2,column=0)
b1 = Button(root, text="Submit", command=lambda: retrieve_input(text,chkvar,v))
b1.grid(row=1, column=2)
img = ImageTk.PhotoImage(Image.open("Hadoken.gif"))
panel = Label(root, image = img)
panel.grid(row=0, column=2)
root.mainloop()
print textarea_result
print checkbox_result
print radiobutton_result
You have several problems in your code, though most of them produce errors that should be self-explanatory. My suggestion is to start over with just a single widget, and get the logic working for that to reduce the number of things that could go wrong. Once you have that working, you can then add one widget at a time as you learn how to use that widget.
That being said, here are the most obvious errors that I spotted:
The first problem is that you are incorrectly calling the get method of a text widget. This method is documented to take two arguments -- a starting index and an ending index. Since tkinter always adds a trailing newline, you want to get everything from the start ("1.0"), to the end minus one character ("end-1c"). Thus, you should be getting the value in the text widget like this:
textarea_result = text.get("1.0", "end-1c")
The second problem is that retrieve_input seems to assume that v is a StringVar or IntVa since you are calling a get method on it. Given that you are using that variable with a radiobutton, that's what it should be. However, you created it as a normal variable, which of course doesn't have a get method. You should declare it as one of the special tkinter variables:
...
v = StringVar()
radio1 = Radiobutton(root, text="Src", variable=v, value=1)
...
The third problem is, retrieve_input is setting local variables. If you want to set the value of global variables (which I assume, since you are later trying to access them after the widget is destroyed), then you need to declare them as global:
def retrieve_input(text,chkvar,v):
global textarea_result, checkbox_result, radiobutton_result
...