10000 Use light icons on dark themes for wx and gtk, too. · matplotlib/matplotlib@4c86c0b · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c86c0b

Browse files
committed
Use light icons on dark themes for wx and gtk, too.
These can be tried e.g. by setting `GTK_THEME` to `Adwaita:dark`. I couldn't find how to get the foreground color in GTK3 so the check for dark themes is just looking at the luminance of the background, and the icon color is set to white. (Better approaches welcome...) I made the `_icon` API "consistent" across qt, wx, and gtk.
1 parent 661100a commit 4c86c0b

File tree

4 files changed

+69
-14
lines changed

4 files changed

+69
-14
lines changed

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import colorsys
12
import functools
23
import logging
34
import os
45
from pathlib import Path
56
import sys
67

8+
import numpy as np
9+
import PIL
10+
711
import matplotlib as mpl
812
from matplotlib import backend_tools, cbook
913
from matplotlib._pylab_helpers import Gcf
@@ -13,6 +17,10 @@
1317
from matplotlib.figure import Figure
1418
from matplotlib.widgets import SubplotTool
1519

20+
try:
21+
from .backend_cairo import cairo
22+
except ImportError as err:
23+
raise ImportError('The GTK3 backends require cairo') from err
1624
try:
1725
import gi
1826
except ImportError as err:
@@ -452,14 +460,11 @@ def __init__(self, canvas, window):
452460
if text is None:
453461
self.insert(Gtk.SeparatorToolItem(), -1)
454462
continue
455-
image = Gtk.Image()
456-
image.set_from_file(
457-
str(cbook._get_data_path('images', image_file + '.png')))
458463
self._gtk_ids[text] = tbutton = (
459464
Gtk.ToggleToolButton() if callback in ['zoom', 'pan'] else
460465
Gtk.ToolButton())
461466
tbutton.set_label(text)
462-
tbutton.set_icon_widget(image)
467+
tbutton.set_icon_widget(self._icon(f"{image_file}.png"))
463468
self.insert(tbutton, -1)
464469
# Save the handler id, so that we can block it as needed.
465470
tbutton._signal_handler = tbutton.connect(
@@ -480,6 +485,27 @@ def __init__(self, canvas, window):
480485

481486
NavigationToolbar2.__init__(self, canvas)
482487

488+
def _icon(self, name):
489+
"""
490+
Construct a `.GtkImage` suitable for use as icon from an image file
491+
*name*, including the extension and relative to Matplotlib's "images"
492+
data directory.
493+
"""
494+
path = cbook._get_data_path("images", name)
495+
bg = self.get_style_context().get_background_color(
496+
Gtk.StateFlags.NORMAL)
497+
lum, _, _ = colorsys.rgb_to_yiq(bg.red, bg.green, bg.blue)
498+
if lum < .5:
499+
image = np.array(PIL.Image.open(path))
500+
image[(image[..., :3] == 0).all(axis=-1), :3] = (255, 255, 255)
501+
image = cbook._unmultiplied_rgba8888_to_premultiplied_argb32(image)
502+
return Gtk.Image.new_from_surface(
503+
cairo.ImageSurface.create_for_data(
504+
image.ravel().data, cairo.FORMAT_ARGB32,
505+
image.shape[1], image.shape[0]))
506+
else:
507+
return Gtk.Image.new_from_file(str(path))
508+
483509
@cbook.deprecated("3.3")
484510
@property
485511
def ctx(self):
@@ -632,9 +658,8 @@ def add_toolitem(self, name, group, position, image_file, description,
632658
tbutton.set_label(name)
633659

634660
if image_file is not None:
635-
image = Gtk.Image()
636-
image.set_from_file(image_file)
637-
tbutton.set_icon_widget(image)
661+
tbutton.set_icon_widget(
662+
NavigationToolbar2GTK3._icon(self, image_file))
638663

639664
if position is None:
640665
position = -1

lib/matplotlib/backends/backend_gtk3agg.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import numpy as np
22

33
from .. import cbook
4-
try:
5-
from . import backend_cairo
6-
except ImportError as e:
7-
raise ImportError('backend Gtk3Agg requires cairo') from e
8-
from . import backend_agg, backend_gtk3
4+
# First import backend_gtk3, which will provide a proper error message if cairo
5+
# is missing.
6+
from . import backend_agg, backend_gtk3, backend_cairo
97
from .backend_cairo import cairo
108
from .backend_gtk3 import Gtk, _BackendGTK3
119
from matplotlib import transforms

lib/matplotlib/backends/backend_qt5.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,10 @@ def basedir(self):
688688
return str(cbook._get_data_path('images'))
689689

690690
def _icon(self, name):
691+
"""
692+
Construct a `.QIcon` from an image file *name*, including the extension
693+
and relative to Matplotlib's "images" data directory.
694+
"""
691695
if QtCore.QT_VERSION_STR >= '5.':
692696
name = name.replace('.png', '_large.png')
693697
pm = QtGui.QPixmap(str(cbook._get_data_path('images', name)))

lib/matplotlib/backends/backend_wx.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import sys
1414
import weakref
1515

16+
import numpy as np
17+
import PIL
18+
1619
import matplotlib as mpl
1720
from matplotlib.backend_bases import (
1821
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
@@ -1112,7 +1115,7 @@ def __init__(self, canvas):
11121115
self.wx_ids[text] = (
11131116
self.AddTool(
11141117
-1,
1115-
bitmap=_load_bitmap(f"{image_file}.png"),
1118+
bitmap=self._icon(f"{image_file}.png"),
11161119
bmpDisabled=wx.NullBitmap,
11171120
label=text, shortHelp=text, longHelp=tooltip_text,
11181121
kind=(wx.ITEM_CHECK if text in ["Pan", "Zoom"]
@@ -1144,6 +1147,31 @@ def __init__(self, canvas):
11441147
zoomStartX = cbook._deprecate_privatize_attribute("3.3")
11451148
zoomStartY = cbook._deprecate_privatize_attribute("3.3")
11461149

1150+
@staticmethod
1151+
def _icon(name):
1152+
"""
1153+
Construct a `wx.Bitmap` suitable for use as icon from an image file
1154+
*name*, including the extension and relative to Matplotlib's "images"
1155+
data directory.
1156+
"""
1157+
image = np.array(PIL.Image.open(cbook._get_data_path("images", name)))
1158+
try:
1159+
dark = wx.SystemSettings.GetAppearance().IsDark()
1160+
except AttributeError: # wxpython < 4.1
1161+
# copied from wx's IsUsingDarkBackground / GetLuminance.
1162+
bg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
1163+
fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
1164+
# See wx.Colour.GetLuminance.
1165+
bg_lum = (.299 * bg.red + .587 * bg.green + .114 * bg.blue) / 255
1166+
fg_lum = (.299 * fg.red + .587 * fg.green + .114 * fg.blue) / 255
1167+
dark = fg_lum - bg_lum > .2
1168+
if dark:
1169+
fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
1170+
image[(image[..., :3] == 0).all(axis=-1), :3] = (
1171+
fg.Red(), fg.Green(), fg.Blue())
1172+
return wx.Bitmap.FromBufferRGBA(
1173+
image.shape[1], image.shape[0], image.tobytes())
1174+
11471175
def get_canvas(self, frame, fig):
11481176
return type(self.canvas)(frame, -1, fig)
11491177

@@ -1346,7 +1374,7 @@ def add_toolitem(self, name, group, position, image_file, description,
13461374
before, group = self._add_to_group(group, name, position)
13471375
idx = self.GetToolPos(before.Id)
13481376
if image_file:
1349-
bmp = _load_bitmap(image_file)
1377+
bmp = NavigationToolbar2Wx._icon(image_file)
13501378
kind = wx.ITEM_NORMAL if not toggle else wx.ITEM_CHECK
13511379
tool = self.InsertTool(idx, -1, name, bmp, wx.NullBitmap, kind,
13521380
description or "")

0 commit comments

Comments
 (0)
0