I have been looking for a way to display a right-click popup menu on OSX.
So far all of my attempts have been unsuccessful. The same code will work fine on a Linux VM(Ubuntu).
For arguments sake I copied the code written in these two pages and tried to run them on my machine.
tkinter app adding a right click context menu?
http://effbot.org/zone/tkinter-popup-menu.htm
Neither have worked in the way I expect them to on OSX but they do when I run them on an Ubuntu VM.
The machine I am using is a Mac Mini4,1 running OSX 10.6.8.
Has anyone else experienced this and is there a viable workaround?
For odd historical reasons, the right button is button 2 on the Mac, but 3 on unix and windows.
Here is an example that works on my OSX box:
try:
# python 2.x
import Tkinter as tk
except ImportError:
# python 3.x
import tkinter as tk
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.popupMenu = tk.Menu(self, tearoff=0)
self.popupMenu.add_command(label="One", command=self.menu_one)
self.popupMenu.add_command(label="Two", command=self.menu_two)
self.popupMenu.add_command(label="Three", command=self.menu_three)
self.bind("<Button-2>", self.popup)
def menu_one(self):
print "one..."
def menu_two(self):
print "two..."
def menu_three(self):
print "three..."
def popup(self, event):
self.popupMenu.post(event.x_root, event.y_root)
if __name__ == "__main__":
root =tk.Tk()
frame = Example(root, width=200, height=200)
frame.pack(fill="both", expand=True)
root.mainloop()
Because of the MacOS's mouse button index is different from Windows or Linux .You can try this in your code:
MAC_OS = False
if sys.platform == 'darwin':
MAC_OS = True
if MAC_OS:
self.bind('<Button-2>', self.context_popup)
else:
self.bind('<Button-3>', self.context_popup)
Related
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 try to make a download interface via tkinter. The following code works well in Pycharm. But when I try to make it an exe file using pyinstaller, it opens a new window every time I click on button1(the download button), which is undesired. Could you please tell me how to fix it? I'm working on Win10. Thanks in advance.
import tkinter as tk
from download import download_yv as dyv
import multiprocessing
class DownloadPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid()
self.download_flag=True
self.current_row=0
self.current_rowspan=0
self.label1=tk.Label(self,text='Url:',)
self.label1.grid(row=self.current_row,column=0,)
self.text1=tk.Entry(self,width=45)
self.text1.grid(row=self.current_row,column=1,padx=0,pady=0)
self.current_row+=1
self.label2=tk.Label(self,text='Save_Dir:',wraplength=200,)
self.label2.grid(row=self.current_row,column=0)
self.text2=tk.Entry(self,width=45)
self.text2.grid(row=self.current_row,column=1,padx=0,pady=0,)
self.current_row+=1
self.label3=tk.Label(self,text='Overwrite:',)
self.label3.grid(row=self.current_row,column=0)
self.var1=tk.BooleanVar(self)
self.var1.set('False')
self.menu1=tk.OptionMenu(self,self.var1,'True','False')
self.current_rowspan=2
self.menu1.grid(row=self.current_row,column=1,columnspan=2,rowspan=self.current_rowspan,sticky='news')
self.current_row+=self.current_rowspan
self.label4=tk.Label(self,text='Categorize:')
self.label4.grid(row=self.current_row,column=0)
self.var2 = tk.BooleanVar(self)
self.var2.set(True)
self.menu2=tk.OptionMenu(self,self.var2,'True','False')
self.menu2.grid(row=self.current_row,column=1,columnspan=2,rowspan=self.current_rowspan,sticky='news')
self.current_row+=self.current_rowspan
self.current_rowspan=2
self.button1=tk.Button(self,text='download',command=self.download)
self.button1.grid(row=self.current_row,column=1,columnspan=2,rowspan=self.current_rowspan,sticky='news')
self.current_row+=self.current_rowspan
self.button2=tk.Button(self,text='Stop Downloading',command=self.stop_download)
self.button2.grid(row=self.current_row,column=1,columnspan=1,)
def download(self):
var1=self.var1.get()
var2=self.var2.get()
# print(var1,type(var1),var2,type(var2))
self.download_process=multiprocessing.Process(target=dyv,args=(self.text1.get(),self.text2.get(),var1,var2))
self.download_process.start()
def stop_download(self):
if self.download_process and self.download_process.is_alive():
self.download_process.terminate()
if __name__ == '__main__':
root=tk.Tk()
app=DownloadPage(root)
app.mainloop()
Why does the following test not display the menu on my Mac running Python 3.7 on Mojave?
Tried a shebang but that didn't help.
import tkinter as tk
def quit_app():
my_window.destroy()
my_window = tk.Tk()
my_menu = tk.Menu(my_window)
my_menu.add_command(label='Quit',command=quit_app)
my_window.config(menu=my_menu)
my_window.mainloop()
The menu displays in Windows 10 but not on the Mac. The tkinter window is blank on the Mac.
The menu displays in Windows 10 but not on the Mac
Yes, that is because MAC has a design philosophy that all devs for their platform have to conform with.
You cannot have a add_command as a top level menu item on mac, rather:
import tkinter as tk
def quit_app():
my_window.destroy()
my_window = tk.Tk()
my_menu = tk.Menu(my_window)
quit_menu= tk.Menu(my_menu, tearoff=0)
quit_menu.add_command(label='Quit App',command=quit_app)
my_menu.add_cascade(label="Quit", menu=quit_menu, underline=0)
my_window.config(menu=my_menu)
my_window.mainloop()
I'm trying to create a screen "curtain" which blocks parts of the screen except for the mouse cursor's vicinity.
On windows, using root.wm_attributes("-topmost", "true") keeps the window on top, even if I focus on another app, perfectly. However, upon running the code on MacOS, if the focus for the window is lost, it will not keep itself on topmost.
What would be the MacOS equivalent to -topmost window manager attribute which will always keep the window on top, regardless of focus?
import tkinter as tk
class TransparentWindow(tk.Toplevel):
"""
This class is just a Toplevel window.
"""
def __init__(self, background="white", opacity=0.7):
super(TransparentWindow, self).__init__()
#self.master = master
self.configure(background=background)
self.overrideredirect(True)
self.wm_attributes("-alpha", opacity)
self.wm_attributes("-topmost", "true")
self.lift()
if __name__ == '__main__':
root = tk.Tk()
TransparentWindow()
root.mainloop()
Running this code in a High Sierra Virtual Machine resulted in the Toplevel not constantly being on top when another window is selected.
On Mac OS using overrideredirect(True) disables a lot of stuff like bind, Button presses and some events, honestly I don't know why exactly. (If anyone knows please comment). At least on my Mac I have this problem, I've read and seen that not all of Mac users have this problem.
So this is why root.wm_attributes("-topmost", "true") is not working. But don't worry I got a workaround.
From your code I can tell that you want a borderless window, here is how I do it with all bindings and event working still.
I first put overrideredirect(True) then in the next line
overrideredirect(False)
Also you don't need root.lift() in this case.
Ok try this code and see if the button press normally.
Sample
import tkinter as tk
root = tk.Tk()
root.overrideredirect(True)
# root.overrideredirect(False) # Uncomment and try again.
tk.Button(root, text="Borderless").pack()
root.wm_attributes("-topmost", "true")
root.wm_attributes("-alpha", 0.7)
root.wm_attributes("-topmost", "true")
# Doesn't matter if you use lift() or not with the use of root.overrideredirect(False) as well
root.lift()
root.mainloop()
I hope this helped you.
Here is your code which worked exactly you want (At least on my Mac).
import tkinter as tk
class TransparentWindow(tk.Toplevel):
"""
This class is just a Toplevel window.
"""
def __init__(self, background="white", opacity=0.7):
super(TransparentWindow, self).__init__()
#self.master = master
self.configure(background=background)
self.overrideredirect(True)
self.overrideredirect(False)
self.wm_attributes("-alpha", opacity)
self.wm_attributes("-topmost", "true")
# self.lift()
if __name__ == '__main__':
root = tk.Tk()
TransparentWindow()
root.mainloop()
Treeview with mouseclick binding that directly calls a function to open a modal window fails on grab_set()
tkinter.TclError: grab failed: window not viewable
The same works perfectly well if the mouseclick first opens a popup menu that calls the modal window function.
Is it not possible to open a modal window directly from a mouseclick (preferably doubleclick)?
Linux Mint 17/Ubuntu 14.04, 64bit, Python 3
My simple code example below. Please note that though I call two different functions, the code in each is essentially the same. When called from the popup menu it gives a modal window, when called directly from the treeview, it fails miserably. Is that expected behavior?
import tkinter as tk
from tkinter import ttk
class PopupWindow(tk.Toplevel):
def __init__(self,parent):
tk.Toplevel.__init__(self,parent)
self.parent=parent
self.protocol("WM_DELETE_WINDOW",self.destroy)
self.attributes('-topmost',True)
self.transient()
mainWindow=tk.Frame(self)
tFrame=tk.Frame(mainWindow)
self.tLabel=tk.Label(tFrame,text='Is this modal?',height=3,bd=10)
self.tLabel.pack()
self.tLabel2=tk.Label(tFrame,text=' ',height=3,bd=10)
self.tLabel2.pack()
bFrame=tk.Frame(mainWindow,bd=5)
bOK=tk.Button(bFrame,bd=1,text='Ok',command=self.destroy)
bOK.pack(side='left')
tFrame.grid(row=0,column=0)
bFrame.grid(row=1,column=0,sticky=tk.E)
mainWindow.pack()
class App:
def __init__(self):
self.root = tk.Tk()
self.tree = ttk.Treeview()
self.tree.pack()
for i in range(10):
self.tree.insert("", "end", text="Item %s" % i)
self.tree.bind("<Double-1>", self.onDoubleClick)
self.tree.bind("<Button-3>", self.onRightClick)
self.aMenu = tk.Menu(self.root, tearoff=0)
self.aMenu.add_command(label="Show 'Modal' Window",
command=lambda selection=self.tree.selection(): self.showIsModal(selection))
self.aMenu.add_separator()
self.root.mainloop()
def onRightClick(self, event):
try:
self.aMenu.selection = self.tree.identify_row(event.y)
self.aMenu.post(event.x_root, event.y_root)
finally:
self.aMenu.grab_release()
def showIsModal(self, item):
pup=PopupWindow(self.root);
pup.tLabel2['text']="Absolutely!"
pup.grab_set()
self.root.wait_window(pup)
def onDoubleClick(self, event):
item = self.tree.identify('item',event.x,event.y)
self.tree.grab_release()
self.showIsNotModal(item)
def showIsNotModal(self, item):
print("you clicked on", self.tree.item(item,"text"))
pup=PopupWindow(self.root);
pup.tLabel2['text']="Sadly no: grab_set() fails"
# following line will fail:
pup.grab_set()
self.root.wait_window(pup)
if __name__ == "__main__":
app = App()
I use Linux Mint 17/Ubuntu 14.04, 64bit, Python 3 too.
You can always check source code of existing dialogs and see how it works.
For example check filedialogs - path to file with source code:
import tkinter.filedialog
print(tkinter.filedialog.__file__)
# /usr/lib/python3.5/tkinter/filedialog.py
and you will see
self.top.wait_visibility() # window needs to be visible for the grab
self.top.grab_set()
So you have to use wait_visibility() before grab_set()
def showIsNotModal(self, item):
print("you clicked on", self.tree.item(item, "text"))
pup = PopupWindow(self.root);
pup.tLabel2['text'] = "Sadly no: grab_set() fails"
pup.wait_visibility() # <-----
pup.grab_set()
self.root.wait_window(pup)