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

Skip to content

Commit dc9818d

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

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
@@ -637,12 +637,63 @@ def _set_image_for_button(self, button):
637637
path_large = path_regular.with_name(
638638
path_regular.name.replace('.png', '_large.png'))
639639
size = button.winfo_pixels('18p')
640+
641+
# Nested functions because ToolbarTk calls _Button.
642+
def _get_color(color_name):
643+
# `winfo_rgb` returns an (r, g, b) tuple in the range 0-65535
644+
return button.winfo_rgb(button.cget(color_name))
645+
646+
def _is_dark(color):
647+
if isinstance(color, str):
648+
color = _get_color(color)
649+
return max(color) < 65535 / 2
650+
651+
def _recolor_icon(image, color):
652+
image_data = np.asarray(image).copy()
653+
black_mask = (image_data[..., :3] == 0).all(axis=-1)
654+
image_data[black_mask, :3] = color
655+
return Image.fromarray(image_data, mode="RGBA")
656+
640657
# Use the high-resolution (48x48 px) icon if it exists and is needed
641658
with Image.open(path_large if (size > 24 and path_large.exists())
642659
else path_regular) as im:
643660
image = ImageTk.PhotoImage(im.resize((size, size)), master=self)
644-
button.configure(image=image, height='18p', width='18p')
645-
button._ntimage = image # Prevent garbage collection.
661+
button._ntimage = image
662+
663+
# create a version of the icon with the button's text color
664+
foreground = (255 / 65535) * np.array(
665+
button.winfo_rgb(button.cget("foreground")))
666+
im_alt = _recolor_icon(im, foreground)
667+
image_alt = ImageTk.PhotoImage(
668+
im_alt.resize((size, size)), master=self)
669+
button._ntimage_alt = image_alt
670+
671+
if _is_dark("background"):
672+
button.configure(image=image_alt)
673+
else:
674+
button.configure(image=image)
675+
# Checkbuttons may switch the background to `selectcolor` in the
676+
# checked state, so check separately which image it needs to use in
677+
# that state to still ensure enough contrast with the background.
678+
if (
679+
isinstance(button, tk.Checkbutton)
680+
and button.cget("selectcolor") != ""
681+
):
682+
if self._windowingsystem != "x11":
683+
selectcolor = "selectcolor"
684+
else:
685+
# On X11, selectcolor isn't used directly for indicator-less
686+
# buttons. See `::tk::CheckEnter` in the Tk button.tcl source
687+
# code for details.
688+
r1, g1, b1 = _get_color("selectcolor")
689+
r2, g2, b2 = _get_color("activebackground")
690+
selectcolor = ((r1+r2)/2, (g1+g2)/2, (b1+b2)/2)
691+
if _is_dark(selectcolor):
692+
button.configure(selectimage=image_alt)
693+
else:
694+
button.configure(selectimage=image)
695+
696+
button.configure(height='18p', width='18p')
646697

647698
def _Button(self, text, image_file, toggle, command):
648699
if not toggle:

lib/matplotlib/tests/test_backend_tk.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,42 @@ def check_focus():
216216

217217
if success:
218218
print("success")
219+
220+
221+
@pytest.mark.backend('TkAgg', skip_on_importerror=True)
222+
@_isolated_tk_test(success_count=2)
223+
def test_embedding(): # pragma: no cover
224+
import tkinter as tk
225+
from matplotlib.backends.backend_tkagg import (
226+
FigureCanvasTkAgg, NavigationToolbar2Tk)
227+
from matplotlib.backend_bases import key_press_handler
228+
from matplotlib.figure import Figure
229+
230+
root = tk.Tk()
231+
232+
def test_figure(master):
233+
fig = Figure()
234+
ax = fig.add_subplot()
235+
ax.plot([1, 2, 3])
236+
237+
canvas = FigureCanvasTkAgg(fig, master=master)
238+
canvas.draw()
239+
canvas.mpl_connect("key_press_event", key_press_handler)
240+
canvas.get_tk_widget().pack(expand=True, fill="both")
241+
242+
toolbar = NavigationToolbar2Tk(canvas, master, pack_toolbar=False)
243+
toolbar.pack(expand=True, fill="x")
244+
245+
canvas.get_tk_widget().forget()
246+
toolbar.forget()
247+
248+
test_figure(root)
249+
print("success")
250+
251+
# Test with a dark button color. Doesn't actually check whether the icon
252+
# color becomes lighter, just that the code doesn't break.
253+
254+
root.tk_setPalette(background="sky blue", selectColor="midnight blue",
255+
foreground="white")
256+
test_figure(root)
257+
print("success")

0 commit comments

Comments
 (0)