Michael Michael - 24 days ago 14
Python Question

Making a plot in a second window using data from main window

I'm trying to make a program in which I have a main window and a second window. The second window should be opened by checking a Check-Box in the main window and closed by unchecking it.

The following minimal example works already fine (thanks to ImportanceOfBeingErnest !), but I want to spin the arrow (the one, which is already bent when you run the example) by changing the SpinBox in the main window.

import sys
from PyQt4 import QtGui, QtCore

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation

import numpy as np


class Newsphere(QtGui.QMainWindow):

def __init__(self):
super(Newsphere, self).__init__()
self.mainbox = QtGui.QWidget()
self.mainbox.setLayout(QtGui.QHBoxLayout())
self.setCentralWidget(self.mainbox)
self.spin = QtGui.QSpinBox()
self.spin.setValue(20)
self.spin.setMaximum(100)
self.spin.setMinimum(-100)
self.checkPlot = QtGui.QCheckBox("Check")
self.mainbox.layout().addWidget(self.spin)
self.mainbox.layout().addWidget(self.checkPlot)

self.Plot = None
self.checkPlot.clicked.connect(self.showPlot)

def showPlot(self):
if self.Plot == None:
self.Plot = Plot(self.kinematic())
self.Plot.show()
# register signal for closure
self.Plot.signalClose.connect(self.uncheck)
# register signal for spin value changed
self.spin.valueChanged.connect(self.kinematic)
else:
self.Plot.close()
self.Plot = None

def kinematic(self):

x = self.spin.value() / 100

v = np.matrix([[1.,x,0.],[0.,1.,0.],[0.,0.,1.]])
zero = np.matrix([[0.,0.,0.],[0.,0.,0.],[0.,0.,0.]])

pos = np.hstack([v, zero])

return pos

def uncheck(self):
self.checkPlot.setChecked(False)
self.Plot = None

class Plot(QtGui.QWidget):

signalClose = QtCore.pyqtSignal()

def __init__(self, pos=None):
super(Plot, self).__init__()
self.setLayout(QtGui.QHBoxLayout())

self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.fig.tight_layout()
self.ax.view_init(40, 225)

''' dashed coordinate system '''
self.ax.plot([0,1], [0,0], [0,0], label='$X_0$', linestyle="dashed", color="red")
self.ax.plot([0,0], [0,-10], [0,0], label='$Y_0$', linestyle="dashed", color="green")
self.ax.plot([0,0], [0,0], [0,1], label='$Z_0$', linestyle="dashed", color="blue")

self.ax.set_xlim3d(-3,3)
self.ax.set_ylim3d(-3,3)
self.ax.set_zlim3d(-3,3)

self.canvas = FigureCanvas(self.fig)
self.layout().addWidget(self.canvas)

self.pos = pos

self.setup_plot()

self.ani = animation.FuncAnimation(self.fig, self.update_plot, init_func=self.setup_plot, blit=True)

def setup_plot(self):

self.ax.legend(loc='best')

self.position = self.ax.quiver(0, 0, 0, 0, 0, 0, pivot="tail", color="black")

return self.position,

def update_plot(self, i):

x_zero = self.pos[:,3]
y_zero = self.pos[:,4]
z_zero = self.pos[:,5]

v_x = self.pos[0,0:3]
v_y = self.pos[1,0:3]
v_z = self.pos[2,0:3]

self.position = self.ax.quiver(-x_zero, -y_zero, z_zero, -v_x[0,:], v_y[0,:], v_z[0,:], pivot="tail", color="black")
self.canvas.draw()

return self.position,

# We need to make sure the animation stops, when the window is closed
def closeEvent(self, event):
self.signalClose.emit()
self.close()
super(Plot, self).closeEvent(event)

def close(self):
self.ani.event_source.stop()
super(Plot, self).close()


if __name__ == '__main__':

app = QtGui.QApplication(sys.argv)
main = Newsphere()
main.show()
sys.exit(app.exec_())

Answer

Here is an working example of what I think you are trying to achieve.
The Main Window has a spin box and a check box. Once the checkbox is clicked, a new window with a plot will show up and an animation will start. The current value and some array will be given to the plot window. If you change the spin box value while the animation is running, it will be updated. When the plot window is closed or when the checkbox is unchecked, the animation will stop (and be deleted).

import sys
from PyQt4 import QtGui, QtCore

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation

import numpy as np


class Newsphere(QtGui.QMainWindow):

    def __init__(self):
        super(Newsphere, self).__init__()
        self.mainbox = QtGui.QWidget()
        self.mainbox.setLayout(QtGui.QHBoxLayout())
        self.setCentralWidget(self.mainbox)
        self.spin = QtGui.QSpinBox()
        self.spin.setValue(5)
        self.spin.setMaximum(10)
        self.spin.setMinimum(1)
        self.checkPlot = QtGui.QCheckBox("Check")
        self.mainbox.layout().addWidget(self.spin)
        self.mainbox.layout().addWidget(self.checkPlot)

        self.Plot = None
        self.checkPlot.clicked.connect(self.showPlot)

    def showPlot(self):
        if self.Plot == None:
            self.Plot = Plot(self.kinematic(), self.spin.value())
            self.Plot.show()
            # register signal for closure
            self.Plot.signalClose.connect(self.uncheck)
            # register signal for spin value changed
            self.spin.valueChanged.connect(self.Plot.update_factor)
        else:
            self.Plot.close()
            self.Plot = None

    def kinematic(self):
        v = np.array([[1.,2.,3.],[2.,1.,3.],[3.,2.,1.]])
        return v

    def uncheck(self):
        self.checkPlot.setChecked(False)
        self.Plot = None

class Plot(QtGui.QWidget):

    signalClose = QtCore.pyqtSignal()

    def __init__(self, v=None, factor=1):
        super(Plot, self).__init__()
        self.setLayout(QtGui.QHBoxLayout())

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111,projection = '3d')
        self.ax.set_aspect('equal')
        self.fig.tight_layout()
        self.ax.view_init(40, 225)

        self.ax.set_xlim3d(0,3)
        self.ax.set_ylim3d(0,3)
        self.ax.set_zlim3d(0,4)

        self.canvas = FigureCanvas(self.fig)
        self.layout().addWidget(self.canvas)

        self.pos = v

        self.setup_plot()
        self.update_factor(factor)

        self.ani = animation.FuncAnimation(self.fig, self.update_plot, blit=False)

    def setup_plot(self):
        xpos, ypos = np.meshgrid(np.arange(self.pos.shape[0]),np.arange(self.pos.shape[1]) )
        self.xpos = xpos.flatten('F')
        self.ypos = ypos.flatten('F')
        self.zpos = np.zeros_like(self.xpos)
        self.bar = None

    def update_factor(self, factor):
        self.factor = factor
        self.dx = np.ones_like(self.xpos)*np.min(np.abs(self.factor/10.), 0.1)
        self.dy = self.dx.copy()

    def update_plot(self, i):

        if self.bar != None:
            self.bar.remove()
            del self.bar
        pos = self.pos+np.sin(i/8.)
        dz = pos.flatten()

        self.bar = self.ax.bar3d(self.xpos, self.ypos, self.zpos, self.dx, self.dy, dz, 
                        color=(1.-self.factor/10.,0,self.factor/10.), zsort='average', linewidth=0)

        self.canvas.draw()

    # We need to make sure the animation stops, when the window is closed
    def closeEvent(self, event):
        self.signalClose.emit()
        self.close()
        super(Plot, self).closeEvent(event)

    def close(self):
        self.ani.event_source.stop()
        super(Plot, self).close()


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)    
    main = Newsphere()
    main.show()
    sys.exit(app.exec_())

Since I wasn't sure about what you want to animate, I changed the plot to a barplot, but you can change it back to whatever you need. Hope that helps.

Comments