Raj Gupta
Raj Gupta

Reputation: 15

Using a button to open and close a matplotlib widget

The aim of what I've written so far is to have a "Temperature" button in my GUI that when pressed opens up a matplotlib plot that I made separately (mplwidget.py).

However, when I run the code, both the app and the widget open up simultaneously, and the Temperature button appears to have no function (even if I close the widget, pressing the button doesn't open it again).

import sys
from PyQt5.QtCore import QCoreApplication

from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QAction, QMessageBox
from PyQt5.uic.properties import QtGui

from mplwidget import animate #import animation widget

class window(QMainWindow):

    def __init__(self):
        super(window, self).__init__()
        self.setGeometry(50, 50, 500, 300)
        self.setWindowTitle('Temperature Control')
        self.setWindowIcon(QIcon('adn.png'))

        extractAction = QAction('&Quit', self)
        extractAction.setShortcut('Ctrl+Q')
        extractAction.setStatusTip('leave the app')
        extractAction.triggered.connect(self.close_application)

        self.statusBar()

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('&File')
        fileMenu.addAction(extractAction)

        self.home()

    def home(self):
        btn = QPushButton('quit', self)
        btn.clicked.connect(self.close_application)
        btn.resize(btn.sizeHint())
        btn.move(0, 100)

        button = QPushButton('Temperature',self)
        button.clicked.connect(self.opengraph)
        button.move(50,200)
        self.show()

    def opengraph(self):
        animate()

    def close_application(self):

        choice = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)

        if choice == QMessageBox.Yes:
            sys.exit()
        else:
            pass

if __name__ == "__main__":

    def run():
        app = QApplication(sys.argv)
        Gui = window()
        sys.exit(app.exec_())

run()

mplwidget is below

def GraphWidget():

    fig = plt.figure()
    ax1 = fig.add_subplot(1,1,1)

    Time = []
    Temp = []

    def animate(i):

        x = datetime.datetime.now()
        y = numpy.random.randint(48,52)
        Time.append(x)
        Temp.append(int(y))    

    #    print (Temp)
        ax1.plot(Time,Temp)

    ani = animation.FuncAnimation(fig,animate, interval=1000)
    plt.show()

Upvotes: 0

Views: 1929

Answers (2)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339120

The problem is that plt.show() cannot start an event loop itself because the event loop is already running due to the QT window being open. In such cases one would need to call fig.show() instead, where fig is the figure in use.

This in turn leads to the problem that the function from the mplwidget.py module actually returns. Once it returns the reference for the animation is lost and will be garbage collected; hence no animation shows up on screen.

The solution is to let the function return the animation and store it somewhere in the main program.

import sys
from PyQt5.QtCore import QCoreApplication

from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QAction, QMessageBox
from PyQt5.uic.properties import QtGui

from mplwidget import showgraph

class window(QMainWindow):

    def __init__(self):
        super(window, self).__init__()
        self.setGeometry(50, 50, 500, 300)
        self.setWindowTitle('Temperature Control')
        self.setWindowIcon(QIcon('adn.png'))

        extractAction = QAction('&Quit', self)
        extractAction.setShortcut('Ctrl+Q')
        extractAction.setStatusTip('leave the app')
        extractAction.triggered.connect(self.close_application)

        self.statusBar()

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('&File')
        fileMenu.addAction(extractAction)

        self.home()

    def home(self):
        btn = QPushButton('quit', self)
        btn.clicked.connect(self.close_application)
        btn.resize(btn.sizeHint())
        btn.move(0, 100)

        button = QPushButton('Temperature',self)
        button.clicked.connect(self.opengraph)
        button.move(50,200)
        self.show()

    def opengraph(self):
        self.store = showgraph()

    def close_application(self):

        choice = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)

        if choice == QMessageBox.Yes:
            sys.exit()
        else:
            pass

if __name__ == "__main__":

    def run():
        app = QApplication(sys.argv)
        Gui = window()
        sys.exit(app.exec_())

run()

mplwidget.py:

import matplotlib.pyplot as plt
import datetime
import numpy
import matplotlib.animation as animation

def showgraph():

    fig = plt.figure()
    ax1 = fig.add_subplot(1,1,1)

    Time = []
    Temp = []

    def animate(i):

        x = datetime.datetime.now()
        y = numpy.random.randint(48,52)
        Time.append(x)
        Temp.append(int(y))    

        ax1.plot(Time,Temp)

    ani = animation.FuncAnimation(fig,animate, interval=1000)
    fig.show()
    return fig, ani

Upvotes: 2

S. Nick
S. Nick

Reputation: 13651

Try it:

import sys
from PyQt5.QtGui     import QIcon
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QAction, QMessageBox

from mplwidget import MplWindow                     # +++

class window(QMainWindow):

    def __init__(self):
        super(window, self).__init__()
        self.setGeometry(50, 50, 500, 300)
        self.setWindowTitle('Temperature Control')
        self.setWindowIcon(QIcon('adn.png'))
        extractAction = QAction('&Quit', self)
        extractAction.setShortcut('Ctrl+Q')
        extractAction.setStatusTip('leave the app')
        extractAction.triggered.connect(self.close_application)
        self.statusBar()
        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('&File')
        fileMenu.addAction(extractAction)

        self.matplWindow = MplWindow()               # +++

        self.home()


    def home(self):
        btn = QPushButton('quit', self)
        btn.clicked.connect(self.close_application)
        btn.resize(btn.sizeHint())
        btn.move(0, 100)
        button = QPushButton('Temperature',self)
        button.clicked.connect(self.opengraph)
        button.move(50,200)
        self.show()

    def opengraph(self):
        self.matplWindow.funAnimation()              # +++

    def close_application(self):
        choice = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)
        if choice == QMessageBox.Yes:
            sys.exit()
        else:
            pass

if __name__ == "__main__":
    app = QApplication(sys.argv)
    Gui = window()
    sys.exit(app.exec_())

mplwidget.py

import matplotlib.pyplot as plt
import numpy
import datetime
import matplotlib.animation as animation
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout


class MplWindow(QDialog):
    Time = []
    Temp = []
    def __init__(self, parent=None):
        super(MplWindow, self).__init__(parent)

    def funAnimation(self):        
        self.fig = plt.figure()
        self.ax1 = self.fig.add_subplot(1,1,1)
        self.ani = animation.FuncAnimation(self.fig, self.animate, interval=1000)
        plt.show()

    def animate(self, i):
        x = datetime.datetime.now()
        y = numpy.random.randint(48,52)
        self.Time.append(x)
        self.Temp.append(int(y))    
        self.ax1.plot(self.Time, self.Temp)

enter image description here

Upvotes: 1

Related Questions