【问题标题】:Dynamic Plotting with Matplotlib and PyQt - freezing window使用 Matplotlib 和 PyQt 进行动态绘图 - 冻结窗口
【发布时间】:2023-03-29 22:35:01
【问题描述】:

目标:

我想在 pyQt4 GUI 窗口中嵌入 Matplotlib 图。剧情要及时更新。

问题:

窗口冻结,直到绘图完成。我希望情节实时更新。

上下文:

我们有数值算法正在处理一些数据,我希望该图显示数据集如何受到算法的影响。该算法每 0.5 秒完成一次迭代 - 每次迭代都必须更新绘图。

测试代码:

算法替换为test(),它绘制一个随机点 100 次。下面的代码说明了问题:

import sys
from PlotGUI import *
import threading
from random import randint
import time

class GUIForm(QtGui.QDialog):

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self,parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        QtCore.QObject.connect(self.ui.pushButton, QtCore.SIGNAL('clicked()'), self.startSim)
        self.cPlot = None # custom plotter
        self.instantiatePlot()        

    def instantiatePlot(self):
        self.cPlot = CustomPlotter(self.ui.widget.canvas) 
        self.cPlot.prepareFigure()

    def startSim(self):
        self.cPlot.clear();        
        draw_thread = threading.Thread(target=self.cPlot.test())
        draw_thread.start()

class CustomPlotter():
    def __init__(self, canvas):
        print 'constructor'
        self.canvas = canvas        

    def prepareFigure(self):
        ax = self.canvas.ax

        ax.set_ylim([-1,101])
        #ax.set_xlim([dt[0],dt[1]])
        ax.set_ylim([-1, 10])
        self.canvas.draw()

    def clear(self):
        self.canvas.ax.clear()

    def test(self):
        canvas = self.canvas 
        ax = canvas.ax
        for x in range(0,100):
            y = randint(0,9)
            ax.plot(x, y, 'ro')
            print x
            canvas.draw()
            time.sleep(1)
            #canvas.show()
            #canvas.update()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = GUIForm()
    myapp.show()
    sys.exit(app.exec_())

提前致谢。这是一些原型设计,所以我愿意接受所有提供快速解决方案的选项/替代方案。

【问题讨论】:

  • 哪个库有 PlotGUI 模块?
  • QtGui、QtCore、Ui_Dialog()依赖于PlotGUI模块
  • 这里的答案有点脱节,但基本上你没有正确创建线程,因此它为什么会阻塞。但是,正确创建它可能会因为辅助线程中的 Qt 调用而导致崩溃(这是不允许的)。此外,当实际重绘绘图时,GUI 将始终阻塞,没有办法,但您可以将绘图数据的准备/获取卸载到另一个线程。您只需将此数据发送到主线程以更新 GUI(可能使用 QThread 和信号发射 - 有很多关于此的问题)

标签: python qt matplotlib pyqt pyqt4


【解决方案1】:

尝试实例化新线程时出现错误:

draw_thread = threading.Thread(target=self.cPlot.test())

这将立即在当前线程中执行测试方法,然后将结果(None)作为target 传递。您可能想要做的是:

draw_thread = threading.Thread(target=self.cPlot.test)

Thread(target=None) 只是创建一个什么都不做的线程,只是立即退出,因此是有效的,不会产生任何表明此问题的异常。

因为test() 方法是在GUI 线程中启动的,所以GUI 会被阻塞,直到方法返回。

【讨论】:

    【解决方案2】:

    PySide/Qt 无法从另一个线程更新小部件(QPixmap/显示项)。您不能直接在另一个线程中为 GUI 项目调用 plot。

    参见 matplotlib.animation.FuncAnimation。 http://matplotlib.org/api/animation_api.html

    import numpy as np
    
    # ========== Matplotlib [PySide] ==========
    import matplotlib
    matplotlib.use("Qt4Agg")
    matplotlib.rcParams["backend.qt4"] = "PySide"
    
    import matplotlib.animation as mplanimation
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
    
    class InteractivePlotWidget(FigureCanvas):
    
        def __init__(self):
            super().__init__(Figure(tight_layout=True))
            self.axes = self.figure.add_subplot(111)
    
            self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
    
            self.name = ""
            self.data = np.zeros(shape=(100, 2))
        # end __init__
    
        def plot_data(self, interval=0):
            data = np.array([(i, np.sin(i)) for i in range(interval)])
            try:
                self.axes.lines[0].set_data(data[:,0], data[:,1])
            except IndexError:
                self.axes.plot(data, label=self.name) # Lots of overhead. Do once.
    
            self.axes.relim()
            self.axes.autoscale_view(True, True, True)
            return self.axes.lines  # animation handles draw
            # manually trigger draw
            # self.draw_idle()
            # self.flush_events()
        # end plot_data
    # end class InteractivePlotWidget
    
    if __name__ == "__main__":
        QtGui.QApplication([])
    
        w = InteractivePlotWidget()
        w.show()
    
        # Create and start the animation (timer)
        anim = mplanimation.FuncAnimation(w.figure, w.plot_data, interval=0)
        anim._start()
    
        sys.exit(QtGui.qApp.exec_())
    

    【讨论】:

      【解决方案3】:

      matplotlib 库中的 embedding_in_qt4.py 示例应该足够了,对吧?

      # ...
      class MyMplCanvas(FigureCanvas):
          """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
      
          def __init__(self, parent=None, width=5, height=4, dpi=100):
              fig = Figure(figsize=(width, height), dpi=dpi)
              self.axes = fig.add_subplot(111)
              # We want the axes cleared every time plot() is called
              self.axes.hold(False)
      
              self.compute_initial_figure()
      
              #
              FigureCanvas.__init__(self, fig)
              self.setParent(parent)
      
              FigureCanvas.setSizePolicy(self,
                                         QtGui.QSizePolicy.Expanding,
                                         QtGui.QSizePolicy.Expanding)
              FigureCanvas.updateGeometry(self)
      
          def compute_initial_figure(self):
              pass
      # ...
      class MyDynamicMplCanvas(MyMplCanvas):
          """A canvas that updates itself every second with a new plot."""
      
          def __init__(self, *args, **kwargs):
              MyMplCanvas.__init__(self, *args, **kwargs)
              timer = QtCore.QTimer(self)
              timer.timeout.connect(self.update_figure)
              timer.start(1000)
      
          def compute_initial_figure(self):
              self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'r')
      
          def update_figure(self):
              # Build a list of 4 random integers between 0 and 10 (both inclusive)
              l = [random.randint(0, 10) for i in range(4)]
      
              self.axes.plot([0, 1, 2, 3], l, 'r')
              self.draw()
      # ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-02
        • 1970-01-01
        • 2015-01-15
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        相关资源
        最近更新 更多