Dynamic matplotlib pyside widget not displaying
Question: I'm writing a python application using pyside and matplotlib. Following a
combination of [this
tutorial](http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html)
and [this SO post](http://stackoverflow.com/a/9082596/160300), I have created
a matplotlib widget that I can successfully add to a parent. However when I go
to actually add data to it, nothing seems to get displayed.

If I add static data like the SO post had, it shows up, but when I change it
to update on the fly (currently every second on a timer, but it will
eventually be using a signal from another class), I never get anything but the
empty axes to appear. I suspect that I'm missing a call to force a draw or
invalidate or that there is something wrong with the way I'm calling
update_datalim (though the values that get passed to it seem correct).

    
    
    from PySide import QtCore, QtGui
    
    import matplotlib
    import random
    
    matplotlib.use('Qt4Agg')
    matplotlib.rcParams['backend.qt4']='PySide'
    
    from matplotlib import pyplot as plt
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure
    from matplotlib.patches import Rectangle
    
    from collections import namedtuple
    
    DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height'])
    
    class BaseWidget(FigureCanvas):
    
        def __init__(self, parent=None, width=5, height=4, dpi=100):
            fig = Figure(figsize=(width, height), dpi=dpi)
            self.axes = fig.add_subplot(111)
            # We want the axes cleared every time plot() is called
            self.axes.hold(False)
            self.axes.set_xlabel('X Label')
            self.axes.set_ylabel('Y Label')
            self.axes.set_title('My Data')
    
            FigureCanvas.__init__(self, fig)
            self.setParent(parent)
    
            FigureCanvas.setSizePolicy(self,
                                       QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
    
            FigureCanvas.updateGeometry(self)
    
    class DynamicWidget(BaseWidget):
        def set_data(self, the_data):
            self.axes.clear()
            xys = list()
    
            cmap = plt.cm.hot
            for datum in the_data:
                bottom_left = (datum.start_x, datum.start_y)
                top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height)
                rect = Rectangle(
                    xy=bottom_left,
                    width=datum.width, height=datum.height, color=cmap(100)
                )
                xys.append(bottom_left)
                xys.append(top_right)
                self.axes.add_artist(rect)
            self.axes.update_datalim(xys)
            self.axes.figure.canvas.draw_idle()
    
    class RandomDataWidget(DynamicWidget):
        def __init__(self, *args, **kwargs):
            DynamicWidget.__init__(self, *args, **kwargs)
            timer = QtCore.QTimer(self)
            timer.timeout.connect(self.generate_and_set_data)
            timer.start(1000)
    
        def generate_and_set_data(self):
            fake_data = [DataModel(
                start_x=random.randint(1, 100),
                width=random.randint(20, 40),
                start_y=random.randint(80, 160),
                height=random.randint(20, 90)
            ) for i in range(100)]
    
            self.set_data(fake_data)
    

**Edit:** I'm suspecting that there's an issue with updating the limits of the
plot. When running the above code, the plot opens with limits of 0 and 1 on
both the x and y axis. Since none of my generated data falls into that range,
I created another subclass of `DynamicWidget` that plots only data between 0
and 1 (the same data from the linked SO post). When instantiating the class
below, the data shows up successfully. Do I need to do something more than
calling `update_datalim` to get the graph to re-bound itself?

    
    
    class StaticWidget(DynamicWidget):
        def __init__(self):
            DynamicWidget.__init__(self)
            static_data = [
                DataModel(0.5, 0.05, 0.2, 0.05),
                DataModel(0.1, 0.2, 0.7, 0.2),
                DataModel(0.3, 0.1, 0.8, 0.1)
            ]
            self.set_data(static_data)
    


Answer: Yes, `update_datalim` only updates the bounding box that is kept internally by
the axes. You also need to enable auto scaling for it to be used. Add
`self.axes.autoscale(enable=True)` after the `self.axes.clear()` statement and
it will work. Or you can set the axes' range to a fixed value by using
`self.axes.set_xlim` and `self.axes.set_ylim`.

**edit:** here is my code, which works for me

    
    
    from PySide import QtCore, QtGui
    
    import matplotlib
    import random, sys
    
    matplotlib.use('Qt4Agg')
    matplotlib.rcParams['backend.qt4']='PySide'
    
    from matplotlib import pyplot as plt
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure
    from matplotlib.patches import Rectangle
    
    from collections import namedtuple
    
    DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height'])
    
    class BaseWidget(FigureCanvas):
    
        def __init__(self, parent=None, width=5, height=4, dpi=100):
            fig = Figure(figsize=(width, height), dpi=dpi)
            self.axes = fig.add_subplot(111)
            # We want the axes cleared every time plot() is called
            self.axes.hold(False)
            #self.axes.autoscale(enable=True)
            self.axes.set_xlabel('X Label')
            self.axes.set_ylabel('Y Label')
            self.axes.set_title('My Data')
    
            FigureCanvas.__init__(self, fig)
            self.setParent(parent)
    
            FigureCanvas.setSizePolicy(self,
                                       QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
    
            FigureCanvas.updateGeometry(self)
    
    class DynamicWidget(BaseWidget):
        def set_data(self, the_data):
            self.axes.clear()
            self.axes.autoscale(enable=True)
            #self.axes.set_xlim(0, 300)
            #self.axes.set_ylim(0, 300)
            xys = list()
    
            cmap = plt.cm.hot
            for datum in the_data:
                print datum
                bottom_left = (datum.start_x, datum.start_y)
                top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height)
                rect = Rectangle(
                    xy=bottom_left,
                    width=datum.width, height=datum.height, color=cmap(100)
                )
                xys.append(bottom_left)
                xys.append(top_right)
                self.axes.add_artist(rect)
            self.axes.update_datalim(xys)
            self.axes.figure.canvas.draw_idle()
    
    class RandomDataWidget(DynamicWidget):
        def __init__(self, *args, **kwargs):
            DynamicWidget.__init__(self, *args, **kwargs)
            timer = QtCore.QTimer(self)
            timer.timeout.connect(self.generate_and_set_data)
            timer.start(1000)
    
        def generate_and_set_data(self):
            fake_data = [DataModel(
                start_x=random.randint(1, 100),
                width=random.randint(20, 40),
                start_y=random.randint(80, 160),
                height=random.randint(20, 90)) for i in range(100)]
    
            self.set_data(fake_data)
            print "done:...\n\n"
    
    
    def main():
        qApp = QtGui.QApplication(sys.argv)
    
        aw = RandomDataWidget()
    
        aw.show()
        aw.raise_()
        sys.exit(qApp.exec_())
    
    if __name__ == "__main__":
        main()
    

