Skip to content

Commit 2e921df

Browse files
authored
Merge pull request #22163 from daniilS/tk_toolbar_colours
Change colour of Tk toolbar icons on dark backgrounds
2 parents 5971feb + ff60b28 commit 2e921df

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,41 @@ def check_focus():
216216

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy