Search code examples
pythonmacosuser-interfacetkinterwindow

tkinter keeping window on top all times on MacOS


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.


Solution

  • 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.

    Here is your example which works exactly you want on my Mac.

    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()