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

Exposing nv.saveScene on the python side #49

Open
kolibril13 opened this issue Feb 22, 2024 · 3 comments
Open

Exposing nv.saveScene on the python side #49

kolibril13 opened this issue Feb 22, 2024 · 3 comments

Comments

@kolibril13
Copy link
Collaborator

As @alexisthual suggested in today's meeting, it would be nice to expose nv.saveScene in order to generate static iamges.
Here's an example on how nv.saveScene is used in niivue:
https://github.com/niivue/ipyniivue-experimental/blob/main/original_gallery.md#color-maps-for-voxels

@kolibril13 kolibril13 transferred this issue from niivue/ipyniivue-experimental Mar 15, 2024
@kolibril13
Copy link
Collaborator Author

Just investigated this issue a bit:

Python

class AnyNiivue(OptionsMixin, anywidget.AnyWidget):
    _save_scene = t.Bool(False).tag(sync=True)

    def save_scene(self):
        """Saves the current scene to a file"""
        self._save_scene = True

In widget.js

model.on("change:_save_scene", () => {
  nv.saveScene();
});

in notebook:

nv.save_scene()

This does work, but only once, as _save_scene is not being reset to False.
After nv.saveScene(); is called, a pop-up window opens where one can define the filename.

Alternatively, there is also an option to directly pass a file name like this:
nv1.saveScene("ScreenShot.png"); which could be also be implemented on the python side.

I guess the best approach here is to have a JSON traitlet like this
_save_scene_as = t.Dict({"name": "screenshot.png", "counter": 1}).tag(sync=True)

@AnthonyAndroulakis
Copy link
Contributor

Just curious, is there a reason why sending custom messages from py -> ts isn't being used for this purpose?

For example, to implement nv.save_scene, one could make these changes:

src/ipyniivue/_widget.py:

class NiiVue(OptionsMixin, anywidget.AnyWidget):
    # ...

    def save_scene(self, filename: str = "screenshot.png"):
        """Send a message to the frontend to save the scene with the given filename.
        
        Parameters
        ----------
        filename : str
            The filename to save the scene to.
        """
        self.send({
            'type': 'save_scene',
            'data': filename
        })

src/js/widget.ts:

export default {
    async render({ model, el }: { model: Model; el: HTMLElement }) {
        // ...

        // Handle custom messages from the backend
        model.on("msg:custom", (payload: {type: string, data: any}) => {
            const { type, data } = payload;
            switch (type) { 
                case "save_scene":
                    nv.saveScene(data);
                    break;
            }
        });

And then, just use the save scene function nv.save_scene()

@christian-oreilly
Copy link
Collaborator

Probably because @kolibril13 was trying to address this issue it using the AnyWidget philosophy. The approach you propose is closer to the approach iPyNiiVue was using before the switch to AnyWidget. I would normally be hesitant to go down that road because of all the synchronization issues we had with that approach but it might work well for this specific functionality. Worth trying. Thanks for the suggestion @AnthonyAndroulakis

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

No branches or pull requests

3 participants