2016-07-19 28 views
1

Ich versuche, ein Echtzeit-Datendiagramm mit einem PyQt-Plot-Widget zu erstellen. Ich habe gelesen, dass PyQt die beste Option zum Zeichnen von Echtzeitkurven ist, aber bisher habe ich keinen Erfolg.Echtzeit-Plotten mit PyQt PlotWidget - Fehlermeldung PlotWidget-Objekt ist nicht aufrufbar

Ich habe versucht, Zufallsdaten mit der Methode followed here zu plotten, aber es scheint, dass diese Methode nicht auf das PyQt-Diagramm-Widget zutrifft.

Ich habe den folgenden Code kompiliert, um eine GUI zu generieren, die zufällige Punkte auf der x- und y-Achse plottet; aber ich den Fehler:

PlotWidget object is not callable

from PyQt4.QtGui import * 
from PyQt4.QtCore import * 

import numpy as np 
import pyqtgraph as pg 
import sys 


class Window(QMainWindow): 

    def __init__(self): 
     super(Window, self).__init__() 
     self.setWindowIcon(QIcon('pythonlogo.png')) 
     self.setGeometry(50,50,700,300) 
     self.home() 

    def home(self): 

     #Timer for Plot calls the update function 

     self.plot = pg.PlotWidget(self) 
     self.timer2 = pg.QtCore.QTimer() 
     self.timer2.timeout.connect(self.update) 
     self.timer2.start(16) 

     #Plot widget postion 
     self.plot.move(200,50) 
     self.plot.resize(450,200) 

     self.show() 

    def update(self): 
     x = np.random.normal(size=1000) 
     y = np.random.normal(size=1000) 
     self.plot(x,y,clear=True) 

def run():  
     app=QApplication(sys.argv) 
     GUI = Window() 
     sys.exit(app.exec_()) 

run() 

Antwort

1

Ich habe mit ähnlichen Problemen konfrontiert. Aber am Ende habe ich mein Realtime-Plot zum Laufen gebracht!

Ich habe mir meinen Code angeschaut und alle Dinge rausgeworfen, die für Sie nicht relevant sind. Also, was Sie hier finden, ist der grundlegende Code, den Sie benötigen, um eine Live-Grafik anzuzeigen:

################################################################### 
#                 # 
#      PLOTTING A LIVE GRAPH      # 
#     ----------------------------     # 
#   EMBED A MATPLOTLIB ANIMATION INSIDE YOUR    # 
#   OWN GUI!            # 
#                 # 
################################################################### 


import sys 
import os 
from PyQt4 import QtGui 
from PyQt4 import QtCore 
import functools 
import numpy as np 
import random as rd 
import matplotlib 
matplotlib.use("Qt4Agg") 
from matplotlib.figure import Figure 
from matplotlib.animation import TimedAnimation 
from matplotlib.lines import Line2D 
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 
import time 
import threading 



def setCustomSize(x, width, height): 
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) 
    sizePolicy.setHorizontalStretch(0) 
    sizePolicy.setVerticalStretch(0) 
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth()) 
    x.setSizePolicy(sizePolicy) 
    x.setMinimumSize(QtCore.QSize(width, height)) 
    x.setMaximumSize(QtCore.QSize(width, height)) 

'''''' 

class CustomMainWindow(QtGui.QMainWindow): 

    def __init__(self): 

     super(CustomMainWindow, self).__init__() 

     # Define the geometry of the main window 
     self.setGeometry(300, 300, 800, 400) 
     self.setWindowTitle("my first window") 

     # Create FRAME_A 
     self.FRAME_A = QtGui.QFrame(self) 
     self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name()) 
     self.LAYOUT_A = QtGui.QGridLayout() 
     self.FRAME_A.setLayout(self.LAYOUT_A) 
     self.setCentralWidget(self.FRAME_A) 

     # Place the zoom button 
     self.zoomBtn = QtGui.QPushButton(text = 'zoom') 
     setCustomSize(self.zoomBtn, 100, 50) 
     self.zoomBtn.clicked.connect(self.zoomBtnAction) 
     self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0)) 

     # Place the matplotlib figure 
     self.myFig = CustomFigCanvas() 
     self.LAYOUT_A.addWidget(self.myFig, *(0,1)) 

     # Add the callbackfunc to .. 
     myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,)) 
     myDataLoop.start() 

     self.show() 

    '''''' 


    def zoomBtnAction(self): 
     print("zoom in") 
     self.myFig.zoomIn(5) 

    '''''' 

    def addData_callbackFunc(self, value): 
     # print("Add data: " + str(value)) 
     self.myFig.addData(value) 



''' End Class ''' 


class CustomFigCanvas(FigureCanvas, TimedAnimation): 

    def __init__(self): 

     self.addedData = [] 
     print(matplotlib.__version__) 

     # The data 
     self.xlim = 200 
     self.n = np.linspace(0, self.xlim - 1, self.xlim) 
     a = [] 
     b = [] 
     a.append(2.0) 
     a.append(4.0) 
     a.append(2.0) 
     b.append(4.0) 
     b.append(3.0) 
     b.append(4.0) 
     self.y = (self.n * 0.0) + 50 

     # The window 
     self.fig = Figure(figsize=(5,5), dpi=100) 
     self.ax1 = self.fig.add_subplot(111) 


     # self.ax1 settings 
     self.ax1.set_xlabel('time') 
     self.ax1.set_ylabel('raw data') 
     self.line1 = Line2D([], [], color='blue') 
     self.line1_tail = Line2D([], [], color='red', linewidth=2) 
     self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r') 
     self.ax1.add_line(self.line1) 
     self.ax1.add_line(self.line1_tail) 
     self.ax1.add_line(self.line1_head) 
     self.ax1.set_xlim(0, self.xlim - 1) 
     self.ax1.set_ylim(0, 100) 


     FigureCanvas.__init__(self, self.fig) 
     TimedAnimation.__init__(self, self.fig, interval = 50, blit = True) 

    def new_frame_seq(self): 
     return iter(range(self.n.size)) 

    def _init_draw(self): 
     lines = [self.line1, self.line1_tail, self.line1_head] 
     for l in lines: 
      l.set_data([], []) 

    def addData(self, value): 
     self.addedData.append(value) 

    def zoomIn(self, value): 
     bottom = self.ax1.get_ylim()[0] 
     top = self.ax1.get_ylim()[1] 
     bottom += value 
     top -= value 
     self.ax1.set_ylim(bottom,top) 
     self.draw() 


    def _step(self, *args): 
     # Extends the _step() method for the TimedAnimation class. 
     try: 
      TimedAnimation._step(self, *args) 
     except Exception as e: 
      self.abc += 1 
      print(str(self.abc)) 
      TimedAnimation._stop(self) 
      pass 

    def _draw_frame(self, framedata): 
     margin = 2 
     while(len(self.addedData) > 0): 
      self.y = np.roll(self.y, -1) 
      self.y[-1] = self.addedData[0] 
      del(self.addedData[0]) 


     self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ]) 
     self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin])) 
     self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin]) 
     self._drawn_artists = [self.line1, self.line1_tail, self.line1_head] 



''' End Class ''' 


# You need to setup a signal slot mechanism, to 
# send data to your GUI in a thread-safe way. 
# Believe me, if you don't do this right, things 
# go very very wrong.. 
class Communicate(QtCore.QObject): 
    data_signal = QtCore.pyqtSignal(float) 

''' End Class ''' 



def dataSendLoop(addData_callbackFunc): 
    # Setup the signal-slot mechanism. 
    mySrc = Communicate() 
    mySrc.data_signal.connect(addData_callbackFunc) 

    # Simulate some data 
    n = np.linspace(0, 499, 500) 
    y = 50 + 25*(np.sin(n/8.3)) + 10*(np.sin(n/7.5)) - 5*(np.sin(n/1.5)) 
    i = 0 

    while(True): 
     if(i > 499): 
      i = 0 
     time.sleep(0.1) 
     mySrc.data_signal.emit(y[i]) # <- Here you emit a signal! 
     i += 1 
    ### 
### 




if __name__== '__main__': 
    app = QtGui.QApplication(sys.argv) 
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique')) 
    myGUI = CustomMainWindow() 


    sys.exit(app.exec_()) 

'''''' 

Probieren Sie es einfach aus. Kopieren Sie diesen Code in eine neue Python-Datei und führen Sie ihn aus. Sie sollten ein schönes glattes Live-Diagramm erhalten:

enter image description here

+0

vielen Dank. Sie haben eine wirklich ordentliche App erstellt :) Ich habe bemerkt, dass Sie sich entschieden haben, Matplotlib-Canvas über das PyQt4-Plot-Widget zu verwenden. Liegt das daran, dass Sie mit PyQt4 keinen Erfolg hatten? Ich hatte den Eindruck, dass eine der Hauptattraktionen von PyQt4 die Fähigkeit ist, Echtzeitdaten zu plotten, aber dies scheint bei dem Widget nicht der Fall zu sein. Ich habe auch in Ihrem Code bemerkt, dass Sie gesagt haben, dass die Dinge sehr falsch laufen, wenn Sie keine Thread-sichere Methode verwenden? Was sind die Risiken? Ich werde Datensignale von einem Temperatursensor empfangen, der mit einem Rpi verbunden ist. Nochmals vielen Dank für diese Antwort, es ist sehr nützlich :) – alkey

+0

Hallo Allan Key. Die Matplotlib-Bibliothek bietet erstaunliche Unterstützung für alle Arten von Animationen. Und sein Code ist so optimiert, dass die Graphen glatt sind. Für mich ist das wichtig :-). Gut, über die unsicheren Wege des Threads hatte ich einmal sehr merkwürdige Fehlermeldungen, bevor ich meinen Code "threadsicher" gemacht habe. Ich kann es für Sie suchen, wenn Sie interessiert sind. Wie auch immer, der Code in meiner Antwort ist 100% threadsicher. Ist Ihnen aufgefallen, dass alle 0,1 Sekunden ein neuer Datenpunkt an das Diagramm gesendet wird? Es wird durch den Signalschlitzmechanismus "emittiert". Wenn Sie die Temperaturdaten durch senden (statt dessen .. –

+0

.. Sinus-Funktion, die ich erzeuge), sehen Sie Ihre Temperaturkurve live! Vertrau mir, ich habe es mit zwölf Signalen von einem Mikrocontroller gleichzeitig gemacht. Alle zwölf Signale bewegen sich sanft, als ob Sie das Bedienfeld eines Kernkraftwerks betrachten würden ;-) –

0

Ok, so ich in der Lage war, den Fehler zu lösen war ich immer und konnte das Grundstück Widget in Echtzeit aktualisieren. Der folgende Code enthält einige grundlegende Beispiele. Ich hoffe, diese Antwort über die Zeit zu verbessern, um die Funktionalität des Echtzeit-Plottings in PyQt zu zeigen.

from PyQt4.QtGui import * 
from PyQt4.QtCore import * 

import numpy as np 
import pyqtgraph as pg 
import random 
import sys 
import datetime 


class Window(QMainWindow): 

    def __init__(self): 
     super(Window, self).__init__() 
     self.setWindowIcon(QIcon('pythonlogo.png')) 
     self.setGeometry(50,50,700,900) 
     self.home() 

    def home(self):  

     #Labels 

     staticLbl = QLabel("Static Plot",self) 
     staticLbl.move(10,50) 

     dynamicLbl = QLabel("Random Plot",self) 
     dynamicLbl.move(10,300) 

     conLbl = QLabel("Continuous Plot",self) 
     conLbl.move(10,550) 

     #Static plot widget: 

     staticPlt = pg.PlotWidget(self) 
     x = np.random.normal(size=10) 
     y = np.random.normal(size=10) 

     staticPlt.plot(x,y,clear=True) 

     staticPlt.move(200,50) 
     staticPlt.resize(450,200) 

     #Code to run to random plot using timer: 

     self.dynamicPlt = pg.PlotWidget(self) 

     self.dynamicPlt.move(200,300) 
     self.dynamicPlt.resize(450,200) 

     self.timer2 = pg.QtCore.QTimer() 
     self.timer2.timeout.connect(self.update) 
     self.timer2.start(200) 

     #Code to run to get continous plot using timer: 

     self.continuousPlt = pg.PlotWidget(self) 

     self.continuousPlt.move(200,550) 
     self.continuousPlt.resize(450,200) 

     self.timer3 = pg.QtCore.QTimer() 
     self.timer3.timeout.connect(self.cUpdate) 
     self.timer3.start(200) 

     self.show() 

    def update(self): 
     z = np.random.normal(size=1) 
     u = np.random.normal(size=1) 
     self.dynamicPlt.plot(z,u,pen=None, symbol='o') 

    def cUpdate(self): 
     now = datetime.datetime.now() 
     s = np.array([now.second]) 

     self.continuousPlt.plot(s,s,pen=None, symbol='o') 


def run():  
     app=QApplication(sys.argv) 
     GUI = Window() 
     sys.exit(app.exec_()) 

run()