Search code examples
pythontkintertkinter-canvasmousehover

How to implement a mouse hovering callback on canvas items in tkinter?


I used the code below which I found on internet to implement a mouse hovering action in python:

from tkinter import *
import numpy as np

class rect:
    def __init__(self, root):
        self.root = root
        self.size = IntVar()
        self.canvas = Canvas(self.root, width=800, height=300)
        self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
        self.scale.bind('<ButtonRelease>', self.show)
        self.canvas.bind('<Motion>', self.motion)
        self.board = []
        self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
        self.canvas.pack()
        self.scale.pack()

    def motion(self,event):
        if self.canvas.find_withtag(CURRENT):
            current_color = self.canvas.itemcget(CURRENT, 'fill')
            self.canvas.itemconfig(CURRENT, fill="cyan")
            self.canvas.update_idletasks()
            self.canvas.after(150)
            self.canvas.itemconfig(CURRENT, fill=current_color)

    def show(self,event):
        self.canvas.delete('all')
        x = 50
        y = 50
        row = []
        self.board.clear()
        for i in range(self.scale.get()):
            row = []
            for j in range(self.scale.get()):
                rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
                x += 50
                row.append(rectangle)
            x -= j*50
            y +=50
            self.board.append(row)
        print(self.board)

root = Tk()
a = rect(root)
root.mainloop()

The problem with the execution is that the color of the item changes to blue only for a limited time.

I need the color of each item in the canvas to be changed whenever I enter its zone and remain blue until the mouse is leaving the item.


Solution

  • I changed motion method and added self.last = None to __init__ method:

    from tkinter import *
    import numpy as np
    
    class rect:
        def __init__(self, root):
            self.root = root
            self.size = IntVar()
            self.canvas = Canvas(self.root, width=800, height=300)
            self.scale = Scale(self.root, orient=HORIZONTAL, from_=3, to=20, tickinterval=1, variable=self.size)
            self.scale.bind('<ButtonRelease>', self.show)
            self.canvas.bind('<Motion>', self.motion)
            self.board = []
            self.array = np.zeros((self.scale.get(),self.scale.get())).tolist()
            self.canvas.pack()
            self.scale.pack()
            self.last = None
    
        def motion(self, event):
            temp = self.canvas.find_withtag(CURRENT)
            if temp == self.last:
                self.canvas.itemconfig(CURRENT, fill="cyan")
                self.canvas.update_idletasks()
            else:
                self.canvas.itemconfig(self.last, fill="red")
            self.last = temp
    
        def show(self,event):
            self.canvas.delete('all')
            x = 50
            y = 50
            row = []
            self.board.clear()
            for i in range(self.scale.get()):
                row = []
                for j in range(self.scale.get()):
                    rectangle = self.canvas.create_rectangle(x, y, x + 50, y + 50, fill='red')
                    x += 50
                    row.append(rectangle)
                x -= j*50
                y +=50
                self.board.append(row)
            print(self.board)
    
    root = Tk()
    a = rect(root)
    root.mainloop()