在保持约束布局的情况下,将轴重新添加到matplotlib图形的正确方法是什么? [英] What is the proper way of re-adding axes to a matplotlib figure while maintaining a constrained layout?

查看:111
本文介绍了在保持约束布局的情况下,将轴重新添加到matplotlib图形的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序,我想在同一图中的不同轴之间交换,以便根据数据集在极坐标或直线坐标系中相互绘制不同的数据集. change_axes方法实现了此功能,而swap_axesclf_swaphide_swapnew_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_visibleaxes.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屋!

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