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