Can't make sense of this error from matplotlib and tight_layout()

I have gotten this error after in the following scenarios:

  1. If I tend to maximize mpl figure to full screen then I get below-mentioned error “sometimes”.
  2. If I wait for some time then I get the below-mentioned error “sometimes” without fideling around with mpl figure
  3. when I move the figure from one to other screen in extend mode on windows: this again gives error only “sometimes”

It was hard for me to reproduce this error when I wanted to get it to understand what is going on… However, when I wanted a smooth run of my software on my presentation day, it gave this beautiful error “sometimes” which needed me to rerun my code. If even I could catch the exception and retry, but that didn’t redraw my plot.

Error is below (note that userwarning was something that I always had but it didn’t create a issue):
UserWarning: Tight layout not applied. tight_layout cannot make axes width small enough to accommodate all axes decorations
self.fig.tight_layout()
Exception in thread Thread-2 (plotThread):
Traceback (most recent call last):
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib_mathtext.py”, line 2237, in parse
result = self._expression.parseString(s)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\pyparsing\core.py”, line 1141, in parse_string
raise exc.with_traceback(None)
pyparsing.exceptions.ParseException: Expected end of text, found ‘$’ (at char 0), (line:1, col:1)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\threading.py”, line 1009, in _bootstrap_inner
self.run()
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\threading.py”, line 946, in run
self._target(*self._args, **self._kwargs)
File “C:\Users\user\mySvn\myUtil\libs-py\realTimePlot_v16_dataClass.py”, line 502, in plotThread
self.fig.tight_layout()
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\figure.py”, line 3224, in tight_layout
kwargs = get_tight_layout_figure(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\tight_layout.py”, line 320, in get_tight_layout_figure
kwargs = _auto_adjust_subplotpars(fig, renderer,
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\tight_layout.py”, line 82, in _auto_adjust_subplotpars
bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axes_base.py”, line 4637, in get_tightbbox
bb_yaxis = self.yaxis.get_tightbbox(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1105, in get_tightbbox
self._update_label_position(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 2349, in _update_label_position
bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1881, in _get_tick_boxes_siblings
tlb, tlb2 = axis._get_tick_bboxes(ticks_to_draw, renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1085, in _get_tick_bboxes
return ([tick.label1.get_window_extent(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1085, in
return ([tick.label1.get_window_extent(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\text.py”, line 910, in get_window_extent
bbox, info, descent = self._get_layout(self._renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\text.py”, line 317, in _get_layout
w, h, d = renderer.get_text_width_height_descent(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_agg.py”, line 265, in get_text_width_height_descent
self.mathtext_parser.parse(s, self.dpi, prop)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\mathtext.py”, line 435, in parse
Traceback (most recent call last):
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib_mathtext.py”, line 2237, in parse
result = self._expression.parseString(s)
return self._parse_cached(s, dpi, prop, _force_standard_ps_fonts) File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\pyparsing\core.py”, line 1141, in parse_string
raise exc.with_traceback(None)

File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\mathtext.py”, line 456, in _parse_cached
pyparsing.exceptions.ParseException: Expected end of text, found ‘$’ (at char 0), (line:1, col:1)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_qt.py”, line 477, in _draw_idle
self.draw()
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_agg.py”, line 436, in draw
self.figure.draw(self.renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\artist.py”, line 73, in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\artist.py”, line 50, in draw_wrapper
return draw(artist, renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\figure.py”, line 2837, in draw
mimage._draw_list_compositing_images(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\image.py”, line 132, in _draw_list_compositing_images
a.draw(renderer)
box = self._parser.parse(s, font_output, fontsize, dpi)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib_mathtext.py”, line 2239, in parse
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\artist.py”, line 50, in draw_wrapper
return draw(artist, renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axes_base.py”, line 3091, in draw
mimage._draw_list_compositing_images(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\image.py”, line 132, in _draw_list_compositing_images
a.draw(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\artist.py”, line 50, in draw_wrapper
return draw(artist, renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1159, in draw
ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1085, in _get_tick_bboxes
return ([tick.label1.get_window_extent(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\axis.py”, line 1085, in
return ([tick.label1.get_window_extent(renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\text.py”, line 910, in get_window_extent
bbox, info, descent = self._get_layout(self._renderer)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\text.py”, line 317, in _get_layout
w, h, d = renderer.get_text_width_height_descent(
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\backends\backend_agg.py”, line 265, in get_text_width_height_descent
self.mathtext_parser.parse(s, self.dpi, prop)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\mathtext.py”, line 435, in parse
return self._parse_cached(s, dpi, prop, _force_standard_ps_fonts)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib\mathtext.py”, line 456, in _parse_cached
box = self._parser.parse(s, font_output, fontsize, dpi)
File “C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\matplotlib_mathtext.py”, line 2239, in parse
raise ValueError(“\n”.join([“”,
ValueError:
\mathdefault{10^{5}}
^
Expected end of text, found ‘' (at char 0), (line:1, col:1) raise ValueError("\n".join(["", ValueError: \mathdefault{10^{5}} ^ Expected end of text, found '’ (at char 0), (line:1, col:1)

My code:

from future import annotations
from PyQt5.QtWidgets import QMenu, QDialog, QTableWidgetItem
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import threading
import time
import matplotlib.pyplot as plt
from PyQt5 import QtWidgets, uic, QtGui
from PyQt5.QtCore import pyqtSignal
from itertools import zip_longest
from pathlib import Path
import os
import sys
class GWidget(QtWidgets.QWidget):
def init(self, dataClass, name, layers=None, *args, **kwargs):
super(GWidget, self).init(*args, **kwargs)
self._dataClass=dataClass
self.fig, self.ax = plt.subplots(nrows=2, ncols=4, sharex=True)

  self.ln1, =self.ax[0, 1].plot([],[], label='a')
  self.ln2, =self.ax[0, 2].plot([],[], label='b')
    self.ln3, =self.ax[0, 3].plot([],[], label='c')
    self.ln4, =self.ax[0, 4].plot([],[], label='d')
    self.ln5, =self.ax[0, 5].plot([],[], label='e')
    self.ln6, =self.ax[0, 6].plot([],[], label='f')
    self.ln7, =self.ax[0, 7].plot([],[], label='g')
    self.ln8, =self.ax[0, 8].plot([],[], label='h')
    

  for axColCounter in range(0, 4):
  	for axRowCounter in range(0,2):
  		self.ax[axRowCounter, axColCounter].grid()
  		self.ax[axRowCounter, axColCounter].set_title(str(axRowCounter)+","+str(axColCounter))
  
  
    self.ax[-1, -1].axis('off')

    self.ylabels = ["a (V)", "b (A)", "c (W)", "d (C)", "Unknown", "Unknown"]
    for i in range(3):
        self.ax[0, i].set_xlabel('Relative time (s)')
        self.ax[0,i].set_ylabel(self.ylabels[i])
        self.ax[0, i].legend()

    self.ax[0,3].set_xlabel("Relative time (s)")

    for i in range(3):
        self.ax[1, i].set_xlabel('Relative time (s)')
        self.ax[1, i].set_ylabel(self.ylabels[i+3])
        self.ax[1, i].legend()
    self.ax[0,3].set_ylabel("freq")

    plt.grid(visible=True, which='major', color='k', linestyle='dashed')
    plt.grid(visible=True, which='minor', color='w', linestyle='dashed')
    self.ax[0, 3].set_ylim([0, 1e11])
    self.ax[0,3].set_yscale('symlog')

def plotThread(self):
while True:
start = time.time()
self.xk=self._dataClass.dict1[“time”][-self.varLen:]
self.yk=self._dataClass.dict1[“C”][-self.varLen:]
self.ln1.set_data(self.xk, self.yk)
self.ax[0,2].relim()
self.ax[0,2].autoscale_view()

        self.xk=self._dataClass.dict2["time"][-self.varLen:]
        self.yk=self._dataClass.dict2["C"][-self.varLen:]
        self.ln2.set_data(self.xk, self.yk)
        self.ax[0,3].relim()
  	self.ax[0,3].autoscale_view()
        
        self.fig.tight_layout()
        self.canvas.draw()

It is a bit hard to fully read the code due to the formatting, but it looks like you draw from another thread. That is usually not allowed in QT and typically gives this random failure behavior.

Also, it may more convenient to call fig.set_tight_layout(True) instead of repeatedly calling tight_layout().

Hi @oscargus , thanks for your reply. I should have formatted my code well but I thought I would get a option to edit it after posting.
Good that you told me about fig.set_tight_layout(True) as I think that had some delay.
Though, I am not using Qt for plotting. My import statements include PyQt because above code is a section of my whole code. This plotting is done in threading.thread from Python and I used matplotlib.funcanimation earlier but that had some issues with my plotting when my data>1e6 points.
I am trying to stick to mpl even though I think pyqtgraph would be faster for realtime plotting for larger dataset because I wanted some mpl functionalities.

I have a clue about above error but I am not 100% sure. When I comment out the usage of this line:
self.ax[0,3].set_yscale(‘symlog’)
then, I don’t get error for long time (or maybe never). I cannot say 100% because this error comes randomly due to the 3 reasons mentioned in my post and it is difficult to regenerate when I want to solve it but it has to come when I have to shine at work in the middle of a demo…!

Do you have a clue what would be causing it?

Matplotlib is not thread safe: How-to — Matplotlib 3.6.3 documentation

Only if mpl could make itself thread-safe, it would be able to plot 10s of Millions of points from different threads, which would make it best for multithreading applications

Qt is not thread-safe either, so going through that work in Matplotlib would not help you.

Fundamentally you cannot poke pixels from a series of threads - what if one thread wanted to make the pixel green, and the other thread wanted to make it red?

I believe that you can make your data smaller in threads and then plot from the main thread.

Can you please elaborate on what you mean by making the data smaller? Do you mean to plot like just last few 100 thousand points and not the whole million points?

There are many ways to reduce data, depending on your data and visualization - hard to give specific advice without some idea what you are trying to accomplish.

My data is just a dictionary of 42 keys and each key has ~ 500 thousand to 10 million data points as values. I plot the dict[key1]…dict[key41] vs dict[key42].

I often define a varLen=200000 (200 thousand data points) which is to plot last 200 k data points and not all when I want to just not make the plotting sluggish. Then, I plot dict[key1][-varLen:]…dict[key41][-varLen:] vs dict[key42][-varLen:]

These 41 plots are distributed in 6 subplots unevenly and I update all plots in a while True loop, as my code says.