@@ -210,6 +210,9 @@ def __init__(self, parent=None):
210210 # Internal flag to defer repaint scheduling from changed()
211211 self ._suspend_changed_update = 0
212212
213+ # Guard against re-entrant paintEvent calls
214+ self ._in_paint_event = False
215+
213216 # Strong references to dynamically created state transitions
214217 self ._transitions = []
215218
@@ -728,45 +731,54 @@ def changed(self, action):
728731
729732 def paintEvent (self , event , * args ):
730733 """Custom paint routine for the timeline widget."""
731- event .accept ()
732- painter = QPainter (self )
733- painter .setRenderHints (
734- QPainter .Antialiasing |
735- QPainter .SmoothPixmapTransform |
736- QPainter .TextAntialiasing ,
737- True ,
738- )
739-
740- if not get_app ().window .timeline :
741- painter .end ()
734+ if self ._in_paint_event :
735+ log .warning ("TimelineWidgetBase paintEvent skipped due to re-entrancy" )
736+ event .accept ()
737+ self .update ()
742738 return
743739
744- signature = self ._panel_current_signature ()
745- if signature != self ._panel_refresh_signature :
746- self ._panel_refresh_signature = signature
747- if self ._update_track_panel_properties ():
748- self .geometry .mark_dirty ()
740+ self ._in_paint_event = True
741+ painter = QPainter (self )
742+ try :
743+ event .accept ()
744+ painter .setRenderHints (
745+ QPainter .Antialiasing |
746+ QPainter .SmoothPixmapTransform |
747+ QPainter .TextAntialiasing ,
748+ True ,
749+ )
749750
750- self .geometry .ensure ()
751- self ._ensure_keyframe_markers ()
752-
753- self .bg_painter .paint (painter , event .rect ())
754- self .track_painter .paint_background (painter )
755- self .keyframe_panel_painter .paint (painter , mode = "underlay" )
756- self .clip_painter .paint (painter )
757- self .transition_painter .paint (painter )
758- self .playback_cache_painter .paint (painter )
759- self .keyframe_painter .paint (painter )
760- self .track_painter .paint_names (painter )
761- self .keyframe_panel_painter .paint (painter , mode = "overlay" )
762- self .selection_painter .paint (painter )
763- self .ruler_painter .paint (painter )
764- self .marker_painter .paint (painter )
765- self .playhead_painter .paint (painter )
766- self .ruler_painter .paint_overlay (painter )
767- self .scrollbar_painter .paint (painter )
768-
769- painter .end ()
751+ if not get_app ().window .timeline :
752+ return
753+
754+ signature = self ._panel_current_signature ()
755+ if signature != self ._panel_refresh_signature :
756+ self ._panel_refresh_signature = signature
757+ if self ._update_track_panel_properties ():
758+ self .geometry .mark_dirty ()
759+
760+ self .geometry .ensure ()
761+ self ._ensure_keyframe_markers ()
762+
763+ self .bg_painter .paint (painter , event .rect ())
764+ self .track_painter .paint_background (painter )
765+ self .keyframe_panel_painter .paint (painter , mode = "underlay" )
766+ self .clip_painter .paint (painter )
767+ self .transition_painter .paint (painter )
768+ self .playback_cache_painter .paint (painter )
769+ self .keyframe_painter .paint (painter )
770+ self .track_painter .paint_names (painter )
771+ self .keyframe_panel_painter .paint (painter , mode = "overlay" )
772+ self .selection_painter .paint (painter )
773+ self .ruler_painter .paint (painter )
774+ self .marker_painter .paint (painter )
775+ self .playhead_painter .paint (painter )
776+ self .ruler_painter .paint_overlay (painter )
777+ self .scrollbar_painter .paint (painter )
778+ finally :
779+ if painter .isActive ():
780+ painter .end ()
781+ self ._in_paint_event = False
770782
771783 def closeEvent (self , event ):
772784 """Ensure background threads stop when the widget closes."""
0 commit comments