Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit de2005b

Browse filesBrowse files
daniilStimhoffm
authored andcommitted
Change colour of Tk toolbar icons on dark backgrounds
Co-Authored-By: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
1 parent 647afe6 commit de2005b
Copy full SHA for de2005b

File tree

2 files changed

+92
-2
lines changed
Filter options

2 files changed

+92
-2
lines changed

‎lib/matplotlib/backends/_backend_tk.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/_backend_tk.py
+53-2Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_backend_tk.py
+39Lines 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():
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.