I am attempting to implement a trivial MVC application.
When super().__init__() is called, two windows are generated in my python application.
What is the appropriate usage when attempting to inherit from tk.TopLevel, when I only want to generate one window?
import tkinter as tk
from tkinter import ttk
class View(tk.Toplevel):
def __init__(self, controller):
super().__init__()
self.controller = controller
def exec_main(self):
self.mainloop()
class Controller:
def __init__(self):
self.view = View(self)
if __name__ == '__main__':
app = Controller()
app.view.exec_main()
Doing something like super().__init__(master=self) or super().__init__(self) does not seem to be the solution.
An alternative approach would be to do something like this for the main:
import tkinter as tk
class View(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
class Controller:
def __init__(self, root):
self.view = View(root)
if __name__ == '__main__':
root = tk.Tk()
root.withdraw()
app = Controller(root)
root.mainloop()
But this seems wasteful.
Without exception, every widget except the root window requires a parent. If you don't create a root window then one will be created for you. When you create an instance of Toplevel and call super().__init__(), if you don't have a root window then tkinter will create one for you.
As you've observed, the correct workaround is to explicitly create a root window and then hide it. You have to make sure to give the user a way to destroy this root window since it won't automatically be destroyed when you close the Toplevel windows.
i am making a GUI and in it I am keeping the root hidden. So when i close the window which is showing using the x arrow, i wont the whole program to end not just the window that can be seen. Because other wise the user will have problems when closing the program.my root is hidden,
login can be seen,
when login is closed using the red x at the top i want to close the root as well how to do that?
You can bind to the <Destroy> event on your window. Within the bound function, you can do whatever you want including destroying the root window.
In the following example, killing the Toplevel windows will destroy the root window.
import tkinter as tk
class Window(tk.Toplevel):
def __init__(self, root, label):
super().__init__(root)
self.root = root
label = tk.Label(self, text=label)
label.pack(padx=20, pady=20)
self.bind("<Destroy>", self.kill_root)
def kill_root(self, event):
if event.widget == self and self.root.winfo_exists():
self.root.destroy()
root = tk.Tk()
label = tk.Label(root, text="Root window")
label.pack(padx=20, pady=20)
w1 = Window(root, "This is a toplevel window")
root.mainloop()
The reason for checking that event.widget is self is due to the fact that functions bound to the root or toplevel window are automatically inherited by all children within that window You only want to destroy the root window when the actual toplevel window is destroyed.
#game class
import Tkinter as tk
class Game(tk.Canvas):
def __init__(self, master):
canvas = tk.Canvas(master)
canvas.pack()
button = tk.Button(canvas, text='Quit', command=self.quit_game)
button.pack()
def quit_game(self):
root.destroy()#Should i put something else here?
root = tk.Tk()
game = Game(root)
root.mainloop()
Is it good practice, or, in other words, is there a problem with inheriting from canvas directly instead of frame, if for example I am not going to add any widgets except the canvas?
Another question I have is regarding the root.destroy(). I don't understand why I can't say master.destroy() or something to that effect.
There is nothing wrong with inheriting from Canvas or any other Tkinter widget.
re master.destroy() vs root.destroy(): you can call it however you want. You simply need a reference to the root window. If you call it root, to destroy it you would call root.destroy().
In general you should avoid the use of global variables. Given that you're passing in the root widget to your class, you can save a reference and use that instead:
class Game(tk.Canvas):
def __init__(self, master):
self.master = master
...
def quit_game(self):
self.master.destroy()
I am trying to create a program/window that has one action button. This button when clicked, should open a second program/window and close the previous first program. This second program should have an action button that will open the first and close the second.
I found this script from another user and it works the same way I want the code to run. However, the root window is just being hidden with the deconify() when the otherframe is created.
What would be the best way to destroy the root window when the otherframe is created and still be able to be looped back.
Hopefully this made sense and thanks in advance.
import Tkinter as Tk
########################################################################
class OtherFrame(Tk.Toplevel):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
Tk.Toplevel.__init__(self)
self.geometry("100x100")
self.title("otherFrame")
########################################################################
class MyApp(object):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
self.root = parent
self.root.title("Main frame")
self.frame = Tk.Frame(parent)
self.frame.pack()
btn = Tk.Button(self.frame, text="Open Frame", command=self.openFrame)
btn.pack()
#----------------------------------------------------------------------
def hide(self):
""""""
self.root.withdraw()
#----------------------------------------------------------------------
def openFrame(self):
""""""
self.hide()
subFrame = OtherFrame()
handler = lambda: self.onCloseOtherFrame(subFrame)
btn = Tk.Button(subFrame, text="Close", command=handler)
btn.pack()
#----------------------------------------------------------------------
def onCloseOtherFrame(self, otherFrame):
""""""
otherFrame.destroy()
self.show()
#----------------------------------------------------------------------
def show(self):
""""""
self.root.update()
self.root.deiconify()
#----------------------------------------------------------------------
if __name__ == "__main__":
root = Tk.Tk()
root.geometry("800x600")
app = MyApp(root)
root.mainloop()
There is no best way to destroy the root window and then get it back. There is only one way to destroy it, which is to call the destroy() method. When you do that, all children windows will be destroyed and mainloop will exit.
While it's possible to destroy and recreate a root window, that's not how tkinter is designed to work, and it will not behave the way you expect it to behave. For example, all instances of StringVar, etc. will be destroyed. For another, you must have a root window, so by destroying the root window you will destroy all of its children including the Toplevel.
By far, the most common scenario is to simply hide the root window. If you truly want to destroy each window, put nothing in the root window and just leave it hidden. You can then use instances of Toplevel, which you can easily destroy and recreate at will.
Short answer... YOU CANT DO IT. The script that you have already is the best bet.
Try this:
import tkinter
class abc:
def __init__(self):
self.root = tkinter.Tk()
tkinter.Button(self.root, text="Click Me", command=lambda:abc.com(self)).pack()
def com(self):
self.root.destroy()
some = abc()
q = abc()
This doesn't actually jump between the two but creates a new one every time.
I am trying to add a custom title to a window but I am having troubles with it. I know my code isn't right but when I run it, it creates 2 windows instead, one with just the title tk and another bigger window with "Simple Prog". How do I make it so that the tk window has the title "Simple Prog" instead of having a new additional window. I dont think I'm suppose to have the Tk() part because when i have that in my complete code, there's an error
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.root = Tk()
self.root.title("Simple Prog")
If you don't create a root window, Tkinter will create one for you when you try to create any other widget. Thus, in your __init__, because you haven't yet created a root window when you initialize the frame, Tkinter will create one for you. Then, you call make_widgets which creates a second root window. That is why you are seeing two windows.
A well-written Tkinter program should always explicitly create a root window before creating any other widgets.
When you modify your code to explicitly create the root window, you'll end up with one window with the expected title.
Example:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
self.make_widgets()
def make_widgets(self):
# don't assume that self.parent is a root window.
# instead, call `winfo_toplevel to get the root window
self.winfo_toplevel().title("Simple Prog")
# this adds something to the frame, otherwise the default
# size of the window will be very small
label = Entry(self)
label.pack(side="top", fill="x")
root = Tk()
abc = ABC(root)
root.mainloop()
Also note the use of self.make_widgets() rather than ABC.make_widgets(self). While both end up doing the same thing, the former is the proper way to call the function.
Here it is nice and simple.
root = tkinter.Tk()
root.title('My Title')
root is the window you create and root.title() sets the title of that window.
Try something like:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
root = Tk()
app = ABC(master=root)
app.master.title("Simple Prog")
app.mainloop()
root.destroy()
Now you should have a frame with a title, then afterwards you can add windows for
different widgets if you like.
One point that must be stressed out is:
The .title() method must go before the .mainloop()
Example:
from tkinter import *
# Instantiating/Creating the object
main_menu = Tk()
# Set title
main_menu.title("Hello World")
# Infinite loop
main_menu.mainloop()
Otherwise, this error might occur:
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 2217, in wm_title
return self.tk.call('wm', 'title', self._w, string)
_tkinter.TclError: can't invoke "wm" command: application has been destroyed
And the title won't show up on the top frame.
Example of python GUI
Here is an example:
from tkinter import *;
screen = Tk();
screen.geometry("370x420"); //size of screen
Change the name of window
screen.title('Title Name')
Run it:
screen.mainloop();
I found this works:
window = Tk()
window.title('Window')
Maybe this helps?
Easy method:
root = Tk()
root.title('Hello World')
Having just done this myself you can do it this way:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.parent.title("Simple Prog")
You will see the title change, and you won't get two windows. I've left my parent as master as in the Tkinter reference stuff in the python library documentation.
For anybody who runs into the issue of having two windows open and runs across this question, here is how I stumbled upon a solution:
The reason the code in this question is producing two windows is because
Frame.__init__(self, parent)
is being run before
self.root = Tk()
The simple fix is to run Tk() before running Frame.__init__():
self.root = Tk()
Frame.__init__(self, parent)
Why that is the case, I'm not entirely sure.
self.parent is a reference to the actual window, so self.root.title should be self.parent.title, and self.root shouldn't exist.
widget.winfo_toplevel().title("My_Title")
changes the title of either Tk or Toplevel instance that the widget is a child of.
I found a solution that should help you:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.master.title("Simple Prog")
self.make_widgets()
def make_widgets(self):
pass
root = Tk()
app = ABC(master=root)
app.mainloop()
Found at: docs.python.org