RuntimeError:主线程不在主循环中 - 绘制两个数字

时间:2017-02-01 19:39:28

标签: python plot pyqt

我正在尝试从DHT11温度传感器获取值,然后通过具有三个按钮的GUI主窗口小部件绘制它,第一个是从传感器开始获取值,第二个是绘制实时值,第三个绘制值的FFT。

问题在于我无法用两位数来分析实时图和FFT图,因为每次我将plt.figure(1)更改为def plotFFTButton_clicked(self):plt.figure(2)我都会得到按FFT绘图按钮时出现以下错误: RuntimeError:主线程不在主循环中

另一个问题是我无法删除plt.figure(1)下的def runValue(self):,因为它会给我同样的错误。

如果有人可以帮我找到我的代码问题,我将感激不尽。

from PyQt4 import QtCore, QtGui
import sys
import time
from threading import Event, Thread

from mainwindow import Ui_MainWindow

from PyQt4.Qt import QString, QFileDialog
from pylab import *

import threading
from threading import Thread

#DH11 tempSensor Library
import Adafruit_DHT

#Library to find FFT and FFTshift 
from scipy.fftpack import fft, fftshift

from numpy import linspace

class dataAcquisition(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):        
        super(dataAcquisition, self).__init__(parent)
        self.setupUi(self)

        self.startButton.clicked.connect(self.startButton_clicked)
        self.th = Thread(target = self.runValue)

        self.plotRTButton.clicked.connect(self.plotRTButton_clicked)

        self.plotFFTButton.clicked.connect(self.plotFFTButton_clicked)

        self.Value = 0.0
        self.X = 0.0
        self.Y = 0.0

        self.ChageValueState = False
    def runValue(self):
        self.X = []
        self.Y = []
        i = 0.0

        plt.figure(1) #an error comes out if I delete this

        while True:
            self.ChageValueState = True

            self.Value = Adafruit_DHT.read_retry(11, 4)[1]  ‫#‬Or you could give the self.Value any number

            i += 0.10
            self.X.append(i)
            self.Y.append(self.Value)
            pause(0.01)

    def startButton_clicked(self):
        self.th.start()

    def plotRTButton_clicked(self):
        plt.figure(1)
        ax1 = subplot(3, 1, 1)
        Line1 = plot(0,0,'r-')[0]

        Line1.set_xdata(self.X)
        Line1.set_ydata(self.Y)
        ax1.relim()
        ax1.autoscale_view()

    def plotFFTButton_clicked(self):
        Fs = 8000
        N = 256
        f = linspace(-Fs/2, Fs/2, N)

        plt.figure(1) #an error comes out if I plot in a new figure

        ax2 = plt.subplot(3, 1, 2)
        Line2 = plot(f,0*f,'r-')[0]

        FFT = log10(abs(fft(self.Y, N)))
        FFT = fftshift(FFT)

        Line2.set_ydata(FFT)
        ax2.relim()
        ax2.autoscale_view()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    MainWindow = dataAcquisition()
    MainWindow.show()
    sys.exit(app.exec_())

2 个答案:

答案 0 :(得分:0)

错误是一种防止线程干扰的安全预防措施。每个数字都是matplotlib中的一个或者有变量,这样如果多个线程同时处理单独的数字,一个线程就会破坏另一个线程的变量。 python threads类检测到并行的多个对象正在尝试访问同一个类并做出反应。它通常会完成第一个线程并停止其他线程。

在您的代码中,您永远不会关闭图1,因此无法在单独的线程中创建图2而不会产生干扰。所以你可以做一个线程来获取数据,另一个线程来做这个数据的FFT,第三个可以绘制。绘图线程必须以这样一种方式工作,即在收集数据时它会被触发,因此它会将原始数据添加到图(1),然后当FFT结束时再次触发时,数据将被添加图(2)。(循环检查新数据的原始数据,然后检查FFT是否为新的。只有当真的做某事时)(推荐使用队列)因为它们不再是两个单独的线程,你不再需要在运行之前关闭一个另一个。

如果您真的想要将matplotlib并行复制到具有新名称的文件中,并使用matplotlib绘制一个图形,另一个使用matplotlib。

答案 1 :(得分:-1)

我担心这不是完全琐碎的代码,因为你说你是初学者。但话说回来并不是一个完全无足轻重的问题。这对我来说有什么作用。致电'添加'从例如一个GUI线程,用于将事件(要执行的函数)放入队列,然后调用'执行'从您的图形线程在该线程上执行它们。如果您有多个图形线程,那么您将拥有多个队列。

请注意,除了TaskQueue之外,还有一个循环调度程序TaskRing。你现在不需要,但这只是一段生产代码,所以我已经使它普遍适用于多线程调度问题。

class TaskBuffer:
    def __init__ (self):
        self.tasks = []

    def add (self, task):
        self.tasks.append (task)

    def execute (self, time):
        for task in self.tasks:
            try:
                task (time)
            except:
                lg.debug ('{} {}'.format (self.skipMessage, task))

class TaskRing (TaskBuffer):
    skipMessage = 'Ring task skipped:'

taskRing = TaskRing ()

class TaskQueue (TaskBuffer):
    skipMessage = 'Queue task skipped:'

    def execute (self, time):
        super () .execute (time)
        self.tasks = []

taskQueue = TaskQueue ()