在 Matplotlib 中交互式调整图形大小并切换绘图可见性? [英] Interactively resize figure and toggle plot visibility in Matplotlib?

查看:128
本文介绍了在 Matplotlib 中交互式调整图形大小并切换绘图可见性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做的是:

  • 以两个子图开始图(一个叠在一起)
  • 按键盘上的"x"以:调整图形的大小,并在右侧显示第三幅图.
  • 再次按"x"键:将图形调整为原始大小,并隐藏第三幅图(没有空间留出第三幅图).

通过下面的示例代码,我得到了这个(matplotlib 3.1.2,MINGW64 中的 Python3,Windows 10):

With the example code below, I got to this (matplotlib 3.1.2, Python3 in MINGW64, Windows 10):

如gif所示-即使在开始状态下,右侧也有一些空白(因为除了定义网格外,我不知道如何解决此问题的更好方法).然后,当图形窗口扩展/调整大小时,它不会精确地"调整大小,因此适合第三幅图.

As it is shown on the gif - even in the starting state, there is some empty space on the right (since I didn't know any better way how to solve this, other than define a grid). Then, when the figure window extends/resizes, it is not "exactly" resized so it fits the third plot.

我怎样才能实现第三个图的切换,这样当它被隐藏时,右边就没有多余的空间 - 当它显示时,图正好延伸到第三个图适合(包括边距)(现有/初始的两个图的大小都不会改变)?

How can I achieve a toggling of this third plot, such that when it is hidden, there is no extra empty space on the right - and when it is shown, the figure extends exactly so the third plot fits (including margins) ( and the existing/initial two plots do not change in size)?

代码:

#!/usr/bin/env python3

import matplotlib
print("matplotlib.__version__ {}".format(matplotlib.__version__))
import matplotlib.pyplot as plt
import numpy as np

default_size_inch = (9, 6)
showThird = False

def onpress(event):
  global fig, ax1, ax2, ax3, showThird
  if event.key == 'x':
    showThird = not showThird
    if showThird:
      fig.set_size_inches(default_size_inch[0]+3, default_size_inch[1], forward=True)
      plt.subplots_adjust(right=0.85) # leave a bit of space on the right
      ax3.set_visible(True)
      ax3.set_axis_on()
    else:
      fig.set_size_inches(default_size_inch[0], default_size_inch[1], forward=True)
      plt.subplots_adjust(right=0.9) # default
      ax3.set_visible(False)
      ax3.set_axis_off()
    fig.canvas.draw()


def main():
  global fig, ax1, ax2, ax3
  xdata = np.arange(0, 101, 1) # 0 to 100, both included
  ydata1 = np.sin(0.01*xdata*np.pi/2)
  ydata2 = 10*np.sin(0.01*xdata*np.pi/4)

  fig = plt.figure(figsize=default_size_inch, dpi=120)
  ax1 = plt.subplot2grid((3,3), (0,0), colspan=2, rowspan=2)
  ax2 = plt.subplot2grid((3,3), (2,0), colspan=2, sharex=ax1)
  ax3 = plt.subplot2grid((3,3), (0,2), rowspan=3)

  ax3.set_visible(False)
  ax3.set_axis_off()

  ax1.plot(xdata, ydata1, color="Red")
  ax2.plot(xdata, ydata2, color="Khaki")

  fig.canvas.mpl_connect('key_press_event', lambda event: onpress(event))
  plt.show()


# ENTRY POINT
if __name__ == '__main__':
  main()

推荐答案

如前所述,您实际上有两个选择;使用单个gridspec,或者对每个状态使用两个.让我们使用单个gridspec查看第一个选项.为此,您将首先以英寸为单位定义所有需要的参数,然后为两个所需状态中的每一个计算子图参数(以相对单位表示).

As commented, you essentially have two options; use a single gridspec, or use two, one for each state. Let's look at the first option, using a single gridspec. To this end you would first define all needed parameters in inches, then calculate the subplot parameters (in relative units) for each of the two desired states.

当按下 x 时,您将通过 .update() 更新 gridspec 参数来在状态之间切换.

When pressing x you would toggle between the states by updating the gridspec parameters via .update().

import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

w,h = plt.rcParams["figure.figsize"]
# Define dimensions in inches (could also just put numbers here)
left = plt.rcParams["figure.subplot.left"] * w
right = (1 - plt.rcParams["figure.subplot.right"]) * w
wspace = plt.rcParams["figure.subplot.wspace"] * w

figw1, figh1 = (7,5)
ax1width = figw1 - left - right
ax2width = 3.5

#calculate remaining free parameter, the figure width of the enlarged figure
figh2 = figh1
figw2 = left + ax1width + wspace + ax2width + right

#calculate subplot parameters for both cases
subplotpars1 = dict(left = left/figw1, right=(left + ax1width + wspace + ax2width)/figw1,
                    wspace=wspace/(ax1width+ax2width), )
subplotpars2 = dict(left = left/figw2, right=(left + ax1width + wspace + ax2width)/figw2,
                    wspace=wspace/(ax1width+ax2width), )

# create GridSpec
gs = GridSpec(2,2, width_ratios=(ax1width, ax2width), **subplotpars1)
# Create figure with 3 axes
fig = plt.figure(figsize=(figw1, figh1))
ax1 = fig.add_subplot(gs[0,0])
ax2 = fig.add_subplot(gs[1,0])
ax3 = fig.add_subplot(gs[:,1])

ax1.plot([2,4], color="C0")
ax2.plot([0,11], color="C1")
ax3.plot([5,15], color="C2")


# Updating machinery
current_state = [0]
subplotspars = [subplotpars1, subplotpars2]
figsizes = [(figw1, figh1), (figw2, figh2)]

def key_press(evt):
    if evt.key == "x":
        current_state[0] = (current_state[0] + 1) % 2
        gs.update(**subplotspars[current_state[0]])
        fig.set_size_inches(figsizes[current_state[0]], forward=True)
        fig.canvas.draw_idle()

fig.canvas.mpl_connect("key_press_event", key_press)


plt.show()

这篇关于在 Matplotlib 中交互式调整图形大小并切换绘图可见性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆