@@ -70,10 +70,35 @@ async def ensure_installed(extras: tuple[str, ...] | list[str] = ()) -> bool:
7070 requirement = "roboticstoolbox-python"
7171 if extras :
7272 requirement += "[" + "," .join (extras ) + "]"
73+ # Colab does not ship ipympl, which the notebooks select with
74+ # %matplotlib ipympl; matplotlib rejects the backend if it is missing.
7375 subprocess .run (
74- [sys .executable , "-m" , "pip" , "install" , "-q" , requirement ],
76+ [sys .executable , "-m" , "pip" , "install" , "-q" , requirement , "ipympl" ],
7577 check = True ,
7678 )
79+ # matplotlib renames the module://ipympl... backend to "widget", and looks
80+ # it up in a list of backends that it builds once, from the installed
81+ # packages, the first time it needs it. Colab imported matplotlib before
82+ # ipympl was installed, so %matplotlib ipympl is rejected ("... is not a
83+ # valid value for backend") until the session restarts. Make matplotlib
84+ # build the list again. Clearing the registry is what its own tests do,
85+ # and it is needed: only resetting the flag registers the backends that
86+ # were already there (matplotlib-inline's) twice, which is an error.
87+ # _clear is private, hence the guards.
88+ try :
89+ from matplotlib .backends import backend_registry
90+ except ImportError :
91+ pass # an older matplotlib has no registry to refresh
92+ else :
93+ clear = getattr (backend_registry , "_clear" , None )
94+ if clear is not None :
95+ clear ()
96+
97+ # ipympl figures need Colab's custom widget manager, enabled before the
98+ # first figure is created.
99+ from google .colab import output
100+
101+ output .enable_custom_widget_manager ()
77102 where , colab = "on Colab" , True
78103
79104 import roboticstoolbox
0 commit comments