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

Directly save image (without opening in browser) #880

Closed
robinrosenstock opened this issue Nov 14, 2017 · 38 comments
Closed

Directly save image (without opening in browser) #880

robinrosenstock opened this issue Nov 14, 2017 · 38 comments

Comments

@robinrosenstock
Copy link

Referencing: #596 and #564
For saving an image you have first open it in the browser,
for example in jupyter: plotly.offline.plot(fig, filename=filename, image='svg')
opens a new tab and download immediately, when adding auto_open=False:
plotly.offline.plot(fig, filename=filename, image='svg', auto_open=False)
then it will not open a new tab, but it will not download the image as well.
The jupyter command was only an example, of course this functionality would be more useful in text only scripts (python, javascript, R).

@cldougl
Copy link
Member

cldougl commented Nov 15, 2017

As previously mentioned in the issues you've referenced this feature is not possible at this time and we do not have a plan to add it in the immediate future. You can always submit feature requests here: https://support.plot.ly/libraries/python

This is now supported 🎉 https://medium.com/@plotlygraphs/plotly-py-end-of-summer-updates-5422c98b9058

@jwhendy
Copy link
Contributor

jwhendy commented Dec 26, 2017

@cldougl since this is still open, could we do a quick thought experiment? Let's say the plotly team did take this on as a feature request... could someone sketch out the potential implementation?

Like would you need to do this in pure python? Would you get the div contents for the plot and call plotly.js locally (via python, or via js)? Would you use the existing mechanism and just figure out how to "open" the generated html behind the scenes (I think that's what the R version is doing with export())?

As @geniusupgrader referenced, there's issues on this already, and I'll add that there's activity on the forums and at least a couple of posts on SO about this.

The community wants this. I want this... but I'm newer to python and don't think what I might try intuitively would match what those behind the library would actually do to make this a reality.

So, my pitch is that if you could help the community to understand what a possible solution might look like instead of just closing issues outright, perhaps they would do it for you! Just an [optimistic, idealist] thought :)

I submitted a feature request to this effect though it appears plotly screens them as mine isn't showing up.

@jwhendy
Copy link
Contributor

jwhendy commented Dec 27, 2017

I just fiddled with this a bunch and I think there are at least two possible methods:


  1. use selenium to screenshot the page.

I demonstrated the feasibility of this here. I tested minimally, so I'm not sure what the limitations are. I was surprised that basically copying bokeh's method out of the box worked with respect to size/crop.


  1. utilize broken_symlink's method of simulating the button click.

Ultimately, I didn't actually need to use a button click. We just need to specify image=foo and image_filename=bar to plotly.offline.plot() which auto-downloads upon opening. It turns out the opening has to be in something other than phantomjs as it doesn't do downloads well or maybe even at all. That said, both ways work. I demonstrated the feasibility of this here.


Unknowns abound: os portability, plot type/size limitations, other system variables, dependency availability across os's and how complex it would be to detect them.

Still... I'm feeling pretty good. I consider myself not at all good at python and I did this in maybe 4-6hrs of researching, trial, and error.

Would you consider implementing something like this if you tell me where it could go in the api structure and I create a PR?

@robinrosenstock
Copy link
Author

Thanks for your contribution @jwhendy!
I don't like your first solution to take a screenshot with selenium, because I'm mostly interested in downloading plots in vector formats, notably .svg.
And your second solution is not really ideal also, because as you already mentioned, there are many dependencies when using an browser automation software. For example I had first to install selenium, pillow, pyvirtualdisplay, Xvfb and ChromeDriver to get it to work.
Also I've tried to download a .svg but get this traceback:

---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-3-73a37a4afc39> in <module>()
     14 display.stop()
     15 
---> 16 image = Image.open('{}.svg'.format(os.path.join(dload, fname)))
     17 image

~/miniconda3/lib/python3.6/site-packages/PIL/Image.py in open(fp, mode)
   2570         fp.close()
   2571     raise IOError("cannot identify image file %r"
-> 2572                   % (filename if filename else fp))
   2573 
   2574 #

OSError: cannot identify image file '/home/robin/Downloads/plotly-fruit-plot.svg'

Is this a limitation of selenium that it can't handle .svg or similar?
Personally, I think, it must be possible to convert the generated (and structured) html to some other file format without the help of a browser?

@ghtmtt
Copy link

ghtmtt commented Dec 27, 2017

thanks for raising this issue again. I tried @jwhendy methods but in the way I'm using Plotly Python API it seems not feasible (https://github.com/ghtmtt/DataPlotly).

If someone is taking care to find a solution, please consider me as help/tester. Having also this feature would be awesome

@jwhendy
Copy link
Contributor

jwhendy commented Dec 27, 2017

@geniusupgrader thanks for the feedback. I certainly don't love either of them myself, but I'm not sure how to go about it otherwise.

Personally, I think, it must be possible to convert the generated (and structured) html to some other file format without the help of a browser?

Indeed... one would think so. I will say, from my noob perspective these are notable data points:

In other words, if this is possible, it's intriguing that two legit solutions out there did not use it and opted for these sort of painful and dependency-heavy workarounds.

Good feedback on the svg aims, as if that was a big deal for others it certainly helps steer the ship in a particular direction. Do you just prefer for usage/non-lossy, or perhaps you modify them later in some vector editor?

Re. the error, it appears it's just a limitation of pillow I wasn't aware of. Technically, it's not a true requirement; I was using that code just to show that the plot file was there in image format.

Try opening in a different viewer (or browser) and you should see it! Also, from looking around, it seems I could have just done this instead:

from IPython.display import SVG
SVG('{}.svg'.format(os.path.join(dload, fname)))

@robinrosenstock
Copy link
Author

Do you just prefer for usage/non-lossy, or perhaps you modify them later in some vector editor?

it should be an end product, so no modification after the download is needed

@jwhendy
Copy link
Contributor

jwhendy commented Dec 27, 2017

Another idea was to run the js locally, as the functionality exists, but I don't know how to trigger it without loading the page. This is the magic in the file that auto-triggers the download:

<script>function downloadimage(format, height, width, filename) {var p =
    document.getElementById('div_uuid');Plotly.downloadImage(p,
    {format: format, height: height, width: width, filename:
      filename});};{downloadimage('svg', 600, 800, 'foo');}
</script>

Looking around for how to call js directly from python led me to py_mini_racer, but for reasons beyond my understanding, plotly is not loadable like that; it's a "browser package".

There is a node interface, but it requires providing a username and api key, so it defeats the purpose of offline image export.

@geniusupgrader

it should be an end product, so no modification after the download is needed

I don't understand. I wanted to know "why svg"? My guesses were either that it won't pixelate regardless of scale, or that you're editing it. To ask another way, at a high enough res, why isn't png okay?

@ghtmtt
Copy link

ghtmtt commented Dec 27, 2017

@jwhendy I step into the discussion "why svg". Having the chance to heavily editing the plot after it has been made (like a simple legend moving in a whatever other position) is a nice stuff to have.

@robinrosenstock
Copy link
Author

@jwhendy vector format is (most of the times) definitely better for plots, cause scalable and such

@jwhendy
Copy link
Contributor

jwhendy commented Dec 27, 2017

In lieu of something better, feel free to tinker around with my export_img fork. It adds a save_img argument to plotly.offline.plot(). I leveraged the fact that image='type' makes the page auto-download. It allows more formats than screenshotting with selenium and might be less dependency heavy, as I suspect more users have one of the selenium webdrivers available vs. phantomjs.

You'd use it like this:

import plotly
import plotly.graph_objs as go

data = [go.Bar(x=['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries'],
               y=[5, 3, 4, 2, 4, 6])]

html_file = 'plotly-fruit-plot.html'
fname = 'plotly-fruit-plot'

plotly.offline.plot(data, filename=html_file, auto_open=False,
                    image_width=1280, image_height=800,
                    image_filename=fname, image='svg', save_img=True)

Some ideas for improvement/testing:

  • does this work at all on Windows?
  • allow (or detect) other browser backends in case chromium is not available
  • same for pyvirtualdisplay (does this work on other platforms?)
  • add checks I'm not thinking of... currently you're warned if auto_open=True (will download anyway and you'll get a duplicate) and if image=None (won't add the plot generation script)
  • I could see making this move the image file to one's working dir after saving to ~/Downloads
  • handle redundant downloads/duplicate names, which gives stuff like filename (2).png
  • other stuff I'm not thinking of

@ghtmtt sorry this might not work for your case! I'm pretty stumped other than what I've found :(

@geniusupgrader understood re. svg, though I take this to mean you're primarily concerned with appearance quality? In other words, given a png of whatever dpi/size you wanted, is there any difference for your use case? I like svg in theory as well, but in practice as long as the png isn't ugly, I never notice the difference. In any case, the current method just exposes the image='type' options that plotly already gives us, so hopefully that works!

@robinrosenstock
Copy link
Author

@jwhendy thanks again for your effort. In regard to png vs. svg: you're right, if post-editing is not needed and the png is high quality, then the format is not that important (except maybe the filesize, which depends on the use case wheter png or svg will be less in filesize or better manageable) That said I still favor svg.

@jackparmer
Copy link
Contributor

jackparmer commented Dec 30, 2017

Happy holidays @jwhendy @bluprince13 @geniusupgrader !

Just wanted to let you know that Plotly is aware of this issue.

We're currently testing a new image server on plotly cloud against more than 5 million user generated charts. If this goes well, we plan to open-source and package this server for easy, cross-platform use by Python users. Timeline is hopefully late February for a release.

since this is still open, could we do a quick thought experiment? Let's say the plotly team did take this on as a feature request... could someone sketch out the potential implementation?

TLDR; We're using Electron for the new image server. The previous image server on Plotly Cloud and Plotly On-Premises used Batik for vector images (PDF, SVG, EPS) and node webkit for raster images (PNG, JPEG). The Electron rewrite has simplified things to an extent where we feel comfortable maintaining this as a pip installable library for Python users without (hopefully) a nightmare of issues related to complexities you've already mentioned - cross-platform support, font families, sprawling dependencies, etc.

Stay tuned. This is taking a while, but we are taking the time to do it right. I'll try to update this issue as things get closer!

@jackparmer
Copy link
Contributor

👋 @jwhendy @bluprince13 @geniusupgrader and others Googling into this issue - Wanted to share an update since it's been 2 months:

  1. The Electron image server mentioned above has been open-sourced here
  2. We've successfully tested this image server with more than 5M user generated charts on plot.ly. It is now generating all plot.ly chart images and chart thumbnails, so we feel comfortable with its robustness.
  3. We are still working on a Python-specific distribution that will be simple to install and does not spawn nightmares of installation issues 👻 If you're comfortable with both node and Python, you can install plotly/image-exporter today and run plotly-graph-exporter from Python (with e.g. subprocess). If you find a way to bundle all of this into a pip package, we'd love to hear from you. We'd like to ultimately hide all of the Electron inner workings from the typical Python user.

@DuaneNielsen
Copy link

Download and setup a dedicated process so I can export to png?
Seems a bit of overkill no? Most other plotting librarys just export to png directly, with no web browser or other process needed in between. In any case... 3 issues later, I think the message is clear. Plotly is a html / online web tool only. I will look elsewhere for use cases that require images as output.

@eagleEggs
Copy link

eagleEggs commented May 9, 2018

FYI this is now possible with one line after setting your data and figure:

edit [051518]: actually this doesn't work :/

data = Data([tasks, tasks2, tasks3])
fig = Figure(data=data, layout=layout)
offline.iplot(fig, filename='graph', image='png')

It saves to the /Downloads directory.

https://gist.github.com/amaIgam/c441ffa88a90a8a360d771fae3ba2d8e

Looking for a way to change that default directory currently...

@ghtmtt
Copy link

ghtmtt commented May 15, 2018

@eagleEggs not exactly. In your example it will work with an IPython notebook, not as pure python code

@eagleEggs
Copy link

True. A bit later I realized that the result was either from a Jupyter test I did, or from Firefox alerting that there was already a window open and it initiated the download anyway. Unfortunately the latter is not easily controllable or stable... I was hoping that someone would answer with the solution at this point though :) For now I switched my plotting to matplotlib.

@mwouts
Copy link

mwouts commented May 15, 2018

Hello - I've been watching this, and #483 for a while. I like very much the new perspective of allowing offline image export in plotly. Also the development on orca is great news !

@jwhendy, you mention above the possibility to use phantomjs for taking 'webshots' of html files. This is how the R community has implemented the export, and this is how I do my offline exports to both PNG and PDF (python port at https://github.com/mwouts/plotly_offline_export). The approach cannot compete with orca, but still it has very few dependencies (just phantomjs) and it works pretty well - only issue I found over hundreds of plots exported was with long legends which were truncated.

@jwhendy
Copy link
Contributor

jwhendy commented May 15, 2018

@mwouts Agreed, and mentioned up a bit. It's been a while, but apparently I got it working on this fork.

@mwouts
Copy link

mwouts commented May 16, 2018

@jwhendy, yes I had a look at your plotly fork (I also had seen this attempt based on selenium).
Do you think you could replace the dependency on Chrome with PhantomJS ? I'm asking because at work we use an older linux distribution, which unfortunately means that I'm not able to install any of chrome, chromium, modern firefox, etc. (but: phantoms from conda-forge does work).

@jackparmer, yesterday I tried to install orca with npm install -g electron orca, but electron would not start (and I'm not a node expert !). Above you mention that plans are to offer easy install from python, that's great. Do you have an ETA for that ? Do you think that a phantomjs solution (PNG and PDF export, tested on linux only for now, cf. my previous post) would be worth including in plotly meanwhile ? Thanks

@chriddyp
Copy link
Member

Do you think that a phantomjs solution (PNG and PDF export, tested on linux only for now, cf. my previous post) would be worth including in plotly meanwhile ?

In our experience, Electron is going to be much more reliable. We'd also like to officially support only a single way to create these images and that is going to be through our Orca project.

Above you mention that plans are to offer easy install from python, that's great. Do you have an ETA for that ?

Soon! For now, check out the latest releases of Orca here: https://github.com/plotly/orca/releases

@robinrosenstock
Copy link
Author

It seems that orca is the recommended way to save images from plotly graphs and the like. But I dunno if I should close this issue? Thoughts?

@bluprince13
Copy link

Well, I think the issue should only be closed when plotly provides its own method for saving plots - as long as that's in the long-term plan.

@jackparmer
Copy link
Contributor

Well, I think the issue should only be closed when plotly provides its own method for saving plots - as long as that's in the long-term plan.

Yeah, it seems like what folks really want here is for Orca to be bundled with plotly.py, so that they don't have to install 2 different things.

@jmmease was taking a look at this for the plotly.py 3.0 release, though it would likely happen after this release.

@jonmmease
Copy link
Contributor

My current thought is that we'll add a new image export entry point to plotly.py that will require orca to be installed on the system (And if it's not installed we'll raise an error message with instructions). pip users will follow the instructions on the orca README (install either using npm or using the particular OS installer). For conda users, can build an orca package and eventually make it a dependency of the plotly.py conda package (unless we decide orca is just too large to be a full dependency, then we'll leave it as a separate installation step).

@PlotlyUser2
Copy link

Well, I've just tried to create an image by using orca within the python api. However this code doesn't produce any image:

import subprocess

json_res = json.dumps({"data": [{"y": [1,2,1]}]})
command = f"orca graph {json_res} -o fig.png" 
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=True)

#Launch the shell command:
output = process.communicate()

print (output[0])

It returns: done with code 1 in 2.59 ms - failed or incomplete task(s)

What am I doing wrong here?

@chriddyp
Copy link
Member

chriddyp commented Jun 2, 2018

Well, I've just tried to create an image by using orca within the python api. However this code doesn't produce any image:

Would you mind opening an issue in the Orca repository? https://github.com/plotly/orca/issues

@PlotlyUser2
Copy link

@chriddyp

Done: plotly/orca#95

@jonmmease
Copy link
Contributor

Done in 3.2.0 🎉 See https://plot.ly/python/static-image-export/

@ghost
Copy link

ghost commented Jun 15, 2019

it's not done. orca is not working, it times out when saving images and there are several issues about it.
522: client socket timeout
We are just trying to save image of a plot. It's extremely basic. It cannot be done without opening a browser! So am I going open browser in Ubuntu LTS? So I installed a plugin called orca and try to show executable paths, and then as a python programmer I'm trying run node based electron servers and docker containers and open browsers and catch pictures using selenium and at the end nothing works anyway. Let me show you no bs way to save a image as it's in matplotlib.

plt.savefig('image")

@FromTravis
Copy link

hi Canercak, did you find a solution which works to saving image offline ?

thanks!

@nicolaskruchten
Copy link
Contributor

@FromTravis are you encountering issues? static image export generally works very well using Orca. Some people have trouble installing it but in my experience the vast majority of the time fig.write_image() "just works".

@FromTravis
Copy link

FromTravis commented Sep 6, 2019

Hi Nicolas, well no.
I’m on windows with a firewall ( within a company that’s why i need full offline)
when using
!orca graph “{ “data”: [{“y”: [1,2,60]}] }” -o fig.png
on a command window this creates a fig.png file and the conda install says all is ok.

I have followed the figure creation from
https://plot.ly/python/static-image-export/#write-image-file

any help welcome ! thx

conda install -c plotly plotly-orca
Collecting package metadata (current_repodata.json): done
Solving environment: done

All requested packages already installed.


error message when calling fig.write_image() :
ValueError Traceback (most recent call last)
in
1 import plotly.io as pio
2 pio.orca.config
----> 3 pio.write_image(fig, ‘images/fig1.png’)

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in write_image(fig, file, format, scale, width, height, validate)
1703 # Do this first so we don’t create a file if image conversion fails
1704 img_data = to_image(
-> 1705 fig, format=format, scale=scale, width=width, height=height, validate=validate
1706 )
1707

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in to_image(fig, format, width, height, scale, validate)
1480 # Make sure orca sever is running
1481 # -------------------------------
-> 1482 ensure_server()
1483
1484 # Handle defaults

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in ensure_server()
1342 # Validate orca executable
1343 if status.state == “unvalidated”:
-> 1344 validate_executable()
1345
1346 # Acquire lock to make sure that we keep the properties of orca_state

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in validate_executable()
1041 executable=config.executable,
1042 formatted_path=formatted_path,
-> 1043 instructions=install_location_instructions,
1044 )
1045 )

ValueError:
The orca executable is required to export figures as static images,
but it could not be found on the system path.

Searched for executable ‘C:\ProgramData\Anaconda3\orca_app’ on the following path:
C:\ProgramData\Anaconda3
...
C:\ProgramData\Anaconda3\scripts
C:\ProgramData\Anaconda3\orca_app

If you haven’t installed orca yet, you can do so using conda as follows:

$ conda install -c plotly plotly-orca

Alternatively, see other installation methods in the orca project README at
https://github.com/plotly/orca.

After installation is complete, no further configuration should be needed.

If you have installed orca, then for some reason plotly.py was unable to
locate it. In this case, set the plotly.io.orca.config.executable
property to the full path of your orca executable. For example:

plotly.io.orca.config.executable = '/path/to/orca'

After updating this executable property, try the export operation again.
If it is successful then you may want to save this configuration so that it
will be applied automatically in future sessions. You can do this as follows:

plotly.io.orca.config.save()

If you’re still having trouble, feel free to ask for help on the forums at
https://community.plot.ly/c/api/python

@nicolaskruchten
Copy link
Contributor

OK, so this error message is saying that the orca executable isn't on your system path... Where is orca installed?

@FromTravis
Copy link

Orca cmd is on C:\ProgramData\Anaconda3
Orca.exe is on C:\ProgramData\Anaconda3\orca_app

and i ve added both to PATH.

@FromTravis
Copy link

It does work on my Mac though .
Not sure what creates the problem for windows. It seems to be a quite frequent error for many people .
If you happen to have an idea i m definitely interested . Thanks again !

@FromTravis
Copy link

I have uninstalled orca and reinstalled it.

(base) C:\Users\P885880\ml>conda install -c plotly plotly-orca psutil requests
Collecting package metadata (current_repodata.json): done
Solving environment: done

All requested packages already installed.

(base) C:\Users\P123456\ml>where orca
C:\ProgramData\Anaconda3\orca.cmd
C:\Users\P123456\AppData\Local\Programs\orca\orca.exe

When i try to save the image following the plotly orca exemple in jupyter i get:

ValueError Traceback (most recent call last)
in
27 ))
28
---> 29 fig.write_image("fig1.png")
30 #fig.show()

C:\ProgramData\Anaconda3\lib\site-packages\plotly\basedatatypes.py in write_image(self, *args, **kwargs)
2686 import plotly.io as pio
2687
-> 2688 return pio.write_image(self, *args, **kwargs)
2689
2690 # Static helpers

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in write_image(fig, file, format, scale, width, height, validate)
1704 # Do this first so we don't create a file if image conversion fails
1705 img_data = to_image(
-> 1706 fig, format=format, scale=scale, width=width, height=height, validate=validate
1707 )
1708

C:\ProgramData\Anaconda3\lib\site-packages\plotly\io_orca.py in to_image(fig, format, width, height, scale, validate)
1613 Unfortunately, we don't yet know of an easy way to install poppler on Windows.
1614 """
-> 1615 raise ValueError(err_message)
1616
1617

ValueError:
The image request was rejected by the orca conversion utility
with the following error:
403:

<title>Connection Failed Message</title> <style type="text/css"> .Body { font-family: Arial, Helvetica, sans-serif;color: #666;width: 100%;} .ExceptionTable {width: 78%;margin-left: 10%;margin-top: 10%;border: thin solid #999;padding: 1%;} .Body table .Headline { color: #FFF; background-color: #F00;} .Body .ExceptionTable table .Subtitle { color: #F00; background-color: #CCC; font-weight: bold;} .Body .ExceptionTable table .exceptionsummary { color: #000;} .Body .ExceptionTable table .techinfo {font-size: x-small; } </style>
Global Information Services
EXCEPTION
Connection Failed Message
Description
localhost:60051
Connection Failed Message.
Help
Check if the address is correct or try again in a few minutes.
Technical Details
Generated by: /Common/usf5prx-muc-vip2 3128
Client-IP: 10.10.100.00
Host: localhost:60051
Time: 09/09/2019:15:48:12

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

No branches or pull requests