1

I created a matplotlib´s figure in a QMainWindow, using PyQt and I am trying to add a button to the matplotlib´s toolbar in my code. This is the NavigationToolbar that I created:

enter image description here

I added those buttons using the addWidget method. But, what I need is to create an Icon and put it on the toolbar. This is some part of my code:

class A(QMainWindow):
  def __init__(self):
    QMainWindow.__init__(self)

    self.mainWidget = QWidget()
    self.setCentralWidget(self.mainWidget)
    layout = QVBoxLayout()
    self.mainWidget.setLayout(layout)

    self.figure_canvas = FigureCanvas(Figure())
    layout.addWidget(self.figure_canvas, 10)

    self.axes = self.figure_canvas.figure.add_subplot(111)

    self.navigation_toolbar = NavigationToolbar2(self.figure_canvas, self)
    self.addToolBar(Qt.TopToolBarArea, self.navigation_toolbar) 

    self.btn_selection_tool3 = QPushButton(, "Connect")
    self.navigation_toolbar.addWidget(self.btn_selection_tool3)

    self.btn_selection_tool2 = QPushButton()
    self.navigation_toolbar.addWidget(self.btn_selection_tool2)

    self.btn_showgrid = QPushButton("Show Grid")
    self.navigation_toolbar.addWidget(self.btn_showgrid)

    self.btn_hidegrid = QPushButton("Hide Grid")
    self.navigation_toolbar.addWidget(self.btn_hidegrid)

app = QApplication(sys.argv)
window = A()
window.show()
sys.exit(app.exec_())

I saw some codes and questions made, and I found these, but I could not accomplish what I need. These are the links that I read:

dale lane - customize navigation toolbar

Modify the toolbar

The links only told me how to delete some of them, and one works with wx.

How can I add these buttons in the toolbar, without using QPushbutton or addWidget methods?. Hope you can help me

------ EDIT ------

Based on @three_pineapples comment, Ihave tried to add this class to my code:

class MyToolbar(NavigationToolbar2):
  def __init__(self):
    NavigationToolbar2.__init__(self)
    self.iconDir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
        "..", "images", "icons", "")

    self.a = self.addAction(QIcon(iconDir + "BYE2.ico"),
        "Bye", self.bye)
    self.a.setToolTip("GoodBye")

  def bye(self):
    print "See you next time")

And I instantiated doing:

self.navigation_toolbar = MyToolbar(), instead of:

self.navigation_toolbar = NavigationToolbar2(self.figure_canvas, self)

But, I am getting this error:

TypeError: __init__() takes at least 3 arguments (1 given)

I´ve tried adding *args and kwargs, but I do not know what i am missing here.

Is this the way to add a button to the matplotlib´s toolbar? Hope you can help me.

4
  • 1
    Please make sure that your question is complete (in your case: can be copied and will execute) as described here in more detail: stackoverflow.com/help/mcve . You will get quicker and better responses on complete questions. Commented May 29, 2016 at 7:16
  • 1
    This may be of some use? Commented May 29, 2016 at 12:56
  • Thank you for your comments, I´ve modified the question based on your comments. I still can not accomplish to do add the button that I need. Hope you can help me, and thank you again for your comments. Commented May 29, 2016 at 15:36
  • I'm reasonably certain that the code in your "edit" above will work if you correctly pass in the arguments to NavigationToolbar2.__init__(...). You've only passed in self. You also need to pass in those other arguments (used in your previous code that creates the toolbar) into the MyToolbar __init__ method and then as the second and third argument to the 'NavigationToolbar2.__init__(...)` call. Commented Jun 2, 2016 at 3:18

3 Answers 3

1

Based on this related SO answer, I wanted a way that doesn't involve extending any class and is much less verbose (pretty much 3 lines). Taking a look to this sources and using fig.canvas.toolbar I made this snippet work:

It plots an image and adds two buttons to the toolbar: previous and next, which can be bound to any parameter-less function. This can be very convenient, for instance, for scrolling through the images on a folder and drawing some points or lines on them using scatter, as explained in this other SO post. Let me know how it works for you!

fig, ax = plt.subplots()
fig.canvas.manager.toolbar._Button("PREVIOUS", "back_large", <ACTION_PREV>)
fig.canvas.manager.toolbar._Button("NEXT", "forward_large", <ACTION_NEXT>)
im = plt.imread("hello.png")
implot = ax.imshow(im)
fig.show()

enter image description here

  • Note the bigger arrows that were added to the toolbar.

  • The drawback of this method is that it uses the protected _Button method, whose interface may change in further versions, but this way seems fine to me for some casual scripting.

  • Also note that the method signature (see the sources) requires a path to an image that (in my case) has to be found at /usr/local/lib/python2.7/dist-packages/matplotlib/mpl-data/images. Again, there is probably a way to overcome this but probably more verbose than the inheriting alternative.

Cheers,
Andres


BONUS:

class ImageViewer(object):
    """Given a path to a directory, this class opens a matplotib window with two
       custom buttons that allow scrolling through the images in the directory.
       Usage example: iv = ImageViewer("/home/pau/Images")
    """
    def __init__(self, seq_path, img_extension=".png"):
        self.ids = [join(seq_path, splitext(img)[0]) for img in listdir(seq_path)
                    if img.endswith(img_extension)]
        self.mod = len(self.ids)
        self.idx = 0
        self.fig, self.ax = plt.subplots()
        self.fig.canvas.manager.toolbar._Button("PREVIOUS", "back_large", self.prev)
        self.fig.canvas.manager.toolbar._Button("NEXT", "forward_large", self.next)
        self._plot(0)
    def _plot(self, idx):
        im = plt.imread(self.ids[idx]+".png")
        implot = self.ax.imshow(im, cmap='Greys_r',  interpolation='nearest')
        self.fig.show()
    def next(self):
        self.idx = (self.idx+1)%self.mod
        self._plot(self.idx)
    def prev(self):
        self.idx = (self.idx-1)%self.mod
        self._plot(self.idx)
Sign up to request clarification or add additional context in comments.

Comments

1

I have solved the problem. I´ ve found this code:

Matplotlib/Tkinter - customizing toolbar tooltips (broken link)

So, I created a subclass and added as it says in the link. This is the code:

class MyToolbar(NavigationToolbar2):
  def __init__(self, figure_canvas, parent= None):
    self.toolitems = (('Home', 'Lorem ipsum dolor sit amet', 'home', 'home'),
        ('Back', 'consectetuer adipiscing elit', 'back', 'back'),
        ('Forward', 'sed diam nonummy nibh euismod', 'forward', 'forward'),
        (None, None, None, None),
        ('Pan', 'tincidunt ut laoreet', 'move', 'pan'),
        ('Zoom', 'dolore magna aliquam', 'zoom_to_rect', 'zoom'),
        (None, None, None, None),
        ('Subplots', 'putamus parum claram', 'subplots', 'configure_subplots'),
        ('Save', 'sollemnes in futurum', 'filesave', 'save_figure'),
        ('Port', 'Select', "select", 'select_tool'),
        )

    NavigationToolbar2.__init__(self, figure_canvas, parent= None)

  def select_tool(self):
    print "You clicked the selection tool"

And the, you can add this toolbar by writing:

self.navigation_toolbar = MyToolbar(self.figure_canvas, self)            
self.navigation_toolbar.update()

If you want to only let your own button, you must erase all the others items from self.toolitems. For example:

self.toolitems = (
    ('Port', 'Select', "select", 'select_tool'),
    )

With this, you will only see your own button in the NavigationToolbar

Hope this helps.

1 Comment

The link is dead (ok, not exactly dead;) )
0

As noted, if you don't mind extending the class you can add buttons to the toolbar by taking the list of toolitems and modifying it accordingly. To get a custom icon for the new toolbar item for that particular approach, use something like this for a NewButton:

class MyNavigationBar(NavigationToolbar2Tk):

    def __init__(self,canvas,frame,pack_toolbar=False):
        self.toolitems = (
            ('Home', 'Reset original view', 'home', 'home'),
            ('Back', 'Back to previous view', 'back', 'back'),
            ('Forward', 'Forward to next view', 'forward', 'forward'),
            (None, None, None, None),
            ('Pan',
            'Left button pans, Right button zooms\n'
            'x/y fixes axis, CTRL fixes aspect',
            'move', 'pan'),
            ('NewButton', 'Adjust window and level','filesave','windowlevel'),
            (None, None, None, None),
            ('Save', 'Save the figure', 'filesave', 'save_figure'),
        )
        super().__init__(canvas,frame,pack_toolbar=pack_toolbar)
        path = os.path.join('/path/to/icondir','tool_icon.png')
        self._buttons['NewButton']._image_file = path
        NavigationToolbar2Tk._set_image_for_button(self, self._buttons['NewButton'])

   def windowlevel(self,*args):
        do something...

note the use of an existing tk navigation bar icon filesave in the list above for NewButton, it is just a placeholder until it gets replaced by the custom icon.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.