Skip to content

Commit c1fa385

Browse files
OmkarSarkar204amilcarlucas
authored andcommitted
fix(frontend_tkinter_show): timer and on-demand load along with fast loading on large parameter table
Signed-off-by: Omkar Sarkar <omkarsarkar24@gmail.com>
1 parent 7c0e6fd commit c1fa385

2 files changed

Lines changed: 304 additions & 120 deletions

File tree

ardupilot_methodic_configurator/frontend_tkinter_show.py

Lines changed: 28 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
TOOLTIP_MAX_OFFSET = 100 # Maximum horizontal offset from widget edge
2424
TOOLTIP_VERTICAL_OFFSET = 10 # Vertical offset when positioning above widget
2525
TOOLTIP_SHOW_DELAY_MS = 250 # Delay before showing to avoid flicker while moving across dense UIs
26-
TOOLTIP_HIDE_DELAY_MS = 75 # Small delay prevents leave/enter jitter from leaving stale tooltips behind
2726

2827

2928
class MonitorBounds(NamedTuple):
@@ -475,35 +474,16 @@ def __init__( # pylint: disable=too-many-arguments, too-many-positional-argumen
475474
self.position_below: bool = position_below
476475
self.toplevel_class = toplevel_class or tk.Toplevel
477476
self.timers: dict[str, Optional[str]] = {}
477+
self._is_aqua: bool = widget.tk.call("tk", "windowingsystem") == "aqua"
478478

479479
# Bind the <Enter> and <Leave> events to show and hide the tooltip
480-
if platform_system() == "Darwin":
481-
# On macOS, defer tooltip creation slightly to avoid flashing while
482-
# moving through dense tables and controls.
483-
if tag_name and isinstance(self.widget, tk.Text):
484-
self.widget.tag_bind(tag_name, "<Enter>", self.schedule_show, "+")
485-
self.widget.tag_bind(tag_name, "<Leave>", self.destroy_hide, "+")
486-
else:
487-
self.widget.bind("<Enter>", self.schedule_show, "+")
488-
self.widget.bind("<Leave>", self.destroy_hide, "+")
480+
# Defer tooltip creation slightly to avoid flashing while moving through dense tables.
481+
if tag_name and isinstance(self.widget, tk.Text):
482+
self.widget.tag_bind(tag_name, "<Enter>", self.schedule_show, "+")
483+
self.widget.tag_bind(tag_name, "<Leave>", self.destroy_hide, "+")
489484
else:
490-
if tag_name and isinstance(self.widget, tk.Text):
491-
self.widget.tag_bind(tag_name, "<Enter>", self.show, "+")
492-
self.widget.tag_bind(tag_name, "<Leave>", self.hide, "+")
493-
else:
494-
self.widget.bind("<Enter>", self.show, "+")
495-
self.widget.bind("<Leave>", self.hide, "+")
496-
# On non-macOS, create the tooltip immediately and show/hide it on events
497-
self.tooltip = cast("tk.Toplevel", self.toplevel_class(widget))
498-
self.tooltip.wm_overrideredirect(boolean=True)
499-
tooltip_label = ttk.Label(
500-
self.tooltip, text=text, background="#ffffe0", relief="solid", borderwidth=1, justify=tk.LEFT
501-
)
502-
tooltip_label.pack()
503-
self.tooltip.withdraw() # Initially hide the tooltip
504-
# Bind to tooltip to prevent hiding when mouse is over it
505-
self.tooltip.bind("<Enter>", self._cancel_hide)
506-
self.tooltip.bind("<Leave>", self.hide)
485+
self.widget.bind("<Enter>", self.schedule_show, "+")
486+
self.widget.bind("<Leave>", self.destroy_hide, "+")
507487

508488
self.widget.bind("<Destroy>", self._on_widget_destroy, "+")
509489

@@ -517,22 +497,9 @@ def _cancel_timer(self, name: str) -> None:
517497
def _cancel_show(self) -> None:
518498
self._cancel_timer("show")
519499

520-
def show(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
521-
"""On non-macOS, tooltip already exists, show it on events."""
522-
self._cancel_hide()
523-
self._hide_active_tooltip()
524-
if self.tooltip:
525-
self.position_tooltip()
526-
self.tooltip.deiconify()
527-
Tooltip._active_tooltip = self
528-
529-
def _cancel_hide(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
530-
self._cancel_timer("hide")
531-
532500
def _on_widget_destroy(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
533501
"""Stop any active timers if the widget is destroyed."""
534502
self._cancel_show()
535-
self._cancel_hide()
536503
self._cancel_timer("alpha")
537504

538505
if self.tooltip:
@@ -547,16 +514,14 @@ def _hide_active_tooltip(self) -> None:
547514
Tooltip._active_tooltip.force_hide()
548515
Tooltip._active_tooltip = None
549516

550-
def schedule_show(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
517+
def schedule_show(self, _event: Optional[tk.Event] = None) -> None:
551518
"""Delay tooltip creation slightly to avoid flicker during pointer movement."""
552-
self._cancel_hide()
553519
self._cancel_show()
554520
self.timers["show"] = self.widget.after(TOOLTIP_SHOW_DELAY_MS, self.create_show)
555521

556-
def create_show(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
557-
"""On macOS, only create the tooltip when the mouse enters the widget."""
522+
def create_show(self, _event: Optional[tk.Event] = None) -> None:
523+
"""Create and show the tooltip when the pointer is still over the widget after the delay."""
558524
self._cancel_show()
559-
self._cancel_hide()
560525

561526
try:
562527
pointed = self.widget.winfo_containing(self.widget.winfo_pointerx(), self.widget.winfo_pointery())
@@ -577,22 +542,21 @@ def create_show(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002
577542
self.tooltip.wm_overrideredirect(True) # noqa: FBT003
578543
self.tooltip.withdraw()
579544

580-
if self.widget.tk.call("tk", "windowingsystem") == "aqua":
545+
if self._is_aqua:
581546
self.tooltip.attributes("-alpha", 0.0)
582547

583-
try:
584-
self.tooltip.tk.call(
585-
"::tk::unsupported::MacWindowStyle",
586-
"style",
587-
self.tooltip._w, # type: ignore[attr-defined] # noqa: SLF001 # pylint: disable=protected-access
588-
"help",
589-
"noActivates",
590-
)
591-
self.tooltip.configure(bg="#ffffe0")
592-
except AttributeError: # Catches protected member access error
593-
self.tooltip.wm_attributes("-alpha", 1.0) # Ensure opacity
594-
self.tooltip.wm_attributes("-topmost", True) # Keep on top # noqa: FBT003
595-
self.tooltip.configure(bg="#ffffe0")
548+
try:
549+
self.tooltip.tk.call(
550+
"::tk::unsupported::MacWindowStyle",
551+
"style",
552+
self.tooltip._w, # type: ignore[attr-defined] # noqa: SLF001 # pylint: disable=protected-access
553+
"help",
554+
"noActivates",
555+
)
556+
except (AttributeError, tk.TclError): # Fallback when MacWindowStyle or Tk attribute access is unsupported
557+
with contextlib.suppress(tk.TclError):
558+
self.tooltip.wm_attributes("-alpha", 1.0) # Ensure opacity
559+
self.tooltip.wm_attributes("-topmost", True) # Keep on top # noqa: FBT003
596560
tooltip_label = ttk.Label(
597561
self.tooltip, text=self.text, background="#ffffe0", relief="solid", borderwidth=1, justify=tk.LEFT
598562
)
@@ -605,7 +569,7 @@ def create_show(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002
605569
self.tooltip.update_idletasks() # Force macOS to finish rendering text and colors
606570
self.tooltip.deiconify() # still invisible on Mac
607571

608-
if self.widget.tk.call("tk", "windowingsystem") == "aqua":
572+
if self._is_aqua:
609573

610574
def _activate_alpha() -> None:
611575
self.timers.pop("alpha", None)
@@ -652,35 +616,19 @@ def position_tooltip(self) -> None:
652616
# Silently ignore - tooltip will be recreated on next hover if needed
653617
pass
654618

655-
def hide(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
656-
"""Hide the tooltip after a delay on non-macOS."""
657-
self._cancel_hide()
658-
self.timers["hide"] = self.widget.after(TOOLTIP_HIDE_DELAY_MS, self._do_hide)
659-
660-
def _do_hide(self) -> None:
661-
"""Actually hide or destroy the tooltip depending on platform."""
662-
if self.tooltip:
663-
self.tooltip.withdraw()
664-
if Tooltip._active_tooltip is self:
665-
Tooltip._active_tooltip = None
666-
self.timers.pop("hide", None)
667-
668619
def force_hide(self) -> None:
669-
"""Immediately hide or destroy the tooltip, depending on platform."""
620+
"""Immediately destroy the tooltip globally across all OSs."""
670621
self._cancel_show()
671-
self._cancel_hide()
672622
self._cancel_timer("alpha")
673623
if self.tooltip:
674-
if platform_system() == "Darwin":
624+
with contextlib.suppress(tk.TclError):
675625
self.tooltip.destroy()
676-
self.tooltip = None
677-
else:
678-
self.tooltip.withdraw()
626+
self.tooltip = None
679627
if Tooltip._active_tooltip is self:
680628
Tooltip._active_tooltip = None
681629

682630
def destroy_hide(self, event: Optional[tk.Event] = None) -> None: # noqa: ARG002 # pylint: disable=unused-argument
683-
"""On macOS, fully destroy the tooltip when the mouse leaves the widget."""
631+
"""Immediately destroy the tooltip when the mouse leaves the widget, on all platforms."""
684632
self.force_hide()
685633

686634

0 commit comments

Comments
 (0)