0

Architecture

I have a plot. And a curve in that plot. I have a node in the file and a subscriber. This subscriber subscribes to some float data, that is being published. Every time some data is published, I update the curve by adding the new datapoint to the existing set.

Problem

The graph is not updated properly. As the data comes in every second, the GUI is getting hanged up and after some time, the GUI is aborted due to a Segmentation Fault.

Code

def initUI(self):

    # x11.XInitThreads()

    # xlib.XInitThreads()

    # initialising the window

    QtGui.QWidget.__init__(self)

    # self.setGeometry(300, 300, 160, 1000)
    # self.setWindowTitle('Visualizer')

    # main layout

    self.layout = QtGui.QVBoxLayout(self)

    # Creating the elements in this widget

    a = QtGui.QLabel("Navigation", self)

    a.setStyleSheet("QLabel{ background-color: white; color: black; font-size: 25px; }")

    self.plot = Qwt.QwtPlot(self)
    self.plot.setCanvasBackground(Qt.black)
    self.plot.setAxisTitle(Qwt.QwtPlot.xBottom, 'Time')
    self.plot.setAxisScale(Qwt.QwtPlot.xBottom, 0, 10, 1)
    self.plot.setAxisTitle(Qwt.QwtPlot.yLeft, 'Temperature')
    self.plot.setAxisScale(Qwt.QwtPlot.yLeft, 0, 250, 40)
    self.plot.replot()

    self.curve = Qwt.QwtPlotCurve('')
    self.curve.setRenderHint(Qwt.QwtPlotItem.RenderAntialiased)
    pen = QPen(QColor('limegreen'))
    pen.setWidth(2)
    self.curve.setPen(pen)
    self.curve.attach(self.plot)

    self.layout.addWidget(a)
    self.layout.addWidget(self.plot)

def listener(self):       

    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber(TOPIC_NAME, String, self.callback)

def callback(self, data):

    self.xData.append(self.counter + 1)
    self.yData.append(int(str(data.data)))
    self.counter += 1

    self.curve.setData(self.xData, self.yData)
    self.plot.replot()

Calling these functions :-

self.listener()
self.initUI()

Once the listener is called, the subscriber is automatically associated with the callback function. The callback function looks at the new data, adds it to the y-axis and then, replots the graph.

Error

I am getting this error everytime a new datapoint is published :-

QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
QPainter::drawRects: Painter not active
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::translate: Painter not active
QPainter::save: Painter not active
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::save: Painter not active
QPainter::setPen: Painter not active
QPainter::restore: Unbalanced save/restore
QPainter::restore: Unbalanced save/restore
QPainter::end: Painter not active, aborted

I don't understand this error.

About the publisher

ROS follows the Publisher-Subscribe pattern. I have already created a node that publishes a random integer. This integer should be plotted on the graph.

Specifications

Ubuntu 12.04
ROS Hydro
PyQt4
Qwt5
Daan
  • 2,680
  • 20
  • 39
IcyFlame
  • 5,059
  • 21
  • 50
  • 74

1 Answers1

2

Your callback method is running in thread. You cannot update Qt GUI objects from another thread. This is why you see errors and get segfaults.

The solutions are:

  • In the callback, append the data to a list. Use a QTimer started from the main thread to periodically check the list for updates and replot the graph (not an ideal solutions, but will likely get the job done)

  • In the callback, place the data in a python Queue.Queue(). Have a QThread block on reading from this queue and emit a qt signal (with the data in it) each time something is read from the Queue. Connect a method in your main thread to this qt signal. Your method in the main thread thus gets the data and can update the plot from the main thread.

Here are a bunch of other stack overflow questions which do something similar (send data from a thread into the qt main thread to avoid segfaults) or will be useful when delving into multithreaded pyqt applications:

Community
  • 1
  • 1
three_pineapples
  • 11,579
  • 5
  • 38
  • 75