{"id":778,"date":"2009-06-06T20:11:34","date_gmt":"2009-06-06T20:11:34","guid":{"rendered":"http:\/\/dalelane.co.uk\/blog\/?p=778"},"modified":"2009-06-06T20:11:34","modified_gmt":"2009-06-06T20:11:34","slug":"how-to-customise-the-navigationtoolbar2-toolbar-in-matplotlib","status":"publish","type":"post","link":"https:\/\/dalelane.co.uk\/blog\/?p=778","title":{"rendered":"How to customise the NavigationToolbar2 toolbar in matplotlib"},"content":{"rendered":"<p>My <a href=\"http:\/\/code.google.com\/p\/currentcostgui\" target=\"_blank\">CurrentCost desktop software<\/a> is written in <a href=\"http:\/\/www.python.org\/\" target=\"_blank\">Python<\/a>, and uses the <a href=\"http:\/\/matplotlib.sourceforge.net\/\" target=\"_blank\">matplotlib<\/a> library for plotting graphs.<\/p>\n<p>I am a big fan of matplotlib &#8211; you can create some <a href=\"http:\/\/matplotlib.sourceforge.net\/gallery.html\" target=\"_blank\">very cool graphs<\/a> with it. It&#8217;s not without it&#8217;s issues&#8230; for example, I&#8217;ve still yet to work out how to do realtime graphing with it that isn&#8217;t massively inefficient and resource intensive. But that&#8217;s for another post \ud83d\ude42<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/dalelane.co.uk\/blog\/post-images\/090606-toolbar.png\" align=\"left\" hspace=10 vspace=10\/>One of the nice things that you get with it is a <a href=\"http:\/\/matplotlib.sourceforge.net\/users\/navigation_toolbar.html\" target=\"_blank\">toolbar that lets the user switch between different modes<\/a> that the mouse supports &#8211; zooming, panning, and so on. <\/p>\n<p><a href=\"http:\/\/code.google.com\/p\/currentcostgui\/source\/browse\/trunk\/currentcostgraphs.py?r=148#47\" target=\"_blank\">Adding the toolbar<\/a> is straightforward &#8211; I create an object of the class <code>NavigationToolbar2Wx<\/code> and add it to a sizer which in turn I add to the graph <code>Plot<\/code>.<\/p>\n<pre style=\"overflow: scroll; font-size: 1.1em; border: thin solid silver; background-color: #eeeeee; padding: 0.8em\">self.toolbar = Toolbar(self.canvas)\r\nself.toolbar.Realize()\r\nsizer = wx.BoxSizer(wx.VERTICAL)\r\nsizer.Add(self.canvas, 1, wx.EXPAND)\r\nsizer.Add(self.toolbar, 0 , wx.LEFT | wx.EXPAND)\r\nself.SetSizer(sizer)<\/pre>\n<p>The <code>NavigationToolbar2<\/code> toolbar <a href=\"http:\/\/matplotlib.sourceforge.net\/users\/navigation_toolbar.html\" target=\"_blank\">provides a number of features<\/a> but it doesn&#8217;t exactly meet my needs so last night I had a go at customising it. <\/p>\n<p>I wanted to:<\/p>\n<ul>\n<li>remove the configure subplots button &#8211; as I don&#8217;t use subplots in my graphs\n<\/li>\n<li>add a couple of new buttons &#8211; for different navigation abilities<\/li>\n<\/ul>\n<p>I couldn&#8217;t find this documented clearly anywhere, so thought it was worth sharing the code that I came up with.<\/p>\n<p><!--more--><strong>Creating a custom toolbar<\/strong><\/p>\n<p>Step one for customising the toolbar is to <a target=\"_blank\" href=\"http:\/\/code.google.com\/p\/currentcostgui\/source\/browse\/trunk\/currentcostgraphs.py?r=148#90\">create a subclass of NavigationToolbar2Wx<\/a>, and add this to your <code>Plot<\/code> instead.<\/p>\n<pre style=\"overflow: scroll; font-size: 1.1em; border: thin solid silver; background-color: #eeeeee; padding: 0.8em\">class MyCustomToolbar(NavigationToolbar2Wx): \r\n    def __init__(self, plotCanvas):\r\n        NavigationToolbar2Wx.__init__(self, plotCanvas)<\/pre>\n<p>You&#8217;ve now got something you can start customising.<\/p>\n<p><strong>Removing buttons from the toolbar<\/strong><\/p>\n<p>The most straightforward approach I&#8217;ve settled on is to run the <code>init<\/code> method from the <code>NavigationToolbar2Wx<\/code> superclass, and then go back and remove the bits I don&#8217;t want. <\/p>\n<pre style=\"overflow: scroll; font-size: 1.1em; border: thin solid silver; background-color: #eeeeee; padding: 0.8em\">class MyCustomToolbar(NavigationToolbar2Wx): \r\n    def __init__(self, plotCanvas):\r\n        # create the default toolbar\r\n        NavigationToolbar2Wx.__init__(self, plotCanvas)\r\n        # remove the unwanted button\r\n        POSITION_OF_CONFIGURE_SUBPLOTS_BTN = 6\r\n        self.DeleteToolByPos(POSITION_OF_CONFIGURE_SUBPLOTS_BTN) <\/pre>\n<p><strong>Adding new buttons to the toolbar<\/strong><\/p>\n<p>You can <a href=\"http:\/\/matplotlib.sourceforge.net\/users\/navigation_toolbar.html?highlight=Pan\/Zoom\">pan matplotlib graphs by dragging the mouse across<\/a> but a couple of users of my CurrentCost app said that they found this difficult and clunky, and would rather have another way to scroll the graph from the toolbar.<\/p>\n<p>So I <a href=\"http:\/\/code.google.com\/p\/currentcostgui\/source\/browse\/trunk\/currentcostgraphs.py?r=148#104\" target=\"_blank\">added a couple of extra buttons<\/a>: one which scrolls the graph a screen to the left, and one which scrolls the graph a screen to the right.<\/p>\n<p>I&#8217;m using stock images from matplotlib for the button icons, but you could use your own custom images &#8211; either way you need to use <code>_load_bitmap<\/code> to load the image to use for the icon.<\/p>\n<p>In the action methods that are called when the custom buttons are clicked, I use <code>axes.get_xlim()<\/code> to get the min and max values for the x axis, then get the difference between them to work out the horizontal length of the graph. I can then move the x axis min and max by that amount to scroll the graph by one screen.<\/p>\n<pre style=\"overflow: scroll; font-size: 1.1em; border: thin solid silver; background-color: #eeeeee; padding: 0.8em\">class MyCustomToolbar(NavigationToolbar2Wx): \r\n    ON_CUSTOM_LEFT  = wx.NewId()\r\n    ON_CUSTOM_RIGHT = wx.NewId()\r\n\r\n    def __init__(self, plotCanvas):\r\n        # create the default toolbar\r\n        NavigationToolbar2Wx.__init__(self, plotCanvas)\r\n        # add new toolbar buttons \r\n        self.AddSimpleTool(self.ON_CUSTOM_LEFT, _load_bitmap('stock_left.xpm'),\r\n                           'Pan to the left', 'Pan graph to the left')\r\n        wx.EVT_TOOL(self, self.ON_CUSTOM_LEFT, self._on_custom_pan_left)\r\n        self.AddSimpleTool(self.ON_CUSTOM_RIGHT, _load_bitmap('stock_right.xpm'),\r\n                           'Pan to the right', 'Pan graph to the right')\r\n        wx.EVT_TOOL(self, self.ON_CUSTOM_RIGHT, self._on_custom_pan_right)\r\n\r\n    # pan the graph to the left\r\n    def _on_custom_pan_left(self, evt):\r\n        ONE_SCREEN = 1\r\n        axes = self.canvas.figure.axes[0]\r\n        x1,x2 = axes.get_xlim()\r\n        ONE_SCREEN = x2 - x1\r\n        axes.set_xlim(x1 - ONE_SCREEN, x2 - ONE_SCREEN)\r\n        self.canvas.draw()\r\n\r\n    # pan the graph to the right\r\n    def _on_custom_pan_right(self, evt):\r\n        ONE_SCREEN = 1\r\n        axes = self.canvas.figure.axes[0]\r\n        x1,x2 = axes.get_xlim()\r\n        ONE_SCREEN = x2 - x1\r\n        axes.set_xlim(x1 + ONE_SCREEN, x2 + ONE_SCREEN)\r\n        self.canvas.draw()<\/pre>\n<p>Now back to thinking about how to do realtime graphing with matplotlib&#8230; \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>My CurrentCost desktop software is written in Python, and uses the matplotlib library for plotting graphs. I am a big fan of matplotlib &#8211; you can create some very cool graphs with it. It&#8217;s not without it&#8217;s issues&#8230; for example, I&#8217;ve still yet to work out how to do realtime graphing with it that isn&#8217;t [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[267,396,212],"class_list":["post-778","post","type-post","status-publish","format-standard","hentry","category-code","tag-matplotlib","tag-navigationtoolbar2wx","tag-python"],"_links":{"self":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=778"}],"version-history":[{"count":0,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/778\/revisions"}],"wp:attachment":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}