время.sleep() требуется, чтобы QThread оставался отзывчивым?



Во-первых, я новичок в Python. Я-давний пользователь MatLab (инженер, а не компьютерщик), и я начинаю процесс попытки работать с Python, NumPy, SciPy и т. д. в мой рабочий процесс. Так что, пожалуйста, простите мое явное незнание того, что такое замечательный язык программирования!



В качестве моей первой попытки я решил создать приложение для взаимодействия с датчиком, который я разрабатываю. Датчик имеет микросекундное разрешение (данные от 512 высокоэнергетических и 512 низкоэнергетических "пикселей" каждые 500 микросекунды), но ввод-вывод будет блокироваться. Поскольку я буду постоянно опрашивать устройство, я знаю, что потоковая обработка будет важна для обеспечения отзывчивости графического интерфейса (графический интерфейс в конечном счете также интегрирует последовательную связь с другим устройством и имеет подпрограмму обработки изображений, которая работает с данными датчика). Я создал потоковый экземпляр MatPlotLib, чтобы построить эти данные" в реальном времени " от датчика. Хотя я построил модуль, который связывается с датчиком независимо и проверил, что я знаю как это сделать в Python, я начинаю здесь просто с "моделирования" данных, генерируя 512 случайных чисел между 8 и 12 для низкоэнергетических " пикселей "и 512 случайных чисел между 90 и 110 для высокоэнергетических"пикселей". Вот что такое резьба. Работая со многими примерами здесь, я также научился использовать blitting, чтобы получить достаточно быстрое обновление экрана с MatPlotLib - но проблема в том, что если я не использую put the threaded process to sleep for 20ms using time.sleep(0.02), GUI неотзывчивый. Это можно проверить, потому что интерактивная обратная связь точек данных X, Y от MatPlotLib не работает, и кнопка "стоп" не может быть использована для прерывания процесса. Все, что длиннее time.sleep(0.02), делает графический интерфейс еще более плавным, но за счет "скорости передачи данных". Все, что медленнее, чем time.sleep(0.02), делает графический интерфейс невосприимчивым. Я не уверен, что понимаю почему. Я собирался пойти и попробовать использовать GUIqwt вместо этого, но подумал, что спрошу здесь, прежде чем отказаться от MatPlotLib, так как я не уверен в этом-то и проблема. Я обеспокоен тем, что перевод потока в спящий режим на 20 мс будет означать, что я пропустил по крайней мере 40 потенциальных строк данных из массива датчиков (40 строк * 500us/line = 20 мс).

Вот текущий код:



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 для построения графиков "в реальном времени" в графическом интерфейсе. Спасибо за помощь в этом!

546   1  

1 ответ:

[отредактировано, потому что QThread сбивает с толку / путает]

Существует два способа его использования: либо подклассировать его (как говорится в вашем примере и документации), либо создать рабочий объект и затем переместить его в поток (см. этот пост в блоге). Затем я еще больше запутываюсь, когда вы смешиваете сигнал / слоты. Как говорит Аварис, это изменение не может быть вашей проблемой.

Я переработал ваш Worker класс как подкласс QObject (потому что это стиль, который я понимаю).

Проблема это означает, что если вы не помещаете sleep в вашу поддельную систему данных, то вы генерируете все обратные вызовы к главному окну в

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()

Comments

    Ничего не найдено.