【发布时间】:2013-01-17 21:45:51
【问题描述】:
首先,我是 Python 新手。我是一名长期使用 MatLab 的用户(工程师,而不是计算机科学家),我正在尝试将 Python、NumPy、SciPy 等应用到我的工作流程中。所以,请原谅我对什么是美妙的编程语言的明显无知!
作为我的第一个尝试,我决定构建一个应用程序来与我正在开发的传感器进行交互。传感器具有微秒分辨率(每 500 微秒来自 512 个高能“像素”和 512 个低能“像素”的数据),但 I/O 会阻塞。由于我会不断地轮询设备,我知道线程对于保持 GUI 响应很重要(GUI 最终还将与另一个设备集成串行通信,并具有对传感器数据进行操作的图像处理子程序)。我创建了一个 MatPlotLib 的线程实例来绘制来自传感器的这些“实时”数据。尽管我已经构建了与传感器独立通信的模块并验证了我知道如何在 Python 中做到这一点,但我只是从数据的“模拟”开始,通过为低能量生成 8 到 12 之间的 512 个随机数“像素”,以及 90 到 110 之间的 512 个随机数,用于高能“像素”。这就是线程。通过这里的许多示例,我还学会了使用 blitting 通过 MatPlotLib 获得足够快的屏幕更新——但是,问题是除非我使用 time.sleep(0.02) 将线程进程休眠 20 毫秒,否则 GUI 没有响应。这可以验证,因为来自 MatPlotLib 的交互式 X、Y 数据点反馈不起作用,并且“停止”按钮不能用于中断该过程。任何比time.sleep(0.02) 更长的东西都会使GUI 操作更加流畅,但会以“数据速率”为代价。任何比time.sleep(0.02) 慢的东西都会使GUI 无响应。我不确定我明白为什么。我打算离开并尝试使用 GUIqwt,但我想在放弃 MatPlotLib 之前在这里问一下,因为我不确定这甚至是问题所在。我担心让线程休眠 20 毫秒意味着我会错过传感器阵列中至少 40 条潜在的数据行(40 行 * 500us/行 = 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 更快的东西在 GUI 中进行“实时”绘图。感谢您对此的帮助!
【问题讨论】:
-
您的代码运行良好,线程使用也正常。没有
sleep的无响应来自matplotlib 部分,即update_figure。更新/渲染绘图不是免费,它正在主线程上工作。如果你这样做太快,事件循环就没有太多机会处理事件。 -
@Avaris 感谢您的澄清。尽管线程的使用多种多样,但您和 tcaswell 都同意数据生成对于 UI 来说太快了,尤其是使用 MatPlotLib。我将努力对数据进行下采样,并可能实施一个更轻量级的绘图工具包,并将结果发回此处。
-
另一种可能性是将 matplotlib 渲染移动到线程并取回您可以在 GUI 中显示的
QImage。这对于主线程来说会更便宜。这类似于 matplotlib 使用Qt后端显示绘图的方式。 This 可能会给你一个想法。
标签: python matplotlib pyqt4 qthread blit