在matplotlib中保存地物时,是否有方法更改样式表?

2024-09-29 19:20:28 发布

您现在位置:Python中文网/ 问答频道 /正文

是否可以在保存图形时临时更改其style sheet,而无需重新打印(两次)?我知道您可以将图as parametersfacecoloredgecolor传递给savefig,但我不理解为什么它仅限于这两个参数。您似乎对axes实例的rcParams没有任何影响。Matplotlib的样式表上下文管理器只影响图形的样式表。下面是一个可以使用的MWE:

import matplotlib.pyplot as plt


fig, ax = plt.subplots()
ax.plot((1, 2), (0, 0), '--')

with plt.style.context('dark_background'):
    # if you replot it, it works, however, this messses up the style outside
    # this context as well
    # fig.clf()
    # ax = fig.add_subplot(111)
    # ax.plot((1, 2), (0, 0), '--')
    fig.savefig('test.png')

# you would need to replot everything again for the style to be correct again
# fig.clf()
# ax = fig.add_subplot(111)
# ax.plot((1, 2), (0, 0), '--')
fig.show()


Tags: theyou图形plotstyleascontextfig
1条回答
网友
1楼 · 发布于 2024-09-29 19:20:28

好的,经过大量的研究和反复试验,我终于找到了一种可行的方法,虽然很复杂。基本思想是编写您自己的(“智能”)上下文管理器,该管理器在抽象级别上理解您想要做什么,然后进行适当的更改。例如,如果您想更改axes{}的figureaxes{},它知道哪些颜色更改与之相关

在更改edgecolor的情况下,有许多障碍需要注意,因为更改关联艺术家的(边缘)颜色对于不同艺术家的效果不同。以下是我遇到的一些警告,当然这个列表并不全面,因为我对更复杂的数字很感兴趣,还有其他一些事情需要考虑:

  • 虽然可以通过使用^{}简单地更改axes实例的facecolor,但由于某些原因,edgecolor没有等效的方法。因此,您需要手工操作并收集所有spinelabeltick实例,并分别更改它们的颜色
  • 虽然^{}艺术家接受colorfacecolor以及edgecolor关键字,甚至有一个^{}方法,但它没有get_color()方法。相反,您需要调用get_edgecolor()
  • 如果最重要的是,你也碰巧在你的绘图中加入了一个colorbar,你必须找到另一种方法,因为如果你想改变颜色栏的脊椎颜色,你不是通过改变脊椎的颜色来实现的,but by changing the color of the ^{}。出于某种原因,doesn't seem to be documented in the official documentation。但是如果您想更改颜色栏的一个方面,you somehow need to retrieve it from the figure instance first

我相信有很好的理由解释为什么事情会这样,但是从想要改变edgecolor的简单想法来看,这似乎是不必要的武断

下面是应用于一个简单示例图的上下文管理器的代码。我相信有很多方法可以改进这种方法

import matplotlib.pyplot as plt
from matplotlib.spines import Spine
from matplotlib.patches import Polygon


class StyleChange:
    def __init__(self, color):
        self._color = color
        self._original_colors = {}

    def apply(self, fig):
        pass

    def revert(self):
        pass


class AxesEdgeColorChange(StyleChange):
    def apply(self, fig):
        for ax in fig.axes:
            self._original_colors[ax] = {}
            ticks = [*ax.get_xticklines(), *ax.get_yticklines()]
            mticks = [
                *ax.get_xaxis().get_minor_ticks(),
                *ax.get_yaxis().get_minor_ticks(),
            ]
            labels = [*ax.get_xticklabels(), *ax.get_yticklabels()]
            spines = ax.spines.values()
            cbars = [
                im.colorbar.outline for im in ax.images
                if (im is not None and im.colorbar is not None)
            ]
            for artist in [*ticks, *mticks, *labels, *spines, *cbars]:
                self._original_colors[ax][artist] = self._get_color(artist)
                self._set_color(artist)

    def revert(self):
        for axes_artists in self._original_colors.values():
            for artist, color in axes_artists.items():
                self._set_color(artist, color)

    @staticmethod
    def _get_color(artist):
        if isinstance(artist, (Spine, Polygon)):
            return artist.get_edgecolor()

        return artist.get_color()

    def _set_color(self, artist, color=None):
        if color is None:
            color = self._color
        if isinstance(artist, Polygon):
            artist.set_edgecolor(color)
        else:
            artist.set_color(color)


class AxesFaceColorChange(StyleChange):
    def apply(self, fig):
        for ax in fig.axes:
            self._original_colors[ax] = ax.get_facecolor()
            ax.set_facecolor(self._color)

    def revert(self):
        for ax, color in self._original_colors.items():
            ax.set_facecolor(color)


class StyleChangeManager:
    def __init__(
            self, fig: plt.Figure, *style_changes: StyleChange,
            redraw: bool = False
    ):
        self._fig = fig
        self._style_changes = style_changes
        self._redraw = redraw

    def __enter__(self):
        for change in self._style_changes:
            change.apply(self._fig)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        for change in self._style_changes:
            change.revert()
        if self._redraw:
            self._fig.canvas.draw_idle()


def example():
    image = np.random.uniform(0, 1, (100, 100))
    fig, ax = plt.subplots()
    image_artist = ax.imshow(image)
    fig.colorbar(image_artist)
    ec_change = AxesEdgeColorChange('red')
    with StyleChangeManager(fig, ec_change):
        fig.show()

    fig.show()


if __name__ == '__main__':
    import numpy as np

    example()

相关问题 更多 >

    热门问题