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()
Related
I am placing labels on a Tab in Tkinter with a for loop. How can I identify in the event handler which label was clicked (or its loop index)? I guess it is functionally similar to a ListBox but without the formatting restrictions. I might want to put the labels in a circle or place them diagonally. I tried finding the coordinates of the label but these are available only if the tab is the first one visible or the tab is redrawn when made active. Also the x, y passed in the event handler is the x, y within the label which does not help to identify the label.
I could copy the label code about 10 times and and have about 10 event handlers. This would work but this is no longer 1970!
Perhaps I could bind a handler to the tab canvas and identify the label from its coordinates. The label would need to be on the first tab or the tab drawn when active.
Perhaps I could create a different event handler for each label by holding the event handlers in an array. I would need an event handler for each label. The code would need to change if the number of labels changed.
I am currently trying a label with ''. Would using buttons with command be easier?
What simple part of Python am I missing? I cannot be the first person to need this! Any help or advice would be appreciated.
You can save a reference to the label text for each label widget in a dict.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('+800+50')
notebook = ttk.Notebook(root, width=300, height=200, padding=[10,10,10,10])
notebook.pack()
tab_one = tk.Frame(notebook, bg='wheat')
notebook.add(tab_one, text='Cheese', padding=[10,10,10,10])
tab_two = tk.Frame(notebook, bg='mint cream')
notebook.add(tab_two, text='Misc.', padding=[10,10,10,10])
def clicked(event):
print('Clicked:', name_dict[event.widget])
# Populating Cheese tab with clickable Labels
name_list = ['Cheddar', 'Ilchester', 'Limburger']
name_dict = {}
for index, name in enumerate(name_list):
a = tk.Label(tab_one, width=10, text=name, bg='peru')
a.grid(row=index, column=0, padx=5, pady=5)
name_dict[a] = name # Save references in a dict
a.bind('<Button-1>', clicked)
tk.Label(tab_two, text='Just some text...', bg='powder blue').pack(anchor='nw')
root.mainloop()
Is this what you had in mind?
When you bind events, the function receives an object that includes a reference to the widget that received the event. In the following example, notice how it uses event.widget to refer to the widget that was clicked on.
import tkinter as tk
def update_label(event):
event.widget.configure(text="You clicked me")
root = tk.Tk()
for i in range(10):
l = tk.Label(root, text="Click me", width=20)
l.pack()
l.bind("<1>", update_label)
root.mainloop()
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.)
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)
Question
I'm trying to make a 'ttk Label' which a) is 20 pixels high by 300 pixels wide, b) is scrollable (in this case horizontally), and c) uses the simplest code possible within reason (except for the fact that the text and scrollbar are both within a frame*). I've found stackoverflow to be helpful in describing the processes I need to go through (put the label in a frame, put the frame in a canvas, put the scroll bar next to or underneath the canvas and 'bind' them together somehow), but despite looking at a fair few docs and stackoverflow questions, I can't figure out why my code isn't working properly. Please could someone a) update the code so that it satisfies the conditions above, and b) let me know if I've done anything unnecessary? Thanks
*the frame will be going in a project of mine, with text that is relevant
Current code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
myframe_outer = ttk.Frame(root)
mycanvas = tk.Canvas(myframe_outer, height=20, width=300)
myframe_inner = ttk.Frame(mycanvas)
myscroll = ttk.Scrollbar(myframe_outer, orient='horizontal', command=mycanvas.xview)
mycanvas.configure(xscrollcommand=myscroll.set)
myframe_outer.grid()
mycanvas.grid(row=1, sticky='nesw')
myscroll.grid(row=2, sticky='ew')
mycanvas.create_window(0, 0, window=myframe_inner, anchor='nw')
ttk.Label(myframe_inner, text='test ' * 30).grid(sticky='w')
root.mainloop()
Edit:
Current result
Answer
Use a readonly 'entry' widget - it looks the same as a label, and doesn't need to be put in a canvas.
Code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
mytext = tk.StringVar(value='test ' * 30)
myframe = ttk.Frame(root)
myentry = ttk.Entry(myframe, textvariable=mytext, state='readonly')
myscroll = ttk.Scrollbar(myframe, orient='horizontal', command=myentry.xview)
myentry.config(xscrollcommand=myscroll.set)
myframe.grid()
myentry.grid(row=1, sticky='ew')
myscroll.grid(row=2, sticky='ew')
root.mainloop()
Result
I have a list of tkinter widgets that I want to change dynamically.
How to delete the widgets from the window?
You can call pack_forget to remove a widget (if you use pack to add it to the window).
Example:
from tkinter import *
root = Tk()
b = Button(root, text="Delete me", command=lambda: b.pack_forget())
b.pack()
root.mainloop()
If you use pack_forget, you can later show the widget again calling pack again. If you want to permanently delete it, call destroy on the widget (then you won't be able to re-add it).
If you use the grid method, you can use grid_forget or grid_remove to hide the widget.
One way you can do it, is to get the slaves list from the frame that needs to be cleared and destroy or "hide" them according to your needs. To get a clear frame you can do it like this:
from tkinter import *
root = Tk()
def clear():
list = root.grid_slaves()
for l in list:
l.destroy()
Label(root,text='Hello World!').grid(row=0)
Button(root,text='Clear',command=clear).grid(row=1)
root.mainloop()
You should call grid_slaves(), pack_slaves() or slaves() depending on the method you used to add the widget to the frame.
You simply use the destroy() method to delete the specified widgets like this:
lbl = tk.Label(....)
btn = tk.Button(....., command=lambda: lbl.destroy())
Using this you can completely destroy the specific widgets.
You say that you have a list of widgets to change dynamically. Do you want to reuse and reconfigure existing widgets, or create all new widgets and delete the old ones? It affects the answer.
If you want to reuse the existing widgets, just reconfigure them. Or, if you just want to hide some of them temporarily, use the corresponding "forget" method to hide them. If you mapped them with pack() calls, you would hide with pack_forget() (or just forget()) calls. Accordingly, grid_forget() to hide gridded widgets, and place_forget() for placed widgets.
If you do not intend to reuse the widgets, you can destroy them with a straight destroy() call, like widget.destroy(), to free up resources.
clear_btm=Button(master,text="Clear") #this button will delete the widgets
clear_btm["command"] = lambda one = button1, two = text1, three = entry1: clear(one,two,three) #pass the widgets
clear_btm.pack()
def clear(*widgets):
for widget in widgets:
widget.destroy() #finally we are deleting the widgets.
Today I learn some simple and good click event handling using tkinter gui library in python3, which I would like to share inside this thread.
from tkinter import *
cnt = 0
def MsgClick(event):
children = root.winfo_children()
for child in children:
# print("type of widget is : " + str(type(child)))
if str(type(child)) == "<class 'tkinter.Message'>":
# print("Here Message widget will destroy")
child.destroy()
return
def MsgMotion(event):
print("Mouse position: (%s %s)" % (event.x, event.y))
return
def ButtonClick(event):
global cnt, msg
cnt += 1
msg = Message(root, text="you just clicked the button..." + str(cnt) + "...time...")
msg.config(bg='lightgreen', font=('times', 24, 'italic'))
msg.bind("<Button-1>", MsgClick)
msg.bind("<Motion>", MsgMotion)
msg.pack()
#print(type(msg)) tkinter.Message
def ButtonDoubleClick(event):
import sys; sys.exit()
root = Tk()
root.title("My First GUI App in Python")
root.minsize(width=300, height=300)
root.maxsize(width=400, height=350)
button = Button(
root, text="Click Me!", width=40, height=3
)
button.pack()
button.bind("<Button-1>", ButtonClick)
button.bind("<Double-1>", ButtonDoubleClick)
root.mainloop()
Hope it will help someone...
You can use forget method on the widget
from tkinter import *
root = Tk()
b = Button(root, text="Delete me", command=b.forget)
b.pack()
b['command'] = b.forget
root.mainloop()
I found that when the widget is part of a function and the grid_remove is part of another function it does not remove the label. In this example...
def somefunction(self):
Label(self, text=" ").grid(row = 0, column = 0)
self.text_ent = Entry(self)
self.text_ent.grid(row = 1, column = 0)
def someotherfunction(self):
somefunction.text_ent.grid_remove()
...there is no valid way of removing the Label.
The only solution I could find is to give the label a name and make it global:
def somefunction(self):
global label
label = Label(self, text=" ")
label.grid(row = 0, column = 0)
self.text_ent = Entry(self)
self.text_ent.grid(row = 1, column = 0)
def someotherfunction(self):
global label
somefunction.text_ent.grid_remove()
label.grid_remove()
When I ran into this problem there was a class involved, one function being in the class and one not, so I'm not sure the global label lines are really needed in the above.