I am creating an tkinter app. For now I just want to get a very basic menubar to work, with a file section, and an exit button in the sub menu. Here is my object oriented code, which may be where I am going wrong:
import tkinter as tk
class MainApplication(tk.Frame):
def __init_(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.menubar = tk.Menu(self)
self.filemenu = tk.Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="Exit", command=self.quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.config(menu=self.menubar)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
However, this only creates a blank tkinter window. This usually works for me when I use procedural programming so I think I am doing something wrong with OOP. I am trying to say self.config() as root.config(), but this does not work.
2 big issues there. The first is that you misspelled __init__, so none of your custom code is being run. The second is that you need to apply the menu to the root window, aka self.master (default name) or self.parent (your name). Try like this:
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.menubar = tk.Menu(self)
self.filemenu = tk.Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="Exit", command=self.quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.master.config(menu=self.menubar)
if __name__ == "__main__":
root = tk.Tk()
root.geometry('200x200') # remove once you've added window content
win = MainApplication(root)
win.pack(side="top", fill="both", expand=True)
root.mainloop()
I also moved you to a python3 inheritance style, and defined a size so that you actually see something.
Related
So i just want to make this window a text widget thats always on top. Im trying to add this widget but it keeps creating another window...
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
hello="hello"
self.tb = tk.Text(self)
self.tb.pack(expand=1, fill = tk.BOTH)
self.tb.insert(tk.END, hello)
topLevelWindow = tk.Toplevel(self)
# Make topLevelWindow remain on top until destroyed, or attribute changes.
topLevelWindow.attributes('-topmost', 'true')
if __name__ == "__main__":
root = tk.Tk()
main = App(root)
root.mainloop()
If you want your text widget to be active you need to call this instead:
self.tb.focus_set()
What you're doing is instead create a Toplevel widget, which is a window that stays topmost, as in above all windows in desktop, you should remove:
topLevelWindow = tk.Toplevel(self)
# Make topLevelWindow remain on top until destroyed, or attribute changes.
topLevelWindow.attributes('-topmost', 'true')
If you also want that your entire window you can do that for your root instead in your 'main' according to this answer:
root.call('wm', 'attributes', '.', '-topmost', '1')
Finally to have:
import tkinter as tk
from tkinter import ttk
class App(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
hello="hello"
self.tb = tk.Text(self)
self.tb.pack(expand=1, fill = tk.BOTH)
self.tb.insert(tk.END, hello)
self.tb.focus_set()
if __name__ == "__main__":
root = tk.Tk()
root.call('wm', 'attributes', '.', '-topmost', '1')
main = App(root)
root.mainloop()
Also if you want to make other widgets unfocusable:
widget.config(takefocus=False)
I've seen many examples of grab_set() being used for modal windows for tkinter but I can't get it to work for my application.
I am creating a second window as my 'Settings' window which is called from the Menu of the main application.
example:
import tkinter as tk
class Main(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self,*args, *kwargs)
button = tk.Button(self,text="second window", command=lambda:Settings())
button.pack()
class Settings(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self,*args, *kwargs)
button = tk.Button(self,text="quit", command=lambda: quit())
button.pack()
self.grab_set()
if __name__ == "__main__":
app = Main()
app.mainloop()
Right now I can still click the 'Settings' button to create as many instances of Settings as the pc would allow. How do I restrict clickability to the main application window until the second one is closed first?
Here is a super simple example of how you can open another window using Toplevel and how you can edit stuff on the main window from the Toplevel window.
Its very basic but it should be a good enough example to illustrate what is required in tkinter to open new window.
UPDATE: Added the grab_set() method as pointed out by Bryan in the comments.
The grab_set() method according to the documentation routes all events for this application to this widget.
Note: This would be along the lines of a Minimal, Complete, and Verifiable example. It is the smallest possible bit of code to get the point across while also being testable.
from tkinter import *
class GUI(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.my_frame = Frame(self.master)
self.my_frame.pack()
self.button1 = Button(self.master, text="Open New Window", command = self.open_toplevel_window)
self.button1.pack()
self.text = Text(self.master, width = 20, height = 3)
self.text.pack()
self.text.insert(END, "Before\ntop window\ninteraction")
def open_toplevel_window(self):
self.top = Toplevel(self.master)
#this forces all focus on the top level until Toplevel is closed
self.top.grab_set()
def replace_text():
self.text.delete(1.0, END)
self.text.insert(END, "Text From\nToplevel")
top_button = Button(self.top, text = "Replace text in main window",
command = replace_text)
top_button.pack()
if __name__ == "__main__":
root = Tk()
app = GUI(root)
root.mainloop()
Here is an example when using a separate class for the Toplevel:
from tkinter import *
class GUI(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.my_frame = Frame(self.master)
self.my_frame.pack()
self.button1 = Button(self.master, text="Open New Window",
command = open_toplevel_window)
self.button1.pack()
self.text = Text(self.master, width = 20, height = 3)
self.text.pack()
self.text.insert(END, "Before\ntop window\ninteraction")
class open_toplevel_window(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.grab_set()
def replace_text():
app.text.delete(1.0, END)
app.text.insert(END, "Text From\nToplevel")
top_button = Button(self, text = "Replace text in main window",
command = replace_text)
top_button.pack()
if __name__ == "__main__":
root = Tk()
app = GUI(root)
root.mainloop()
I figured out my problem
import tkinter as tk
class Main(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self,*args, *kwargs)
self.button = tk.Button(self,text="second window", command=lambda: SecondWindow())
self.button.pack()
class SecondWindow(tk.Toplevel):
def __init__(self,*args, **kwargs):
tk.Toplevel.__init__(self,*args, *kwargs)
self.button = tk.Button(self,text="quit", command=lambda: quit())
self.button.pack()
self.grab_set()
if __name__ == "__main__":
app = Main()
app.mainloop()
as per Sierra Mountain Tech and Bryan Oakley's suggestion. I have changed my Settings class to Toplevel and it does exactly what I want.
My acutal application has the two in different modules but yield the same results.
Try adding the following line after the line containing the grab_set method:
self.wait_window(self)
You need to allow focus with takefocus = True and you give the focus to it with focus_set()
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.takefocus = True
self.focus_set()
To keep this as short as possible - In my program I start with Page1 and when I press a button I want to open Page2 and close Page1, I have managed to open Page2 but I cant close Page1, I have tried using .destroy() but it closes everything not just the page. I looked around some questions here on SO but couldn't find much in the same layout as my code so I wasnt sure how to apply it to mine. This is my first tkinter project so I am still getting to grips with it.
My code is;
class Page1:
def __init__(self,master):
self.master = master
#lots of labels and buttons
self.BTNNextPage = ttk.Button(master, text = "Proceed",
command = self.NextPage)
self.BTNNextPage.place(x=450, y=420)
def NextPage(self):
self.newWindow = tk.Toplevel(self.master)
self.app = Page2(self.newWindow)
self.master.destroy()
class Page2():
def __init__(self,master):
self.master = master
#tried Page1.destroy() here but Page1 has no attibute destroy
#more labels and buttons
def main():
widthpixels=690
heightpixels=500
root = tk.Tk()
root.resizable(width=False, height=False)
root.configure(background='black')
root.iconbitmap("Image")
root.wm_title("Title")
root.geometry('{}x{}'.format(widthpixels, heightpixels))
app = Page1(root)
root.mainloop()
if __name__ == "__main__":
main()
If you destroy root, it destroys all the widgets contained, including Page2. To destroy only page 1, one possibility is to make the page classes inherit from tk.Frame, so that they have a destroy method:
import tkinter as tk
from tkinter import ttk
class Page1(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.pack(fill='both', expand=True) # display page 1
#lots of labels and buttons:
tk.Label(self, text='Page 1').place(relx=0.5, rely=0.5)
self.BTNNextPage = ttk.Button(self, text="Proceed", command=self.NextPage)
self.BTNNextPage.place(x=450, y=420)
def NextPage(self):
self.app = Page2(self.master) # create page 2
self.destroy() # remove page 1
class Page2(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.pack(fill='both', expand=True) # display page 2
# create widgets on page 2
tk.Label(self, text='Page 2').pack()
tk.Button(self, text='Quit', command=self.master.destroy).pack(side='bottom')
def main():
widthpixels=690
heightpixels=500
root = tk.Tk()
root.resizable(width=False, height=False)
root.configure(background='black')
root.wm_title("Title")
root.geometry('{}x{}'.format(widthpixels, heightpixels))
app = Page1(root)
root.mainloop()
if __name__ == "__main__":
main()
I have created a script using tkinter, that brings up a child frame when you press a button. The frame occupies the full screen size on my mac laptop. Now I need to create an option to exit/ or close this new frame. What is the best option to do that?
-Thanks
from Tkinter import *
import tkFont
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.apple = Button(self,
text="Apple", command=self.write_apple)
self.apple.pack(side=LEFT)
def write_apple(self):
self.customFont = tkFont.Font(family="Helvetica", size=80)
t = Toplevel(self)
t.overrideredirect(True)
t.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
l = Label(t, text="This is a green apple.",font=self.customFont)
l.pack(side="top", fill="both", expand=True)
if __name__ == "__main__":
root = Tk()
main = App(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
This solution handles the case that multiple instances of Toplevel are created:
from Tkinter import *
import tkFont
class App(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.apple = Button(self,
text="Apple", command=self.write_apple)
self.apple.pack(side=LEFT)
self.top_dict = dict()
def destroy_top(self, event):
# event.widget is the instance of Label that was clicked
# Get the instance of Toplevel
top = self.top_dict[event.widget]
# Destroy the instance of Toplevel
top.destroy()
# Remove the instance of Toplevel from the list
del self.top_dict[event.widget]
def write_apple(self):
self.customFont = tkFont.Font(family="Helvetica", size=80)
# Create an instance of Toplevel
top = Toplevel(self)
top.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
label = Label(top, text="This is a green apple.",font=self.customFont)
label.pack(side="top", fill="both", expand=True)
# Bind the destroy_top method to the mouse-button-1 click
label.bind('<Button-1>', self.destroy_top)
# Save the instance of Toplevel using the label as the key
self.top_dict[label] = top
if __name__ == "__main__":
root = Tk()
main = App(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
NOTE: As #Bryan Oakley noted, removing the call to overrideredirect adds window decorations to the instance of Toplevel.
I'm new to python and I have to program some sort of gui. But the gui consist of child windows, currently there is one child window. The prolem is at startup the child window also starts up. It's supposed to wait until the button has been clicked and then launch the child window. I have no idea why it beheaves like this....
#!/usr/bin/env python
from Tkinter import *
import tkMessageBox as box
import rospy
class gui(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background="white")
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Baxter analyse tool")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
submenu = Menu(fileMenu)
submenu.add_command(label="camera tool", command=self.camera_window())
submenu.add_command(label="range tool")
submenu.add_command(label="control tool")
submenu.add_command(label="sonar tool")
submenu.add_command(label="quick check tool")
fileMenu.add_cascade(label="tools", menu=submenu, underline=0)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", command=self.onExit)
menubar.add_cascade(label="File", menu=fileMenu)
menubar.add_command(label="about", command=self.about)
def camera_window(self):
cameraGui = CameraGui()
def about(self):
box.showinfo("Baxter","Analyse tool.")
def onExit(self):
self.quit()
class CameraGui(object):
def __init__(self):
self.initUI()
def initUI(self):
win = Toplevel()
Label(win, text="testestest").pack()
Button(win, text="hello", command=win.destroy).pack()
def main():
rospy.init_node('baxter_lput_analyse_tool')
root = Tk()
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth()/2, root.winfo_screenheight()-50))
root.focus_set()
root.bind("<Escape>", lambda e: e.widget.quit())
app = gui(root)
root.mainloop()
if __name__=='__main__':
main()
The program runs fine just that it automatically opens the child window
Don't call the function self.camera_window().
Remove the (). Your self.camera_window method gets called as soon as the main loop starts.
Do this:
submenu.add_command(label="camera tool", command=self.camera_window)
Or if you want to send some argument then:
submenu.add_command(label="camera tool", command=lambda:self.camera_window(args))