Blender with Solara

20 Replies
Jan-Hendrik Müller
A bit of additional context: Radamés Ajna was trying the same thing with Gradio and said: "It won't work well on a shared web env due to a single Blender context, so set I set Gradio concurrency_count=1." https://twitter.com/radamar/status/1701348276853960908 Is this the same reason here? Also, When I work locally, for each notebook a separate Python symbol is showing in my task bar:
No description
MaartenBreddels
MaartenBreddels10mo ago
yes, i think the same is happening in solara
Jan-Hendrik Müller
Yes, the code is interacting and modifying the global state of the Blender scene via the bpy module like this
import bpy
# ...
light = bpy.data.objects["Light"]
light.location = (1, 0, 2)
import bpy
# ...
light = bpy.data.objects["Light"]
light.location = (1, 0, 2)
MaartenBreddels
MaartenBreddels10mo ago
is there no way around that? there is no 'context' or 'scene' object to isolatie all the settings
Jan-Hendrik Müller
I'm really new to that API, I'll check! And another question to another scenario: Now, I've got a new solara project with only one notebook that renders a protein. It runs totally fine in a normal notebook environment (see screen recording). But as soon as I run solara run pages/. and click the "Start Rendering" Button in my Solara App, I get the error. Any ideas why this might happen?
Traceback (most recent call last):
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/reacton/core.py", line 1647, in _render
root_element = el.component.f(*el.args, **el.kwargs)
File "/Users/jan-hendrik/projects/blender-with-solara/pages/03_molecule.ipynb input cell 1", line 85, in Page
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/load.py", line 116, in molecule_rcsb
nodes.create_starting_node_tree(
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 349, in create_starting_node_tree
node_color_set = add_custom_node_group(node_mod, 'MN_color_set', [200, 0])
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 122, in add_custom_node_group
append(node_name, link=link)
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 43, in append
bpy.ops.wm.append(
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/bpy/3.6/scripts/modules/bpy/ops.py", line 113, in __call__
ret = _op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.wm.append.poll() failed, context is incorrect
Traceback (most recent call last):
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/reacton/core.py", line 1647, in _render
root_element = el.component.f(*el.args, **el.kwargs)
File "/Users/jan-hendrik/projects/blender-with-solara/pages/03_molecule.ipynb input cell 1", line 85, in Page
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/load.py", line 116, in molecule_rcsb
nodes.create_starting_node_tree(
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 349, in create_starting_node_tree
node_color_set = add_custom_node_group(node_mod, 'MN_color_set', [200, 0])
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 122, in add_custom_node_group
append(node_name, link=link)
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/molecularnodes/nodes.py", line 43, in append
bpy.ops.wm.append(
File "/Users/jan-hendrik/projects/blender-with-solara/.venv/lib/python3.10/site-packages/bpy/3.6/scripts/modules/bpy/ops.py", line 113, in __call__
ret = _op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.wm.append.poll() failed, context is incorrect
MaartenBreddels
MaartenBreddels10mo ago
seems like there is some default context in jupyter
Jan-Hendrik Müller
yes, I think so too! Can I get this default context somehow to solara?
MaartenBreddels
MaartenBreddels10mo ago
i don't know how their notebook integration works can't even find their github pages, or anything about notebook integration is this very new?
Jan-Hendrik Müller
It's just a few months since the blender py module bpy is pip-installable. I achieved the notebook integration only a few weeks ago via
import bpy
from IPython.display import display, Image

path = "test.png"
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
import bpy
from IPython.display import display, Image

path = "test.png"
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
So there's no documentation about blender in notebook around yet. Converting this to a solara component works fine both in notebook and in the solara server:
import bpy
from IPython.display import display, Image

import solara

@solara.component
def Page():
path = "test.png"
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
Page()
import bpy
from IPython.display import display, Image

import solara

@solara.component
def Page():
path = "test.png"
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
Page()
Then, importing the molecule model works fine in Jupyter, but fails in the solara server with the "context is incorrect" error.
import bpy
from IPython.display import display, Image

import solara
import molecularnodes as mn

@solara.component
def Page():
path = "test.png"
mn.load.molecule_rcsb('6N2Y') # <- this line causes the error 🐛
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
Page()
import bpy
from IPython.display import display, Image

import solara
import molecularnodes as mn

@solara.component
def Page():
path = "test.png"
mn.load.molecule_rcsb('6N2Y') # <- this line causes the error 🐛
bpy.context.scene.render.resolution_x = 500
bpy.context.scene.render.resolution_y = 200
bpy.ops.render.render()
bpy.data.images["Render Result"].save_render(filepath=path)
display(Image(filename=path))
Page()
Jan-Hendrik Müller
this is deftly not related to solara, when I try to run this script with Gradio, I get the same error https://github.com/radames/Gradio-Blender-bpy/issues/1 so I will reach out and ask in the blender community next 🙂
GitHub
Making molecularnodes a stand-alone web app with gradio 🧬 · Issue #...
hi @radames thanks for creating this project, which converts python blender pipelines to stand-alone web apps. I want to share an idea for a follow-up project: @BradyAJohnston just created "mo...
MaartenBreddels
MaartenBreddels10mo ago
keep me up to date on this!
Jan-Hendrik Müller
Hi @MaartenBreddels, the issue is solved now and was related to bpy context handling, the full conversation that solved this issue is here: https://github.com/radames/Gradio-Blender-bpy/issues/1 I've now hosted a solara app on huggingface (code is only a very quick write-up, nothing too pretty): https://huggingface.co/spaces/kolibril13/solara-bpy The solara app is very performant when I run it on my MAC M1 (rendering the molecule takes only 1 second). Rendering on the huggingface free plan is a bit slower, takes 40 seconds. Here's a screenshot:
MaartenBreddels
MaartenBreddels10mo ago
very cool i couldn't install it, because it needs a specific python version you might be interested in this 'pattern':
import solara
from IPython.display import Image, display

light_position = solara.reactive(3)
do_render = solara.reactive(False)

@solara.component
def Page():

def render():
if do_render.value:
import time
time.sleep(2)
# do you rendering here
return "https://s.yimg.com/ny/api/res/1.2/djPKKfbQP1PAJO2ZdPzDPw--/YXBwaWQ9aGlnaGxhbmRlcjt3PTk2MDtoPTcyMDtjZj13ZWJw/https://s.yimg.com/os/en_US/News/BGR_News/funny-cat.jpg"
result = solara.use_thread(render, [do_render.value])
if not do_render.value:
solara.Button("Start Rendering", on_click=lambda: do_render.set(True))
else:
if result.state == solara.ResultState.RUNNING:
solara.Info("Rendering in progress...")
solara.ProgressLinear()
elif result.state == solara.ResultState.ERROR:
solara.Error("Rendering failed!: %s" % result.error)
elif result.state == solara.ResultState.FINISHED:
with solara.Column():
solara.Success("Rendering complete. ")
print(result.value)
# workaround for https://github.com/widgetti/solara/pull/267
if result.value:
display(Image(result.value))
# display(Image(result.value))
# solara.Image(result.value)
import solara
from IPython.display import Image, display

light_position = solara.reactive(3)
do_render = solara.reactive(False)

@solara.component
def Page():

def render():
if do_render.value:
import time
time.sleep(2)
# do you rendering here
return "https://s.yimg.com/ny/api/res/1.2/djPKKfbQP1PAJO2ZdPzDPw--/YXBwaWQ9aGlnaGxhbmRlcjt3PTk2MDtoPTcyMDtjZj13ZWJw/https://s.yimg.com/os/en_US/News/BGR_News/funny-cat.jpg"
result = solara.use_thread(render, [do_render.value])
if not do_render.value:
solara.Button("Start Rendering", on_click=lambda: do_render.set(True))
else:
if result.state == solara.ResultState.RUNNING:
solara.Info("Rendering in progress...")
solara.ProgressLinear()
elif result.state == solara.ResultState.ERROR:
solara.Error("Rendering failed!: %s" % result.error)
elif result.state == solara.ResultState.FINISHED:
with solara.Column():
solara.Success("Rendering complete. ")
print(result.value)
# workaround for https://github.com/widgetti/solara/pull/267
if result.value:
display(Image(result.value))
# display(Image(result.value))
# solara.Image(result.value)
Jan-Hendrik Müller
oh yes, it runs only on python3.10
python3.10 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
solara run ./pages
python3.10 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
solara run ./pages
should work I've just tested your snippet, what a cute surprise 🐱✨ Very useful indeed! Is there also a way to show a progress bar to run from 0 to 2 seconds? All renderings will approx. take the same time, so that would give the user even a better estimate how to long to wait.
MaartenBreddels
MaartenBreddels10mo ago
hmm, not easily
Jan-Hendrik Müller
also not that important⏳ And another thought: ipyMolecularNodes is a proof of concept that blender models can be turned into notebooks for the scientific python visualization workflow. That means that for every blender model ever created, and all future models that will be created, this is theoretically possible as well. With solara, these notebooks can then even be turned into standalone-zero-install web apps. I don't know yet where this can be of practical use, but I see huge potential for this pipeline!