-
Notifications
You must be signed in to change notification settings - Fork 73
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
plugin active status based on pings #2295
Conversation
<jupyter-widget :widget="trayItem.widget"></jupyter-widget> | ||
<jupyter-widget :widget="trayItem.widget" v-if="state.tray_items_open.includes(index)"></jupyter-widget> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is needed so that the same logic works for plugins in the tray (otherwise the plugin element when collapsed will continue sending pings)
74df202
to
b5f52ad
Compare
jdaviz/components/tray_plugin.vue
Outdated
sendPing() { | ||
if (!this.$el.isConnected) { | ||
return | ||
} | ||
this.$emit('update:plugin_ping', Date.now()) | ||
setTimeout(() => { | ||
this.sendPing() | ||
}, 200) // ms | ||
} | ||
}, | ||
mounted() { | ||
this.sendPing(); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is where any instance of the UI sends pings back to python (by updating the plugin_ping
traitlet with the current timestamp) at regular intervals until it is no longer connected.
jdaviz/core/template_mixin.py
Outdated
@observe('plugin_ping') | ||
def _plugin_ping_changed(self, *args): | ||
if self.plugin_ping == 0: | ||
# initial traitlet state | ||
return | ||
# we've received a ping, so immediately set plugin_opened state to True | ||
if not self.plugin_opened: | ||
self.plugin_opened = True | ||
|
||
if self._inactive_thread is not None and self._inactive_thread.is_alive(): | ||
# a thread already exists to check for pings, the latest ping will allow | ||
# the existing while-loop to continue | ||
return | ||
|
||
# create a thread to monitor for pings. If a ping hasn't been received in the | ||
# expected time, then plugin_opened will be set to False. | ||
self._inactive_thread = threading.Thread(target=self._watch_active) | ||
self._inactive_thread.start() | ||
|
||
def _watch_active(self): | ||
# expected_delay_ms hould match value in setTimeout in tray_plugin.vue | ||
# NOTE: could control with a traitlet, but then would need to pass through each | ||
# <j-tray-plugin> component | ||
expected_delay_ms = 200 | ||
# plugin_ping (ms) set by setTimeout in tray_plugin.vue | ||
# time.time() is in s, so need to convert to ms | ||
while time.time()*1000 - self.plugin_ping < 2 * expected_delay_ms: | ||
# at least one plugin has sent an "alive" ping within twice of the expected | ||
# interval, wait a full (double) interval and then check again | ||
time.sleep(2 * expected_delay_ms / 1000) | ||
|
||
# "alive" ping has not been received within the expected time, consider all instances | ||
# of the plugin to be closed | ||
self.plugin_opened = False | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the python-side, the first ping sets plugin_opened = True
and then starts a thread which will continually check to see if a new ping has been received in the expected interval... once a ping is failed to be received, plugin_opened
is set to False
and the thread terminates. If the plugin ever re-opens, a new thread is created.
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #2295 +/- ##
==========================================
- Coverage 90.94% 90.62% -0.32%
==========================================
Files 152 152
Lines 17140 17370 +230
==========================================
+ Hits 15588 15742 +154
- Misses 1552 1628 +76
☔ View full report in Codecov by Sentry. |
* mosviz.test_parsers was failing with an interpolation error on initial load
The vast majority of uncovered lines are not possible to cover without an actual UI to send the JS pings... 🤔 |
temporarily moving back to draft until addressing the following ideas/suggestions from @mariobuikhuizen offline:
|
* other tab, minimized window, window completely behind window, etc. * prevents "flickering" caused by browser throttling of hidden windows
@javerbukh - made a few changes since your approval, but hopefully nothing too major/controversial 🤞 |
* to avoid unnecessary traitlet syncing
@kecnry Still works well for me! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compass plugin appears to be broken in Imviz. (I rebased this PR on top of current main when I tested it.) I loaded images in the Imviz example notebook with JWST data and now Compass no longer shows any matplotlib image with the compass on it.
Press "l" on Line Profile in Imviz while the plugin is opened also no longer works for me. Nothing happens but there is also no error message in snackbar nor browser dev console.
Markers plugin seems to work but I kinda want the markers to still show when I hide the plugin to disable the "m to mark" feature. I feel like visibility of the existing markers should be controlled separately from this feature. Though perhaps you can also argue this is out of scope, not sure.
Out of scope but I wondered while testing this PR: Do we have plan to tie the Markers plugin to astrowidgets markers API? They don't seem to talk to each other.
assert la_plugin.selected_line_redshift == -1.0 | ||
assert_allclose(la_plugin.selected_line_redshift, 0.4594414354783614) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this independently confirmed? I am not familiar with this workflow to confirm this myself.
expected_delay_ms = 200 | ||
# plugin_ping (ms) set by setTimeout in tray_plugin.vue | ||
# time.time() is in s, so need to convert to ms | ||
while time.time()*1000 - self._ping_timestamp < 2 * expected_delay_ms: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parenthesis make it easier for me to read
while time.time()*1000 - self._ping_timestamp < 2 * expected_delay_ms: | |
while (time.time()*1000 - self._ping_timestamp) < (2 * expected_delay_ms): |
Also, why repeating 2 * expected_delay_ms
so many times instead of setting expected_delay_ms = 400
and be done with it?
Furthermore, why not define the expected delay in seconds to save you some unit conversion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this way expected_delay_ms
matches the value in javascript (as the comments state). We could have a separate variable that converts to seconds and adds the multiplicative factor, but as we tweak the delays, we might want different factors in the for loop and the sleep 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But right now they are the same, and seems like a lot of repeated calculations that are unnecessary. 🤷
This is concerning - are you on windows? Can someone else with windows confirm (@duytnguyendtn)? |
WSL2 (Debian) on Windows 10. But the browser is Chrome on Windows. They have worked before. |
by not passing to template, this then means all plugins should listen to is_active and never plugin_opened
tested on the platform and didn't seem to have any issues with respect to network lag causing flickering (except the popout windows fail in general, but that's unrelated to this PR) @pllim - compass and line profile have been updated to use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compass and Line Profile both work now on Imviz. Thanks!
Description
This pull request is an alternative to #2293 and controls the live-previews (as well as keypress events and any other optimizations) based on determining whether any instance of a particular plugin is opened by having the individual instances send "I'm alive" pings back to python at a regular interval. Once these pings are no longer received, the plugin (on the python side) is determined to be inactive.
NOTE: this can result in flickering if the intervals are not tuned correctly, so it would be useful to have multiple devs/machines/data to test and make sure the interval and interval-factor are robustly set here.
Screen.Recording.2023-07-12.at.2.39.58.PM.mov
Lastly, for any plugin for which
plugin.uses_active_status == True
, this exposes thekeep_active
traitlet (and switch in the UI if it is passed to the template) as well as anas_active
context manager that can be used for workflows in the API:Screen.Recording.2023-07-12.at.2.35.56.PM.mov
This PR affects the following plugins:
Development considerations:
self.uses_active_status = True
and pass:uses_active_status="uses_active_status" @plugin-ping="plugin_ping($event)" :keep_active.sync="keep_active"
toj-tray-plugin
(as indicated in the developer docs). It should then observe theis_active
traitlet to control the visibility of any marks.plugin.keep_active = True
orplugin.plugin_opened = True
rather thanplugin.open_in_tray()
as the traitlet is no longer required to be immediately set.Change log entry
CHANGES.rst
? If you want to avoid merge conflicts,list the proposed change log here for review and add to
CHANGES.rst
before merge. If no, maintainershould add a
no-changelog-entry-needed
label.Checklist for package maintainer(s)
This checklist is meant to remind the package maintainer(s) who will review this pull request of some common things to look for. This list is not exhaustive.
trivial
label.