0
$\begingroup$

Rosanswers logo

Hi everyone,

does an example exist for the implementation of a continuous updated diagram with matplotlib?

I want to plot a histogram of the last n measurements. Hence, I integrated a hist and a fig.canvas.draw() into the callback of the related subscription. But matplotlib don't like such concurrent constructs and throw an error:

RuntimeError: main thread is not in main loop

How to handle this problem?

Thanks for your help

Best wishes

Poseidonius


Originally posted by Poseidonius on ROS Answers with karma: 427 on 2012-04-06

Post score: 5

$\endgroup$

1 Answer 1

0
$\begingroup$

Rosanswers logo

Hi,

I used the following solution for a online histogram. The first class defines the canvas and the matplotlib figure:

#!/usr/bin/env python
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as  NavigationToolbar
from matplotlib.figure import Figure

class PlotWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle('Sliding histogramm')
        self.create_main_frame()
        self.on_draw()
    
    def save_plot(self):
        pass

    def on_about(self):
        pass

    def on_pick(self, event):
        pass

    def on_draw(self):
        self.axes.clear()        
        self.axes.grid(True)
        self.canvas.draw()

    def create_main_frame(self):
        self.main_frame = QWidget()
        self.dpi = 100
        self.fig = Figure((5.0, 4.0), dpi=self.dpi)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)
        self.axes = self.fig.add_subplot(111)
        self.canvas.mpl_connect('pick_event', self.on_pick)
        self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)     
        vbox = QVBoxLayout()
        vbox.addWidget(self.canvas)
        vbox.addWidget(self.mpl_toolbar)
        self.main_frame.setLayout(vbox)
        self.setCentralWidget(self.main_frame)

... and the second one uses the first one to plot a histogram for instance.

from PlotWindow import PlotWindow

import rospy
import sys, random
from PyQt4.QtCore import *
from PyQt4.QtGui import *

import numpy
from std_msgs.msg import Int8

class OnlineHist(PlotWindow):
  def __init__(self):
    PlotWindow.__init__(self)

    self.window_size=20
    self.values=numpy.zeros((self.window_size))
    self.index=0

    rospy.init_node('visualizer', anonymous=True)
    self.subscriber = rospy.Subscriber("random_value", Int8, self.plotResults, queue_size = 1 )


  def plotResults(self, data): 
    self.axes.clear()        
    self.axes.set_autoscaley_on(False)

    if self.index==self.window_size-1:
      self.index=0
    else:
      self.index=self.index+1
    self.values[self.index]=data.data 
  
    n, bins, patches = self.axes.hist(self.values, 10, (0, 10), normed=True, facecolor='green', alpha=0.75)

    output= "Data index "+str(self.index)
    min_x, max_x=self.axes.get_xlim()
    min_y, max_y=self.axes.get_ylim()   
    self.axes.text(max_x*0.6,max_y*0.7,output,horizontalalignment='left',verticalalignment='center')

    self.canvas.draw()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = OnlineHist()
    window.show()
    app.exec_()

For testing a random node:

#!/usr/bin/env python
import rospy
import random
from std_msgs.msg import Int8
def talker():
    pub = rospy.Publisher('random_value', Int8)
    rospy.init_node('random_value')
    while not rospy.is_shutdown():
        pub.publish(random.random()*10)
        rospy.sleep(0.1)

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException: pass

Feel free to improve the solution :-)

Best wishes

Poseidonius


Originally posted by Poseidonius with karma: 427 on 2012-09-13

This answer was ACCEPTED on the original site

Post score: 4

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.