widgetti / reacton

A pure Python port of React for ipywidgets
https://reacton.solara.dev/
MIT License
289 stars 19 forks source link

Matplotlib plots not rendering properly in output widgets. #11

Closed brendan0powers closed 1 year ago

brendan0powers commented 1 year ago

When using an ipywidgets.Output widget in a reacton component, plots are not displayed in notebook. I've tested this with the ipympl and inline backends. It works fine when using ipywidgets directly, and used to work with react-ipywdgets 0.11.

Here's an example that shows both the working case (with ipywidgeds) and the non-working case (reacton).

import matplotlib.pyplot as plt
import reacton as react
import reacton.ipywidgets as w
import ipywidgets as widgets

#%matplotlib inline
%matplotlib ipympl

def plot(outputWidget):
    with plt.ioff():
        fig, ax = plt.subplots()

        fruits = ['apple', 'blueberry', 'cherry', 'orange']
        counts = [40, 100, 30, 55]
        bar_labels = ['red', 'blue', '_red', 'orange']
        bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange']

        ax.bar(fruits, counts, label=bar_labels, color=bar_colors)

        ax.set_ylabel('fruit supply')
        ax.set_title('Fruit supply by kind and color')
        ax.legend(title='Fruit color')

        with outputWidget:
            print("Displaying")
            plt.show()

# Reacton Test Case
@react.component()
def Test():
    render, setRender = react.use_state(False)

    def displayReacton():
        if render == False:
            return

        outputWidget = react.get_widget(output)
        plot(outputWidget)

    with w.VBox() as vbox:
        w.Button(description="Render", on_click=lambda: setRender(True))
        output = w.Output()

    react.use_effect(displayReacton, [render])

    return vbox

Test()

# Ipywidgets test case
outputWidget = widgets.Output()
button = widgets.Button(description="Render")

def displayIpywidgets(_):
    plot(outputWidget)

button.on_click(displayIpywidgets)

widgets.VBox([
    button,
    outputWidget
])

Software Versions:

python                          3.10.9
matplotlib-base           3.7.1
ipympl                           0.9.3
reacton                          1.2.2
maartenbreddels commented 1 year ago

Thanks for the great bug report.

Note that we now automatically capture anything in the render phase to an output widget that is using the display mechanism. E.g. you could do:

...
        ax.legend(title='Fruit color')
#         with outputWidget:
        plt.show()
...
    with w.VBox() as vbox:
        if render:
            plot(None)
        w.Button(description="Render", on_click=lambda: setRender(True))

#     react.use_effect(displayReacton, [render])

See https://solara.dev/api/display

brendan0powers commented 1 year ago

Awesome! Thanks for the quick response. I've tested against master, and it seems to fix the issue. The version in conda-forge (1.3.2) still has the bug though, I assume 1.3.3 will be released soonish?

maartenbreddels commented 1 year ago

merged it 10 seconds ago https://github.com/conda-forge/reacton-feedstock/pull/14 :)

It can take up to an hour to be available.

brendan0powers commented 1 year ago

Thanks! 1.3.3 from conda-forge resolves the issue.