为啥从独立函数生成的 pyplot 窗口与从事件处理程序调用的函数生成时的行为不同?

Posted

技术标签:

【中文标题】为啥从独立函数生成的 pyplot 窗口与从事件处理程序调用的函数生成时的行为不同?【英文标题】:Why does a pyplot window behave differently when it is generated from a stand-alone function vs. a function called from an event handler?为什么从独立函数生成的 pyplot 窗口与从事件处理程序调用的函数生成时的行为不同? 【发布时间】:2015-08-13 18:40:54 【问题描述】:

当我自己运行 setup_plotdataq 函数时,pyplot 窗口的行为与预期一样,即在运行完成后,导航工具栏中的按钮(平移/缩放等)处于活动状态。但是,当我导入函数并从主程序中的按钮事件处理程序调用它们时,pyplot 窗口在运行完成后保持活动状态:它仍在接受数据并且工具栏按钮处于非活动状态。我应该如何在不关闭绘图窗口的情况下停止绘图?这是一个sn-p:

class MyWindowClass(QtGui.QWidget, form_class):
    def __init__(self, parent=None):
       QtGui.QWidget.__init__(self, parent)
       self.setupUi(self)
       self.pushButton_Run.clicked.connect(self.pushbutton_run_clicked)
       self.pushButton_Stop.clicked.connect(self.pushbutton_stop_clicked)

    def pushbutton_run_clicked(self):
        # get line edit values here 
        functions.setup_plot()
        functions.dataq(steptime, runtime, CO2_1, CO2_2)

【问题讨论】:

您认为您可以为您的问题提供一个最小、完整且可验证的示例(请参阅***.com/help/mcve)?我们目前掌握的信息量有限,很难确定问题所在。 【参考方案1】:

我试图通过根据您提供给我们的代码 sn-p 创建一个简单的 MCVE 来重现您的问题。我能找到的唯一能让我的简单应用程序像你描述的那样运行的事情是,如果我没有将 matplotlib 的 backend 设置为 Qt4Agg

from PyQt4 import QtGui
import sys
import numpy as np

import matplotlib as mpl
mpl.use('Qt4Agg')
import matplotlib.pyplot as plt

class MyWindowClass(QtGui.QWidget):
    def __init__(self, parent=None):
       super(MyWindowClass, self).__init__(parent)

       pushButton_Run = QtGui.QPushButton('Run')
       pushButton_Run.clicked.connect(self.pushbutton_run_clicked)

       layout = QtGui.QGridLayout()
       layout.addWidget(pushButton_Run, 0, 0)
       self.setLayout(layout)

    def pushbutton_run_clicked(self):

        fig, ax = setup_plot()                        
        dataq(fig, ax)

def setup_plot():

    fig, ax = plt.subplots()
    fig.canvas.manager.show()

    return fig, ax

def dataq(fig, ax):
    for i in range(200):
        ax.plot(np.random.rand(1), np.random.rand(1), 'o')
        fig.canvas.draw()
        fig.canvas.flush_events()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)    
    mywindow = MyWindowClass()
    mywindow.show()      
    sys.exit(app.exec_())

更新 - 第二个示例:

这是设置艺术家的另一种方式,可以让您在多个功能中更轻松地使用它们,并使他们的操作更方便。

from PyQt4 import QtGui
import sys
import numpy as np

import matplotlib as mpl
mpl.use('Qt4Agg')
import matplotlib.pyplot as plt

class MyWindowClass(QtGui.QWidget):
    def __init__(self, parent=None):
       super(MyWindowClass, self).__init__(parent)

       #---- generate some data ----

       self.x = np.random.rand(50)
       self.y = np.random.rand(50)

       #---- create a layout ----

       pushButton_Run = QtGui.QPushButton('Run')
       pushButton_Run.clicked.connect(self.pushbutton_run_clicked)

       pushButton_Clear = QtGui.QPushButton('Clear')
       pushButton_Clear.clicked.connect(self.clear_plot)

       layout = QtGui.QGridLayout()
       layout.addWidget(pushButton_Run, 0, 0)
       layout.addWidget(pushButton_Clear, 1, 0)
       self.setLayout(layout)

       #---- init artists ----

       self.fig, self.ax = plt.subplots()
       self.fig.canvas.manager.show()

    def clear_plot(self):
        self.ax.cla()
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def pushbutton_run_clicked(self):
        self.setEnabled(False)
        self.ax.cla()   
        dataq(self.fig, self.ax, self.x, self.y)
        dataq2(self.fig, self.ax, self.x, self.y)
        self.setEnabled(True)

def dataq(fig, ax, x, y):            
    for i in range(len(x)):
        ax.plot(x[i], y[i], 'o')
        fig.canvas.draw()
        fig.canvas.flush_events()

def dataq2(fig, ax, x, y):
    for i in range(len(x)-1):
        ax.plot(x[i:i+2], y[i:i+2], '-')
        fig.canvas.draw()
        fig.canvas.flush_events()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)    
    mywindow = MyWindowClass()
    mywindow.show()      
    sys.exit(app.exec_())

【讨论】:

更改后端设置对我没有影响:您的示例双向工作。但是,您的代码结构揭示了我的错误。我在setup_plot 之外定义了figax,因为它们被多个函数使用。将它们移入内部解决了问题。谢谢! @Drew 很高兴它有帮助。您的意思是“在 setup_plot 之外定义 fig 和 ax”是指它们被定义为全局变量吗? 是的,它们是在functions 中全局定义的。我正在努力清理这个。你可能会说我是个初学者。 @Drew 好的,我明白了。您仍然可以在setup_plot 之外定义figax,如果您愿意并且它允许您在多个函数中更轻松地使用它们。我已经用第二个示例更新了我的答案,该示例显示了如何通过将您的艺术家定义为类变量来完成此操作。 按照您的建议使用类变量对我有用。再次感谢!

以上是关于为啥从独立函数生成的 pyplot 窗口与从事件处理程序调用的函数生成时的行为不同?的主要内容,如果未能解决你的问题,请参考以下文章

从函数返回“本地”字符*与从函数返回“本地”int*之间的区别[重复]

1.22.FLINK WatermarkFlink窗口(Window)watermark有什么用?如何使用Watermarks处理乱序的数据流?机制及实例详解生成方式代码实例

JVM函数调用与从本机方法返回的性能?

关闭 pyplot 窗口

为啥我的 pyplot.plot 数字没有正确保存?

如何用matplotlib画多个独立窗口的图