2016-03-31 2 views
0

Я пишу приложение python, используя pyside и matplotlib. После комбинации this tutorial и this SO post, я создал виджет matplotlib, который я могу успешно добавить к родительскому. Однако, когда я собираюсь фактически добавлять данные к нему, ничего не отображается.Динамический виджет mplotlib pyside не отображается

Если я добавляю статические данные, такие как сообщение SO, оно появляется, но когда я меняю его на обновление «на лету» (в настоящее время каждую секунду на таймере, но в конечном итоге он будет использовать сигнал другого класса) Я никогда не получаю ничего, кроме пустых осей. Я подозреваю, что мне не хватает вызова, чтобы заставить ничью или сделать недействительным или что что-то не так с тем, как я вызываю update_datalim (хотя значения, которые передаются ему, кажутся правильными).

from PySide import QtCore, QtGui 

import matplotlib 
import random 

matplotlib.use('Qt4Agg') 
matplotlib.rcParams['backend.qt4']='PySide' 

from matplotlib import pyplot as plt 
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 
from matplotlib.figure import Figure 
from matplotlib.patches import Rectangle 

from collections import namedtuple 

DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height']) 

class BaseWidget(FigureCanvas): 

    def __init__(self, parent=None, width=5, height=4, dpi=100): 
     fig = Figure(figsize=(width, height), dpi=dpi) 
     self.axes = fig.add_subplot(111) 
     # We want the axes cleared every time plot() is called 
     self.axes.hold(False) 
     self.axes.set_xlabel('X Label') 
     self.axes.set_ylabel('Y Label') 
     self.axes.set_title('My Data') 

     FigureCanvas.__init__(self, fig) 
     self.setParent(parent) 

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

     FigureCanvas.updateGeometry(self) 

class DynamicWidget(BaseWidget): 
    def set_data(self, the_data): 
     self.axes.clear() 
     xys = list() 

     cmap = plt.cm.hot 
     for datum in the_data: 
      bottom_left = (datum.start_x, datum.start_y) 
      top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height) 
      rect = Rectangle(
       xy=bottom_left, 
       width=datum.width, height=datum.height, color=cmap(100) 
      ) 
      xys.append(bottom_left) 
      xys.append(top_right) 
      self.axes.add_artist(rect) 
     self.axes.update_datalim(xys) 
     self.axes.figure.canvas.draw_idle() 

class RandomDataWidget(DynamicWidget): 
    def __init__(self, *args, **kwargs): 
     DynamicWidget.__init__(self, *args, **kwargs) 
     timer = QtCore.QTimer(self) 
     timer.timeout.connect(self.generate_and_set_data) 
     timer.start(1000) 

    def generate_and_set_data(self): 
     fake_data = [DataModel(
      start_x=random.randint(1, 100), 
      width=random.randint(20, 40), 
      start_y=random.randint(80, 160), 
      height=random.randint(20, 90) 
     ) for i in range(100)] 

     self.set_data(fake_data) 

Edit: Я подозревал, что существует проблема с обновлением пределы участка. При запуске вышеуказанного кода график открывается с пределами 0 и 1 на оси x и y. Поскольку ни один из моих сгенерированных данных не попадает в этот диапазон, я создал другой подкласс DynamicWidget, который отображает только данные между 0 и 1 (те же данные из связанного сообщения SO). При создании экземпляра класса ниже данные отображаются успешно. Нужно ли мне делать что-то большее, чем позвонить update_datalim, чтобы получить график, чтобы снова привязать себя?

class StaticWidget(DynamicWidget): 
    def __init__(self): 
     DynamicWidget.__init__(self) 
     static_data = [ 
      DataModel(0.5, 0.05, 0.2, 0.05), 
      DataModel(0.1, 0.2, 0.7, 0.2), 
      DataModel(0.3, 0.1, 0.8, 0.1) 
     ] 
     self.set_data(static_data) 
+0

добавить 'self.axes.figure.canvas.draw_idle()' в ваш 'set_data' метод. – tacaswell

+0

@tcaswell Я попытался добавить его в конец функции, но, похоже, ничего не изменил. –

+0

Я пропустил, что вы вызывали 'update_datalim', это внутренняя функция, не делайте этого. Пожалуйста. – tacaswell

ответ

2

Да, update_datalim обновляет только прямоугольник, который хранится внутри осями. Вам также необходимо включить автомасштабирование для его использования. Добавить self.axes.autoscale(enable=True) после заявления self.axes.clear() и он будет работать. Или вы можете установить диапазон осей на фиксированное значение, используя self.axes.set_xlim и self.axes.set_ylim.

редактировать: Вот мой код, который работает для меня

from PySide import QtCore, QtGui 

import matplotlib 
import random, sys 

matplotlib.use('Qt4Agg') 
matplotlib.rcParams['backend.qt4']='PySide' 

from matplotlib import pyplot as plt 
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 
from matplotlib.figure import Figure 
from matplotlib.patches import Rectangle 

from collections import namedtuple 

DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height']) 

class BaseWidget(FigureCanvas): 

    def __init__(self, parent=None, width=5, height=4, dpi=100): 
     fig = Figure(figsize=(width, height), dpi=dpi) 
     self.axes = fig.add_subplot(111) 
     # We want the axes cleared every time plot() is called 
     self.axes.hold(False) 
     #self.axes.autoscale(enable=True) 
     self.axes.set_xlabel('X Label') 
     self.axes.set_ylabel('Y Label') 
     self.axes.set_title('My Data') 

     FigureCanvas.__init__(self, fig) 
     self.setParent(parent) 

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

     FigureCanvas.updateGeometry(self) 

class DynamicWidget(BaseWidget): 
    def set_data(self, the_data): 
     self.axes.clear() 
     self.axes.autoscale(enable=True) 
     #self.axes.set_xlim(0, 300) 
     #self.axes.set_ylim(0, 300) 
     xys = list() 

     cmap = plt.cm.hot 
     for datum in the_data: 
      print datum 
      bottom_left = (datum.start_x, datum.start_y) 
      top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height) 
      rect = Rectangle(
       xy=bottom_left, 
       width=datum.width, height=datum.height, color=cmap(100) 
      ) 
      xys.append(bottom_left) 
      xys.append(top_right) 
      self.axes.add_artist(rect) 
     self.axes.update_datalim(xys) 
     self.axes.figure.canvas.draw_idle() 

class RandomDataWidget(DynamicWidget): 
    def __init__(self, *args, **kwargs): 
     DynamicWidget.__init__(self, *args, **kwargs) 
     timer = QtCore.QTimer(self) 
     timer.timeout.connect(self.generate_and_set_data) 
     timer.start(1000) 

    def generate_and_set_data(self): 
     fake_data = [DataModel(
      start_x=random.randint(1, 100), 
      width=random.randint(20, 40), 
      start_y=random.randint(80, 160), 
      height=random.randint(20, 90)) for i in range(100)] 

     self.set_data(fake_data) 
     print "done:...\n\n" 


def main(): 
    qApp = QtGui.QApplication(sys.argv) 

    aw = RandomDataWidget() 

    aw.show() 
    aw.raise_() 
    sys.exit(qApp.exec_()) 

if __name__ == "__main__": 
    main() 
+0

Добавление только 'autoscale (enable = True)', похоже, не заставило его работать для меня , Однако я ценю помощь. Любые другие идеи? –

+0

Обязательно добавьте его после 'axes.clear', иначе он будет сброшен. Или, возможно, попытайтесь использовать 'set_xlim' и' set_ylim'. В любом случае я отредактировал свой ответ, чтобы включить мой адаптированный код. Это работает для меня, поэтому я был бы очень удивлен, если он не сработает для вас. Вы можете попробовать? – titusjan

+0

Я думаю, что я идиот. Я удалил вызов 'draw_idle' всякий раз, когда он не работал для меня. Сейчас он работает, спасибо! –

Смежные вопросы