19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 | class FluToolTip(FluPopupWindow):
def __init__(
self,
widget: Widget,
text,
mode="light",
delay=400,
show_time=100.0,
*args,
**kwargs,
):
super().__init__(*args, **kwargs)
from .frame import FluFrame
from .label import FluLabel
self.overrideredirect(True)
self._delay = delay
self._show_time = show_time
self._widget = widget
self._frame = FluFrame(self)
self._frame.theme(mode, style="popupmenu")
self._label = FluLabel(self._frame, text=text)
self._label.pack(fill="both", expand=True, padx=3, pady=3)
self._frame.pack(fill="both", expand=True, padx=3, pady=3)
self._widget.bind("<Enter>", self.enter, add="+")
self._widget.bind("<Leave>", self.leave, add="+")
self.theme(mode)
def enter(self, event: Event):
self._enter = True
def check() -> None:
# 延迟期间被悬停的控件可能已经被销毁(切页、刷新列表都很常见),
# 这时读它的 winfo_* 会抛 "bad window path name"。
if not self._enter:
return
try:
if not self._widget.winfo_exists():
return
x = round(
self._widget.winfo_rootx()
+ self._widget.winfo_width() / 2
- self.winfo_width() / 2
)
y = round(
self._widget.winfo_rooty() + self._widget.winfo_height() + 2
)
except Exception:
return
try:
self.popup(x, y)
except Exception:
pass
self.id = self.after(self._delay, check)
def leave(self, event=None):
self._enter = False
after_id = getattr(self, "id", None)
if after_id is not None:
try:
self.after_cancel(after_id)
except Exception:
pass
self.id = None
try:
self.withdraw()
except Exception:
pass
def destroy(self):
"""销毁提示窗口时把它自己排的 ``after`` 撤掉。"""
self.leave()
super().destroy()
def theme(self, mode=None):
from .designs.tooltip import tooltip
n = tooltip(mode)
self.configure(background=n["back_color"])
self.wm_attributes("-transparentcolor", n["back_color"])
# print(n["back_color"])
if hasattr(self, "_frame"):
self._frame.dconfigure(
back_color=n["frame_color"],
border_color=n["frame_border_color"],
border_color_opacity=1,
border_width=2,
radius=7,
)
if hasattr(self, "_label"):
self._label.theme(mode)
super().theme(mode)
|