I have three windows:
Root window
Toplevel window
Color-chooser window.
The root window has a menu command that opens the toplevel. The toplevel has a button that opens the color-chooser.
When the color-chooser button is pressed and the color-chooser opens, something weird happens. The toplevel window gets sent BEHIND the root window.
The layering of the windows is like this before clicking the button:
ROOT
TOPLEVEL
The layering of the windows is like this after clicking the button:
TOPLEVEL
ROOT
COLORCHOOSER
Why is this happening? How can I stop this from happening? (I'm using Windows 7, and python 2.7)
Here is a simplified working code example:
from Tkinter import *
import ttk
import tkColorChooser
class Root(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.menu = Menu(self)
self.menu.add_command(label="Open Toplevel", command=self.create_toplevel)
self.config(menu=self.menu)
def create_toplevel(self):
self.new_toplevel = TopLevelWithButton(self)
class TopLevelWithButton(Toplevel):
def __init__(self, *args, **kwargs):
Toplevel.__init__(self, *args, **kwargs)
self.button = ttk.Button(self, text="Color Chooser", command=self.open_chooser)
self.button.grid(row=0, column=0)
def open_chooser(self):
tkColorChooser.askcolor()
root = Root()
root.mainloop()
You aren't telling the color dialog which window it belongs to, so by default it attaches itself to the root window. With some window managers this will cause the parent window to be raised to the top of the stacking order.
Try passing in the parent attribute, giving it a value of the toplevel window:
tkChooseColor.askcolor(parent=self)
Related
I just want to close/destroy an overriden TopLevel widget when the main close button ('X' button) is clicked.
The overriden TopLevel widget is not created from the root of tKinter, but from a frame.
class MyToplevel(Tki.Toplevel):
def __init__(self, parent, *args, **kwargs): # parent is not the root of tKinter, it's a frame
super().__init__(*args, **kwargs)
self.protocol("WM_DELETE_WINDOW", self.on_closing) # Not working
self.wm_protocol("WM_DELETE_WINDOW", self.on_closing) # Not working
def on_closing(self):
# The key is, how can I call this method when main close button is clicked?
self.destroy()
I also have tried self.winfo_ismapped() and self.winfo_exists(), but when I click in the close button, nothing happens, because the main window exists.
If you want to close the toplevel when the close button ("X") on the root window is clicked, then you need to bind the protocol "WM_DELETE_WINDOW" on the root window as well:
class MyToplevel(Tki.Toplevel):
def __init__(self, parent, *args, **kwargs): # parent is not the root of tKinter, it's a frame
super().__init__(parent, *args, **kwargs)
# find root window
self.root = self.winfo_toplevel()
while self.root.master:
self.root = self.root.master.winfo_toplevel()
self.handler = self.root.protocol("WM_DELETE_WINDOW")
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.protocol("WM_DELETE_WINDOW", self.on_closing)
def on_closing(self):
# restore default handler for root window
self.root.protocol("WM_DELETE_WINDOW", self.handler)
self.destroy()
Note that this does not work when more than one toplevel is open simultaneously.
I try to create a menu with tkinter and python. I am using latest Python and I am on latest Mac OS Monterey.
import tkinter as tk
class MainApp(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
my_menu = tk.Menu(self.parent)
self.parent.config(menu=my_menu)
app_menu = tk.Menu(my_menu)
my_menu.add_cascade(label="Options", menu=app_menu)
app_menu.add_separator()
app_menu.add_command(label="Exit", command=self.confirm_exit)
self.label = tk.Label(self.parent, text="testing", pady=10, borderwidth=1)
self.label.pack(fill='both')
def confirm_exit(self):
self.parent.destroy()
def main():
root = tk.Tk()
app = MainApp(root)
app.pack()
root.mainloop()
if __name__ == "__main__":
main()
The menu shows at the top of the screen as a native menubar on Mac. Is there any way you can add the menubar to the tkinter app itself on Mac?
The menu shows at the top of the screen as a native menubar on Mac. Is there any way you can add the menubar to the tkinter app itself on Mac?
No, there is not, unless you use XQuartz and an X11 build of tkinter. Menus and menubars are designed to be native on OSX and Windows.
You can simulate a menubar by using a Frame and some Menubutton widgets. However, you'll still have the menu at the top.
I want to create a window which doesn't allow the user to access other windows until you give an input.
I tried win.attribute("ontop", True) but it allows the user to access other windows.
or is there any function like a force_focus_lock() in Tkinter python 3.8 which doesn't allow other window to
get focus until you give a input or the close present window.
I believe the below is what you are trying to do. Explanation is given in comments.
method #1: (PopOut1)
you can still move the main window
the new window assumes focus if there is a mouse release on main window
method #2: (PopOut2)
the main window is locked in place
the new window will assume focus, blink and "ding" if there is a mouse release on main window
import tkinter as tk
#first method
class PopOut1(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#releasing on any other tkinter window, within this process, forces focus back to this window
self.grab_set()
#second method
class PopOut2(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#disable the main window
master.attributes('-disabled', True)
#so this window can't end up behind the disabled window
#only necessary if this window is not transient
#self.attributes('-topmost', True)
#capture close event
self.protocol("WM_DELETE_WINDOW", self.close)
#event=None ~ in case you also want to bind this to something
def close(self, event=None):
#re-enable the main window
self.master.attributes('-disabled', False)
#destroy this window
self.destroy()
class App(tk.Tk):
TITLE = 'Application'
WIDTH, HEIGHT, X, Y = 800, 600, 50, 50
def __init__(self):
tk.Tk.__init__(self)
tk.Button(self, text="open popout 1", command=self.open1).grid()
tk.Button(self, text="open popout 2", command=self.open2).grid()
def open1(self):
PopOut1(self)
def open2(self):
#.transient(self) ~
# flash PopOut if focus is attempted on main
# automatically drawn above parent
# will not appear in taskbar
PopOut2(self).transient(self)
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}+{App.X}+{App.Y}')
#app.resizable(width=False, height=False)
app.mainloop()
I am working on a program that uses a tkinter TopLevel window to display periodically updating log information to the user. My problem is that the main program is fullscreen, so whenever they interact with it after opening the log window, the log window isn't visible since it is now behind the main program.
Is there a way to force a Toplevel window (or actually, any Tkinter window) to remain permanently ontop of all other windows?
Consider this quick setup for example:
import tkinter as tk
from tkinter import ttk
class Example(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
btn = ttk.Button(self, text = "Press", command = self.openTopLevel)
btn.pack()
def openTopLevel(self):
topLevelWindow = tk.Toplevel(self)
root = tk.Tk()
main = Example(root)
root.mainloop()
When you Press the button and open the Toplevel Window, it is on top. But if you grab the Frame, move it around, etc, the Toplevel goes behind it. How do I stop that? Or is that not something Tkinter allows me to do?
To make a window stay in front of others in a tkinter application, use attributes('-topmost', 'true'). In your code, it is a one-line to add.
import tkinter as tk
from tkinter import ttk
class Example(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.pack()
btn = ttk.Button(self, text = "Press", command = self.openTopLevel)
btn.pack()
def openTopLevel(self):
topLevelWindow = tk.Toplevel(self)
# Make topLevelWindow remain on top until destroyed, or attribute changes.
topLevelWindow.attributes('-topmost', 'true')
root = tk.Tk()
main = Example(root)
root.mainloop()
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