import tkinter as tk
class Main:
def __init__(self, parent):
self.parent = parent
self.button = tk.Button(text="Build", command=self.new_window)
self.button.grid(row=1, column=0)
def new_window(self):
self.window = tk.Tk()
self.app = Graph(self.window)
self.window.mainloop()
class Graph:
def __init__(self, parent):
self.parent = parent
self.new_button = tk.Button(text="text")
self.new_button.grid(in_=self.parent)
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == "__main__":
main()
Here's my code and I'm trying to create a button on a new widget using grid() + in_, but there is a problem - button dont creates on a new widget instead of this it creates on the main one.
In tkinter widgets are by default assigned to root window, Tk unless passed a parent widget as the first positional argument, which is omitted in:
self.new_button = tk.Button(text="Destroy", command=self.destroy)
You should replace it with:
self.new_button = tk.Button(parent, text="Destroy", command=self.destroy)
I am new to python so I was trying to make a GUI, in that I have to place a button in a particular position.
I tried using self.nxt_form.place(x=200,y=100) instead of self.nxt_form.pack().
But the button disappeared and only the frame appeared when it ran. Can you tell me how to place the button in a particular position?
Here is the code:
import tkinter as tk
class Main_form:
def __init__(self, root,title="Simulated MTBF"):
self.root = root
self.frame = tk.Frame(self.root)
"""Button nxt_form which moves to next form"""
self.nxt_form = tk.Button(self.frame, text = 'Next Form', width = 25,command = self.new_window)
self.nxt_form.pack()
self.frame.pack()
"""command to open new window by clicking Button """
def new_window(self):
self.newWindow = tk.Toplevel(self.root)
self.app = Demo2(self.newWindow)
class Demo2:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(self.root)
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
self.quitButton.pack()
self.frame.pack()
def close_windows(self):
self.root.destroy()
def main():
root = tk.Tk()
app = Main_form(root)
root.mainloop()
if __name__ == '__main__':
main()
when i am using tkinter i used column and row to position objects
self.btn = tk.Button(self, text = "button")
self.btn.grid(row = 1, column = 1)
EDIT - expanded on information in response to comment (below)
I would make an label and change its width and height to make the spacing you need (note im a beginer at python as well so this is probly a bad way but it works)
from tkinter import *
import tkinter as tk
from tkinter.ttk import Combobox,Treeview,Scrollbar
class MainMenu(Frame):
def __init__(self, master):
""" Initialize the frame. """
super(MainMenu, self).__init__(master)
self.grid()
self.create_GUI()
def create_GUI(self):
frame1 = tk.LabelFrame(self, text="frame1", width=300, height=130, bd=5)
frame1.grid(row=0, column=0, columnspan=3, padx=8)
#the frame is not needed but it is a good thing to use as can group
#parts of your interface together
self.text1 = Entry(frame1)
#note if you were not using frames would just put self here
self.text1.grid(row = 1, column = 0)
self.text2 = Label(frame1, text = "",height = 10)
self.text2.grid(row = 2 , column = 0)
self.text3 = Entry(frame1)
self.text3.grid(row = 3, column = 0)
root = Tk()
root.title("hi")
root.geometry("500x500")
root.configure(bg="white")
app = MainMenu(root)
root.mainloop()
Also note that you can not use pack and grid together what you could do is group your objects in different frames then use grid in one frame and pack in a different frame. I personally prefer to use grid to pack as it gives you more control over your object then pack does
I am attempting to have a function in python that clears the screen upon a button being pressed. I am aware of grid_remove but am unsure of how to use it. Also is there a way to clear everything from a specific function, ie both "hi" and "clear"?
from tkinter import *
class Movies:
def __init__(self, master):
hi = Label(text = "Hello")
hi.grid(row = 0, column = 0)
clear = Button(text = "Click", command=self.clear)
clear.grid(row = 1, column = 0)
def clear(self):
hi.grid_remove()
root = Tk()
gui = Movies(root)
root.geometry("100x200+0+0")
root.mainloop()
You could use the built in winfo_children method if you're just wanting to toggle hiding / showing all of the widgets in whatever parent holds the widgets. Small example:
from tkinter import *
class Movies:
def __init__(self, master):
self.master = master
self.state = 1
for i in range(5):
Label(self.master, text='Label %d' % i).grid(row=0, column=i)
self.magic_btn = Button(self.master, text='Make the Magic!',
command=self.magic)
self.magic_btn.grid(columnspan=5)
def magic(self):
self.state = not self.state
for widget in self.master.winfo_children(): #iterate over all child widgets in the parent
#Comment out to clear the button too, or leave to toggle widget states
if widget != self.magic_btn: #or some other widget you want to stay shown
if self.state:
widget.grid()
else:
widget.grid_remove()
print(self.state)
root = Tk()
gui = Movies(root)
root.mainloop()
I am trying to create a program in tkinter which allows me to open an initial window then to keep it throughout all classes used. For example, if I was to create a button in a window then when I click this button, it would exuecute a method that destroys the widget, and then executes a new class that builds a new screen within the same window, such as text opposed to a button.
from tkinter import *
class Window1:
def __init__(self, master):
self.master = master
self.label = Button(self.master, text = "Example", command = self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
## Code to execute next class
class Window2:
def __init__(self, master):
self.master = master
self.label = Label(self.master, text = "Example")
self.label.pack()
def main():
root = Tk()
run = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
I understand this is less practical, but I am curious. Cheers.
Tk() creates main window and variable root gives you access to this window. You can use root as argument for Window2 and you will have access to main window inside Window2
from tkinter import *
class Window1:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Button(self.master, text="Example", command=self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
# use `root` with another class
self.another = Window2(self.master)
class Window2:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Label(self.master, text="Example")
self.label.pack()
root = Tk()
run = Window1(root)
root.mainloop()
--
Probably nobody use another class to create Label in place of Button ;)
--
EDIT: In this example using names Window1 and Windows2 is misleading because there is only one window and two classes which use this window. I would rather use names FirstOwner, SecondOwner
Everything is implemented in one Tk class and in this case there always is only one window.
from tkinter import *
from tkinter import ttk
class MainWindow():
def __init__(self, mainWidget):
self.main_frame = ttk.Frame(mainWidget, width=300, height=150, padding=(0, 0, 0, 0))
self.main_frame.grid(row=0, column=0)
self.some_kind_of_controler = 0
self.main_gui()
def main_gui(self):
root.title('My Window')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=0, column=0)
self.main_label_2 = ttk.Label(self.main_frame, text='Object_2')
self.main_label_2.grid(row=1, column=0)
self.main_label_3 = ttk.Label(self.main_frame, text='Object_3')
self.main_label_3.grid(row=2, column=0)
self.setings_button = ttk.Button(self.main_frame, text='Setings')
self.setings_button.grid(row=0, column=1)
self.setings_button.bind('<Button-1>', self.setings_gui)
self.gui_elements = [self.main_label_1,
self.main_label_2,
self.main_label_3,
self.setings_button]
def setings_gui(self, event):
self.gui_elements_remove(self.gui_elements)
root.title('Setings')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=2, column=0)
self.main_menu_button = ttk.Button(self.main_frame, text='Main menu')
self.main_menu_button.grid(row=0, column=1)
self.main_menu_button.bind('<Button-1>', self.back_to_main)
self.some_kind_of_controler = 1
self.gui_elements = [self.main_label_1,
self.main_menu_button]
def back_to_main(self, event):
if self.some_kind_of_controler == 1:
self.gui_elements_remove(self.gui_elements)
else:
pass
self.main_gui()
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def main():
global root
root = Tk()
root.geometry('300x150+50+50')
window = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
I have created a login window in tkinter which has two Entry field, first one is Username and second one is Password.
code
from tkinter import *
ui = Tk()
e1 = Entry(ui)
#i need a placeholder "Username" in the above entry field
e1.pack()
ui.mainloop()
I want a placeholder called "Username" in the Entry, but if you click inside the entry box, the text should disappear.
You can create a class that inherits from Entry like below:
import tkinter as tk
class EntryWithPlaceholder(tk.Entry):
def __init__(self, master=None, placeholder="PLACEHOLDER", color='grey'):
super().__init__(master)
self.placeholder = placeholder
self.placeholder_color = color
self.default_fg_color = self['fg']
self.bind("<FocusIn>", self.foc_in)
self.bind("<FocusOut>", self.foc_out)
self.put_placeholder()
def put_placeholder(self):
self.insert(0, self.placeholder)
self['fg'] = self.placeholder_color
def foc_in(self, *args):
if self['fg'] == self.placeholder_color:
self.delete('0', 'end')
self['fg'] = self.default_fg_color
def foc_out(self, *args):
if not self.get():
self.put_placeholder()
if __name__ == "__main__":
root = tk.Tk()
username = EntryWithPlaceholder(root, "username")
password = EntryWithPlaceholder(root, "password", 'blue')
username.pack()
password.pack()
root.mainloop()
You need to set a default value for this entry. Like this:
from tkinter import *
ui = Tk()
e1 = Entry(ui)
e1.insert(0, 'username')
e1.pack()
ui.mainloop()
Then if you want to delete the content when you click the entry, then you have to bind a mouse click event with an event handler method to update content of this entry.
Here is a link for you.
Updated (Improved Answer):
Use the on_focus_out function to reinsert the placeholder if the text field is empty (if you don't want this to happen, you can use the method from the older code)
import tkinter as tk
def on_focus_in(entry):
if entry.cget('state') == 'disabled':
entry.configure(state='normal')
entry.delete(0, 'end')
def on_focus_out(entry, placeholder):
if entry.get() == "":
entry.insert(0, placeholder)
entry.configure(state='disabled')
root = tk.Tk()
entry_x = tk.Entry(root, width=50)
entry_x.pack(pady=10)
entry_x.insert(0, "Place Holder X")
entry_x.configure(state='disabled')
entry_y = tk.Entry(root, width=50)
entry_y.pack(pady=10)
entry_y.insert(0, "Place Holder Y")
entry_y.configure(state='disabled')
x_focus_in = entry_x.bind('<Button-1>', lambda x: on_focus_in(entry_x))
x_focus_out = entry_x.bind(
'<FocusOut>', lambda x: on_focus_out(entry_x, 'Place Holder X'))
y_focus_in = entry_y.bind('<Button-1>', lambda x: on_focus_in(entry_y))
y_focus_out = entry_y.bind(
'<FocusOut>', lambda x: on_focus_out(entry_y, 'Place Holder Y'))
root.mainloop()
Note:
It is discouraged to import *, so we should import like this import tkinter as tk.
I have created two Entry widgets to depict the changes.
Old (Not Recommended):
This will work for any placeholder you want.
from tkinter import *
root = Tk()
my_entry = Entry(root, width=50)
my_entry.pack()
my_entry.insert(0, "Place Holder")
my_entry.configure(state=DISABLED)
def on_click(event):
my_entry.configure(state=NORMAL)
my_entry.delete(0, END)
# make the callback only work once
my_entry.unbind('<Button-1>', on_click_id)
on_click_id = my_entry.bind('<Button-1>', on_click)
root.mainloop()
My solution is to subclass the tk.Entry and control the content and color, binding the <FocusIn> and <FocusOut> events to methods that fill and clear the text as necessary. This is the behavior:
Here the complete example code:
import tkinter as tk
class PlaceholderEntry(tk.Entry):
def __init__(self, master=None, placeholder='', cnf={}, fg='black',
fg_placeholder='grey50', *args, **kw):
super().__init__(master=None, cnf={}, bg='white', *args, **kw)
self.fg = fg
self.fg_placeholder = fg_placeholder
self.placeholder = placeholder
self.bind('<FocusOut>', lambda event: self.fill_placeholder())
self.bind('<FocusIn>', lambda event: self.clear_box())
self.fill_placeholder()
def clear_box(self):
if not self.get() and super().get():
self.config(fg=self.fg)
self.delete(0, tk.END)
def fill_placeholder(self):
if not super().get():
self.config(fg=self.fg_placeholder)
self.insert(0, self.placeholder)
def get(self):
content = super().get()
if content == self.placeholder:
return ''
return content
class App(tk.Frame):
def __init__(self, master=None):
self.root = master
super().__init__(master, borderwidth=0, relief=tk.RAISED)
self.root.title('Placeholder example')
self.pack_propagate(False)
self.pack()
self.entry = PlaceholderEntry(self.root, placeholder='This text is a placeholder')
self.entry.pack()
self.btn = tk.Button(self.root, text='Nothing', highlightcolor='cyan')
self.btn.pack()
root = tk.Tk()
app = App(master=root)
app.root.mainloop()
A working placeholder class. What this does is that it binds to <FocusIn> and <FocusOut> so that when you put focus on it if there is no text it will insert your placeholder into it. You can also change the color on if it is selected or not.
class Placeholder:
def __init__(self,master,placeholder='',placeholdercolor='grey',color='black',**kwargs):
self.e = Entry(master,fg=placeholdercolor,**kwargs)
self.e.bind('<FocusIn>',self.focus_in)
self.e.bind('<FocusOut>',self.focus_out)
self.e.insert(0, placeholder)
self.placeholder = placeholder
self.placeholdercolor=placeholdercolor
self.color = color
def pack(self,side=None,**kwargs):
self.e.pack(side=side,**kwargs)
def place(self,side=None,**kwargs):
self.e.place(side=side,**kwargs)
def grid(self,column=None,**kwargs):
self.e.grid(column=column,**kwargs)
def focus_in(self,e):
if self.e.get() == self.placeholder:
self.e.delete(0,END)
self.e.configure(fg=self.color)
def focus_out(self,e):
if self.e.get() == '':
self.e.configure(fg=self.placeholdercolor)
self.e.delete(0,END)
self.e.insert(0,self.placeholder)
from tkinter import *
root=Tk()
root.geometry("300x200+600+250")
root.config(background="#E0FFFF")
root.resizable(False,False)
def userText(event):
e1.delete(0,END)
usercheck=True
def passText(event):
e2.delete(0, END)
passcheck=True
a=StringVar()
b=StringVar()
usercheck=False
passcheck=False
Label(root,text="User name",bg="#E0FFFF").place(x=20,y=50)
e1= Entry(root,textvariable=a)
e1.place(x=100,y=50)
e1.insert(0,"Enter username")
e1.bind("<Button>",userText)
Label(root,text="Password",bg="#E0FFFF").place(x=20,y=95)
e2= Entry(root,textvariable=b)
e2.place(x=100,y=95)
e2.insert(0,"Enter password")
e2.bind("<Button>",passText)
root.mainloop()
For a more compact solution than the above listed, I suggest that you create a function that would erase the text box on a click event (lambda), as shown here.
from tkinter import *
def clear_entry(event, entry):
entry.delete(0, END)
entry.unbind('<Button-1>', click_event)
ui = Tk()
entry = Entry(ui)
entry.pack()
placeholder_text = '<enter-placeholder>'
entry.insert(0, placeholder_text)
entry.bind("<Button-1>", lambda event: clear_entry(event, entry))
ui.mainloop()
The "<Button-1>" stands for when you left-click the entry box, so do not alter it, and once you click on the box, it will trigger the event and run the function clear_entry. You have to declare the function and the entry element before defining placeholder_text and using entry.insert. Hopefully, this is a viable solution to this problem.
Joining Nae and Stephen Lins solutions with text field
text = Text(ui)
text.pack()
text.insert('1.0', 'text placeholder')
text.bind("<FocusIn>", lambda args: text.delete('1.0', 'end'))
# This is a easy way to add placeholder to an Entry
from tkinter import *
w = Tk()
w.title("Demo")
w.geomerty("500x500")
EntrySetText = StringVar()
EntryText = Entry(w)
EntryText.pack()
EntrySetText.set("Hello World!")
w.mainloop()
If you combine the entry field with a string variable all of this becomes a lot easier
from tkinter import *
from tkinter import ttk
from typing import Optional
class PlaceholderEntry:
def __init__(self, root: Tk | Frame | ttk.Frame | ttk.LabelFrame | Toplevel, string_variable: Optional[StringVar] = None, style: str = '', width: int = 25, placeholder_text: str = '', text_color: str = 'black', placeholder_color: str = 'grey50'):
self._placeholder_text = placeholder_text
self._placeholder_color = placeholder_color
self._text_color = text_color
self._text_var = string_variable if string_variable else StringVar(root)
self.__text_var.set(placeholder_text)
self.__entry = ttk.Entry(root, textvariable=self.__text_var, style=style, width=width)
self._entry.bind('<FocusOut>', self._focus_out)
self._entry.bind('<FocusIn>', self._focus_in)
self._change_color(False)
#property
def entry(self) -> ttk.Entry:
return self.__entry
#property
def string_var(self) -> StringVar:
return self.__text_var
def _change_color(self, placeholder: bool) -> None:
if placeholder:
self.__entry.configure(foreground=self._text_color)
else:
self.__entry.configure(foreground=self._placeholder_color)
def _focus_in(self, event: Event) -> None:
if self.__text_var.get() == self._placeholder_text:
self.__text_var.set('')
self._change_color(True)
def _focus_out(self, event: Event) -> None:
if not self._entry.get():
self.__text_var.set(self._placeholder_text)
if self.__entry.get() == self._placeholder_text:
self._change_color(False)
else:
self._change_color(True)