Во-первых, я новичок в Python. Я давний пользователь MatLab (инженер, а не компьютерный ученый), и я начинаю процесс работы с Python, NumPy, SciPy и т.д. В моем рабочем процессе. Поэтому, пожалуйста, извините мое явное незнание того, что является прекрасным языком программирования!
В качестве моей первой попытки я решил создать приложение для взаимодействия с датчиком, который я разрабатываю. Датчик имеет разрешение в микросекундах (данные 512 и 512 пикселей с низким энергопотреблением каждые 500 микросекунд), но ввод-вывод будет блокироваться. Поскольку я буду постоянно опробовать устройство, я знаю, что потоковая передача будет важна, чтобы поддерживать графический интерфейс (графический интерфейс в конечном итоге также будет интегрировать последовательную связь с другим устройством и иметь подпрограмму обработки изображений, которая работает с данными датчика). Я создал потоковый экземпляр MatPlotLib для построения данных "реального времени" с датчика. Хотя я построил модуль, который обменивается данными с датчиком самостоятельно и проверен, я знаю, как это сделать на Python, я начинаю здесь просто с "моделирования" данных, генерируя 512 случайных чисел от 8 до 12 для низкой энергии "пиксели" и 512 случайных чисел от 90 до 110 для "пикселей" с высокой энергией. Это то, что нарезается резьбой. Работая со многими примерами здесь, я также научился использовать blitting, чтобы получить достаточно быстрое обновление экрана с помощью MatPlotLib - НО, проблема в том, что если я не использую, чтобы потоковый процесс спал в течение 20 мс с помощью time.sleep(0.02), GUI не отвечает. Это можно проверить, потому что интерактивная обратная связь данных X, Y от MatPlotLib не работает, и кнопка "STOP" не может использоваться для прерывания процесса. Все, что превышает time.sleep(0.02), делает графический интерфейс более плавным, но за счет "скорости передачи данных". Все, что медленнее, чем time.sleep(0.02), делает GUI невосприимчивым. Я не уверен, что понимаю, почему. Я собирался уйти и попытаться использовать 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 для построения "реального времени" в графическом интерфейсе. Спасибо за помощь в этом!