Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Make svg, jpeg and png images resizable in notebook. #1832

Merged
merged 4 commits into from

6 participants

@ellisonbg
Owner

This addresses #1193. All images are now resizable in the notebook by dragging handle in the lower R corner of the image. I am having problems with svg images not working properly, but I don't know enough about svg to fix this this second. Wondering if any of the matplotlib devs could help on this.

@minrk
Owner

Do we really want to allow resizing raster images? Zooming looks pretty horrible.

@fperez
Owner

While I agree it's not perfect, I still think it's a useful capability: say giving a talk you may need to quickly resize an image so it fits on the projector...

@minrk
Owner

Fair point. I would at least add the autoHide: true option, so it isn't always drawing the resize handle on all your figures.

@fperez
Owner

And I think the svg issue does need to be fixed before we can merge this. It also doesn't work for me...

@ellisonbg
Owner
@minrk
Owner

I agree that rezooming matplotlib figures with the default dpi looks
pretty bad. But there is no reason a user couldn't 1) have a nice
high-res image or 2) set matplotlib to generate higher res images.

In that case, you should set maxHeight/Width to the natural height/width. Aside from preventing ugliness, this lets gives a mechanism for restoring to 'no-zoom', which this currently lacks (and I think is critical).

@ellisonbg
Owner

I think I have fixed the svg problem and will push soon.

@ellisonbg
Owner

What a crazy pain. I thought I had the svg resizing working properly now in FF+Webkit, but that was an illusion. I have pushed it anyways. The reason is rather subtle:

  • Unlike <img> tags, <svg> tags cannot be directly made resizable. Thus we have to wrap it in a <svg> and do extra work to keep the <svg> and <div> sizes in sync. This means I have to find out the initial size of the <svg> and set the <div> to that size. For matplotlib generated svg, I can do that because they set the width and height attributes of the svg in points. I can read those and use them to set everything. The only thing that doesn't work for matplotlib generated svg it setting minHeight and minWidth. This doesn't work because they have to be in pixels, but there is no reliable way of getting those numbers.
  • One might think that you could simple use the width and height jQuery methods to get the initial size of the <svg> in pixels. In Chrome that works, but in FF they both return 0.

The current version works fine with svg tags that have their width and height attributes set (matplotlib does). But those attributes are not required, so the following fails

from IPython.core.display import SVG

SVG("""<svg>
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red"/>
</svg>""")

I see two options:

  1. We can try to figure out how to get the intial size of the <svg> in pixels.
  2. We can give up.
  3. We can simply make up default sizes if the <svg> tag doesn't have the attributes.
@minrk
Owner

That's rough. I think 3. is right out, because we don't want to be changing the sizes of SVGs from their natural defaults unnecessarily.

@ellisonbg
Owner

Yes, 3 is bad because we would have to guess at what the correct aspect ratio was - and we would most often be wrong.

@Carreau
Owner

Do you think we could add an option to set the curent matplotlib rc figwidth and figheight to the current size of the resized image, so that you could choose your size and rerun the plotting cell to have a nice image ?

Or maybe this would be better as a plotting function decorator.

@takluyver
Owner
@ellisonbg
Owner

The other option is that we could just remove this ability for SVG images for now.

@fperez
Owner

My vote would be for not having the svg resizing unless it can work well. I prefer not having a feature than having a half-broken one. It's a bummer that you've put so much effort into it without a satisfactory solution, but it just seems that life in the SVG garden is still not a very pleasant one...

@ellisonbg
Owner

OK, I will pull out the svg code and leave the logic there for png and jpeg.

@mcelrath

I just tried this in combination with my SVG patch #1881. The result is that figures are much larger than the browser window, because I cranked up the figure.figsize to prevent pixelation in the SVG file.

Perhaps instead we can use width: 100% and use your code to compute the appropriate height, as the SVG enforces a fixed aspect ratio. That makes everything look really nice.

@ellisonbg
Owner

Let's do the following. I am going to remove the svg code handling from this branch and why don't you add it to yours. It probably makes sense to do all of the svg stuff in one place. That way, this branch can be merged to get the resizable jpeg/png in place.

@mcelrath

Ok. I can't figure out how to get the width of the enclosing output area, to set the initial svg width/height. Any suggestions?

element.parent.offsetWidth etc don't seem to work.

@ellisonbg
Owner
@ellisonbg
Owner

Here is the code I removed for resizing SVG in case we need it later:

https://gist.github.com/2897174

@ellisonbg
Owner

I think this is ready to merge now. The SVG work will be continued in #1881.

@minrk
Owner

The most important part of this UI for me for raster images is that there be an easy mechanism for restoring to zero-zoom. What do you have for that?

@ellisonbg
Owner

Rebased.

@ellisonbg ellisonbg merged commit 2097353 into ipython:master
@minrk
Owner

This merge after rebase made invalid javascript syntax, rendering the notebook as a totally empty page. It was just a missing comma, so I fixed it in the online GitHub editor.

I see no mechanism for restoring zero zoom. Any ideas on that? 99%/101% raster images look quite a lot worse than 100%, so this seems pretty important.

@minrk
Owner

This appears to have caused an additional problem: some fraction of the time (seems ~10%), plots do not appear at all when loading from a file. I would guess this is due to resize() being called while the element is invisible, and thus starting with an initial height of 0.

@ellisonbg
Owner
@ellisonbg
Owner
@minrk
Owner

Can you clarify what you mean by "loading from a file"?

Opening a saved notebook with figures.

@ellisonbg
Owner
@mcelrath
@ellisonbg
Owner
@mcelrath
@ellisonbg
Owner
@mcelrath

I moved the SVG resize code to my branch #1881, fixed some things, and added doubleclick-to-restore-size as requested by Min. You may want to copy that for png/jpeg too. I'm quite happy with the result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
12 IPython/frontend/html/notebook/static/js/outputarea.js
@@ -282,14 +282,22 @@ var IPython = (function (IPython) {
OutputArea.prototype.append_png = function (png, element) {
var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
- toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
+ var img = $("<img/>").attr('src','data:image/png;base64,'+png);
+ img.load(function () {
+ $(this).resizable({'aspectRatio': true, 'autoHide': true})
+ });
+ toinsert.append(img);
element.append(toinsert);
};
OutputArea.prototype.append_jpeg = function (jpeg, element) {
var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
- toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
+ var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
+ img.load(function () {
+ $(this).resizable({'aspectRatio': true, 'autoHide': true})
+ });
+ toinsert.append(img);
element.append(toinsert);
};
View
12 IPython/frontend/html/notebook/static/js/utils.js
@@ -126,12 +126,24 @@ IPython.utils = (function (IPython) {
DOWN : 40,
};
+
+ points_to_pixels = function (points) {
+ // A reasonably good way of converting between points and pixels.
+ var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
+ $(body).append(test);
+ var pixel_per_point = test.width()/10000;
+ test.remove();
+ return Math.floor(points*pixel_per_point);
+ }
+
+
return {
uuid : uuid,
fixConsole : fixConsole,
keycodes : keycodes,
grow : grow,
fixCarriageReturn : fixCarriageReturn
+ points_to_pixels : points_to_pixels
};
}(IPython));
Something went wrong with that request. Please try again.