Skip to content

Commit d3e4665

Browse files
committed
Fix Windows title menu lifecycle bugs and preserve host Configure bindings
1 parent 75f7400 commit d3e4665

1 file changed

Lines changed: 63 additions & 6 deletions

File tree

CTkMenuBar/title_menu_win.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def __init__(
7171

7272
self.after(10)
7373
self.master = master
74+
self._master_bind_ids = {}
7475
master_type = self.master.winfo_name()
7576

7677
if master_type=="tk":
@@ -118,18 +119,65 @@ def __init__(
118119

119120
self.padding = padx
120121

121-
self.master.bind("<Configure>", lambda _: self.change_dimension())
122-
self.master.bind("<Destroy>", lambda _: self.destroy_window() if not self.master.winfo_viewable() else None)
122+
self._master_bind_ids["<Configure>"] = self.master.bind("<Configure>", self._on_master_configure, add="+")
123+
self._master_bind_ids["<Destroy>"] = self.master.bind("<Destroy>", self._on_master_destroy, add="+")
123124
self.num = 0
124-
125-
self.master.bind("<Map>", lambda e: self.withdraw)
125+
self._master_bind_ids["<Map>"] = self.master.bind("<Map>", self._on_master_map, add="+")
126+
self._master_bind_ids["<Unmap>"] = self.master.bind("<Unmap>", self._on_master_unmap, add="+")
127+
128+
def _on_master_configure(self, _event=None):
129+
if _event is not None and _event.widget is not self.master:
130+
return
131+
self.change_dimension()
132+
133+
def _on_master_destroy(self, _event=None):
134+
if _event is not None and _event.widget is not self.master:
135+
return
136+
try:
137+
if not self.master.winfo_viewable():
138+
self.destroy_window()
139+
except tk.TclError:
140+
self.destroy_window()
141+
142+
def _on_master_map(self, _event=None):
143+
if _event is not None and _event.widget is not self.master:
144+
return
145+
try:
146+
self.deiconify()
147+
self.change_dimension()
148+
except tk.TclError:
149+
pass
150+
151+
def _on_master_unmap(self, _event=None):
152+
if _event is not None and _event.widget is not self.master:
153+
return
154+
try:
155+
self.withdraw()
156+
except tk.TclError:
157+
pass
158+
159+
def _unbind_master_events(self):
160+
for sequence, bind_id in self._master_bind_ids.items():
161+
try:
162+
self.master.unbind(sequence, bind_id)
163+
except (tk.TclError, AttributeError):
164+
pass
165+
self._master_bind_ids.clear()
126166

127167
def destroy_window(self):
128168
"""
129169
Destroy the title menu window.
130170
"""
171+
self._unbind_master_events()
172+
if not self.winfo_exists():
173+
return
131174
super().destroy()
132175

176+
def destroy(self):
177+
self._unbind_master_events()
178+
if self.winfo_exists():
179+
super().destroy()
180+
133181
def _set_appearance_mode(self, mode_string):
134182
"""
135183
Update the title bar color based on the current appearance mode.
@@ -205,6 +253,12 @@ def change_dimension(self):
205253
- Minimized ("iconic"): Hides the menu
206254
- Too small: Hides the menu if width becomes negative
207255
"""
256+
try:
257+
if not self.winfo_exists() or not self.master.winfo_exists():
258+
return
259+
except tk.TclError:
260+
return
261+
208262
width = self.master.winfo_width()-130-self.x_offset
209263
if width<0:
210264
self.withdraw()
@@ -218,8 +272,11 @@ def change_dimension(self):
218272
if self.master.state()=="zoomed":
219273
y += 4
220274
x -= 7
221-
self.geometry(f"{width}x{height}+{x}+{y}")
222-
self.deiconify()
275+
try:
276+
self.geometry(f"{width}x{height}+{x}+{y}")
277+
self.deiconify()
278+
except tk.TclError:
279+
return
223280

224281
def change_header_color(self, caption_color):
225282
"""

0 commit comments

Comments
 (0)