Dash: latex support

Created on 9 Apr 2018  Â·  20Comments  Â·  Source: plotly/dash

I tried to use latex in dash, but it is not working.
It seems that the mathjax javacript library is not loaded.

dash-meta-good_first_issue help wanted

Most helpful comment

Is there any progress on this? Latex support is quite important for scientific dashboards...

All 20 comments

It is possible to render MathJax in static Dash content, but not in dynamic content.

Here is a MWE

import dash
import dash_html_components as html

app = dash.Dash(__name__)
mathjax = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML'
app.scripts.append_script({ 'external_url' : mathjax })

app.layout = html.Div(id='main',children=[
  html.Div(id='static',children='$$ x=1 $$'),
  html.Div(id='dynamic',children=''),
  html.Button('Add Math',id='button'),
])

@app.callback(
  dash.dependencies.Output('dynamic','children'),
 [dash.dependencies.Input('button','n_clicks')]
)
def addmath(n_clicks):
  if n_clicks:
    return '$$ x=1 $$'
  else:
    return None

if __name__ == '__main__':
  app.run_server(debug=True)

related community.plot.ly thread

Hi, I'm wondering if there has been progress on this issue? Specifically, I would like to be able to render latex in the axis titles/labels of a graph.

There is a pull request in progress to solve this issue, but it seems like it has stalled at the moment.

I created dash-katex, a library that let you render latex inside a component DashKatex, obviously using katex js. This does not solve the problem of rendering latex inside a plotly graph object, but lets you dynamically run latex inside divs.

Link: https://github.com/xhlulu/dash-katex

Is this issue still available for development? I am seeking to contribute.

Is there any progress on this? Latex support is quite important for scientific dashboards...

What about now?

After searching around, I found Latex to Image API very useful.
And I wrote a function to make it work with $a+b$ and $$a+b$$ for dcc.markdown.

import dash_core_components as dcc
import dash_html_components as html
import dash
import urllib.parse
import re


def convert(text):
    def toimage(x):
        if x[1] and x[-2] == r'$':
            x = x[2:-2]
            img = "\n<img src='https://math.now.sh?from={}' style='display: block; margin: 0.5em auto;'>\n"
            return img.format(urllib.parse.quote_plus(x))
        else:
            x = x[1:-1]
            return r'![](https://math.now.sh?from={})'.format(urllib.parse.quote_plus(x))
    return re.sub(r'\${2}([^$]+)\${2}|\$(.+?)\$', lambda x: toimage(x.group()), text)


app = dash.Dash()

Markdown_text = r"""
Let's see if it works:  
$$\hat P \psi_k(x) =p \psi_k(x)$$ 

$$-i\hbar \frac{\partial {c\ e^{ikx}}}{\partial x} =-i\hbar\ c\ ik\ e^{ikx} $$ 

$$\hbar k\ c\ e^{ikx} = \hbar k\ \psi_k(x) \tag{2}$$
with $p=\hbar k$
"""

Markdown_text = convert(Markdown_text)


app.layout = html.Div([
    dcc.Markdown(Markdown_text, dangerously_allow_html=True)
])

if __name__ == '__main__':
    app.run_server(host='0.0.0.0')

Does this also work for axis labels?

I will look into this in the next 2 weeks. For now, here are some ideas somebody may want to try:

  • load mathjax as suggested above
  • define and include javascript function to re-render some element by mathjax: examples
  • add a clientside callback in plotly to trigger the js function to re-render

Does this also work for axis labels?

Hey, I just found some (tricky) workaround for axis labels, as well as latex in markdown.
I'll put this into a repo soon and share how to use this.

An example
image

Richard

Does this also work for axis labels?

Hey, I just found some (tricky) workaround for axis labels, as well as latex in markdown.
I'll put this into a repo soon and share how to use this.

An example
image

Richard

This looks great! How did you do it? And is this something you could provide a pull request for? ;)

This looks great! How did you do it? And is this something you could provide a pull request for? ;)

Hey sorry for the delay, I just created a complete example https://github.com/yueyericardo/dash_latex explain how to make it work.

Example2 will come later.

Well, now I'm confused. It looks like dynamic MathJax in axes labels now works. I thought that this was not working before. Can anybody confirm that this was part of the problem and now fixed?

This MWE will compile the user-input LaTeX live and render the result on y-axis label.

import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__, external_scripts=[
  'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML',
])

def fig_fun(label='default'):
  return {
    'data': [{'x': [-5, 0,+5], 'y': [0,0,5] }],
    'layout': {'yaxis': {'title': label}},
  }

ylabel = '$y = \\begin{cases} 0 & x < 0 \\\\ x & x \ge 0 \\end{cases}$'

app.layout = html.Div(id='main',children=[
  dcc.Input(id='input',value=ylabel,style={'width':'100%'}),
  dcc.Graph(id='graph',figure=fig_fun())
])

@app.callback( Output('graph','figure'), [Input('input','value')] )
def update_fun(label):
  return fig_fun(label=label)

if __name__ == '__main__':
  app.run_server(debug=True)

dash-axis-live

Okay. Here is another hack that will probably solve all dynamic LaTeX rendering issues but you have to decide the refresh rate (trading speed vs CPU).

After including MathJax like above:

app = dash.Dash(__name__, external_scripts=[
  'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML',
])

add the file ./assets/mathjax-refresh.js with the following content:

setInterval("MathJax.Hub.Queue(['Typeset',MathJax.Hub])",1000);

This asks MathJax to scan the page for any new $...$ every 1000 milliseconds, and re-render it. It solved all my MWE here without any extra modification of the Python code. You may want to increase the refresh rate by decreasing 1000. I tried 1 and it was very fast, but used CPU 5% ¯\_(ツ)_/¯.

more info:

That workaround seems to work, thank you so much!

Just one thing is a bit suboptimal, but that's a luxury problem. When I set the axis label to something like $\sigma_x$ [mm], only the sigma_x part is rendered, and the units are omitted.

Workaround:

Use $\sigma_x \mathrm{[mm]}$ instead.

Ugh. It seems the mathjax-refresh hack somehow interferes with the dynamic axis labelling, making it very slow. Apologies that I didn't test this enough before.

@renatobellotti I wouldn't call that a luxury problem. Mixing TeX and non-TeX might be quite common, and we shouldn't have to wrap all non-TeX in \textrm{...}.

Sometime in the past year it seems TeX support was added to axis labels, but I can't find any commit that references this. Essentially I'd like to turn that off, since I'd prefer the refresh hack.

EDIT: It seems our problems might be upstream at: plotly.js

@jessexknight , thank you for your solution, it works nice if there is a Graph output!

However, when there is no Graph output (but the app has some other outputs), then it seems MathJax is not loaded and the latex code in other controls is not rendered. Is there a quick workaround?

I don't have time to look into this right now but this may be useful, or perhaps any one of the the MathJax issues upstream at plotly.js

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mpkuse picture mpkuse  Â·  4Comments

ncdingari picture ncdingari  Â·  4Comments

jwhendy picture jwhendy  Â·  3Comments

Jerry-Ma picture Jerry-Ma  Â·  3Comments

lochbrunner picture lochbrunner  Â·  3Comments