Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plotly.io.renderer proposal #1459

Closed
jonmmease opened this issue Mar 11, 2019 · 6 comments
Closed

plotly.io.renderer proposal #1459

jonmmease opened this issue Mar 11, 2019 · 6 comments

Comments

@jonmmease
Copy link
Contributor

jonmmease commented Mar 11, 2019

Overview

This issue is a design proposal for a new renderer abstraction for plotly.py to replace the iplot functions.

Goals

Several goals motivate this work

  1. Make it possible to support new plot rendering contexts like static images, rendering inside colab, and rendering in popup windows.
  2. Remove the "offline" language from plot rendering. As discussed in the v4 plans, the Chart Studio integration (the plotly.plotly stuff) will be split off into a separate package for v4, and the remaining plotly package will be only "offline" only. So having the "offline" language will be unnecessary and potentially confusing.
  3. Provide a single location to change the plot rendering approach across an entire notebook. This will make it much easier for folks to copy/paste documentation examples and change the example to their preferred rendering approach.

Background

There are currently 2 ways to display plots in plotly.py in "offline" mode:

  1. plotly.offline.iplot: This will display plots inline in a Jupyter Notebook or in JupyterLab. The use in the classic notebook requires calling plotly.offline.init_notebook_mode() to initialize the notebook before rendering. The use in JupyterLab requires the @jupyterlab/plotly-extension
  2. plotly.offline.plot: This will save the plot to a standalone HTML file and, by default, open it in the default system web browser.

Design Influences

This design is somewhat inspired by the approach used by the Altair project (https://altair-viz.github.io/user_guide/renderers.html), and I expect the Altair implementation will be very helpful reference during the implementation phase of this effort.

The design also draws on elements of the plotly.io.templates design (See #1224), and is intended to have a consistent user experience.

API

Default renderer

I propose we add a new configuration object named renderers in the plotly.io module. This will sit alongside the plotly.io.templates and plotly.io.orca configuration objects. Users will view and specify the current default renderer using property assignment. For example, to specify the equivalent of plotly.offline.iplot:

import plotly.io as pio
pio.renderers.default = 'notebook'  # `init_notebook_mode` and output with `iplot`

Here are some other mimetype-based. renderer options

pio.renderers.default = 'notebook_connected'
pio.renderers.default = 'plotly_mimetype'  # Render using plotly mimetype
pio.renderers.default = 'jupyterlab' # Same as 'default'
pio.renderers.default = 'nteract'  # Same as 'default'
pio.renderers.default = 'kaggle'  # Same as 'notebook'
pio.renderers.default = 'colab'  # New
pio.renderers.default = 'png'  # Init orca server and output as png
pio.renderers.default = 'svg'  # Init orca server and output as svg
pio.renderers.default = 'pdf'  # Init orca server and output as pdf

In addition to the mime type based renderers for use in a Jupyter context, we can support additional renderers that display figures externally (not inline). For example

pio.renderers.default = 'browser' # Popup in local default browser tab like plotly.offline.plot
pio.renderers.default = 'chrome' # Popup in local chrome tab like plotly.offline.plot
pio.renderers.default = 'firefox' # Popup in local firefox tab like plotly.offline.plot
pio.renderers.default = 'qtwindow' # (someday) Popup interactive figure in a pyqt browser

Combining renderers

Multiple renderers can be registered as the default by separating the renderer names with '+' characters. This is the same combination syntax used to combine templates in plotly.io.templates. When multiple mime type renderers are specified this way, a single bundle will be created with the render representation for each one. As motivation, consider

pio.renderers.default = 'notebook+jupyterlab+png'

A notebook with this renderer specification would display figures properly in the classic notebook ('notebook'), in jupyterlab ('jupyterlab'), in exported HTML ('notebook'), in the QtConsole/PyCharm ('png'), and in exported PDFs ('png').

Of course this would result in fairly large notebook sizes, but the user will have the flexibility to define where this code needs to render figures.

repr and show

There will be two ways to display figures. If one or more mime-type based renderers are defined and the plotly.io.renderers.render_on_display property is set to True, then the go.Figure instances will display themself automatically as their notebook representation. If plotly.io.renderers.render_on_display is set to False, then mimetype based rendering will not be performed when figures are displayed.

In addition, all renderer types (including external renderers) can be invokes using a new plotly.io.show function. This function will display the figure as a side effect and will return None.

The show functions will also allow the user to override the default renderer. e.g.

import plotly.io as pio
pio.renderers.default = 'notebook'  # `init_notebook_mode` and output with `iplot`

fig = go.Figure(...)

# By default show will display figure inline, override to display in browser tab
pio.show(fig, renderer='browser')

Customize renderer properties

Some renderers may need to expose additional configuration options. In this case the default renderer can be specified as an instance of the corresponding class. Each built-in renderer will be defined by a class accessible under plotly.io.renderers.

E.g. to specify a static image renderer with custom properties:

import plotly.io as pio
pio.renderers['my_png'] = pio.renderers.PngRenderer(width=1000, height=650, scale=1.5)
pio.renderers.default = 'my_png'

Or the properties of an existing renderer can be modified directly:

pio.renderers['png'].width = 1000
pio.renderers['png'].height = 650
pio.renderers['png'].scale = 1.5
pio.renderers.default = 'png'

Or the properties can be overridden temporarily in plotly.io.show:

pio.show(fig, renderer='png', width=1000, height=650, scale=1.5)

Registering new renderers

Users or third-party-libraries may register new renderers by implementing a class following a TBD interface.

import plotly.io as pio

# Define custom mimetype renderer
class MyRenderer(pio.base_renderers.MimetypeRenderer):
    ...
    def activate():
        ...

    def to_mimebundle(self, fig_dict):
        ...

# Register for use by name
my_renderer = MyRenderer()
pio.renderers['my_renderer'] = my_renderer

# Set as default
pio.renderers.default = 'my_renderer'

Backward compatibility

For backward compatibility, plotly.offline.init_notebook_mode and plotly.offline.iplot will stay around, but they will be implemented on top of the new renderer framework.

For plotly.py v3 the default renderer will be plotly_mimetype and iplot will call plotly.io.show. Calling plotly.offline.init_notebook_mode() will set the default renderer to 'plotly_mimetype+notebook' and plotly.offline.init_notebook_mode(connected=True) will set the default renderer to 'plotly_mimetype+notebook_connected'.

cc @nicolaskruchten @chriddyp @jackparmer

@nicolaskruchten
Copy link
Contributor

I like this a lot!

@jonmmease
Copy link
Contributor Author

See #1474 for implementation

@jonmmease
Copy link
Contributor Author

Done in #1474

@wesszabo
Copy link

renderer="colab+notebook_connected" does not work together when a 3d surface is plotted.
https://nbviewer.jupyter.org/github/wesszabo/Analizis-2-informatikusoknak/blob/0d81b8895acac765fb35215254b009f969942397/MultiVariable_limit.ipynb

@matanox
Copy link

matanox commented Apr 22, 2022

Given that this great modular design has been implemented, would there be any renderer that can be recommended for headless testing of code where plotly.show() is being used? Maybe something that is used by plotly's python own test code?

@nicolaskruchten
Copy link
Contributor

Yes, for headless testing, we export figures to PNG using Kaleido: https://plotly.com/python/static-image-export/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants