Skip to content

Instantly share code, notes, and snippets.

@anntzer
Created April 11, 2020 15:39
Show Gist options
  • Save anntzer/8909837f8438210538336651a87aed9c to your computer and use it in GitHub Desktop.
Save anntzer/8909837f8438210538336651a87aed9c to your computer and use it in GitHub Desktop.
Improve support for dark themes in GTK3/matplotlib
Patch to have "light" icons when using a dark theme in GTK3.
Unfortunately the foreground color is reported as full white, which gives too bright icons?
diff --git i/lib/matplotlib/backends/backend_gtk3.py w/lib/matplotlib/backends/backend_gtk3.py
index 60537cf64..ed9f755a0 100644
--- i/lib/matplotlib/backends/backend_gtk3.py
+++ w/lib/matplotlib/backends/backend_gtk3.py
@@ -4,6 +4,9 @@ import os
from pathlib import Path
import sys
+import numpy as np
+import PIL
+
import matplotlib as mpl
from matplotlib import backend_tools, cbook
from matplotlib._pylab_helpers import Gcf
@@ -13,6 +16,7 @@ from matplotlib.backend_bases import (
from matplotlib.backend_managers import ToolManager
from matplotlib.figure import Figure
from matplotlib.widgets import SubplotTool
+from .backend_cairo import cairo
try:
import gi
@@ -489,22 +493,39 @@ class NavigationToolbar2GTK3(NavigationToolbar2, Gtk.Toolbar):
self.ctx.set_source_rgb(0, 0, 0)
self.ctx.stroke()
+ def _icon(self, name, color=None):
+ # Use PIL.Image instead of imread to get integer values.
+ img = np.array(PIL.Image.open(cbook._get_data_path("images", name)))
+ height, width, _ = img.shape
+ img[..., :3] = ((np.array([*color][:-1]) * 255).astype(int)
+ * (img[..., 3:] / 255))
+ argb32 = img.take(
+ [2, 1, 0, 3] if sys.byteorder == "little" else [3, 0, 1, 2],
+ axis=2)
+ surface = cairo.ImageSurface.create_for_data(
+ bytearray(argb32), cairo.FORMAT_ARGB32, width, height)
+ return Gtk.Image.new_from_surface(surface)
+
def _init_toolbar(self):
self.set_style(Gtk.ToolbarStyle.ICONS)
+ st_ctx = self.get_style_context()
+ background_color = st_ctx.get_background_color(Gtk.StateFlags.NORMAL)
+ foreground_color = st_ctx.get_color(Gtk.StateFlags.NORMAL)
+ _, _, background_v = mpl.colors.rgb_to_hsv([*background_color][:-1])
+ icon_color = foreground_color if background_v < .5 else None
+
self._gtk_ids = {}
for text, tooltip_text, image_file, callback in self.toolitems:
if text is None:
self.insert(Gtk.SeparatorToolItem(), -1)
continue
- image = Gtk.Image()
- image.set_from_file(
- str(cbook._get_data_path('images', image_file + '.png')))
self._gtk_ids[text] = tbutton = (
Gtk.ToggleToolButton() if callback in ['zoom', 'pan'] else
Gtk.ToolButton())
tbutton.set_label(text)
- tbutton.set_icon_widget(image)
+ tbutton.set_icon_widget(
+ self._icon(image_file + '.png', icon_color))
self.insert(tbutton, -1)
# Save the handler id, so that we can block it as needed.
tbutton._signal_handler = tbutton.connect(
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment