-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
MRG, ENH: Add a screenshot button to the notebook 3d backend #8708
MRG, ENH: Add a screenshot button to the notebook 3d backend #8708
Conversation
did you consider using a dialog box to be able to save the screenshot where you want on disk? see eg https://pypi.org/project/ipyfilechooser/ just a quick google search... |
I considered it but this solution is simple and does not add any dependency. |
Would be good to add some "interaction" tests in |
After e3198f7, there is a tool bar for standard 3d plots too: |
Known issue When I resize the page in my web browser, the 3d viewer and the matplotlib widget don't scale the same way leading to issues when I save the screenshot. For example
When everything works fine at |
If this is not a big issue, we can leave this as is otherwise I can mitigate by using the standard screenshot function but the matplotlib figure won't be present. |
I would write a mne.viz.utils.concatenate_images that allows you to pass a list of array/images and concatenates them vertically (or horizontally, could be an axis argument that must be 0 or 1) padding and centering the smaller one(s) as needed. This will require a bgcolor argument, which could be 'auto' to use for example the most common color amongst all 4 corner pixels across all images or something. I've written something like this and needed it multiple times for paper publications and the like so making this public would be useful. We can probably even use it in the publication figure example. |
That looks like a hidpi issue |
Oh no, not again... 😟 |
I'm going to give you a mac so you can test this :)
|
Also at some point we must have broken screenshot size on macOS, on the test locally I get:
So even without the time viewer the correct size is not preserved. :( |
Does it mean that |
At least it doesn't on my macOS system (with HiDPI), and apparently Azure macOS (no HiDPI) agrees:
I can |
Looks like I broke it back in #8082. Do you want to try to fix it? You probably need a macOS system to be able to try different things out effectively so if you don't have one, I can do it |
I looked at #8082 again and I did not see anything obvious except maybe the changes of
You are right. I'll push one last commit but I can't try locally so you can go for it if you have the time |
I got no luck with 930fdcf |
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.
@GuillaumeFavelier feel free to take over again based on my comments, but in theory in the meantime the CIs should be green
mne/viz/_brain/_brain.py
Outdated
fig = self.mpl_canvas.fig | ||
with BytesIO() as output: | ||
# Need to pass dpi here so it uses the physical (HiDPI) DPI | ||
# rather than logical DPI when saving | ||
fig.savefig(output, dpi=fig.get_dpi(), format='raw') | ||
output.seek(0) | ||
trace_img = np.reshape( | ||
np.frombuffer(output.getvalue(), dtype=np.uint8), | ||
newshape=(-1, img.shape[1], 4))[:, :, :3] | ||
img = concatenate_images( | ||
[img, trace_img], bgcolor=self._brain_color[:3]) |
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.
@GuillaumeFavelier this seems to work for both code paths now on Linux and macOS for me (notebook and standard renderer)
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 awesome news!
" number_of_buttons += 1\n", | ||
" assert number_of_buttons == total_number_of_buttons\n", | ||
" img_nv = brain.screenshot()\n", | ||
" assert img_nv.shape == (300, 300, 3), img_nv.shape\n", |
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.
Notebook always renders non-HiDPI. At some point we should look into how to enable HiDPI support in the notebook -- basically we need the background renderer to be ratio
(usually 2) times the size, and we need to tell it to display the image in native resolution (rather than treating physical and logical pixels the same). I suspect this is supported at some level in browsers, but TBD whether ipyvtk_simple allows setting this display resolution factor (i.e., to make images that are 600x600 pixels show up in a 300x300 logical pixel HTML element). Could be a cool PR there. Then we'd need a way to probe the physical-to-logical pixel ratio. Ideally this would come from the notebook but if not, we can quickly import Qt and query the screens and take a best guess...
" img_v = brain.screenshot(time_viewer=True)\n", | ||
" assert img_v.shape[1:] == (300, 3), img_v.shape\n", | ||
" # XXX This rtol is not very good, ideally would be zero\n", | ||
" assert_allclose(img_v.shape[0], img_nv.shape[0] * 1.25, err_msg=img_nv.shape, rtol=0.1)\n", |
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.
HiDPI aside, on Linux I was getting values that were off by ~7%. It would be good if this were as good as the non-notebook backend (which uses atol=3
instead of the rtol=0.1
here).
" finally:\n", | ||
" plt.interactive(old)\n", | ||
"\n", | ||
"with interactive(False):\n", |
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.
I'm not sure how critical the iteration over interactive levels was so I killed it to save time now that this isn't marked slowtest
.
@agramfort what is your I'll push a fix to deal with it, but shame on you for having a non-standard env... |
@agramfort feel free to try this, works for me on macOS in pure Python and Jupyter with PyQt5 < 5.14 (5.13.2) and >= 5.14 (5.15.2) |
# (e.g., macOS w/Qt 5.14+ and VTK9) then things won't work, | ||
# so let's just calculate the DPI we need to get | ||
# the correct size output based on the widths being equal | ||
dpi = img.shape[1] / fig.get_size_inches()[0] |
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.
Thank you for this @larsoner
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.
It also works for me on Linux in pure Python and Jupyter
thanks @GuillaumeFavelier success !!! |
There is actually a button to save effectively plus a text field to change the file name:
I also switched to icon + tooltip because it was a bit cramped with all those descriptions:
It's an item of #8704.