Tkinter <Enter> 绑定循环永远



我正在尝试为事件制作一个可选择的时钟面。我有我的画布对象,并创建了一堆圆圈,里面有文字。

当鼠标悬停在上面时,我想反转圆圈和文本的颜色。

问题是:

  • 在圆圈内输入文本时不检测。
  • 移动太快会导致重复的Enter/Leave循环,导致程序崩溃。
  • 代码:

from tkinter import *
from math import sin, cos, pi
root = Tk()
class TimePicker(Canvas):
def __init__(self, master, **kwargs):
self.tformat = kwargs.pop('format', 24)
self.width, self.height, self.radious = kwargs['width'], kwargs['height'], rd = (kwargs.pop('radious', 100)*2, )*3
self.radious /=4
assert self.tformat in [12, 24], "Time Format must be '12' or '24'"
super(TimePicker, self).__init__(master, **kwargs)
self.active_line = None
self.create_center_circle(self.width/2, self.height/2, self.radious*2, fill="white", outline="#000", width=1)
self.circle_numbers(self.width/2, self.height/2, self.radious+5, 10, [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 'Helvetica 11 bold', "Hours")
self.circle_numbers(self.width/2, self.height/2, self.radious*2-15, 10, [0, 15, 30, 45], 'Helvetica 11 bold', "Minutes")
def create_center_circle(self, x, y, r, **kwargs):
return super().create_oval(x-r, y-r, x+r, y+r, **kwargs)
def create_circle_arc(self, x, y, r, **kwargs):
if "start" in kwargs and "end" in kwargs:
kwargs["extent"] = kwargs["end"] - kwargs["start"]
del kwargs["end"]
return super().create_arc(x-r, y-r, x+r, y+r, **kwargs)
def circle_numbers(self, x: int, y: int, r: int, cr:int, numbers: list, font: str, tp:str):
_angle = 360/len(numbers)
for n in numbers:
ax =  r * sin(pi * 2 * (360-_angle*n-180) / 360);
ay = r * cos(pi * 2 * (360-_angle*n-180) / 360);
tag = f'{tp}:{str(n)}'
cl = self.create_center_circle(x+ax, y+ay, cr, fill="white", outline="#000", width=1, tag=tag)
tx = self.create_text(x+ax, y+ay, text=str(n).zfill(2), fill="black", font=(font), tag='tx'+tag )
self.tag_bind(f'{tp}:{str(n)}', '<Enter>', lambda e=Event(), c=(x+ax, y+ay), t=tag, s=True: self._hover(e, c, s, t))
self.tag_bind(f'{tp}:{str(n)}', '<Leave>', lambda e=Event(), c=(x+ax, y+ay), t=tag, s=False: self._hover(e, c, s, t))
self.tag_bind(f'{tp}:{str(n)}', '<Button-1>', lambda e=Event(), c=cl, s=tx, n=n, t=tp,: self._set_number(e, c, s, n, t))
def _hover(self, event, coords, state, tag):
print('hover')
if state: # If hovering inside the object
print("hovering")
cl =event.widget.find_withtag(tag) ## for the text and circle with the hovered tag
tx = event.widget.find_withtag('tx'+tag)
self.itemconfigure(cl, fill='black')
self.itemconfigure(tx, fill="white")
self.active_line = self.create_line(self.width/2, self.height/2, coords[0], coords[1], fill="black", width=2) ##create new line
else: ##If left the object
print("exited")
if self.active_line is not None: ##if there is a line
cl = event.widget.find_withtag(tag) ## for the text and circle in the tag
tx = event.widget.find_withtag('tx'+tag)
self.itemconfigure(cl, fill='white')
self.itemconfigure(tx, fill="black")
self.delete(self.active_line)
self.active_line = None
def _set_number(self, event, cl, tx, number, tp):
print('set', cl, tx, number, tp)


if __name__ == '__main__':
tp=TimePicker(root, format=24, background="red", radious=100)
tp.pack(side=TOP)
root.mainloop()

我在测试时意识到,当创建一行时,该行恰好位于鼠标光标下方,<Leave>的事件将触发文本,然后删除行,导致<Enter>事件再次在光标下方绘制线。

解决这个问题的方法是使行变短,并且不允许它进入与鼠标相同的空间。

作为一个快速修复,我已经使行缩短了20%,但是一个完整的解决方案将需要检查行是否在鼠标下并触发标志以防止行被破坏。

def _hover(self, event, coords, state, tag):
...
self.itemconfigure(tx, fill="white")
dx = (1 - 0.8) * self.width/2 + 0.8 * coords[0]
dy = (1 - 0.8) * self.height/2 + 0.8 * coords[1]
self.active_line = self.create_line(self.width/2, self.height/2, dx, dy, fill="#0797FF", width=2, tag=None) ##create new line

最新更新