ageller / Firefly

A WebGL interactive particle viewer
GNU Affero General Public License v3.0
66 stars 13 forks source link

pass settings to and from firefly via flask #166

Closed ageller closed 8 months ago

ageller commented 1 year ago

We would like a method to pass the settings (and selected data once that it available) between Python and Firefly. If a user is in a Jupyter notebook, they should be able to get the current settings without having to download a file, and pass new settings via the Jupyter notebook.

ageller commented 1 year ago

I created a branch called python_data_passing to work on this. At this point I can access the settings within Python and also send updated settings to Firefly via Python. I have not checked this carefully yet. (I've really only tried changing the useStereo key.)

There are a few questions that we should discuss on how to finish this off:

  1. Currently if the user wants to send settings to firefly, they need to send the full settings object (I think). This is easier to code, but might be more cumbersome for a user, since they may first have to download the settings from firefly, then change what they want, then upload it. (Which is not hard.) Alternatively, we could think of a way to allow users to also send individual components of the settings. Then we'd need some way to detect that, and update the existing settings object in javascript (rather than replacing it, which is what I do now).
  2. Once the settings are passed to firefly and something changes in the viewer, we need a graceful way to update the GUI (e.g., to toggle a checkbox, or change a slider value). I don't know if we have an easy way to do this...
  3. Should we wrap the requests calls to get and set the settings within the functions in the Python API? They are only 1-liners, but maybe it would be cleaner (and/or more complete) to have them in the API.
  4. Currently the way this works is if the user wants to be able to download the settings, they must send a flag to the server that says they want to access the settings from python (python_data_passing = True). That starts a "continuous" download of the settings from JS to flask (currently every 3 seconds). It would be cleaner to only do this download when something is updated that also exists in the settings, but I don't think we have any easy way to detect that. (I guess we could build the settings every 3 seconds, check for a difference with the old settings, and then download if needed. But that still requires building the settings every 3 seconds and the extra check against the old settings). This "continuous" method seems to work, but I could imagine some issue with it clogging the server if many people are trying to do this at once. Really the ideal scenario is to have one command that goes from Python to the flask server to the JS saying we want settings (which can be accomplished with a POST command and then an emit from Flask) and then back from JS to flask (which could be an emit from JS) with somehow enabling output to a Python notebook (which I can't solve). I can't think of way to accomplish this in flask, which is why I went with the current "continuous" download method. But maybe there is a solution that I haven't thought of yet.
ageller commented 1 year ago

For the GUI (2. above), we could just remake the entire GUI. Currently I have that implemented. However that is a bit of overkill, and also collapses the UI back down. That might be our only easy option, but I'm not sure it's ideal.

agurvich commented 1 year ago

looking at this briefly now, 'collapseGUIAtStart' is a setting that if set to False will prevent the collapsing issue when rebuilding the GUI. I agree it's overkill but it could work for now

ageller commented 1 year ago

I have an idea to simplify 4. above, and remove the "continuous" download. I;ll will write it here in pseudocode so that I don't forget:

fireflyData = {}

@socketio.on(...
def settingsReturner(...):
    # get settings back from JS
    global fireflyData
    fireflyData[room][settingsReady] = True
    fireflyData[room][settings] = settings

@app.route(..., , [GET])
def getSettings(...):
    global fireflyData
    fireflyData[room][settingsReady] = False
    socketio.emit('retrieve_settings' ...) # goes to JS, gets settings, returns in settingsReturner above
    timeCount = 0 #count seconds
    while (!fireflyData[room][settingsReady] and timeCount < 5):
        # increment timeCount in seconds

    if (fireflyData[room][settingsReady)):
        return fireflyData[room][settings]
    else:
        return 'Error: timeout'
ageller commented 1 year ago

Nope. A while loop does not work as expected in this context. I found an interesting alternative here that I'm working on now.

ageller commented 1 year ago

OK. That method with events worked! The current version is ready for review by @agurvich . (Then we should discuss the remaining questions from above, except 4 which is solved.)

I tried using the collapseGUIAtStart, but that didn't fully work the way I had hoped. It doesn't collapse, but it also doesn't go back to the same exact menu screen as before. I think for now I'll leave the menu collapsed.

This test also revealed some strange "ghosting" of the text from the previously opened menu (regardless of collapseGUIAtStart). You may be able to recreate it if you (i) have the menu, (ii) send new settings, (iii) look at the main/general menu. (I'll probably have to show you.)

agurvich commented 1 year ago

Ah yeah, as far as I know there's no way to get it back to where it was. I think we'd have to build something that saves/reads GUI state trees.

ageller commented 1 year ago

A thought for "selected data": in addition to having a physical selection region, it might be nice to have the ability to get all particles that are visible on screen, for instance if the user applies some filters and wants to know what remains. (Of course they could download the settings and then apply the filters to their data, but this saves that step.) This should be more straightforward to code up than the selection region, so maybe is the first thing to try for downloading data.