Название картинки в блокнотах Google Colab или Jupyter

В блокноте Google colab у меня формируется некий график, который я хочу сохранить на свой компьютер. Если я нажимаю на правую кнопку мыши и выбираю пункт выпадающего меня "Сохранить картинку как...", то название файла по умолчанию "Без названия". Я бы хотел, чтобы предлагаемое по умолчанию название было бы таким, каким я его назову в коде. Видимо, такая же проблема и в Jupiter. Подскажите, пожалуйста, как это можно сделать?


Ответы (1 шт):

Автор решения: Vitalizzare

В ноутбуке, будь то Jupyter Notebook или Google Colab, графики отображаются как Data URL. В этой схеме нет возможности задать имя по умолчанию для сохранения картинки. Браузер попытается вычленить имя либо из HTTP-заголовка Content-Disposition, либо из URL-адреса. Следовательно, чтобы решить задачу в лоб, нужно сохранить картинку на доступном онлайн-ресурсе под заданным именем и использовать адрес доступа к ней при отображении в блокноте.

Я полагаю, что будет проще видоизменить задачу, не требуя определенного имени по умолчанию при сохранении через контекстное меню Save Image As.... Например, картинку, представленную как Data URL, мы могли бы упаковать в HTML-ссылку, снабдив последнюю параметром download="произвольное_имя.png". Тогда при нажатии на картинку левой кнопкой мыши, будет предложено сохранить ссылку (т.е. картинку в виде Data URL) как "произвольное_имя.png":

import numpy as np
from matplotlib import pyplot as plt
from IPython.display import display_html
from base64 import b64encode
from io import BytesIO

y = np.random.randn(100)
x = range(len(y))
data = BytesIO()

plt.plot(x, y)
plt.savefig(data)
plt.close()        # не выводить график под ячейкой

img = f'data:image/png;base64,{b64encode(data.getvalue()).decode()}'
fname = 'graph_of_X_vs_Y.png'
hint = 'Left click to save'      # подсказка при наведении мышки на картинку
msg = f'<a href="{img}" download="{fname}"><img src="{img}" title="{hint}"></a>'
display_html(msg, raw=True)

Как вариант, мы могли бы переписать код в виде процедуры:

def display_image(img, fname=None, hint="Left click to save"):
    """Show the given `img` picture in a notebook as a downlodable link
    with `fname` a default file name to save as, and a `hint` message
    to show when a mouse cursor is over the picture. 

    img: a file name as a string or `Path` or a file object with an image.
    fname: a default name to use when saving the picture; if it's None
        and `img` is a file name or a file object with a `name` attribute,
        then the latter will be used as `fname`, otherwise some arbitrary 
        name will be supplied and image type detected if possible (image 
        type is supposed to be PNG by default; png, jpg, jpeg, webp can 
        be detected and displayed).
    """
    from base64 import b64encode
    from IPython.display import display_html
    from io import BufferedIOBase
    from pathlib import Path
    from matplotlib import rcParams

    DEFAULT_NAME = 'plot.%s'
    DEFAULT_EXT = rcParams['savefig.format']

    if isinstance(img, str) or isinstance(img, Path):
        img = Path(img)
        assert img.exists() and img.is_file(), "Path to an image isn't relevant"
        if fname is None:
            fname = img.name
        with open(img, 'br') as f:
            img = f.read()
    elif isinstance(img, BufferedIOBase) and img.readable() and img.seekable():
        img.seek(0)
        if fname is None:
            if isinstance(getattr(img, 'name', None), str):
                fname = img.name
            else:
                try:
                    import imghdr      # python>=3.13: pip install standard-imghdr
                    ext = imghdr.what(img) or DEFAULT_EXT
                except ImportError:
                    ext = DEFAULT_EXT   
                fname = DEFAULT_NAME % ext
        img = img.read()
    else:
        raise ValueError('Not supported image data')
    
    img = f'data:image;base64,{b64encode(img).decode()}'
    msg = f'<a href="{img}" download="{fname}"><img src="{img}" title="{hint}"></a>'
    display_html(msg, raw=True)  

И тогда вывод графика на экран мог бы выглядеть, например, так:

import numpy as np
import matplotlib.pyplot as plt

y = np.random.randn(100)
x = range(len(y))
fname = 'plotXvsY.jpg'

plt.plot(x, y)
plt.savefig(fname)
plt.close()

display_image(fname)

screenshot of the saving operation

→ Ссылка