Note

Go to the endto download the full example code.

mplcvd -- an example of figure hook#

To use this hook, ensure that this module is in yourPYTHONPATH, and setrcParams["figure.hooks"]=["mplcvd:setup"]. This hook depends onthecolorspacious third-party module.

mplcvd
importfunctoolsfrompathlibimportPathimportcolorspaciousimportnumpyasnp_BUTTON_NAME="Filter"_BUTTON_HELP="Simulate color vision deficiencies"_MENU_ENTRIES={"None":None,"Greyscale":"greyscale","Deuteranopia":"deuteranomaly","Protanopia":"protanomaly","Tritanopia":"tritanomaly",}def_get_color_filter(name):"""    Given a color filter name, create a color filter function.    Parameters    ----------    name : str        The color filter name, one of the following:        - ``"none"``: ...        - ``"greyscale"``: Convert the input to luminosity.        - ``"deuteranopia"``: Simulate the most common form of red-green          colorblindness.        - ``"protanopia"``: Simulate a rarer form of red-green colorblindness.        - ``"tritanopia"``: Simulate the rare form of blue-yellow          colorblindness.        Color conversions use `colorspacious`_.    Returns    -------    callable        A color filter function that has the form:        def filter(input: np.ndarray[M, N, D])-> np.ndarray[M, N, D]        where (M, N) are the image dimensions, and D is the color depth (3 for        RGB, 4 for RGBA). Alpha is passed through unchanged and otherwise        ignored.    """ifnamenotin_MENU_ENTRIES:raiseValueError(f"Unsupported filter name:{name!r}")name=_MENU_ENTRIES[name]ifnameisNone:returnNoneelifname=="greyscale":rgb_to_jch=colorspacious.cspace_converter("sRGB1","JCh")jch_to_rgb=colorspacious.cspace_converter("JCh","sRGB1")defconvert(im):greyscale_JCh=rgb_to_jch(im)greyscale_JCh[...,1]=0im=jch_to_rgb(greyscale_JCh)returnimelse:cvd_space={"name":"sRGB1+CVD","cvd_type":name,"severity":100}convert=colorspacious.cspace_converter(cvd_space,"sRGB1")deffilter_func(im,dpi):alpha=Noneifim.shape[-1]==4:im,alpha=im[...,:3],im[...,3]im=convert(im)ifalphaisnotNone:im=np.dstack((im,alpha))returnnp.clip(im,0,1),0,0returnfilter_funcdef_set_menu_entry(tb,name):tb.canvas.figure.set_agg_filter(_get_color_filter(name))tb.canvas.draw_idle()defsetup(figure):tb=figure.canvas.toolbariftbisNone:returnforclsintype(tb).__mro__:pkg=cls.__module__.split(".")[0]ifpkg!="matplotlib":breakifpkg=="gi":_setup_gtk(tb)elifpkgin("PyQt5","PySide2","PyQt6","PySide6"):_setup_qt(tb)elifpkg=="tkinter":_setup_tk(tb)elifpkg=="wx":_setup_wx(tb)else:raiseNotImplementedError("The current backend is not supported")def_setup_gtk(tb):fromgi.repositoryimportGio,GLib,Gtkforidxinrange(tb.get_n_items()):children=tb.get_nth_item(idx).get_children()ifchildrenandisinstance(children[0],Gtk.Label):breaktoolitem=Gtk.SeparatorToolItem()tb.insert(toolitem,idx)image=Gtk.Image.new_from_gicon(Gio.Icon.new_for_string(str(Path(__file__).parent/"images/eye-symbolic.svg")),Gtk.IconSize.LARGE_TOOLBAR)# The type of menu is progressively downgraded depending on GTK version.ifGtk.check_version(3,6,0)isNone:group=Gio.SimpleActionGroup.new()action=Gio.SimpleAction.new_stateful("cvdsim",GLib.VariantType("s"),GLib.Variant("s","none"))group.add_action(action)@functools.partial(action.connect,"activate")defset_filter(action,parameter):_set_menu_entry(tb,parameter.get_string())action.set_state(parameter)menu=Gio.Menu()fornamein_MENU_ENTRIES:menu.append(name,f"local.cvdsim::{name}")button=Gtk.MenuButton.new()button.remove(button.get_children()[0])button.add(image)button.insert_action_group("local",group)button.set_menu_model(menu)button.get_style_context().add_class("flat")item=Gtk.ToolItem()item.add(button)tb.insert(item,idx+1)else:menu=Gtk.Menu()group=[]fornamein_MENU_ENTRIES:item=Gtk.RadioMenuItem.new_with_label(group,name)item.set_active(name=="None")item.connect("activate",lambdaitem:_set_menu_entry(tb,item.get_label()))group.append(item)menu.append(item)menu.show_all()tbutton=Gtk.MenuToolButton.new(image,_BUTTON_NAME)tbutton.set_menu(menu)tb.insert(tbutton,idx+1)tb.show_all()def_setup_qt(tb):frommatplotlib.backends.qt_compatimportQtGui,QtWidgetsmenu=QtWidgets.QMenu()try:QActionGroup=QtGui.QActionGroup# Qt6exceptAttributeError:QActionGroup=QtWidgets.QActionGroup# Qt5group=QActionGroup(menu)group.triggered.connect(lambdaaction:_set_menu_entry(tb,action.text()))fornamein_MENU_ENTRIES:action=menu.addAction(name)action.setCheckable(True)action.setActionGroup(group)action.setChecked(name=="None")actions=tb.actions()before=next((actionforactioninactionsifisinstance(tb.widgetForAction(action),QtWidgets.QLabel)),None)tb.insertSeparator(before)button=QtWidgets.QToolButton()# FIXME: _icon needs public API.button.setIcon(tb._icon(str(Path(__file__).parent/"images/eye.png")))button.setText(_BUTTON_NAME)button.setToolTip(_BUTTON_HELP)button.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)button.setMenu(menu)tb.insertWidget(before,button)def_setup_tk(tb):importtkinterastktb._Spacer()# FIXME: _Spacer needs public API.button=tk.Menubutton(master=tb,relief="raised")button._image_file=str(Path(__file__).parent/"images/eye.png")# FIXME: _set_image_for_button needs public API (perhaps like _icon).tb._set_image_for_button(button)button.pack(side=tk.LEFT)menu=tk.Menu(master=button,tearoff=False)fornamein_MENU_ENTRIES:menu.add("radiobutton",label=name,command=lambda_name=name:_set_menu_entry(tb,_name))menu.invoke(0)button.config(menu=menu)def_setup_wx(tb):importwxidx=next(idxforidxinrange(tb.ToolsCount)iftb.GetToolByPos(idx).IsStretchableSpace())tb.InsertSeparator(idx)tool=tb.InsertTool(idx+1,-1,_BUTTON_NAME,# FIXME: _icon needs public API.tb._icon(str(Path(__file__).parent/"images/eye.png")),# FIXME: ITEM_DROPDOWN is not supported on macOS.kind=wx.ITEM_DROPDOWN,shortHelp=_BUTTON_HELP)menu=wx.Menu()fornamein_MENU_ENTRIES:item=menu.AppendRadioItem(-1,name)menu.Bind(wx.EVT_MENU,lambdaevent,_name=name:_set_menu_entry(tb,_name),id=item.Id,)tb.SetDropdownMenu(tool.Id,menu)if__name__=='__main__':importmatplotlib.pyplotaspltfrommatplotlibimportcbookplt.rcParams['figure.hooks'].append('mplcvd:setup')fig,axd=plt.subplot_mosaic([['viridis','turbo'],['photo','lines']])delta=0.025x=y=np.arange(-3.0,3.0,delta)X,Y=np.meshgrid(x,y)Z1=np.exp(-X**2-Y**2)Z2=np.exp(-(X-1)**2-(Y-1)**2)Z=(Z1-Z2)*2imv=axd['viridis'].imshow(Z,interpolation='bilinear',origin='lower',extent=[-3,3,-3,3],vmax=abs(Z).max(),vmin=-abs(Z).max())fig.colorbar(imv)imt=axd['turbo'].imshow(Z,interpolation='bilinear',cmap='turbo',origin='lower',extent=[-3,3,-3,3],vmax=abs(Z).max(),vmin=-abs(Z).max())fig.colorbar(imt)# A sample imagewithcbook.get_sample_data('grace_hopper.jpg')asimage_file:photo=plt.imread(image_file)axd['photo'].imshow(photo)th=np.linspace(0,2*np.pi,1024)forjin[1,2,4,6]:axd['lines'].plot(th,np.sin(th*j),label=f'$\\omega={j}$')axd['lines'].legend(ncols=2,loc='upper right')plt.show()

Total running time of the script: (0 minutes 1.063 seconds)

Gallery generated by Sphinx-Gallery