time.sleep()需要保持QThread的响应?(time.sleep() required

2019-07-19 04:11发布

首先,我是新来的Python。 我是一个长期MatLab的用户(工程师,而不是计算机科学家)和我开始尝试到Python,NumPy的,SciPy的,等工作到我的工作流的过程。 所以,请原谅我明显的是一个美好的编程语言的无知!

作为我的第一个努力,我决定建立一个应用程序来与我正在开发的传感器进行交互。 该传感器具有微秒分辨率(从512高和512低能量“像素”每500微秒的数据),但I / O将被阻塞。 因为我会不断地轮询设备,我知道线程将是重要的是要响应的GUI(图形用户界面将最终还集成了串行通信与其他设备,并具有对传感器数据进行操作的图像处理子程序)。 我创建MatPlotLib的螺纹实例从传感器绘制这些“实时”的数据。 虽然我已经建立了与独立的传感器通信模块和验证,我知道怎么做,在Python,我只是从这里开始了数据的“模拟”的产生8和12之间随机512个数字为低能量“像素”,和90和110之间512支的随机数的高能量“像素”。 这就是拧。 从很多例子在这里工作,我也学会了用阻击器来获得一个足够快的屏幕更新MatPlotLib -但是,问题是,除非我把使用线程化的过程为20ms的使用睡眠time.sleep(0.02)图形用户界面是反应迟钝。 这可以验证,因为交互式X,从MatPlotLib Y数据点反馈不工作和“STOP”按钮无法使用中断的过程。 任何长于time.sleep(0.02)使GUI操作甚至更平滑,但在“数据率”为代价。 任何比慢time.sleep(0.02)使GUI不响应。 我不知道我明白为什么。 我正想走下车,并尝试使用GUIqwt代替,但想到我会问这里放弃MatPlotLib之前,因为我不知道那是连问题。 我担心把线程20ms的睡眠将意味着我错过至少40条从传感器阵列数据的电势线(40行* 500US /行= 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绘制。 感谢这个帮助!

Answer 1:

[编辑,因为QThread是混乱/困惑]

似乎是使用它的方式有两种子类它(如你的例子和文档说),或创建一个工人对象,然后将其移动到一个线程(见本博客文章 )。 当您在混合信号/插槽然后我得到更多的困惑。作为阿瓦里斯说,这种变化可能不是你的问题。

我重新工作的Worker类为AA子类QObject (因为这是我理解的风格)。

一个问题是,如果你不把一个sleep在你的假数据系统,那么你生成所有的回调在<1秒的主窗口。 主线程然后基本上阻塞,因为它清除出的信号队列。 如果设置延迟到您指定的延迟,(0.0005s),然后通过曲柄远快于它可以显示产生的数据,这似乎表明,这可能不适合您的问题(务实,你也可以“看不到那么快,它似乎在做30个OK - 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()


文章来源: time.sleep() required to keep QThread responsive?