【问题标题】:Make interactive matplotlib window not pop to front on each update (Windows 7)使交互式 matplotlib 窗口不会在每次更新时弹出到前面(Windows 7)
【发布时间】:2017-08-17 07:32:05
【问题描述】:

今天我将 matplotlib 升级到 2.0.2 版,可能有 3 年没有升级了。

现在我遇到的问题是,在交互式绘图中,窗口总是在前面,好像这里 make matplotlib plotting window pop up as the active one 已经成为默认行为。

如何停用它?我不希望窗口每 5 秒出现一次,然后在我的文本编辑器、浏览器、...

我希望它保持在我放置它的 z 顺序中,无论是在活动窗口的前面还是后面。

我认为 2016 年 1 月 31 日的以下提交是造成这种问题行为的原因:tkagg: raise each new window; partially addresses #596

在Github上找到了相关评论https://github.com/matplotlib/matplotlib/issues/596#issuecomment-305298577

看来是我对plt.pause 的调用导致了此问题,而不是plt.plot 调用。

【问题讨论】:

  • 我认为如果您在窗口中发出命令执行某些操作,您希望窗口位于顶部是很自然的。虽然它不会窃取焦点(这确实很烦人)。如果您认为 matplotlib 窗口更像是一个应用程序,应该简单地留在原处,无论里面发生什么(可能就像一个视频播放器,即使最小化也会继续播放电影),您可能想要这样做:创建一个独立申请。如果您更新您的问题以显示实际问题(或者更确切地说是minimal reproducible example),可能会帮助您这样做。
  • @ImportanceOfBeingErnest 问题在于plt.pause()。它执行类似于time.sleep() 的操作,但不会阻塞用户界面,即您可以拖动窗口并调整其大小。使用plt.show() 完全可以理解将窗口完全带到前面,但是plt.pause()?具有进入操作系统友好的空闲状态功能的命令没有理由对窗口管理执行任何操作,例如在浏览器前移动窗口。我通过使用matplotlib.animation 解决了这个问题,但是这个“功能”严重破坏了plt.pause() 功能。
  • plt.pause 做了三个主要的事情。 (1) 它重绘画布。 (2) 它调用plt.show (3) 它启动事件队列。因此,如果show 使窗口位于前面是有效的,那么它肯定也适用于任何调用show 的命令。但是,我不确定这个问题实际上是关于什么的。是对某些功能的抱怨吗?您需要帮助吗?
  • @ImportanceOfBeingErnest 这是一个问题。我不知道这种行为是从哪里来的。我不只是等待我更深入地搜索的答案,而是认为这没问题。浮出水面的信息被添加到问题中。我认为这也可以。是的,plt.pause() 调用plt.show(),但是让它以这种方式行事是一种回归。我不再需要这个问题的帮助。您是要我删除它,还是留给可能遇到相同问题的其他人?
  • 您找到了适合您的解决方案这一事实以某种方式告诉我们,此问题可能存在解决方法。因此,它也可能对其他人有帮助,您可以用它来回答您自己的问题。我还添加了另一种解决方法,以防更改后端是一种选择。

标签: python windows matplotlib plot


【解决方案1】:

改变后端

这个问题似乎只存在于使用Tk 后端。使用Qt 后端,窗口将保持在原来的位置,同时使用plt.pause 进行更新。

要更改后端,请在脚本开头使用这些行。

import matplotlib
matplotlib.use("Qt4agg") # or "Qt5agg" depending on you version of Qt

修改plt.pause

如果更改后端不是一个选项,以下可能会有所帮助。窗口不断弹出到最前面的原因来自plt.pause内部调用plt.show()。因此,您实现了自己的 pause 函数,而无需调用 show。这需要首先处于交互模式plt.ion(),然后至少调用一次plt.show()。之后,您可以使用自定义 mypause 函数更新绘图,如下所示。

import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from time import time
from random import random

plt.ion()
# set up the figure
fig = plt.figure()
plt.xlabel('Time')
plt.ylabel('Value')

plt.show(block=False)

def mypause(interval):
    backend = plt.rcParams['backend']
    if backend in matplotlib.rcsetup.interactive_bk:
        figManager = matplotlib._pylab_helpers.Gcf.get_active()
        if figManager is not None:
            canvas = figManager.canvas
            if canvas.figure.stale:
                canvas.draw()
            canvas.start_event_loop(interval)
            return


t0 = time()
t = []
y = []
while True:
    t.append( time()-t0 )
    y.append( random() )
    plt.gca().clear()
    plt.plot( t , y )
    mypause(1)

使用animation

最后,使用matplotlib.animation 类将使上述所有内容都过时。 matplotlib.animation.FuncAnimation 的示例显示为 on the matplotlib page

【讨论】:

  • 我试过了,但Tk 是唯一一个没有我的机器无法满足的依赖项,所以我选择了“使用matplotlib.animation”-route
  • 好的,所以解决方案有点复杂。您可以使用自己的 plt.pause 版本,它不会像上面更新的答案那样调用 show
  • 这很好用。我将它(“修改plt.pause”)应用到另一个也使用plt.pause() 的脚本,这是一个快速修复。感谢您的努力!
  • 这里使用draw_idle会好一些,它会推迟实际渲染,直到GUI重新绘制。
  • @tacaswell canvas.draw()pause uses internally 的代码。所以我想如果你认为应该改用canvas.draw_idle(),为什么不在matplotlib代码中改变它呢?
【解决方案2】:

我在 Mac 上遇到了同样的问题。我不确定这是否是最好的方法,但我没有使用plt.pause(0.001),而是切换到fig.canvas.start_event_loop(0.001) 来更新我的动画的每一帧。这允许窗口留在后台。

【讨论】:

  • 这在 Gnome/Ubuntu 上对我有用,但我还必须添加对 @pepgma 提到的 fig.canvas.draw_idle() 的调用。
【解决方案3】:

现在是 2019 年 4 月,Matplotlib 3.0.3 的 mypause() 函数(复制自最新的 pyplot 实现)应该看起来更像

import time
import matplotlib.pyplot as plt
def mypause(interval):
    manager = plt._pylab_helpers.Gcf.get_active()
    if manager is not None:
        canvas = manager.canvas
        if canvas.figure.stale:
            canvas.draw_idle()        
        #plt.show(block=False)
        canvas.start_event_loop(interval)
    else:
        time.sleep(interval)

经过一些测试(Qt5 后端/Spyder/Windows 7 64 位),对我有用的调用是:

#plt.pause(0.001) #Brings plot to foreground
#fig.canvas.draw() #Does not work
#plt.draw_all() #Does not work
#plt.draw() #Does not work
#fig.canvas.flush_events() #Updates only if I click the figure 
#import time; time.sleep(0.001) #Does not help flush_events()
#fig.canvas.draw_idle() #Does not work by itself
#fig.canvas.start_event_loop(0.001) #Does not work by itself
#mypause(0.001) #Works!

#Works!
fig.canvas.draw_idle()
fig.canvas.start_event_loop(0.001)

Ware fig 是你的图形对象。在我的情况下,两者中的任何一个都不起作用。根据animation 文档,这基本上就是 FuncAnimation 所做的。

【讨论】:

    【解决方案4】:

    在此文档页面 https://matplotlib.org/devdocs/api/animation_api.html#module-matplotlib.animation 的帮助下,我重写了代码以使用 matplotlib.animation

    我通过这个https://matplotlib.org/devdocs/api/_as_gen/matplotlib.pyplot.pause.html进入了那个页面

    其中陈述了关于matplotlib.pyplot.pause的以下内容

    这可以用于粗略的动画。有关更复杂的动画,请参阅 matplotlib.animation.

    这个功能是实验性的;它的行为可能会改变或扩展 在未来的版本中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多