Skip to content

Setting plt.rc('text', usetex=True) after ticker.ScalarFormatter(useMathText=True) causes Error #10317

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

Closed
svrnwnsch opened this issue Jan 25, 2018 · 12 comments · Fixed by #15695

Comments

@svrnwnsch
Copy link

Bug report

Bug summary

If I call plt.rc('text', usetex=True) after ticker.ScalarFormatter(useMathText=True) and plot a image with a colorbar. The plot will not work and a Latex runtime error is thrown.
If you switch the commands everything works fine.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker

cbformat = ticker.ScalarFormatter(useMathText=True)
plt.rc('text', usetex=True)

plt.imshow(np.random.random((5, 5)))
plt.colorbar(format=cbformat)
plt.show()

Actual outcome

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 393, in make_dvi
    stderr=subprocess.STDOUT)
  File "/usr/lib/python3.6/subprocess.py", line 336, in check_output
    **kwargs).stdout
  File "/usr/lib/python3.6/subprocess.py", line 418, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['latex', '-interaction=nonstopmode', '3a8b8f2d899e29dc96ac7c7cd31a754a.tex']' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 748, in callit
    func(*args)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_tkagg.py", line 323, in idle_draw
    self.draw()
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_tkagg.py", line 304, in draw
    FigureCanvasAgg.draw(self)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_agg.py", line 430, in draw
    self.figure.draw(self.renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/figure.py", line 1299, in draw
    renderer, self, artists, self.suppressComposite)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axes/_base.py", line 2437, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axis.py", line 1135, in draw
    renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axis.py", line 1078, in _get_tick_bboxes
    extent = tick.label2.get_window_extent(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/text.py", line 933, in get_window_extent
    bbox, info, descent = self._get_layout(self._renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/text.py", line 317, in _get_layout
    ismath=ismath)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_agg.py", line 226, in get_text_width_height_descent
    s, fontsize, renderer=self)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 602, in get_text_width_height_descent
    dvifile = self.make_dvi(tex, fontsize)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 400, in make_dvi
    exc.output.decode("utf-8"))))
RuntimeError: LaTeX was not able to process the following string:
b'$\\\\mathdefault{0.6}$'

Here is the full report generated by LaTeX:
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(./3a8b8f2d899e29dc96ac7c7cd31a754a.tex
LaTeX2e <2017-04-15>
Babel <3.12> and hyphenation patterns for 14 language(s) loaded.
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/type1cm/type1cm.sty)
(/usr/share/texlive/texmf-dist/tex/latex/base/textcomp.sty
(/usr/share/texlive/texmf-dist/tex/latex/base/ts1enc.def))
(/usr/share/texlive/texmf-dist/tex/latex/geometry/geometry.sty
(/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty)
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifpdf.sty)
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifvtex.sty)
(/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty)

Package geometry Warning: Over-specification in `h'-direction.
    `width' (5058.9pt) is ignored.


Package geometry Warning: Over-specification in `v'-direction.
    `height' (5058.9pt) is ignored.

)
No file 3a8b8f2d899e29dc96ac7c7cd31a754a.aux.
(/usr/share/texlive/texmf-dist/tex/latex/base/ts1cmr.fd)
*geometry* driver: auto-detecting
*geometry* detected driver: dvips
! Undefined control sequence.
<recently read> \mathdefault 
                             
l.12 ...000000}{12.500000}{\sffamily $\mathdefault
                                                  {0.6}$}
[1] (./3a8b8f2d899e29dc96ac7c7cd31a754a.aux) )
(see the transcript file for additional information)
Output written on 3a8b8f2d899e29dc96ac7c7cd31a754a.dvi (1 page, 288 bytes).
Transcript written on 3a8b8f2d899e29dc96ac7c7cd31a754a.log.
 


Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 393, in make_dvi
    stderr=subprocess.STDOUT)
  File "/usr/lib/python3.6/subprocess.py", line 336, in check_output
    **kwargs).stdout
  File "/usr/lib/python3.6/subprocess.py", line 418, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['latex', '-interaction=nonstopmode', '3763d70561037cddae6a97acb53ffec4.tex']' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_tkagg.py", line 233, in resize
    self.show()
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_tkagg.py", line 304, in draw
    FigureCanvasAgg.draw(self)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_agg.py", line 430, in draw
    self.figure.draw(self.renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/figure.py", line 1299, in draw
    renderer, self, artists, self.suppressComposite)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axes/_base.py", line 2437, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axis.py", line 1135, in draw
    renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/axis.py", line 1078, in _get_tick_bboxes
    extent = tick.label2.get_window_extent(renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/text.py", line 933, in get_window_extent
    bbox, info, descent = self._get_layout(self._renderer)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/text.py", line 317, in _get_layout
    ismath=ismath)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/backends/backend_agg.py", line 226, in get_text_width_height_descent
    s, fontsize, renderer=self)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 602, in get_text_width_height_descent
    dvifile = self.make_dvi(tex, fontsize)
  File "/usr/local/lib/python3.6/dist-packages/matplotlib/texmanager.py", line 400, in make_dvi
    exc.output.decode("utf-8"))))
RuntimeError: LaTeX was not able to process the following string:
b'$\\\\mathdefault{0.8}$'

Here is the full report generated by LaTeX:
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(./3763d70561037cddae6a97acb53ffec4.tex
LaTeX2e <2017-04-15>
Babel <3.12> and hyphenation patterns for 14 language(s) loaded.
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/type1cm/type1cm.sty)
(/usr/share/texlive/texmf-dist/tex/latex/base/textcomp.sty
(/usr/share/texlive/texmf-dist/tex/latex/base/ts1enc.def))
(/usr/share/texlive/texmf-dist/tex/latex/geometry/geometry.sty
(/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty)
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifpdf.sty)
(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ifvtex.sty)
(/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty)

Package geometry Warning: Over-specification in `h'-direction.
    `width' (5058.9pt) is ignored.


Package geometry Warning: Over-specification in `v'-direction.
    `height' (5058.9pt) is ignored.

)
No file 3763d70561037cddae6a97acb53ffec4.aux.
(/usr/share/texlive/texmf-dist/tex/latex/base/ts1cmr.fd)
*geometry* driver: auto-detecting
*geometry* detected driver: dvips
! Undefined control sequence.
<recently read> \mathdefault 
                             
l.12 ...000000}{12.500000}{\sffamily $\mathdefault
                                                  {0.8}$}
[1] (./3763d70561037cddae6a97acb53ffec4.aux) )
(see the transcript file for additional information)
Output written on 3763d70561037cddae6a97acb53ffec4.dvi (1 page, 288 bytes).
Transcript written on 3763d70561037cddae6a97acb53ffec4.log.

Matplotlib version

  • Operating system: Kubuntu 17.10
  • Matplotlib version: 2.1.1
  • Matplotlib backend (print(matplotlib.get_backend())): TkAgg
  • Python version: 3.6
@anntzer
Copy link
Contributor

anntzer commented Jan 25, 2018

While an ad-hoc fix may be possible, I think this is a good opportunity to get rid of \mathdefault and replace it by whatever tex package does the same thing (write math using the externally specified non-math font), e.g. mathastext (https://tex.stackexchange.com/a/39520/4101) or the solution hinted at in https://tex.stackexchange.com/questions/22350/difference-between-textrm-and-mathrm/22353#22353 (i.e. actually define \mathdefault in our tex preamble.

Other possibly relevant stuff:
http://milde.users.sourceforge.net/LUCR/Math/math-font-selection.xhtml

@huxiaoqian
Copy link

@svrnwnsch do you solve this problem? I have the same problem like you.

@svrnwnsch
Copy link
Author

I think it worked for me to switch the commands.

@huxiaoqian
Copy link

@svrnwnsch do you mean to change usetex=False or the Formatter of ticker ?

@svrnwnsch
Copy link
Author

@huxiaoqian I mean that I called plt.rc('text', usetex=True) before i called ticker.ScalarFormatter(useMathText=True)

@smithsp
Copy link
Contributor

smithsp commented Mar 14, 2019

I think that @kathreen8 is seeing a similar issue where \mathdefault is not defined. Was it supposed to be replaced in the output string by matplotlib?

@smithsp
Copy link
Contributor

smithsp commented Mar 14, 2019

Note that this issue does not occur for matplotlib version 1.5.3, but I do see it with 2.2.2.

@pyZerrenner
Copy link

pyZerrenner commented Apr 30, 2019

A look in the source code (ticker.py) shows, that the ScalarFormatter class sets self._usetex = rcParams['text.usetex'] in its __init__ method. This value is never updated after creation (as far as I can see). Only the value in self._usetex is used to check which kind of ticklabels should be generated (except in the method ScalarFormatter.fix_minus).

Consequently, if a ScalarFormatter instance is created while usetex = False, it will always generate ticklabels using the \mathdefault command. If TeX rendering is switched on after the instance initialisation, the label string is passed to TeX, which does not know \mathdefault and raises an error. If however we have usetex=True on creation of the ScalarFormatter instance, TeX gets the correct string and it all works out fine.

@pyZerrenner
Copy link

pyZerrenner commented May 1, 2019

I originally came here, because I encountered the same error in a different setting. I now found, that my case is also caused by the inconsistent request of the usetex state by the ticklabel formatter. (Disclaimer: The following is the result of me quickly scanning through a bit of source code and making some guesses. So the explanation might not be entirely correct.)

The following code generates the same error as for @svrnwnsch:

import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True

fig, ax = plt.subplots(1, 1)
ax.set_xscale('log')
fig.savefig('test_tex.png')

plt.rcParams['text.usetex'] = False

This happens for matplotlib 2.2.4 (Win7) and 3.0.3 (Win10) with Python 3.6 and Qt5Agg backend. The interesting part is, that the error vanishes, if the figure is not saved (remove the fig.savefig call) or if a short pause (plt.pause(0.1)) is inserted before setting usetex = False. The culprit of the error is, again, the ticklabel formatter. The x-axis uses a LogFormatterSciNotation which is derived from LogFormatterMathtext. In contrast to ScalarFormatter, this formatter requests the usetex state every time the label strings are created, not during the initialisation of the object (see my previous comment). Since the error can be avoided by waiting for a bit, this seems to be some kind of race condition (here come my guesses): After the figure is saved, the backend wants to display the figure and finds usetex = True (or it has gotten the value during the figure creation). So now it sends all text through TeX. But the backend seems to run in parallel the script and once it requests the ticklabels from the formatter, the last line has already executed so the formatter sees usetex = False. Consequently, it inserts the \mathdefault command, which is sent to TeX, which raises the error.

So I think there are two inconsistencies:

  1. Different formatter classes request the state of usetex at different times and thus behave differently.
  2. If the usetex state changes during execution, the formatter might have a different value than the backend which displays the text.

Maybe the state of whether to use or not use TeX for a figure should be set as a property of the figure, when the backend starts displaying it. All texts then refer to the value of the parent figure instead of the global state. The problem with formatters is that they do not know their parent figure or can be used in multiple figures. The only solution I can think of is to add a usetex argument to the __call__ method of the formatter so the calling object (probably an axis) has to pass the correct state to the method. But this is a change in syntax and probably has to be implemented in quite a lot of places throughout matplotlib, so maybe there is a better solution.

@daddy32
Copy link

daddy32 commented Nov 14, 2019

For the reference, two following examples (both without ticker and saving figure) produce similar error in my environments.
In these cases error goes away when not using underscore or other special characters in labels.

no. 1

# Minimal fail example:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
df = pd.DataFrame({'a_b': [1,2,3]})

df.plot();

no. 2

# example without pandas
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['text.usetex'] = True

# Data for plotting
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)

fig, ax = plt.subplots()
ax.plot(t, s)

ax.set(xlabel='time (s)', ylabel='voltage (mV)',
       title=r'About as simple as it gets,_folks')
ax.grid()

plt.show()

@tacaswell
Copy link
Member

In the second case, r'About as simple as it gets,_folks' is not valid LaTeX so it should be failing.

@daddy32
Copy link

daddy32 commented Nov 14, 2019

Thanks for the answer! Is it because of the unescaped underscore? If so, the same then applies to the first example then. It doesn't seem to be because of the 'rawness' of the string, as the snippet fails without the 'r' prefix too.

@QuLogic QuLogic added this to the v3.3.0 milestone Dec 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants