Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d8d4b03

Browse files
daniilStimhoffm
andcommitted
Change colour of Tk toolbar icons on dark backgrounds
Co-Authored-By: Tim Hoffmann <[email protected]>
1 parent eaadeb6 commit d8d4b03

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

lib/matplotlib/backends/_backend_tk.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,12 +649,63 @@ def _set_image_for_button(self, button):
649649
path_large = path_regular.with_name(
650650
path_regular.name.replace('.png', '_large.png'))
651651
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+
652669
# Use the high-resolution (48x48 px) icon if it exists and is needed
653670
with Image.open(path_large if (size > 24 and path_large.exists())
654671
else path_regular) as im:
655672
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+
# Checkbuttons may switch the background to `selectcolor` in the
688+
# checked state, so check separately which image it needs to use in
689+
# that state to still ensure enough contrast with the background.
690+
if (
691+
isinstance(button, tk.Checkbutton)
692+
and button.cget("selectcolor") != ""
693+
):
694+
if self._windowingsystem != "x11":
695+
selectcolor = "selectcolor"
696+
else:
697+
# On X11, selectcolor isn't used directly for indicator-less
698+
# buttons. See `::tk::CheckEnter` in the Tk button.tcl source
699+
# code for details.
700+
r1, g1, b1 = _get_color("selectcolor")
701+
r2, g2, b2 = _get_color("activebackground")
702+
selectcolor = ((r1+r2)/2, (g1+g2)/2, (b1+b2)/2)
703+
if _is_dark(selectcolor):
704+
button.configure(selectimage=image_alt)
705+
else:
706+
button.configure(selectimage=image)
707+
708+
button.configure(height='18p', width='18p')
658709

659710
def _Button(self, text, image_file, toggle, command):
660711
if not toggle:

lib/matplotlib/tests/test_backend_tk.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,42 @@ class Toolbar(NavigationToolbar2Tk):
185185
print("success")
186186
Toolbar(fig.canvas, fig.canvas.manager.window) # This should not raise.
187187
print("success")
188+
189+
190+
@pytest.mark.backend('TkAgg', skip_on_importerror=True)
191+
@_isolated_tk_test(success_count=2)
192+
def test_embedding(): # pragma: no cover
193+
import tkinter as tk
194+
from matplotlib.backends.backend_tkagg import (
195+
FigureCanvasTkAgg, NavigationToolbar2Tk)
196+
from matplotlib.backend_bases import key_press_handler
197+
from matplotlib.figure import Figure
198+
199+
root = tk.Tk()
200+
201+
def test_figure(master):
202+
fig = Figure()
203+
ax = fig.add_subplot()
204+
ax.plot([1, 2, 3])
205+
206+
canvas = FigureCanvasTkAgg(fig, master=master)
207+
canvas.draw()
208+
canvas.mpl_connect("key_press_event", key_press_handler)
209+
canvas.get_tk_widget().pack(expand=True, fill="both")
210+
211+
toolbar = NavigationToolbar2Tk(canvas, master, pack_toolbar=False)
212+
toolbar.pack(expand=True, fill="x")
213+
214+
canvas.get_tk_widget().forget()
215+
toolbar.forget()
216+
217+
test_figure(root)
218+
print("success")
219+
220+
# Test with a dark button color. Doesn't actually check whether the icon
221+
# color becomes lighter, just that the code doesn't break.
222+
223+
root.tk_setPalette(background="sky blue", selectColor="midnight blue",
224+
foreground="white")
225+
test_figure(root)
226+
print("success")

0 commit comments

Comments
 (0)