Is there a way to properly isolate hover and click events so that they only apply to the currently active window?
Is there some class-related solution that can prevent this event contamination?
I’m encountering a phantom click and hover issue when opening a second window in my Tkinter + CustomTkinter application.
Please note: I am working with MacOS and my Python Version is 3.11.
Steps to Reproduce the Issue:
- Run the program.
- Click the "Phenotype Selection" button to open the new window.
- Click somewhere on the main GUI, preferably on the edge of the window.
- Without clicking inside the Phenotype Selection window, move your mouse over it.
What Happens:
- The hover effects from the main GUI's buttons get triggered even though the cursor is on the second window.
- It appears that hover and click events are leaking between windows.
- The main application (MainApp) uses overrideredirect(True), meaning it manually handles hover events, but it gets confused when another window is open.
Additional Observations:
- The second window does not use overrideredirect(True), and it behaves normally.
- However, the main GUI starts incorrectly registering hover events, even though the mouse is on the second window.
- We have tried multiple fixes, but nothing has worked.
import tkinter as tk
import customtkinter as ctk
from tkinterdnd2 import DND_FILES, TkinterDnD # Use this import for drag-and-drop support
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
class LayoutBarApp(ctk.CTkFrame):
""" Main Layout Bar with custom buttons. """
def __init__(self, parent):
super().__init__(parent)
self.pack_propagate(False)
self.pack(side="top", fill="x")
self.active_button = None
self.buttons = {}
# Create layout buttons
self.create_layout_buttons()
def open_phenotype_selection_window(self):
""" Opens a new window for Phenotype Selection. """
if hasattr(self, "phenotype_window") and self.phenotype_window.winfo_exists():
self.phenotype_window.lift() # Bring existing window to front
return
self.phenotype_window = ctk.CTkToplevel(self) # Use CTkToplevel for consistency with CTk
self.phenotype_window.title("Phenotype Selection")
self.phenotype_window.geometry("400x300+400+100") # Adjust size/position as needed
self.phenotype_window.resizable(False, False)
# Add a label
label = ctk.CTkLabel(self.phenotype_window, text="Select Phenotype", font=("Arial", 16, "bold"))
label.pack(pady=10)
# Example: Button inside the new window
close_button = ctk.CTkButton(self.phenotype_window, text="Close", command=self.phenotype_window.destroy)
close_button.pack(pady=10)
def create_layout_buttons(self):
""" Create buttons with their icons and commands. """
button_actions = {
"Order Search": None,
"Phenotype Selection": self.open_phenotype_selection_window, # ✅ Assigned function
"Bounding Box": None,
"Landmark Movement": None,
"Face Measurement": None,
}
for btn_text, action in button_actions.items():
button = ctk.CTkButton(
self,
text=btn_text.replace(" ", "\n"),
command=lambda b=btn_text, action=action: self.activate_button(b, action), # ✅ Corrected lambda
fg_color=("#3b8ed0", "#1f6aa5"),
hover_color=("#2a75c0", "#155a8a")
)
button.pack(side="left", padx=(5, 0), pady=5)
self.buttons[btn_text] = button
def activate_button(self, button_name, action):
""" Handles button activation (highlighting) and executes the assigned action. """
# ✅ Get the actual root coordinates to adjust hover issues
root_x = self.winfo_rootx()
root_y = self.winfo_rooty()
mouse_x = self.winfo_pointerx() - root_x
mouse_y = self.winfo_pointery() - root_y
print(f"Mouse Position Adjusted: ({mouse_x}, {mouse_y}), Button:{button_name} Self:{self}, Action{action}") # ✅ Debugging hover issue
# Reset previous button color
if self.active_button:
self.buttons[self.active_button].configure(
fg_color=("#3b8ed0", "#1f6aa5"),
hover_color=("#2a75c0", "#155a8a")
)
# Set new active button
self.active_button = button_name
self.buttons[button_name].configure(
fg_color="green",
hover_color="#006400"
)
# Execute the function if it exists
if action:
action()
class MainApp(TkinterDnD.Tk):
""" Root Tkinter App holding both Sidebar and Layout Bar. """
def __init__(self):
super().__init__()
self.overrideredirect(True) # Removes system title bar
self.geometry("782x60+330+0")
self.resizable(False, False)
self.title("GUI")
# Main Layout Bar
self.layout_bar = LayoutBarApp(self)
if __name__ == "__main__":
app = MainApp()
app.mainloop()