3

I'm trying to maximise use of space in a Qt GUI with an embedded matplotlib figure, including the standard matplotlib navigation bar to provide useful functionality for the user (zoom, save, move etc.)

Unfortunately, the NavigationBar ends up taking up a a lot of space when the plot window has a 'widescreen' ratio, as seen below. All of the buttons are on the left hand side, but the NavigationBar creates a whole bar of white space as well. enter image description here

While my intended application is embedding a matplotlib figure in a Qt GUI, I believe this is a general question that could also apply to the standard figure displayed by the default matplotlib ax.plot operation.

Is it possible to use this space? I tried setting text and mpl widgets here, but they are always hidden behind the navigation bar. Perhaps there is a setting to make the colour of this bar transparent?

Alternatively, is there a quick way to add in the buttons as widgets on to my figure, so the NavigationBar is not needed. I like the style and functionality offered by the bar, so would rather not have to manually re-implement each one.

Thanks.

PhysLQ
  • 149
  • 2
  • 9

1 Answers1

3

The NavigationToolbar is a normal QWidget. This means that you can do everything with it that you would also be able to do with other widgets, including

  • placing it into layouts of other widgets
  • putting it into menus
  • changing it's layout
  • position it absolutely inside the GUI
  • etc...

The question is a bit too broad to provide solutions for all of those; but there are also enough resources available on how to manipulate and position widgets in PyQt.

Below is an example for an absolutely positionned NavigationToolbar, which is (by default) transparent. The toolbar is positionned at the origin of the axes and will stay there if the figure is resized.

GUI with tranparent navigation toolbar

import matplotlib.pyplot as plt
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar

class Window(QtGui.QMainWindow):
    def __init__(self, fig):
        self.qapp = QtGui.QApplication([])

        QtGui.QMainWindow.__init__(self)
        self.widget = QtGui.QWidget()
        self.setCentralWidget(self.widget)
        self.widget.setLayout(QtGui.QVBoxLayout())
        self.widget.layout().setContentsMargins(0,0,0,0)
        self.widget.layout().setSpacing(0)

        self.fig = fig
        self.canvas = FigureCanvas(self.fig)
        self.canvas.draw()
        self.canvas.mpl_connect("resize_event", self.resize)

        self.widget.layout().addWidget(self.canvas)

        self.nav = NavigationToolbar(self.canvas, self.widget, coordinates=False)
        self.nav.setMinimumWidth(300)
        self.nav.setStyleSheet("QToolBar { border: 0px }")

        self.show()
        self.qapp.exec_()

    def resize(self, event):
        # on resize reposition the navigation toolbar to (0,0) of the axes.
        x,y = self.fig.axes[0].transAxes.transform((0,0))
        figw, figh = self.fig.get_size_inches()
        ynew = figh*self.fig.dpi-y - self.nav.frameGeometry().height()
        self.nav.move(x,ynew)

# create a figure with a subplot
fig, ax = plt.subplots(figsize=(5,3))
# colorize figure and axes to make transparency obvious
fig.set_facecolor("#e9c9ef") 
ax.set_facecolor("#f7ecf9")
ax.plot([2,3,5,1], color="#ab39c1")
fig.tight_layout()

# pass the figure to the custom window
a = Window(fig)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks - this works well to position the NavBar in the GUI somewhere more compact. As a point of interest, is it possible to overlay this (i.e. Qt Widgets generally) over the top of other layers, ideally including some transparency too? – PhysLQ May 30 '17 at 23:10
  • 1
    Why don't you try it out and if it doesn't work, update your question with a [mcve] and a clear problem description? – ImportanceOfBeingErnest May 30 '17 at 23:23
  • 1
    I had a closer look at this and provided a complete example in the updated answer. – ImportanceOfBeingErnest Jun 01 '17 at 10:55
  • I was trying to achieve this but it didn't work nearly as well as that code example. Thanks so much for this! – PhysLQ Jun 01 '17 at 11:30