Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 23131

detect when mouse hovers/leaves a tkinter canvas element that moves by itself, because the canvas element moved, not the mouse

$
0
0

I have a program with a canvas text element that moves around. I use tag_bind to detect when a mouse hovers and leaves the element. It fails when the element itself moves into or away from the mouse.

import mathclass Easing:    @staticmethod    def linear(t, b, c, d):        return c*t/d + b    @staticmethod    def inSine(t, b, c, d):        return -c * math.cos(t/d * (math.pi/2)) + c + bdef apply_easing(easing, t, beginning, destination, duration):    #TODO: make this more efficient if possible    return {key: easing(t, beginning[key], destination[key] - beginning[key], duration) for key in destination.keys()}class Tween:    easing = Easing()    _playing = []    def __init__(self, duration, subject, target, easing=easing.linear, options={"loop":"forever"}):        self.duration = duration        self.subject = subject        self.target = target        self.initial = {key: self.subject[key] for key in self.target.keys()}        self.easing = easing        self.loop = options.get("loop", "once") if options is not None else "once"        self.playbackdirection = "forward"        self.elapsed_time = 0        self.play()    def play(self):        if not self in Tween._playing:            Tween._playing.append(self)    def stop(self):        Tween._playing.remove(self)    def update(self, deltatime):        if self.elapsed_time >= self.duration:            if self.loop == "once":                self.stop()            if self.loop == "forever":                self.playbackdirection = "backward"        if self.elapsed_time <= 0:            self.playbackdirection = "forward"        if self.playbackdirection == "forward":            self.elapsed_time += deltatime        elif self.playbackdirection == "backward":            self.elapsed_time -= deltatime        result = apply_easing(self.easing, self.elapsed_time,             self.initial,             self.target,             self.duration)        self.subject.update(result)        return resultfrom tkinter import *import timeWIDTH=640HEIGHT=480root = Tk()root.title("Minimal Example")root.geometry(f"{WIDTH}x{HEIGHT}")class InteractiveText:    def __init__(self, canvas, x, y, content, default_fill='black'):        self.position = {"x":x, "y":y}        self.content = content        self.canvas = canvas        self.default_fill = default_fill        self.text = canvas.create_text(self.position["x"], self.position["y"], text=self.content, font = ("monaco", 24), fill=default_fill)        self.canvas.tag_bind(self.text, '<Enter>', lambda event: self.on_hover())        self.canvas.tag_bind(self.text, '<Leave>', lambda event: self.on_leave())        self.canvas.tag_bind(self.text, '<Button-1>', self.on_click)    def update_position(self):        self.canvas.coords(self.text, self.position["x"], self.position["y"])    def on_hover(self):                self.canvas.itemconfig(self.text, fill="red")    def on_leave(self):        self.canvas.itemconfig(self.text, fill=self.default_fill)    def on_click(self, event):        print(self.content, "clicked!")class Scene:    def __init__(self):        self.canvas = Canvas(root, width=WIDTH, height=HEIGHT, bg="white")        self.canvas.pack()        self.canvas_items = []    def text(self, x, y, content):        t = InteractiveText(self.canvas, x, y, content)        self.canvas_items.append(t)        return t    def rect(self, x, y, w, h):        r = MyRect(self.canvas, x, y, w, h)        self.canvas_items.append(r)        return rdef update(lastFrameTime):    currentTime = time.time()    for tw in Tween._playing:        tw.update(currentTime - lastFrameTime)    for obj in CURRENT_SCENE.canvas_items:        obj.update_position()    next_deltatime = int(round(lastFrameTime + 1/60*1000 - currentTime, 0))    root.after(next_deltatime, update, currentTime)root.after(0, update, time.time())mainmenu =Scene()CURRENT_SCENE = mainmenu# There are the buttons for the Main Menuplaytext = mainmenu.text(-100, 65, "Play game")quittext = mainmenu.text(WIDTH+150, 150, "Quit")Tween(4.99, playtext.position, {"x": WIDTH/2+25, "y":50},  Tween.easing.linear)Tween(4.99, quittext.position, {"x": WIDTH/2, "y":390}, Tween.easing.inSine)root.mainloop()

I know the program is a little long for a SO quesiton but its mostly boilerplate. The important part is in the InteractiveText class, where I bind the mouse events to the canvas item. When the canvas item leaves the mouse behind on its own, it doesn't trigger a hover/leave event and thus doesn't trigger the functions on_leave and on_hover.

Expected hover to end after element coords changed so that the mouse was outside the element.


Viewing all articles
Browse latest Browse all 23131

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>