Sudarshan S
Sudarshan S

Reputation: 1295

Embed matplotlib widget in multiple places

I have a matplotlib graph which I want repeated in two separate windows, under PyQt4. I've tried adding the widget to the layout of both, but then the widget vanishes from the first one. Is there any way to do this except creating two identical graphs and keeping them in sync?

Upvotes: 4

Views: 1862

Answers (2)

Alvaro Fuentes
Alvaro Fuentes

Reputation: 17455

The problem is that you can't add the same qt widget to two differents parents widgets because in the process of adding a widget Qt also make a reparent process which does what you see:

... the widget vanishes from the first one[window]...

So the solution is to make two canvas that share the same figure. Here is an example code, this will show you two main windows each with two canvas and the four plots will be syncronized:

import sys
from PyQt4 import QtGui
import numpy as np
import numpy.random as rd
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class ApplicationWindow(QtGui.QMainWindow):

    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.main_widget = QtGui.QWidget(self)
        vbl = QtGui.QVBoxLayout(self.main_widget)

        self._fig = Figure()        
        self.ax = self._fig.add_subplot(111)

        #note the same fig for two canvas
        self.fc1 = FigureCanvas(self._fig) #canvas #1
        self.fc2 = FigureCanvas(self._fig) #canvas #1

        self.but = QtGui.QPushButton(self.main_widget)
        self.but.setText("Update") #for testing the sync

        vbl.addWidget(self.fc1)
        vbl.addWidget(self.fc2)
        vbl.addWidget(self.but)        

        self.setCentralWidget(self.main_widget)

    @property  
    def fig(self):
        return self._fig

    @fig.setter
    def fig(self, value):
        self._fig = value
        #keep the same fig in both canvas
        self.fc1.figure = value
        self.fc2.figure = value        

    def redraw_plot(self):
        self.fc1.draw()
        self.fc2.draw()


qApp = QtGui.QApplication(sys.argv)

aw1 = ApplicationWindow() #window #1
aw2 = ApplicationWindow() #window #2

aw1.fig = aw2.fig #THE SAME FIG FOR THE TWO WINDOWS!

def update_plot():
    '''Just a random plot for test the sync!'''
    #note that the update is only in the first window
    ax = aw1.fig.gca()
    ax.clear()
    ax.plot(range(10),rd.random(10))

    #calls to redraw the canvas
    aw1.redraw_plot()
    aw2.redraw_plot()

#just for testing the update
aw1.but.clicked.connect(update_plot)
aw2.but.clicked.connect(update_plot)        

aw1.show()
aw2.show()

sys.exit(qApp.exec_())

Upvotes: 2

Joe Kington
Joe Kington

Reputation: 284582

While it's not a perfect solution, matplotlib has a built-in way to keep the limits, ticks, etc of two separate plots in sync.

E.g.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 4 * np.pi, 100)
y = np.cos(x)

figures = [plt.figure() for _ in range(3)]
ax1 = figures[0].add_subplot(111)
axes = [ax1] + [fig.add_subplot(111, sharex=ax1, sharey=ax1) for fig in figures[1:]]

for ax in axes:
    ax.plot(x, y, 'go-')

ax1.set_xlabel('test')

plt.show()

Notice that all 3 plots will stay in-sync as you zoom, pan, etc.

There's probably a better way of doing it though.

Upvotes: 0

Related Questions