diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index e29b5a7be320..21e29378c81d 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -747,6 +747,8 @@ def _recolor_icon(image, color): # Use the high-resolution (48x48 px) icon if it exists and is needed with Image.open(path_large if (size > 24 and path_large.exists()) else path_regular) as im: + # assure a RGBA image as foreground color is RGB + im = im.convert("RGBA") image = ImageTk.PhotoImage(im.resize((size, size)), master=self) button._ntimage = image diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index d41b98a47266..d4eef8e22705 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -1112,7 +1112,9 @@ def _icon(name): *name*, including the extension and relative to Matplotlib's "images" data directory. """ - image = np.array(PIL.Image.open(cbook._get_data_path("images", name))) + pilimg = PIL.Image.open(cbook._get_data_path("images", name)) + # ensure RGBA as wx BitMap expects RGBA format + image = np.array(pilimg.convert("RGBA")) try: dark = wx.SystemSettings.GetAppearance().IsDark() except AttributeError: # wxpython < 4.1 diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index eefaefbb023f..f3257acd5bd3 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -7,8 +7,9 @@ import pytest -from matplotlib.testing import subprocess_run_helper from matplotlib import _c_internal_utils +from matplotlib.testing import subprocess_run_helper + _test_timeout = 60 # A reasonably safe value for slower architectures. diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index 498aa3b48c06..fa5566876919 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -7,13 +7,17 @@ import signal import subprocess import sys +import tempfile import time import urllib.request +from PIL import Image + import pytest import matplotlib as mpl from matplotlib import _c_internal_utils +from matplotlib.backend_tools import ToolToggleBase from matplotlib.testing import subprocess_run_helper as _run_helper @@ -71,6 +75,24 @@ def _get_testable_interactive_backends(): _test_timeout = 60 # A reasonably safe value for slower architectures. +def _test_toolbar_button_la_mode_icon(fig): + # test a toolbar button icon using an image in LA mode (GH issue 25174) + # create an icon in LA mode + with tempfile.TemporaryDirectory() as tempdir: + img = Image.new("LA", (26, 26)) + tmp_img_path = os.path.join(tempdir, "test_la_icon.png") + img.save(tmp_img_path) + + class CustomTool(ToolToggleBase): + image = tmp_img_path + description = "" # gtk3 backend does not allow None + + toolmanager = fig.canvas.manager.toolmanager + toolbar = fig.canvas.manager.toolbar + toolmanager.add_tool("test", CustomTool) + toolbar.add_tool("test", "group") + + # The source of this function gets extracted and run in another process, so it # must be fully self-contained. # Using a timer not only allows testing of timers (on other backends), but is @@ -122,7 +144,6 @@ def check_alt_backend(alt_backend): if importlib.util.find_spec("cairocffi"): check_alt_backend(backend[:-3] + "cairo") check_alt_backend("svg") - mpl.use(backend, force=True) fig, ax = plt.subplots() @@ -130,6 +151,10 @@ def check_alt_backend(alt_backend): type(fig.canvas).__module__, f"matplotlib.backends.backend_{backend}") + if mpl.rcParams["toolbar"] == "toolmanager": + # test toolbar button icon LA mode see GH issue 25174 + _test_toolbar_button_la_mode_icon(fig) + ax.plot([0, 1], [2, 3]) if fig.canvas.toolbar: # i.e toolbar2. fig.canvas.toolbar.draw_rubberband(None, 1., 1, 2., 2) @@ -168,11 +193,17 @@ def test_interactive_backend(env, toolbar): pytest.skip("toolmanager is not implemented for macosx.") if env["MPLBACKEND"] == "wx": pytest.skip("wx backend is deprecated; tests failed on appveyor") - proc = _run_helper(_test_interactive_impl, - json.dumps({"toolbar": toolbar}), - timeout=_test_timeout, - extra_env=env) - + try: + proc = _run_helper( + _test_interactive_impl, + json.dumps({"toolbar": toolbar}), + timeout=_test_timeout, + extra_env=env, + ) + except subprocess.CalledProcessError as err: + pytest.fail( + "Subprocess failed to test intended behavior\n" + + str(err.stderr)) assert proc.stdout.count("CloseEvent") == 1