在保持约束布局的情况下,将轴重新添加到matplotlib图形的正确方法是什么? [英] What is the proper way of re-adding axes to a matplotlib figure while maintaining a constrained layout?
问题描述
我有一个程序,我想在同一图中的不同轴之间交换,以便根据数据集在极坐标或直线坐标系中相互绘制不同的数据集.
change_axes
方法实现了此功能,而swap_axes
,clf_swap
,hide_swap
和new_subplot_swap
方法显示了尝试在轴之间交换的不同方法,尽管swap_axes
是唯一产生期望结果的方法. .该代码基于这篇文章.
I have a program where I would like to swap between different axes in the same figure in order to plot different datasets against eachother, either in a polar or rectilinear coordinate system depending on the dataset.
The change_axes
method implements this functionality, while the swap_axes
, clf_swap
, hide_swap
and new_subplot_swap
methods show different ways of trying to swap between axes, although swap_axes
is the only one that produces the desired result. The code is based on this post.
是否有更好的方法?使用axes.set_visible
或axes.set_alpha
对我无济于事,甚至由于某种原因会产生错误(AttributeError: 'NoneType' object has no attribute 'get_points'
).我不明白为什么这行不通,但这是添加"和移除"轴的简单得多的方法.
Is there a better way of doing this? Using axes.set_visible
or axes.set_alpha
does nothing for me and even produces an Error for some reason (AttributeError: 'NoneType' object has no attribute 'get_points'
). I don't understand why this doesn't work, but it would have been a much easier way of 'adding' and 'removing' the axes.
import numpy as np
import sys
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import \
FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import (
QApplication, QDialog, QGridLayout, QComboBox, QPushButton
)
class Test(QDialog):
def __init__(self):
super().__init__()
self.lay = QGridLayout(self)
self.fig, self.ax = plt.subplots(constrained_layout=True)
self.ax2 = self.fig.add_subplot(111, projection='polar')
# self.ax2 = plt.subplot(111, projection='polar')
# uncomment this for 'hide_swap'
# self.ax2.set_visible(False)
self.canvas = FigureCanvas(self.fig)
self.lay.addWidget(self.canvas)
self.data = {
'Dataset 1': np.random.normal(2, 0.5, 10000),
'Dataset 2': np.random.binomial(10000, 0.3, 10000),
'Dataset 3': np.random.uniform(0, 2*np.pi, 10000)
}
_, _, self.artists = self.ax.hist(self.data['Dataset 1'], 100)
self.swapBtn = QPushButton('Swap')
self.lay.addWidget(self.swapBtn, 1, 0)
self.swapBtn.clicked.connect(self.swap_axes)
self.clfSwapBtn = QPushButton('fig.clf() swap')
self.clfSwapBtn.clicked.connect(self.clf_swap)
self.lay.addWidget(self.clfSwapBtn, 2, 0)
self.hideSwapBtn = QPushButton('Hide swap')
self.hideSwapBtn.clicked.connect(self.hide_swap)
self.lay.addWidget(self.hideSwapBtn, 3, 0)
self.subplotSwapBtn = QPushButton('New subplot swap')
self.subplotSwapBtn.clicked.connect(self.new_subplot_swap)
self.lay.addWidget(self.subplotSwapBtn, 4, 0)
self.xParam = QComboBox()
self.xParam.addItem('Dataset 1')
self.xParam.addItem('Dataset 2')
self.xParam.addItem('Dataset 3')
self.xParam.currentTextChanged.connect(self.change_axes)
self.lay.addWidget(self.xParam)
self.yParam = QComboBox()
self.yParam.addItem('Distribution')
self.yParam.addItem('Dataset 1')
self.yParam.addItem('Dataset 2')
self.yParam.addItem('Dataset 3')
self.yParam.currentTextChanged.connect(self.change_axes)
self.lay.addWidget(self.yParam)
self.canvas.draw()
# this is neccessary for
# "self.ax2 = self.fig.add_subplot(111, projection='polar')", and for
# some reason has to be called after 'canvas.draw', otherwise,
# the constrained layout cannot be applied. comment this if using
# "self.ax2 = plt.subplot(111, projection='polar')" or "hide_swap"
self.ax2.remove()
def change_axes(self):
if self.yParam.currentText() == 'Distribution':
if self.xParam.currentText() == 'Dataset 3':
if self.fig.axes[0] == self.ax:
self.ax.remove()
self.ax2.figure = self.fig
self.fig.axes.append(self.ax2)
self.fig.add_axes(self.ax2)
radii, theta = np.histogram(self.data['Dataset 3'], 100)
width = np.diff(theta)
self.fig.axes[0].cla()
self.artists = self.ax2.bar(theta[:-1], radii, width=width)
else:
if self.fig.axes[0] == self.ax2:
self.ax2.remove()
self.ax.figure = self.fig
self.fig.axes.append(self.ax)
self.fig.add_axes(self.ax)
self.fig.axes[0].cla()
_, _, self.artists = self.ax.hist(
self.data[self.xParam.currentText()], 100
)
else:
if (
self.xParam.currentText() == 'Dataset 3'
and self.fig.axes[0] == self.ax
):
self.ax.remove()
self.ax2.figure = self.fig
self.fig.axes.append(self.ax2)
self.fig.add_axes(self.ax2)
elif (
self.xParam.currentText() != 'Dataset 3'
and self.fig.axes[0] == self.ax2
):
self.ax2.remove()
self.ax.figure = self.fig
self.fig.axes.append(self.ax)
self.fig.add_axes(self.ax)
self.fig.axes[0].cla()
self.artists = self.fig.axes[0].plot(
self.data[self.xParam.currentText()],
self.data[self.yParam.currentText()], 'o'
)
self.canvas.draw()
def swap_axes(self):
if self.fig.axes[0] == self.ax:
self.ax.remove()
self.ax2.figure = self.fig
self.fig.axes.append(self.ax2)
self.fig.add_axes(self.ax2)
else:
self.ax2.remove()
self.ax.figure = self.fig
self.fig.axes.append(self.ax)
self.fig.add_axes(self.ax)
self.canvas.draw()
def clf_swap(self):
if self.fig.axes[0] == self.ax:
self.fig.clf()
self.ax2.figure = self.fig
self.fig.add_axes(self.ax2)
else:
self.fig.clf()
self.ax.figure = self.fig
_, _, self.artists = self.ax.hist(self.data['Dataset 1'], 100)
self.fig.add_axes(self.ax)
self.canvas.draw()
def hide_swap(self):
if self.ax.get_visible():
self.ax.set_visible(False)
self.ax2.set_visible(True)
# self.ax.set_alpha(0)
# self.ax2.set_alpha(1)
else:
self.ax.set_visible(True)
self.ax2.set_visible(False)
# self.ax.set_alpha(1)
# self.ax2.set_alpha(0)
self.canvas.draw()
def new_subplot_swap(self):
if self.fig.axes[0].name == 'rectilinear':
self.ax.remove()
self.ax2 = self.fig.add_subplot(111, projection='polar')
else:
self.ax2.remove()
self.ax = self.fig.add_subplot(111)
_, _, self.artists = self.ax.hist(self.data['Dataset 1'], 100)
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec_())
推荐答案
使用matplotlib的当前开发版本不会出现该错误.但是,需要确保从受约束的布局机制中排除相应的轴.
Using the current development version of matplotlib the error would not appear. But one would need to make sure to exclude the respective axes from the constrained layout mechanism.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(constrained_layout=True)
ax2 = fig.add_subplot(111, projection="polar")
ax2.set_visible(False)
ax2.set_in_layout(False)
def swap(evt):
if evt.key == "h":
b = ax.get_visible()
ax.set_visible(not b)
ax.set_in_layout(not b)
ax2.set_visible(b)
ax2.set_in_layout(b)
fig.canvas.draw_idle()
cid = fig.canvas.mpl_connect("key_press_event", swap)
plt.show()
对于任何当前的matplotlib版本,都可以使用tight_layout
而不是受约束的布局,并在每次调整大小事件时手动调用它.
With any current matplotlib version, one could use tight_layout
instead of constrained layout, and call it manually at each resize event.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax2 = fig.add_subplot(111, projection="polar")
ax2.set_visible(False)
def swap(evt):
if evt.key == "h":
b = ax.get_visible()
ax.set_visible(not b)
ax2.set_visible(b)
fig.tight_layout()
fig.canvas.draw_idle()
def onresize(evt):
fig.tight_layout()
cid = fig.canvas.mpl_connect("key_press_event", swap)
cid = fig.canvas.mpl_connect("resize_event", onresize)
fig.tight_layout()
plt.show()
这篇关于在保持约束布局的情况下,将轴重新添加到matplotlib图形的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!