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.
Related
Below is an outline of a tkinter GUI in which I want the same dialog box to be opened in various ways. The response selected by the user from choices in the dialog then needs to be returned to the mainloop.
The SimpleDialog class looks to be ideal for this and here I have just used the example provided in the dialog code. It is accessed by both the button and popup menu in the View class, along with their bindings in the Controller class.
It works just fine when called from the button, but when called from the popup menu (from a right click) the dialog appears and then freezes the entire app.
from tkinter import simpledialog as s
import tkinter as tk
class View(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self)
self.grid(row=0, column=0, sticky='nsew')
self.configure(bg = 'blue')
self.popup = tk.Menu(self, tearoff=0)
self.bind("<Button-2>", self.make_popup) #right-click to show popup
self.button = tk.Button(self, text='Test')
self.button.grid()
def make_popup(self, event):
try:
self.popup.tk_popup(event.x_root + 15, event.y_root, 0)
finally:
self.popup.grab_release()
class Controller():
def __init__(self, view):
view.popup.add_command(label ='do test', command = lambda : self.do_test(None, view))
view.popup.add_command(label ='dummy test', command = print('This one works OK'))
view.button.bind("<Button-1>", lambda e, : self.do_test(e, view))
def do_test(self, event, view):
d = s.SimpleDialog(view,
text="This is a test dialog. "
"Would this have been an actual dialog, "
"the buttons below would have been glowing "
"in soft pink light.\n"
"Do you believe this?",
buttons=["Yes", "No", "Cancel"],
default=0,
cancel=2,
title="Test Dialog")
print(d.go())
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('200x100')
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
view = View(self)
controller = Controller(view)
if __name__ == "__main__":
app = App()
app.mainloop()
It seems to me that the dialog should either work or not work, and not care how it is called. So I would be very grateful for an explanation as to why it responds in one case but not the other, and of course equally grateful for a fix.
The problem appears to lie in the print statement in the do_test callback, as splitting this into two lines fixes it
#print(d.go())
answer = d.go()
print(answer)
As reported in a comment this may be only an issue for MacOS (I am using MacOS 11.1 and Python 3.10.8 ).
I am trying to create an application to show real-time data in a study on electrical systems. The menu will be used to open information on history of each of the graphs shown on the "front page" and will also include a quit option. When I create the menu and add a couple items to it, it does not show up when I run the application. Unfortunately in my searches, all the alternatives I have tried do not show up either.
from tkinter import *
from tkinter import ttk
class PicoGridInterfacing(Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.grid()
menubar = Menu(root)
menu = Menu(menubar)
menubar.add_cascade(label="Menu", menu=menu)
menu.add_command(label="History", command=None)
menu.add_command(label="Quit", command=root.quit)
batteryData = Frame(root)
batteryData.grid(row=1, column=0)
Label(batteryData, text="Hello!").grid()
root = Tk()
root.title("Electric Power Systems Lab Pico Grid Interfacing")
app = PicoGridInterfacing(master=root)
root.mainloop()
Here's a screenshot of the output I see:
Screenshot of output
Any help would be greatly appreciated, thank you!
Try this:
from tkinter import *
from tkinter import ttk
class PicoGridInterfacing(Frame):
def __init__(self, master=None):
super().__init__(master)
# master = master # `super().__init__(master)` already does that so its useless
# Creating the menu
menubar = Menu(root)
menu = Menu(menubar, tearoff=False)
menubar.add_cascade(label="Menu", menu=menu)
menu.add_command(label="History", command=None)
menu.add_command(label="Quit", command=root.destroy)
# Tell the `Tk()` that there is a menu that it need to display:
master.config(menu=menubar)
batteryData = Frame(self)
batteryData.grid(row=1, column=0)
Label(batteryData, text="Hello!").grid()
root = Tk()
root.title("Electric Power Systems Lab Pico Grid Interfacing")
app = PicoGridInterfacing(master=root)
# When you inherit from `Frame` you always what the user to call `.grid`
app.grid()
root.mainloop()
You need to tell the tkinter.Tk() that there is a menu otherwise it will ignore it. Also when inheriting from tkinter.Frame it's the caller's job to call .grid (it's convention).
You need to attach the menu bar to the root window.
So do this: root.config(menu=menubar)
Preface:
I have a Python ControlGPIO code with a working GUI (let us call it MainGUI).
I wish to have a dialog pop up window, prior to running MainGUI, in a way that the user can enable/ disable features in MainGUI. BUT MainGUI should start running only after dialog pop up window is closed.
My question is: How can I make a popup window that will postpone MainGUI untill it is closed?
Code below- boot_windows is my dialog pop up window (where all the enable/disable checkboxes will be ), BUT obviously does not postpone App as I need
root = Tk()
#output_gpioPins = [4,22,6,26]
#input_gpioPins = [3,21,5,27]
#ip = '192.168.2.112'
boot_windows = Toplevel(root)
text1 = ttk.Label(boot_windows, text="Hello World !!!")
text1.grid()
App = ContorlGPIOWindow(root, ip = '192.168.2.113', with_sf_bt=1, with_hw_bt=1, switch_names=['Light Kitchen','Light Room1', 'Window1', 'Window2'])
root.mainloop()
You can't do precisely what you want. Widgets exist in a tree-like structure. All windows except the root require a root window. The root window must be created first (which is why it's called the root window).
If you don't want the user to see it, you can hide it until it is ready to be displayed.
import tkinter as tk
root = tk.Tk()
root.withdraw()
boot_window = tk.Toplevel(...)
...
You can then call root.deiconify() when you are ready for it to be visible.
Another common solution is to use the root window for your dialog or splash screen or whatever, and then simply replace its contents with the real contents when you're ready.
As for how to wait for the popup... the root window has a method named wait_window which will enter the event loop and not return until the given window has been destroyed.
Here's an example of it's use:
import Tkinter as tk
class MainGUI(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Hello, world!")
label.pack(fill="both", expand=True, padx=20, pady=20)
class Popup(tk.Toplevel):
def __init__(self, root):
tk.Toplevel.__init__(self, root)
label = tk.Label(self, text="Click to continue...")
label.pack(fill="both", expand=True, padx=20, pady=20)
button = tk.Button(self, text="OK", command=self.destroy)
button.pack(side="bottom")
if __name__ == "__main__":
root = tk.Tk()
root.withdraw()
popup = Popup(root)
root.wait_window(popup)
main = MainGUI(root)
main.pack(fill="both", expand=True)
root.deiconify()
root.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 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)