График перенастройки на основе данных подписки ROS

Архитектура

У меня есть сюжет. И кривая в этом сюжете. У меня есть узел в файле и подписчик. Этот подписчик подписывается на некоторые плавающие данные, которые публикуются. Каждый раз, когда публикуются некоторые данные, я обновляю кривую, добавляя новую точку данных к существующему набору.

Проблема

График не обновляется должным образом. Поскольку данные поступают каждую секунду, графический интерфейс зависает, и через некоторое время графический интерфейс прерывается из-за ошибки сегментации.

Код

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()

Вызов этих функций: -

self.listener()
self.initUI()

После вызова слушателя подписчик автоматически связывается с функцией обратного вызова. Функция обратного вызова просматривает новые данные, добавляет их к оси Y, а затем заново строит график.

Ошибка

Я получаю эту ошибку каждый раз, когда публикуется новая точка данных: -

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

Я не понимаю эту ошибку.

Об издателе

ROS следует шаблону издатель-подписка. Я уже создал узел, который публикует случайное целое число. Это целое число должно быть нанесено на график.

Технические характеристики

Ubuntu 12.04
ROS Hydro
PyQt4
Qwt5

person IcyFlame    schedule 08.05.2014    source источник


Ответы (1)


Ваш callback метод работает в потоке. Вы не можете обновлять объекты графического интерфейса Qt из другого потока. Вот почему вы видите ошибки и получаете сбои.

Решения:

  • В обратном вызове добавьте данные в список. Используйте QTimer, запущенный из основного потока, чтобы периодически проверять список на наличие обновлений и заново строить график (не идеальное решение, но, вероятно, выполнит свою работу)

  • В обратном вызове поместите данные в python Queue.Queue(). Имейте блок QThread при чтении из этой очереди и испускайте сигнал qt (с данными в нем) каждый раз, когда что-то читается из Queue. Подключите метод в основном потоке к этому сигналу qt. Таким образом, ваш метод в основном потоке получает данные и может обновлять график из основного потока.

Вот куча других вопросов о переполнении стека, которые делают нечто подобное (отправляют данные из потока в основной поток qt, чтобы избежать ошибок сегментации) или будут полезны при изучении многопоточных приложений pyqt:

person three_pineapples    schedule 09.05.2014