保持QThread响应所需的time.sleep()? [英] time.sleep() required to keep QThread responsive?

查看:144
本文介绍了保持QThread响应所需的time.sleep()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我是Python的新手.我是MatLab的长期用户(工程师,而不是计算机科学家),并且我开始尝试将Python,NumPy,SciPy等工作到我的工作流程中的过程.因此,请原谅我对什么是出色的编程语言的明显无知!

First, I am new to Python. I am a long-time MatLab user (engineer, not computer scientist) and I am beginning the process of attempting to work Python, NumPy, SciPy, etc. into my workflow. So, please excuse my obvious ignorance of what is a wonderful programming language!

作为我的第一项努力,我决定构建一个与正在开发的传感器进行交互的应用程序.传感器具有微秒的分辨率(每500微秒来自512个高能量和512个低能量像素"的数据),但是I/O会阻塞.由于我将不断轮询设备,因此我知道线程化对于保持GUI响应至关重要(GUI最终还将最终与其他设备集成串行通信,并具有对传感器数据进行操作的图像处理子例程).我创建了MatPlotLib的线程实例,以绘制来自传感器的这些实时"数据.尽管我已经构建了独立与传感器通信的模块并验证了我知道如何在Python中执行此操作,但我还是从数据的模拟"开始,通过为低能量生成512个随机数(介于8和12之间)开始像素",高能量像素"的512个随机数在90到110之间.那就是线程.通过这里的许多示例,我还学会了使用blitting通过MatPlotLib-BUT来获得足够快的屏幕更新,问题是除非我使用time.sleep(0.02)将线程化进程睡眠20毫秒,否则GUI将无响应.这可以得到验证,因为来自MatPlotLib的交互式X,Y数据点反馈不起作用,并且无法使用停止"按钮来中断该过程.任何长于time.sleep(0.02)的值都会使GUI更加流畅,但要以数据速率"为代价.任何比time.sleep(0.02)慢的速度都会使GUI无响应.我不确定我为什么会这样.我本来打算尝试使用GUIqwt代替,但是我想在放弃MatPlotLib之前会在这里询问,因为我不确定这甚至是问题所在.我担心将线程休眠20ms意味着我错过了传感器阵列中至少40条潜在的数据行(40行* 500us/行= 20ms).

As my first endeavor, I decided to build an application to interact with a sensor that I am developing. The sensor has microsecond resolution (data from 512 high and 512 low energy "pixels" every 500 microseconds), but the I/O will be blocking. Since I will continually poll the device, I know threading will be important to keep the GUI responsive (the GUI will ultimately also integrate serial communication with another device, and have an image processing subroutine that operates on the sensor data). I created a threaded instance of MatPlotLib to plot these "real-time" data from the sensor. Though I've built the module that communicates with the sensor independently and verified I know how to do that in Python, I am starting here simply with a "simulation" of the data by generating 512 random numbers between 8 and 12 for the low energy "pixels", and 512 random numbers between 90 and 110 for the high energy "pixels". That is what is threaded. Working from many examples here, I also learned to use blitting to get a fast enough screen update with MatPlotLib -- BUT, the problem is that unless I use put the threaded process to sleep for 20ms using time.sleep(0.02), the GUI is unresponsive. This can be verified because the interactive X,Y data point feedback from MatPlotLib doesn't work and the 'STOP' button cannot be used to interrupt the process. Anything longer than time.sleep(0.02) makes the GUI operated even smoother, but at the expense of "data rate". Anything slower than time.sleep(0.02) makes the GUI unresponsive. I'm not sure I understand why. I was going to go off and try to use GUIqwt instead, but thought I would ask here before abandoning MatPlotLib since I'm not sure that is even the problem. I am concerned that putting the thread to sleep for 20ms will mean that I miss at least 40 potential lines of data from the sensor array (40 lines * 500us/line = 20ms).

这是当前代码:

import time, random, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class ApplicationWindow(QMainWindow):

    def __init__(self, parent = None):

        QMainWindow.__init__(self, parent)

        self.thread = Worker()

        self.create_main_frame()
        self.create_status_bar()

        self.connect(self.thread, SIGNAL("finished()"), self.update_UI)
        self.connect(self.thread, SIGNAL("terminated()"), self.update_UI)       
        self.connect(self.startButton, SIGNAL("clicked()"), self.start_acquisition)       
        self.connect(self.stopButton, SIGNAL("clicked()"), self.stop_acquisition)
        self.thread.pixel_list.connect(self.update_figure)

    def create_main_frame(self):
        self.main_frame = QWidget()

        self.dpi = 100
        self.width = 10
        self.height = 8
        self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
        self.axes = self.fig.add_subplot(111)               
        self.axes.axis((0,512,0,120))

        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)
        self.canvas.updateGeometry()    
        self.canvas.draw()
        self.background = None

        self.lE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)
        self.hE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)          

        self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        self.startButton = QPushButton(self.tr("&Start"))
        self.stopButton = QPushButton(self.tr("&Stop"))

        layout = QGridLayout()
        layout.addWidget(self.canvas, 0, 0)
        layout.addWidget(self.mpl_toolbar, 1, 0)
        layout.addWidget(self.startButton, 2, 0)       
        layout.addWidget(self.stopButton, 2, 1)

        self.main_frame.setLayout(layout)
        self.setCentralWidget(self.main_frame)

        self.setWindowTitle(self.tr("XRTdev Interface"))

    def create_status_bar(self):
        self.status_text = QLabel("I am a status bar.  I need a status to show!")
        self.statusBar().addWidget(self.status_text, 1)

    def start_acquisition(self):
        self.thread.exiting = False
        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.thread.render()

    def stop_acquisition(self):
        self.thread.exiting = True
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()

    def update_figure(self, lE, hE):
        if self.background == None:
            self.background = self.canvas.copy_from_bbox(self.axes.bbox)
        self.canvas.restore_region(self.background)
        self.lE_line.set_ydata(lE)
        self.hE_line.set_ydata(hE)
        self.axes.draw_artist(self.lE_line)
        self.axes.draw_artist(self.hE_line)
        self.canvas.blit(self.axes.bbox)

    def update_UI(self):
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()        

    def cleanup_UI(self):
        self.background = None
        self.axes.clear()        
        self.canvas.draw()

class Worker(QThread):

    pixel_list = pyqtSignal(list, list)

    def __init__(self, parent = None):
        QThread.__init__(self, parent)
        self.exiting = False

    def __del__(self):
        self.exiting = True
        self.wait()

    def render(self):
        self.start()

    def run(self):
        # simulate I/O
        n = random.randrange(100,200)
        while not self.exiting and n > 0:
            lE = [random.randrange(5,16) for i in xrange(512)]
            hE = [random.randrange(80,121) for i in xrange(512)]
            self.pixel_list.emit(lE, hE)
            time.sleep(0.02)
            n -= 1

def main():
    app = QApplication(sys.argv)
    form = ApplicationWindow()
    form.show()
    app.exec_()

if __name__ == "__main__":
    main()

也许我的问题甚至不是MatPlotLib或PyQT4,而是我实现线程的方式.正如我指出的那样,我对此并不陌生.而且,我什至不确定GUIqwt是否会解决这些问题中的任何一个-但我确实知道我在这里看到了许多建议,它们可以比MatPlotLib更快地使用某些东西来在GUI中进行实时"绘图.感谢您的帮助!

Perhaps my problem isn't even with MatPlotLib or PyQT4, but the way I implemented threading. As I noted, I am new to this and learning. And, I'm not even sure GUIqwt will address any of these issues -- but I do know I've seen many recommendations here to use something faster than MatPlotLib for "real time" plotting in a GUI. Thanks for the help on this!

推荐答案

[已编辑,因为QThread令人困惑/困惑]

[edited because QThread is confusing/confused]

似乎有两种使用方式,要么将其子类化(如您的示例和文档所述),要么创建一个worker对象,然后将其移动到线程中(请参见

There seems to be two ways to use it, either sub-classing it (as your example and the documentation says) or creating a worker object and then moving it to a thread (See this blog post). I then get more confused when you mix signal/slots in. As Avaris says, this change may not be your problem.

我将您的Worker类重新加工为QObject的子类(因为这是我理解的样式).

I re-worked your Worker class as a a sub-class of QObject (because this is the style I understand).

一个问题是,如果您不将sleep放入假数据系统中,则会在<中生成所有对主窗口的回叫. 1秒然后,主线程在清除信号队列时实际上被阻塞了.如果将延迟设置为指定的延迟(0.0005s),则它会以远远超过其显示速度的速度生成数据,这似乎表明这可能不适合您的问题(务实,您也可以没那么快,而且似乎可以以30-40 fps播放.

A problem is that if you do not put a sleep in your fake data system, then you generate all the call backs to the main window in < 1s. The main thread is then essentially blocked as it clears out the signal queue. If you set the delay to your specified delay, (0.0005s), then it cranks through generating the data far faster than it can be displayed, which seems to suggest that this may not be suitable for your problem (being pragmatic, you also can't see that fast, and it seems to do ok at 30 - 40 fps).

import time, random, sys
#from PySide.QtCore import *
#from PySide.QtGui import *

from PyQt4 import QtCore
from PyQt4 import QtGui

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class ApplicationWindow(QtGui.QMainWindow):
    get_data = QtCore.pyqtSignal()

    def __init__(self, parent = None):

        QtGui.QMainWindow.__init__(self, parent)


        self.thread = QtCore.QThread(parent=self)
        self.worker = Worker(parent=None)
        self.worker.moveToThread(self.thread)

        self.create_main_frame()
        self.create_status_bar()

        self.startButton.clicked.connect(self.start_acquisition) 
        self.stopButton.clicked.connect(self.stop_acquisition)
        self.worker.pixel_list.connect(self.update_figure)
        self.worker.done.connect(self.update_UI)

        self.get_data.connect(self.worker.get_data)


        self.thread.start()


    def create_main_frame(self):
        self.main_frame = QtGui.QWidget()

        self.dpi = 100
        self.width = 10
        self.height = 8
        self.fig = Figure(figsize=(self.width, self.height), dpi=self.dpi)
        self.axes = self.fig.add_subplot(111)               
        self.axes.axis((0,512,0,120))

        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)
        self.canvas.updateGeometry()    
        self.canvas.draw()
        self.background = None

        self.lE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)
        self.hE_line, = self.axes.plot(range(512), [0 for i in xrange(512)], animated=True)          

        self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        self.startButton = QtGui.QPushButton(self.tr("&Start"))
        self.stopButton = QtGui.QPushButton(self.tr("&Stop"))

        layout = QtGui.QGridLayout()
        layout.addWidget(self.canvas, 0, 0)
        layout.addWidget(self.mpl_toolbar, 1, 0)
        layout.addWidget(self.startButton, 2, 0)       
        layout.addWidget(self.stopButton, 2, 1)

        self.main_frame.setLayout(layout)
        self.setCentralWidget(self.main_frame)

        self.setWindowTitle(self.tr("XRTdev Interface"))

    def create_status_bar(self):
        self.status_text = QtGui.QLabel("I am a status bar.  I need a status to show!")
        self.statusBar().addWidget(self.status_text, 1)

    def start_acquisition(self):
        self.worker.exiting = False
        self.startButton.setEnabled(False)
        self.stopButton.setEnabled(True)
        self.get_data.emit()

    def stop_acquisition(self):
        self.worker.exiting = True
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()

    def update_figure(self, lE, hE):
        if self.background == None:
            self.background = self.canvas.copy_from_bbox(self.axes.bbox)
        self.canvas.restore_region(self.background)
        self.lE_line.set_ydata(lE)
        self.hE_line.set_ydata(hE)
        self.axes.draw_artist(self.lE_line)
        self.axes.draw_artist(self.hE_line)
        self.canvas.blit(self.axes.bbox)

    def update_UI(self):
        self.startButton.setEnabled(True)
        self.stopButton.setEnabled(False)
        self.cleanup_UI()        

    def cleanup_UI(self):
        self.background = None
        self.axes.clear()        
        self.canvas.draw()

class Worker(QtCore.QObject):

    pixel_list = QtCore.pyqtSignal(list, list)
    done = QtCore.pyqtSignal()

    def __init__(self, parent = None):
        QtCore.QObject.__init__(self, parent)
        self.exiting = True

    @QtCore.pyqtSlot()
    def get_data(self):
        # simulate I/O
        print 'data_start'
        n = random.randrange(100,200)
        while not self.exiting and n > 0:
            lE = [random.randrange(5,16) for i in xrange(512)]
            hE = [random.randrange(80,121) for i in xrange(512)]
            self.pixel_list.emit(lE, hE)
            time.sleep(0.05)
            n -= 1
        print 'n: ', n
        self.done.emit()

这篇关于保持QThread响应所需的time.sleep()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆