1

Is it possible to get the popup option (pop function in the code) on hovering the cursor on the button?

import tkinter as tk
from tkinter import Tk, BOTH, Menu

def pop(bt):
    try:
        x = bt.winfo_rootx()+238
        y = bt.winfo_rooty()+10
        popup.tk_popup(x, y, 0)
    finally:
        popup.grab_release()


root = tk.Tk()

popup = Menu(root, tearoff=0,relief='raised')
popup.add_command(label="About")
popup.add_command(label="User manual")
popup.add_command(label="Contact us")

button1 =tk.Button(root, text="HELP",height=3,width=26,command=lambda: controller.show_frame(HelpPage))                   
button1.configure(command = lambda: pop(button1))
button1.place(x=0,y=0
              )
root.mainloop()

.

button1.bind('<Enter>',pop(button1)) #gives the following output without the mouse cursor over that button.

enter image description here

Jung-suk
  • 172
  • 2
  • 12
  • 1
    You can use bind sequences `` and `` to *post* and *unpost* the menu resp. – Saad Mar 06 '21 at 11:43
  • @Saad, I have tried this method before but getting the above result( please find the screenshot ) – Jung-suk Mar 06 '21 at 13:01
  • 1
    By default, an argument is passed to the callback function in an event. So it should be `button1.bind('', lambda e: pop(button1))`. Also, *please note `` is not the same as ``*, it should start with capital "E". – Saad Mar 06 '21 at 17:23
  • @Saad, Thanks its working. Do you know how to hide the popup when the cursor is not on the button? i know this is not in the question but i couldn't find it so please help me if you know. – Jung-suk Mar 06 '21 at 17:33

3 Answers3

1

Try this:

import tkinter as tk

window = None

def leave_window(event):
    global window
    if 0 < root.winfo_pointerx() - root.winfo_rootx() < button.winfo_width():
        if 0 < root.winfo_pointery() - root.winfo_rooty() < button.winfo_height():
            # Mouse still over button
            return None
    if window is not None:
        window.destroy()
        window = None

def create_window(event):
    global window
    if window is not None:
        # The window is already open
        return None
    window = tk.Toplevel(root)
    window.overrideredirect(True)
    label = tk.Label(window, text="Text", bg="black", fg="white")
    label.pack()
    window.update()
    # Move the window to the cursor's
    x = root.winfo_pointerx()
    y = root.winfo_pointery()-window.winfo_height()
    window.geometry("+%i+%i" % (x, y))
    window.bind("<Leave>", leave_window)


root = tk.Tk()
button = tk.Button(root, text="-------- Hover the mouse here --------")
button.pack(fill="both", expand=True)
button.bind("<Enter>", create_window)
button.bind("<Leave>", leave_window)

root.mainloop()

I binded to <Enter> and <Leave> to check if the mouse is over the button. If it is, it creates a window with a label called text. You can change it to it showing the menu.

For more answers look here.

TheLizzard
  • 7,248
  • 2
  • 11
  • 31
0

Please bind your Button with mouse enter and call pop function. Check out this code :

button1.bind('<Enter>', pop)

Control your popup location as well as destroy popup on '' case again bind it and call custom function that will destroy popup.

Davinder Singh
  • 2,060
  • 2
  • 7
  • 22
  • I have tried this method before but the result is different. please find the attached screenshot. @ExplooreX – Jung-suk Mar 06 '21 at 12:57
  • Your answer will give an error because in the function `pop` the parameter `bt` doesn't have `winfo_rooty()` functions. – Saad Mar 06 '21 at 17:24
  • @Saad thanks for that point : `button1.bind('', pop)` change your code to `button1.bind('',lambda eff: pop(button1)) ` – Davinder Singh Mar 06 '21 at 17:38
  • `x = bt.winfo_rootx()+238` `y = bt.winfo_rooty()+10` `popup.tk_popup(x, y, 0)` also add these line of code which I didn't considered – Davinder Singh Mar 06 '21 at 17:39
0

You need <Enter> and <Leave> bind sequences to map and unmap the Menu. Tkinter Menu has two methods post and unpost where post shows the menu at given coordinates, and unpost hides it away. Unfortunately, I couldn't test it as the unpost functionality doesn't work on macOS or Linux [refer to this link for the same]. I also changed the x, y coords to map the Menu in the center of the widget (Button), it can be changed if required.


Here is the complete sample code.

import tkinter as tk
from tkinter import Tk, BOTH, Menu


def pop(evt):
    but = evt.widget
    if str(evt.type) == "Enter":
        # Map the menu in the center of the width.
        x = but.winfo_rootx() + int(but.winfo_width()/2)
        y = but.winfo_rooty() + int(but.winfo_height()/2)
        popup.tk_popup(x, y)

    elif str(evt.type) == "Leave":
        popup.unpost()


root = tk.Tk()
root.geometry("300x300")

popup = Menu(root, tearoff=0, relief='raised')
popup.add_command(label="About")
popup.add_command(label="User manual")
popup.add_command(label="Contact us")

button1 = tk.Button(root, text="HELP", height=3, width=26)
button1.bind('<Enter>', pop)
button1.bind('<Leave>', pop)
button1.pack(pady=100)

root.mainloop()

Like said, unpost doesn't work on macOS or Linux, so I couldn't test the sample code 100% but it should work fine.

Saad
  • 3,340
  • 2
  • 10
  • 32