Home > Mobile >  How to refresh AxesSubplot in python matplotlib?
How to refresh AxesSubplot in python matplotlib?

Time:07-28

In a QMainWindow, I am creating an AxesSubplot. It is created in the function initEventGraph when the window starts up with:

self.canvas = FigureCanvas(plt.Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True))
self.canvas.setParent(parent)
self.canvas.move(10,20)

self.ax = self.canvas.figure.subplots()

In my code, I have indicated FUNCTION CALL # to illustrate the sequence of function calls for creating the plot.

I start off with wanting to plot 1 day of data.

I created some data with varying datetimes that are both less than 1 day old and more than 1 day old.

        data =  {"Apple"  : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] }, 

                "Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
                }

When I run the GUI and it plots with the plot with num_days=1, it definitely does show the data that is 1 day old or newer.

Now, the problem is that when I activate the function refreshButtonPressed by clicking on the Refresh button, I want to display the past 10 days of data, but the plot does not refresh. This is done by calling the plot function with num_days=10. There should be a difference because the graph was initially created with num_days=1.

I would expect the days older than 1 day old to show up in the plot. But there are no points for these in the plot and the axes do not refresh either.

I've tried many things, such as calling the following within the plot function:

  • self.ax.cla()
  • self.ax.clf()
  • self.ax.show()
  • self.ax.clear()
  • self.ax.draw()
  • plt.show()

However, no matter what I do, I can't get the plot to refresh. I'm really stuck.

Here is my minimal example code. It's just to very simply illustrate the problem:

from __future__ import annotations
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QSizePolicy, QGroupBox, QMessageBox, QTableWidgetItem, QLineEdit, QMainWindow, QHeaderView, QDesktopWidget, QPushButton, QLabel, QTableWidget
import datetime 
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
import sys

class TestClass(QMainWindow):

    def __init__(self, numDays):  ##FUNCTION CALL #1
        super(TestClass, self).__init__()
        width = 1295
        height = 700
        self.setFixedSize(width, height)
        self.setWindowTitle("Test")
        self.centralwidget = QtWidgets.QWidget(self)
        self.centralwidget.setObjectName("centralwidget")
        self.numDays = numDays
        self.initUI()
        
        
    def initUI(self): #FUNCTION CALL #2
        current_time = datetime.datetime.now()
        self.createEventGraph(current_time=current_time, numDays=self.numDays)
        self.createRefreshButton()
        
    def createEventGraph(self, current_time, numDays):  #FUNCTION CALL #3
        self.eventGraphGroupBox = QGroupBox(self)
        self.eventGraphGroupBox.setGeometry(QtCore.QRect(555,20,725,610))

        self.initEventGraph(parent=self.eventGraphGroupBox, current_time=current_time, numDays=numDays)
    
    def initEventGraph( self, parent, current_time , numDays ): #FUNCTION CALL #4
    
        self.canvas = FigureCanvas(plt.Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True))
        self.canvas.setParent(parent)
        self.canvas.move(10,20)

        self.ax = self.canvas.figure.subplots()

        FigureCanvas.setSizePolicy(self,
                QSizePolicy.Expanding,
                QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        self.plot(current_time=current_time, num_days=numDays)
            

    def refreshButtonPressed(self ) : 
        current_time = datetime.datetime.now()
        self.plot(current_time=current_time, num_days=10)  

    def createRefreshButton( self ) : 

            self.refreshButton = QPushButton(self)
            self.refreshButton.setText("Refresh")
            self.refreshButton.setGeometry(460,660,80,30)
            self.refreshButton.clicked.connect(self.refreshButtonPressed)

    def plot(self, current_time, num_days): #FUNCTION CALL #5 and for REFRESH
        data =  {"Apple"  : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] }, 

                "Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
                }

        unique_event_names = dict()
        for name, entries in data.items():
            if name not in unique_event_names: 
                unique_event_names[name] = True
                 
            for index, time_entry in enumerate(entries['time']): 
                if time_entry >= current_time - datetime.timedelta(days=num_days):
                
                    x_axis = entries['time'][index]
                    y_axis = entries['location'][index]
                    self.ax.scatter(x_axis,y_axis, label=name)
            self.ax.xaxis.set_major_locator(ticker.LinearLocator(10))
            self.ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S %d-%m-%y"))
            self.ax.tick_params(labelrotation=15)
            self.ax.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", ncol=len(unique_event_names))
            self.ax.set_ylabel("Location")
            self.ax.grid()

def window(): 
    app = QApplication(sys.argv)
    theTestClass = TestClass(numDays = 1)
    theTestClass.show()
    sys.exit(app.exec_())

if __name__ == "__main__": 
    window()

Edit : I had a typo in my code that was causing every data point to show. Fixed it.

CodePudding user response:

There are a few changes you could make and try.

  1. It is best practice not to import pyplot if you are embedding. Instead use, from matplotlib.figure import Figure
  2. Clear the figure before plotting with self.fig.clear() and create new axes.
  3. Finally, refresh the canvas with self.canvas.draw()
from __future__ import annotations
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QSizePolicy, QGroupBox, QMessageBox, QTableWidgetItem, QLineEdit, QMainWindow, QHeaderView, QDesktopWidget, QPushButton, QLabel, QTableWidget
import datetime 
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys

class TestClass(QMainWindow):

    def __init__(self, numDays):  ##FUNCTION CALL #1
        super(TestClass, self).__init__()
        width = 1295
        height = 700
        self.setFixedSize(width, height)
        self.setWindowTitle("Test")
        self.centralwidget = QtWidgets.QWidget(self)
        self.centralwidget.setObjectName("centralwidget")
        self.numDays = numDays
        self.initUI()
        
        
    def initUI(self): #FUNCTION CALL #2
        current_time = datetime.datetime.now()
        self.createEventGraph(current_time=current_time, numDays=self.numDays)
        self.createRefreshButton()
        
    def createEventGraph(self, current_time, numDays):  #FUNCTION CALL #3
        self.eventGraphGroupBox = QGroupBox(self)
        self.eventGraphGroupBox.setGeometry(QtCore.QRect(555,20,725,610))

        self.initEventGraph(parent=self.eventGraphGroupBox, current_time=current_time, numDays=numDays)
    
    def initEventGraph( self, parent, current_time , numDays ): #FUNCTION CALL #4
    
        self.fig = Figure(figsize=(8.80,7.30), dpi=80, tight_layout=True)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(parent)
        self.canvas.move(10,20)

        FigureCanvas.setSizePolicy(self,
                QSizePolicy.Expanding,
                QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        self.plot(current_time=current_time, num_days=numDays)
            

    def refreshButtonPressed(self ) : 
        current_time = datetime.datetime.now()
        self.plot(current_time=current_time, num_days=10)  

    def createRefreshButton( self ) : 

            self.refreshButton = QPushButton(self)
            self.refreshButton.setText("Refresh")
            self.refreshButton.setGeometry(460,660,80,30)
            self.refreshButton.clicked.connect(self.refreshButtonPressed)

    def plot(self, current_time, num_days): #FUNCTION CALL #5 and for REFRESH
        self.fig.clear()
        # Create a 1x1 subplot
        self.ax = self.fig.add_subplot(1, 1, 1)

        data =  {"Apple"  : { "location" : [25,50,10], 'time' : [datetime.datetime.now(), datetime.datetime.now() - datetime.timedelta(days = 5), datetime.datetime.now() - datetime.timedelta(days = 1) ] }, 

                "Orange" : {"location" : [9,12,89] , 'time' : [datetime.datetime.now() - datetime.timedelta(days = 0.1), datetime.datetime.now() - datetime.timedelta(days = 0.15), datetime.datetime.now() - datetime.timedelta(days = 2)]}
                }

        unique_event_names = dict()
        for name, entries in data.items():
            if name not in unique_event_names: 
                unique_event_names[name] = True
                 
            for index, time_entry in enumerate(entries['time']): 
                if time_entry >= current_time - datetime.timedelta(days=num_days):
                
                    x_axis = entries['time'][index]
                    y_axis = entries['location'][index]
                    self.ax.scatter(x_axis,y_axis, label=name)
            self.ax.xaxis.set_major_locator(ticker.LinearLocator(10))
            self.ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S %d-%m-%y"))
            self.ax.tick_params(labelrotation=15)
            self.ax.legend(bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",
                mode="expand", ncol=len(unique_event_names))
            self.ax.set_ylabel("Location")
            self.ax.grid()

        # refresh canvas
        self.canvas.draw()

def window(): 
    app = QApplication(sys.argv)
    theTestClass = TestClass(numDays = 1)
    theTestClass.show()
    sys.exit(app.exec_())

if __name__ == "__main__": 
    window()
  • Related