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

AttributeError: 'TimerWasm' object has no attribute '_timer' #46

Open
sadukie opened this issue Dec 28, 2023 · 5 comments
Open

AttributeError: 'TimerWasm' object has no attribute '_timer' #46

sadukie opened this issue Dec 28, 2023 · 5 comments
Labels
help wanted Extra attention is needed

Comments

@sadukie
Copy link

sadukie commented Dec 28, 2023

Problem

Another community member and I were trying to see if Matplotlib's animation.FuncAnimation would work in PyScript using this Matplotlib demo.

We ran into the following error:

AttributeError: 'TimerWasm' object has no attribute '_timer'

The code is available here on PyScript.com.

More Details

Here's the stack trace of the error:

Traceback (most recent call last):
  File "/lib/python311.zip/_pyodide/_base.py", line 499, in eval_code
    .run(globals, locals)
     ^^^^^^^^^^^^^^^^^^^^
  File "/lib/python311.zip/_pyodide/_base.py", line 340, in run
    coroutine = eval(self.code, globals, locals)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<exec>", line 78, in <module>
  File "/lib/python3.11/site-packages/matplotlib/animation.py", line 1634, in __init__
    super().__init__(fig, **kwargs)
  File "/lib/python3.11/site-packages/matplotlib/animation.py", line 1395, in __init__
    event_source = fig.canvas.new_timer(interval=self._interval)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/lib/python3.11/site-packages/matplotlib_pyodide/browser_backend.py", line 416, in new_timer
    return TimerWasm(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/lib/python3.11/site-packages/matplotlib/backend_bases.py", line 1097, in __init__
    self.interval = 1000 if interval is None else interval
    ^^^^^^^^^^^^^
  File "/lib/python3.11/site-packages/matplotlib/backend_bases.py", line 1139, in interval
    self._timer_set_interval()
  File "/lib/python3.11/site-packages/matplotlib_pyodide/browser_backend.py", line 510, in _timer_set_interval
    if self._timer is not None:
       ^^^^^^^^^^^
AttributeError: 'TimerWasm' object has no attribute '_timer'
@ryanking13 ryanking13 added the help wanted Extra attention is needed label Dec 30, 2023
@ryanking13
Copy link
Member

Thanks for the report! It seems like there was a _timer attribute in TimerBase object in old matplotlib versions but it has been removed. So the TimerWasm object would need to be updated to be compatible with newer matplotlib versions.

Currently, no Pyodide maintainers are actively maintaining matplotlib-pyodide project, and AFAIK none of them have deep knowledge of matplotlib. Therefore, it would be great if people with knowledge of matplotlib could help resolve this issue.

@sadukie
Copy link
Author

sadukie commented Jan 2, 2024

Ah... I see....

@ryanking13
Copy link
Member

Thanks for the check. Maybe we can remove the self._timer attribute from the TimerWasm object, if it is not used in TimerBase at all... but I think we don't have a test case so I am not sure what would be the side effect of it .

@jburgy
Copy link

jburgy commented Nov 16, 2024

I managed to circumvent the immediate issue by adding

from matplotlib_pyodide.browser_backend import TimerWasm

class Timer(TimerWasm):
    def __init__(self, interval=None):
        self._timer = None
        super().__init__(interval=interval)

and injecting that new implementation to FunctionAnimation via the event_source=Timer(interval=30) parameter. Unfortunately, only the axes and labels render, no animation! You can see the result on PyScript.com

@sadukie @ryanking13 any idea?

@jburgy
Copy link

jburgy commented Nov 20, 2024

The animation was not working because pyscript.display saves the figure to PNG. A silly broke-around is to replace display(fig) by

# pyscript.display doesn't know .to_jshtml so call it ourself
html = ani.to_jshtml()

element = document.getElementById(current_target())
if element.tagName == "SCRIPT":
    element = getattr(element, "target", element)

element.replaceChildren()

script_element = document.createRange().createContextualFragment(html)
element.append(script_element)

I have proposed supporting this directly in pyscript.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants