简体   繁体   English

使用pyqtgraph和线程进行实时绘图

[英]Real-Time-Plotting using pyqtgraph and threading

this is a bit longer, the first part is just a description of the problem, the second one the question if my "fix" is correct. 这有点长,第一部分只是问题的描述,第二部分是我的“修复”是否正确的问题。

I started with python programming. 我从python编程开始。 I created a program that communicates with an Arduino that reads the temperature of a furnace of our melting lab. 我创建了一个与Arduino通信的程序,该程序读取了我们熔炼实验室的熔炉温度。 The temperature is then used in a PID algorithm and an output is set to the Arduino. 然后将温度用于PID算法中,并将输出设置为Arduino。 The communication is done via pyserial. 通信是通过pyserial完成的。 So far, everthing works, including live plotting of the temperature signals, PID-variables and so on. 到目前为止,所有工作都可以进行,包括实时绘制温度信号,PID变量等。 The script includes a the main loop and 3 threads (serial communication, a datashifter that reads from serialport, the set temperature from the QWidget and the output of the PID algorithm. This values are used to create an array for displaying within pyqtgraph. Finally, the third thread shifts the data from the datashifter to the QWidget. 该脚本包括一个主循环和3个线程(串行通信,从串行端口读取的数据移位器,QWidget的设定温度和PID算法的输出。此值用于创建一个数组,以在pyqtgraph中显示。第三个线程将数据从datashifter移至QWidget。

When using my Linux-Notebook, everything works fine, and the GUI never stops updating. 使用我的Linux笔记本时,一切正常,GUI永不停止更新。 In contrast, when using any Windows-Host, i have the problem that some pyqtgraphs stop to refresh. 相反,当使用任何Windows主机时,我有一些pyqtgraphs停止刷新的问题。 The behavior is strange, because i set all data at more or less the same time, with the same numpy array (just different columns) - some plots refresh longer (hours), some stop earlier (minutes). 行为很奇怪,因为我使用相同的numpy数组(只是不同的列)或多或少地同时设置了所有数据-有些图刷新的时间更长(小时),有些图刷新的时间更早(分钟)。 After searching more or less the hole internet ;-) I think that I found the problem: Its the passing of data from from a thread to the GUI. 在或多或少地搜索了互联网漏洞之后;-)我认为我发现了问题所在:将数据从线程传递到GUI。 Some dummy code to explain what's going on: 一些虚拟代码来解释发生了什么:

DataUpdaterToGUI(QThread):

#sets the QWidget from main loop
def setGUI(self, gui):
    self.gui = gui

def run()
    while True:
        with lock(): # RLock() Instance
           copyArray = self.dataArray[:] # copy the array from the shifter
           self.gui.plot1.copyArray(dataArray[:, 0], copyArray[:, 1])
           self.gui.plot2.copyArray(dataArray[:, 0], copyArray[:, 2])
           # self.gui.update()
           # QApplication.instance().processEvents() 

Neither calling self.gui.update() nor processEvents() has any influence on the outcome: The plots stop redrawing after a while (on windows). 调用self.gui.update()或processEvents()都不会对结果产生任何影响:绘图会在一段时间后(在Windows上)停止重绘。

Now i have a very simple example, and just want to make sure if I'm using the threading-stuff correctly. 现在,我有一个非常简单的示例,只想确保我是否正确使用了线程填充。 It works fine, but I have some questions: 它工作正常,但我有一些问题:

  • Does the signal-slot approach copy the passed data? 信号插槽方法是否复制传递的数据?
  • Why is it not necessary to call the update() method of the QWidget? 为什么没有必要调用QWidget的update()方法?
  • Do I have to use any kind of locks when using signals? 使用信号时是否必须使用任何类型的锁?

class Main(QWidget):
    def __init__(self):
        super().__init__()

        self.layout = QGridLayout(self)
        self.graph = pg.PlotWidget()
        self.graph.setYRange(0,1000)
        self.plot = self.graph.plot()
        self.layout.addWidget(self.graph,0,0)
        self.show()

    def make_connection(self, data_object):
        data_object.signal.connect(self.grab_data)

    @pyqtSlot(object)
    def grab_data(self, data):
        print(data)
        self.plot.setData(data)


class Worker(QThread):
    signal = pyqtSignal(object)

    def __init__(self):
        super().__init__()

    def run(self):
        self.data = [0, 1]
        i = 2
        while True:
            self.data[1] = i
            self.signal.emit(self.data)
            time.sleep(0.01)
            i += 1

if __name__ == "__main__": 
    app = QApplication(sys.argv)

    widget = Main()
    worker = Worker()
    widget.make_connection(worker)
    worker.start()

    sys.exit(app.exec_())

Does the signal-slot approach copy the passed data? 信号插槽方法是否复制传递的数据? The signals are thread-safe and when transferring data they make a copy so the thread that precedes the data and the thread that consumes it (GUI Thread) will not have conflicts 信号是线程安全的,并且在传输数据时会进行复制,因此位于数据之前的线程和使用该线程的线程(GUI线程)不会发生冲突

Why is it not necessary to call the update() method of the QWidget? 为什么没有必要调用QWidget的update()方法? Actually pyqtgraph calls the update method, plot is a PlotDataItem, so if we check the source code of setData() method, it calls the updateItems() method, in that method the setData() method of the curve or scatter attribute is called (according to the type of graphics), in the case of curve its setData() method calls updateData(), and the updateData() method calls update, and in the case of the scatter its setData() method calls addpoint(), and addPoints() calls invalidate(), and this invalidate() method calls update(). 实际上pyqtgraph调用了update方法,plot是一个PlotDataItem,因此,如果我们检查setData()方法的源代码,它将调用updateItems()方法,在该方法中, 曲线散点属性的setData()方法称为(根据图形的类型),在曲线的情况下,其setData()方法调用updateData(),而updateData()方法调用update,在散布的情况下,其setData()方法调用addpoint(),以及addPoints()调用invalidate(),而此invalidate()方法调用update()。

Do I have to use any kind of locks when using signals? 使用信号时是否必须使用任何类型的锁? No, as the signals are thread-safe so Qt already has the guards set to avoid the collision. 不可以,因为信号是线程安全的,所以Qt已经设置了防护措施来避免冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM