plotly graph not rendering in Jinja2 template - python
Plotly version = '5.12.0'
Jinja2 version = '3.1.2'
I am creating some visuals for a HTML report in python and then emailing them to relevant people. I am using redmail to send the emails.
I am creating a graph using plotly and then converting it to html. This is the code to recreate the graph and produce the html output.
import plotly
import plotly.express as px
from redmail import EmailSender
import jinja2
fig =px.scatter(x=range(10), y=range(10))
graph = fig.to_html(full_html = False, include_plotlyjs ='cdn')
I am then inserting this into my Jinja HTML template which looks as follows
<!DOCTYPE html>
<html>
<body>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
{{graph}}
</body>
</html>
Next, I send the email to the relevant parties using this code
email = EmailSender(
host='smtp.gmail.com',
port=587,
username='email#gmail.com',
password=pwd
)
email.set_template_paths(
html="",
text="",
)
# Create an env
jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(''))
email.templates_html = jinja_env
email.templates_text = jinja_env
email.send(
subject="An example email",
sender="example#gmail.com",
receivers=['example#live.co.uk'],
html_template='template_file.html',
body_params= {'graph':graph}
)
The problem is, when I receive the email, it is completely blank, with no data/visuals in it. I should expect to see the plotly scatter plot, or at the minimum the raw text of the plotly graph html.
I have tried pasting the div elements stored in the graph variable into an online HTML viewer, and the graph appears. I have seen other posts suggesting adding {{graph|safe}} to the graph variable in the jinja template, but that does not work either.
I am completely lost as why it is not appearing/rendering in the Jinja template. It is not a problem with redmail as I have used it in the past for other templating work and have managed to insert HTML tables successfully.
Any help would be greatly appreciated.
EDIT: Here is the output of the graph variable outputted by plotly.to_html()
<div> <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: \'local\'};</script>\n <script src="https://cdn.plot.ly/plotly-2.17.1.min.js"></script> <div id="6a72d392-1672-450b-9d7f-5ad0cd478fd4" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("6a72d392-1672-450b-9d7f-5ad0cd478fd4")) { Plotly.newPlot( "6a72d392-1672-450b-9d7f-5ad0cd478fd4", [{"hovertemplate":"x=%{x}<br>y=%{y}<extra></extra>","legendgroup":"","marker":{"color":"#636efa","symbol":"circle"},"mode":"markers","name":"","orientation":"v","showlegend":false,"x":[0,1,2,3,4,5,6,7,8,9],"xaxis":"x","y":[0,1,2,3,4,5,6,7,8,9],"yaxis":"y","type":"scatter"}], {"legend":{"tracegroupgap":0},"margin":{"t":60},"template":{"data":{"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"heatmapgl":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmapgl"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"geo":{"bgcolor":"white","lakecolor":"white","landcolor":"#E5ECF6","showlakes":true,"showland":true,"subunitcolor":"white"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"light"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"bgcolor":"#E5ECF6","radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","gridwidth":2,"linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white"},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","gridwidth":2,"linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white"},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","gridwidth":2,"linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white"}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"ternary":{"aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"bgcolor":"#E5ECF6","caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"title":{"x":0.05},"xaxis":{"automargin":true,"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","zerolinewidth":2}}},"xaxis":{"anchor":"y","domain":[0.0,1.0],"title":{"text":"x"}},"yaxis":{"anchor":"x","domain":[0.0,1.0],"title":{"text":"y"}}}, {"responsive": true} ) }; </script> </div>
Related
Correct way of hard-coding html code in python script?
I have developed a web-based tool, and currently trying to make it python-launchable. I figured using CEFpython is probably the way to do it. I followed the tutorial here and wrote the following code: from cefpython3 import cefpython as cef import base64 import platform import sys import threading import os HTML_code = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <link href="static/css/main.css" rel="stylesheet" /> </head> <body> <div id="UI"> </div> <div id="container"></div> <script src="static/main.js"></script> <script type="text/javascript"> function defineData(datainput){ console.log("start") data = datainput; var loc = window.location.pathname; var dir = loc.substring(0, loc.lastIndexOf('/')); console.log(loc); console.log(dir); Main(); } </script> </body> </html> """ def html_to_data_uri(html): html = html.encode("utf-8", "replace") b64 = base64.b64encode(html).decode("utf-8", "replace") ret = "data:text/html;base64,{data}".format(data=b64) return ret def main(config): sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error settings = {} cef.Initialize(settings=settings) browser = cef.CreateBrowserSync(url=html_to_data_uri(HTML_code),window_title="Test") browser.SetClientHandler(LoadHandler(config)) cef.MessageLoop() cef.Shutdown() return class LoadHandler(object): def __init__(self, config): self.config = config def OnLoadingStateChange(self, browser, is_loading, **_): """Called when the loading state has changed.""" if not is_loading: # Loading is complete. DOM is ready. browser.ExecuteFunction("defineData", self.config) unfortunately, unlike in the tutorial, my tool has to load a local .js file where the main function is defined (), and it seems if I code the html file this way, my working directory is not actually the directory where I call the script, but some strange place the output of these lines are: var loc = window.location.pathname; var dir = loc.substring(0, loc.lastIndexOf('/')); console.log(loc); console.log(dir); output: text/html;base64,CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPSJlbiI+Cgk8aGVhZD4KCQk8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CgkJPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgdXNlci1zY2FsYWJsZT1ubywgbWluaW11bS1zY2FsZT0xLjAsIG1heGltdW0tc2NhbGU9MS4wIj4KCQk8bGluayBocmVmPSJzdGF0aWMvY3NzL21haW4uY3NzIiByZWw9InN0eWxlc2hlZXQiIC8+CgkJPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCQkJKiB7CgkJCQkuYm9yZGV.... text Could you help me finding the correct way of hard coding html code in python with the correct path? maybe I need to somehow set the path? PS: I did try including the html code in a separate .html file, and it worked on Windows machines, but it seems MacOS doesn't like it. Since this tutorial did work on MAC, I'm trying to hard code the html part into the python script and hope it would work on both Windows and Mac
Well, the HTML document has been converted to the body of a data URI by html_to_data_uri, so the U[niversal]R[esource]L[ocator] (window.location) of the document isn't a location on a server, but the data URI itself (the "strange place" you mention). Remember that URLs are a subset of URIs, and you passed the URI as a URL to CEF with: browser = cef.CreateBrowserSync(url=html_to_data_uri(HTML_code),window_title="Test") So, as long as you are using a data URI/URL, I don't think that window.location will be helpful. Instead, you could extract the HTML code into a separate .html file, and change that line to: browser = cef.CreateBrowserSync(url="/path/to/that_html_file.html", window_title="Test")
How to generate a PDF with a given template, with dynamic data in Python or NodeJS to be deployed on AWS
Looking for recommendation of a library in Python(first preference) or NodeJS that can generate a pdf file preferably from dynamic html template to be run in AWS. Requirement is to generate invoice pdf to be sent to customers. Have come across below 2 Node libraries: PDFKit jsPDF Here we might have to deal with numbers for X and Y. Better approach would be something where we can simply use html/css to generate template with placeholders which can be replaced with dynamic data(coming from database query). Any suggestions would be appreciated. Thanks!
This approach worked for me in Python using below libraries: Jinja2 - for generating HTML with custom data xhtml2pdf - for generating PDF from HTML Consider within your PROJECT DIR, there is a template file(invoice.html) and python file(pdf_generator.py) pdf_generator.py from xhtml2pdf import pisa import jinja2 templateLoader = jinja2.FileSystemLoader(searchpath="./") templateEnv = jinja2.Environment(loader=templateLoader) TEMPLATE_FILE = "invoice.html" template = templateEnv.get_template(TEMPLATE_FILE) # This data can come from database query body = { "data":{ "order_id": 123, "order_creation_date": "2020-01-01 14:14:52", "company_name": "Test Company", "city": "Mumbai", "state": "MH", } } # This renders template with dynamic data sourceHtml = template.render(json_data=body["data"]) outputFilename = "invoice.pdf" # Utility function def convertHtmlToPdf(sourceHtml, outputFilename): # open output file for writing (truncated binary) resultFile = open(outputFilename, "w+b") # convert HTML to PDF pisaStatus = pisa.CreatePDF( src=sourceHtml, # the HTML to convert dest=resultFile) # file handle to receive result # close output file resultFile.close() # return True on success and False on errors print(pisaStatus.err, type(pisaStatus.err)) return pisaStatus.err if __name__ == "__main__": pisa.showLogging() convertHtmlToPdf(sourceHtml, outputFilename) invoice.html <!DOCTYPE html> <html lang="en"> <body> Name: {{ json_data.company_name }} <br> City/State: {{ json_data.city }}, {{ json_data.state }} <br> Date: {{ json_data.order_creation_date }} <br> Order ID: {{ json_data.order_id }} <br> </body> </html>
https://www.npmjs.com/package/pdfmake The above library allows flexibility when it comes to dynamic invoices in node.js
With https://getpdfapi.com you can design PDF templates using a web-based editor and once you finish, a REST API endpoint will be created just for you. This endpoint can be used to send your data in JSON format to the PDF template you've just created. The only code you need to write is the code that integrates the API. You can check a quick demo here: https://www.youtube.com/watch?v=cv4ZYd_aJO8
How to redirect/render Pyodide output in browser?
I have recently come across the Pyodide project. I have built a little demo using Pyodide, but although I've spent a lot of time looking at the source, it is not obvious (yet) to me how to redirect print output from python (other than modifying the CPython source), and also, how to redirect output from matplotlib.pyplot to the browser. From the source code, FigureCanvasWasm does have a show() method with the appropriate backend for plotting to the browser canvas - however, it is not clear to me how to instantiate this class and invoke it's show() method or indeed, if there is another more obvious way of redirecting plots to canvas. My questions therefore are: How do I redirect print() messages How do I force pyodide to plot matplotlib figures in the browser? Here is my test page: <!doctype html> <meta charset="utf-8"> <html lang="en"> <html> <head> <title>Demo</title> <script src="../../pyodide/build/pyodide.js"></script> </head> <body> </body> <script type="text/javascript"> languagePluginLoader.then(() => { pyodide.loadPackage(['matplotlib']).then(() => { pyodide.runPython(` import matplotlib.pyplot as plt plt.plot([1, 2, 3, 4]) plt.ylabel('some numbers') #fig = plt.gcf() #fig.savefig(imgdata, format='png') print('Done from python!')` ); //var image = pyodide.pyimport('imgdata'); //console.log(image); });}); </script> <html>
First of all let's see if we can get just anything to show up in the browser; e.g. a normal string. Python variables are stored in the pyodide.globals attribute. Hence we can take the python object from there and place it into a <div> element on the page. <!doctype html> <meta charset="utf-8"> <html> <head> <title>Demo</title> <script src="../pyodide/pyodide.js"></script> </head> <body> </body> <script type="text/javascript"> languagePluginLoader.then(() => { pyodide.runPython(`my_string = "This is a python string." `); document.getElementById("textfield").innerText = pyodide.globals.my_string; }); </script> <div id="textfield"></div> <html> Now I guess we can do the same with a matplotlib figure. The following would show a saved png image in the document. <!doctype html> <meta charset="utf-8"> <html lang="en"> <html> <head> <title>Demo</title> <script src="../pyodide/pyodide.js"></script> </head> <body> </body> <script type="text/javascript"> languagePluginLoader.then(() => { pyodide.loadPackage(['matplotlib']).then(() => { pyodide.runPython(` import matplotlib.pyplot as plt import io, base64 fig, ax = plt.subplots() ax.plot([1,3,2]) buf = io.BytesIO() fig.savefig(buf, format='png') buf.seek(0) img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8')` ); document.getElementById("pyplotfigure").src=pyodide.globals.img_str });}); </script> <div id="textfield">A matplotlib figure:</div> <div id="pyplotdiv"><img id="pyplotfigure"/></div> <html> I haven't looked into the backends.wasm_backend yet, so that may allow for a more automated way of the above.
When using the wasm backend, the canvas property of a figure is an instance of FigureCanvasWasm. Calling the show() method of the canvas should be sufficient to display the figure in the browser. Unfortunately a minor bug in the create_root_element() method of the canvas prevents the figure from being displayed. This method creates a div element that will contain the figure. It tries first to create an iodide output div element. If that fails a plain HTML div element is created. This element however is never appended to the document and remains therefore invisible. Below are the lines of code from FigureCanvasWasm were it happens def create_root_element(self): # Designed to be overridden by subclasses for use in contexts other # than iodide. try: from js import iodide return iodide.output.element('div') except ImportError: return document.createElement('div') The comment suggests the non-iodide code is a stub that needs to be extended, by overriding the method. This would require subclassing FigureCanvasWasm, installing it as a pyodide module and configuring matplotlib to use that backend. There is a shortcut however, because python allows overriding a method of an instance, without modifying the class, as per question 394770. Putting the following code in your HTML document gives a figure in the browser import numpy as np from matplotlib import pyplot as plt from js import document x = np.linspace(0, 2*np.pi, 100) y = np.sin(x) f = plt.figure() plt.plot(x,y) # ordinary function to create a div def create_root_element1(self): div = document.createElement('div') document.body.appendChild(div) return div #ordinary function to find an existing div #you'll need to put a div with appropriate id somewhere in the document def create_root_element2(self): return document.getElementById('figure1') #override create_root_element method of canvas by one of the functions above f.canvas.create_root_element = create_root_element1.__get__( create_root_element1, f.canvas.__class__) f.canvas.show() Initially the toolbar did not show icons. I had to download, unzip and install fontawesome alongside pyodide and include the following line in the header to get those <link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css"> Edit: About the first part of your question, redirecting the output stream to the browser, you could take a look at how it is done in pyodide's console.html. It replaces sys.stdout by a StringIO object pyodide.runPython(` import sys import io sys.stdout = io.StringIO() `); Then run the python code (that can be completely oblivious to the fact that it is running in a wasm context) pyodide.runPython(` print("Hello, world!") `); Finally, send the contents of the stdout buffer to an output element var stdout = pyodide.runPython("sys.stdout.getvalue()") var div = document.createElement('div'); div.innerText = stdout; document.body.appendChild(div);
To show print() calls form pyodide you can use the parameters on loadPyodide to redirect stdout: var paragraph = document.getElementById("p"); pyodide = await loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/", stdin: window.prompt, stdout: (text) => {paragraph.textContent += text;}, stderr: (text) => {paragraph.textContent += text;} }); https://github.com/pyodide/pyodide/blob/main/src/js/pyodide.js
I created a simple interactive shell for Python. Read my tutorial if you need more detailed information. const output = document.getElementById("output") const code = document.getElementById("code") code.addEventListener("keydown", function (event) { if (event.ctrlKey && event.key === "Enter") { evaluatePython() } }) function addToOutput(s) { output.value += `>>>${code.value}\n${s}\n` output.scrollTop = output.scrollHeight code.value='' } output.value = 'Initializing...\n' // init pyodide languagePluginLoader.then(() => { output.value += 'Ready!\n' }) function evaluatePython() { pyodide.runPythonAsync(code.value) .then(output => addToOutput(output)) .catch((err) => { addToOutput(err) }) } <!DOCTYPE html> <head> <script type="text/javascript"> // this variable should be changed if you load pyodide from different source window.languagePluginUrl = 'https://pyodide-cdn2.iodide.io/v0.15.0/full/'; </script> <script src="https://pyodide-cdn2.iodide.io/v0.15.0/full/pyodide.js"></script> </head> <body> Output: </div> <textarea id='output' style='width: 100%;' rows='10' disabled></textarea> <textarea id='code' value='' rows='2'></textarea> <button id='run' onclick='evaluatePython()'>Run</button> <p>You can execute any Python code. Just enter something in the box above and click the button (or Ctrl+Enter).</p> <div><a href='https://github.com/karray/truepyxel/demo.html'>Source code</a></div> </body> </html> Here is the example for matplotlib. Note that this will load a bunch of dependencies which will take up to several minutes. let python_code = ` from js import document import numpy as np import scipy.stats as stats import matplotlib.pyplot as plt import io, base64 def generate_plot_img(): # get values from inputs mu = int(document.getElementById('mu').value) sigma = int(document.getElementById('sigma').value) # generate an interval x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100) # calculate PDF for each value in the x given mu and sigma and plot a line plt.plot(x, stats.norm.pdf(x, mu, sigma)) # create buffer for an image buf = io.BytesIO() # copy the plot into the buffer plt.savefig(buf, format='png') buf.seek(0) # encode the image as Base64 string img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8') # show the image img_tag = document.getElementById('fig') img_tag.src = img_str buf.close() ` languagePluginLoader.then(()=>pyodide.runPythonAsync(python_code).then(()=>document.getElementById('status').innerHTML='Done!')) <!DOCTYPE html> <head> <script type="text/javascript"> // this variable should be changed if you load pyodide from different source window.languagePluginUrl = 'https://pyodide-cdn2.iodide.io/v0.15.0/full/'; </script> <script src="https://pyodide-cdn2.iodide.io/v0.15.0/full/pyodide.js"></script> </head> <body> Status: <strong id='status'>Initializing...</strong> <br><br> mu: <input id='mu' value='1' type="number"> <br><br> sigma: <input id='sigma' value='1' type="number"> <br><br> <button onclick='pyodide.globals.generate_plot_img()'>Plot</button> <br> <img id="fig" /> </body> </html>
How to insert a Python Plotted Graph to HTML file?
I had been searching for ways to do this but I seem couldn't get any helpful answer online thus I have to directly ask here. What I want is simple: Process some data from CSV, plot it into graph and post it to my HTML. I do not own any web-domain, the HTML is put in a shared drive and could be view by a certain allowed crowd using Chrome. Here is my python code snippet: import pandas as pd import numpy as np import matplotlib.pyplot as plt from jinja2 import Environment, FileSystemLoader import plotly as py import numpy as np import plotly.tools as plotly_tools import plotly.plotly as py from plotly.graph_objs import * def dashboard_plot_graph(): html_template = __Html_path__.get_template("PublishPG.html") curr_mcl_res = pd.read_csv(os.path.join(__Result_path__, __Chart_csv__)) print curr_mcl_res.head() x = curr_mcl_res['TestSuite'] my_plot = curr_mcl_res.plot(x, kind='bar', stacked=True, title='Latest Trend') fig = my_plot.get_figure() fig.savefig("./my_img.png") html_template_vars = {"curr_mcl_div_placeholder": curr_mcl_res.to_html()} html_out = html_template.render(html_template_vars) f = open ('file.html', 'w') f.write( html_out ) f.close() plt.show() Here is my HTML code snippet: <html> <head> <title> Testing Webpage </title> </head> <body> <h1> Hello World! </h1> <p> This is a test page </p> {{ curr_mcl_div_placeholder }} </body> <html> This is the plot I wish to see in my webpage: I have tried: Follow online tutorial which seem to say plotly able to insert the plot to HTML... however it failed... the most I can do is to insert the data frame (table) as shown in the code snippet above. I tried to think work around such as save the plotted graph as image and try to insert the image to html. But it does not allow to take image from local drive for security reason. It only able to pull image if I uploaded the image somewhere to the cloud / web-domain. But the data is not suitable to be exposed on any social media site. Any suggest for me to get through this limitation?
have a look at Bokeh, it's an amazing framework. Here some instructions for the graph you're looking to do
The short answer, if you just want to move on, is that you don't insert an image directly into HTML, but instead make a reference to the image file. Something like: <html> <head> <title> Testing Webpage </title> </head> <body> <h1> Hello World! </h1> <p> This is a test page </p> <img src="my_img.png" title="My plot" /> </body> <html> But Gsk's suggestion has a lot of merit.
I recently did a Google Map project by plotting a matplotlib generated chart on a web page without using Bokeh and without saving the file into hard disk (as it is slow when involving hardware i/o). I also chose to plot the graph in svg instead of png, as svg is valid html that can be easily inject into html template. Python standard library has io.BytesIO object for writing binary streams, such as images, to a buffer in memory with the same syntax as used for writing binary data to files. The idea is to let Matplotlib write to a io.BytesIO object in the memory and afterwards retrieve the series of bytes in the plot file from this object and embed it in the HTML file directly. from io import BytesIO # prepare your plt.plot here as you normally do svg_file = BytesIO() plt.savefig(svg_file, format='svg') # save the file to io.BytesIO svg_file.seek(0) svg_data = svg_file.getvalue().decode() # retreive the saved data svg_data = '<svg' + svg_data.split('<svg')[1] # strip the xml header The saved SVG image contain the xml header which we don't need it so I split the header and only keep the part that started with <svg tag. You can then add svg_data directly in your template like this: {{ svg_data|safe }}
Bokeh integration with CSS and jinja
I have a bokeh dashboard which needs some custom styling(CSS). I'm relatively new to the bokeh library and from what I understand, I need to use a jinja2 template wherein I can specify the CSS. The dashboard has a 4 level hierarchy and the lower level plots only show up on selection at the level above. The hierarchy is as follows- Dropdown(widget) - This is the only thing shown on first load Plot 1(shows/updates on change of dropdown) Plot 2 (shows/updates on selection in plot 1) Plot 3 (shows/updates on selection in plot 2) My index.html looks something like: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Sales Performance Analysis</title> <link *CDN Links*> <script *CDN Links*</script> {{ script }} </head> <body class="bk-body"> <h1>Sales Performance Analysis</h1> {{ div }} </body> </html> In my main.py, I have the following lines of code at the end to render- script, div_plot = components(curdoc()) curdoc().template_variables["script"] = script curdoc().template_variables["div"] = div_plot When I execute this as a server app (bokeh serve --show folder_name), the browser application shows the dropdown widget but nothing happens on changing the values. The same thing runs perfectly when run in standalone mode as bokeh serve --show main.py Would appreciate any kind of pointers. TIA
You want to use bokeh.embed.server_session instead of bokeh.embed.components (which only statically embed the document) Here's the reference: https://docs.bokeh.org/en/latest/docs/user_guide/embed.html#bokeh-applications