@@ -649,12 +649,59 @@ def _set_image_for_button(self, button):
649
649
path_large = path_regular .with_name (
650
650
path_regular .name .replace ('.png' , '_large.png' ))
651
651
size = button .winfo_pixels ('18p' )
652
+
653
+ # Nested functions because ToolbarTk calls _Button.
654
+ def _get_color (color_name ):
655
+ # `winfo_rgb` returns an (r, g, b) tuple in the range 0-65535
656
+ return button .winfo_rgb (button .cget (color_name ))
657
+
658
+ def _is_dark (color ):
659
+ if isinstance (color , str ):
660
+ color = _get_color (color )
661
+ return max (color ) < 65535 / 2
662
+
663
+ def _recolor_icon (image , color ):
664
+ image_data = np .asarray (image ).copy ()
665
+ black_mask = (image_data [..., :3 ] == 0 ).all (axis = - 1 )
666
+ image_data [black_mask , :3 ] = color
667
+ return Image .fromarray (image_data , mode = "RGBA" )
668
+
652
669
# Use the high-resolution (48x48 px) icon if it exists and is needed
653
670
with Image .open (path_large if (size > 24 and path_large .exists ())
654
671
else path_regular ) as im :
655
672
image = ImageTk .PhotoImage (im .resize ((size , size )), master = self )
656
- button .configure (image = image , height = '18p' , width = '18p' )
657
- button ._ntimage = image # Prevent garbage collection.
673
+ button ._ntimage = image
674
+
675
+ # create a version of the icon with the button's text color
676
+ foreground = (255 / 65535 ) * np .array (
677
+ button .winfo_rgb (button .cget ("foreground" )))
678
+ im_alt = _recolor_icon (im , foreground )
679
+ image_alt = ImageTk .PhotoImage (
680
+ im_alt .resize ((size , size )), master = self )
681
+ button ._ntimage_alt = image_alt
682
+
683
+ if _is_dark ("background" ):
684
+ button .configure (image = image_alt )
685
+ else :
686
+ button .configure (image = image )
687
+ if (
688
+ isinstance (button , tk .Checkbutton )
689
+ and button .cget ("selectcolor" ) != ""
690
+ ):
691
+ if self ._windowingsystem != "x11" :
692
+ selectcolor = "selectcolor"
693
+ else :
694
+ # On X11, selectcolor isn't used directly for indicator-less
695
+ # buttons.
696
+ r1 , g1 , b1 = _get_color ("selectcolor" )
697
+ r2 , g2 , b2 = _get_color ("activebackground" )
698
+ selectcolor = ((r1 + r2 )/ 2 , (g1 + g2 )/ 2 , (b1 + b2 )/ 2 )
699
+ if _is_dark (selectcolor ):
700
+ button .configure (selectimage = image_alt )
701
+ else :
702
+ button .configure (selectimage = image )
703
+
704
+ button .configure (height = '18p' , width = '18p' )
658
705
659
706
def _Button (self , text , image_file , toggle , command ):
660
707
if not toggle :
0 commit comments