Проверить строку MathText во время выполнения

Я пытаюсь создать графический интерфейс, содержащий встроенную панель Matplotlib. Один из виджетов (QLineEdit) предназначен для метки легенды, и когда люди вводят туда значение, оно напрямую изменяется на графике (с помощью self.widget.textChanged). Вся эта часть работает отлично и, как и ожидалось, за исключением следующего случая: когда люди вводят латексные символы, и этот плохо написан, я получаю ValueError, который я не могу понять, как поймать.

Типичная трассировка, когда я ввожу «$$»:

Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/matplotlib/mathtext.py", line 2516, in parse
result = self._expression.parseString(s)
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1632, in parseString
raise exc
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1622, in parseString
loc, tokens = self._parse( instring, 0 )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1529, in _parseCache
value = self._parseNoCache(instring, loc, doActions, callPreParse)
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1379, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 3717, in parseImpl
return self.expr._parse( instring, loc, doActions, callPreParse=False )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1529, in _parseCache
value = self._parseNoCache(instring, loc, doActions, callPreParse)
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1379, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 3395, in parseImpl
loc, exprtokens = e._parse( instring, loc, doActions )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1529, in _parseCache
value = self._parseNoCache(instring, loc, doActions, callPreParse)
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 1383, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/usr/lib/python3.6/site-packages/pyparsing.py", line 3183, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected end of text (at char 0), (line:1, col:1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/alien/Dropbox/Vcatpy/src/VcatPy", line 974, in make_scatplot
self.win.draw()
File "/usr/lib/python3.6/site-packages/matplotlib/backends/backend_qt5agg.py", line 133, in draw
super(FigureCanvasQTAggBase, self).draw()
File "/usr/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py", line 430, in draw
self.figure.draw(self.renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/usr/lib/python3.6/site-packages/matplotlib/figure.py", line 1299, in draw
renderer, self, artists, self.suppressComposite)
File "/usr/lib/python3.6/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
a.draw(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/usr/lib/python3.6/site-packages/matplotlib/axes/_base.py", line 2437, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/usr/lib/python3.6/site-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
a.draw(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/usr/lib/python3.6/site-packages/matplotlib/legend.py", line 772, in draw
bbox = self._legend_box.get_window_extent(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 263, in get_window_extent
w, h, xd, yd, offsets = self.get_extent_offsets(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 385, in get_extent_offsets
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 385, in <listcomp>
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 256, in get_extent
w, h, xd, yd, offsets = self.get_extent_offsets(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 456, in get_extent_offsets
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 456, in <listcomp>
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 256, in get_extent
w, h, xd, yd, offsets = self.get_extent_offsets(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 385, in get_extent_offsets
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 385, in <listcomp>
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 256, in get_extent
w, h, xd, yd, offsets = self.get_extent_offsets(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 456, in get_extent_offsets
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 456, in <listcomp>
for c in self.get_visible_children()]
File "/usr/lib/python3.6/site-packages/matplotlib/offsetbox.py", line 829, in get_extent
bbox, info, d = self._text._get_layout(renderer)
File "/usr/lib/python3.6/site-packages/matplotlib/text.py", line 317, in _get_layout
ismath=ismath)
File "/usr/lib/python3.6/site-packages/matplotlib/backends/backend_agg.py", line 231, in get_text_width_height_descent
self.mathtext_parser.parse(s, self.dpi, prop)
File "/usr/lib/python3.6/site-packages/matplotlib/mathtext.py", line 3303, in parse
box = self._parser.parse(s, font_output, fontsize, dpi)
File "/usr/lib/python3.6/site-packages/matplotlib/mathtext.py", line 2522, in parse
six.text_type(err)]))
ValueError: 
$$
^
Expected end of text (at char 0), (line:1, col:1)
Aborted (core dumped)

И сбой всего графического интерфейса.

Команда, приводящая к этой ошибке:

self.plot.legend(handles, labels) 

откуда handles, labels

handles, labels = self.plot.get_legend_handles_labels()`

Я попытался поймать ошибку с помощью простого:

try:
    self.plot.legend(handles, labels)
except ValueError:
    print('ok')

Я также убедился, что метки имеют формат необработанной строки с r'my label'.

Но я не мог заставить это работать... Любая помощь будет принята с благодарностью.

Ниже приведен пример не с легендами, а с метками осей:

####Public General Libraries
import sys

######Qt5
from PyQt5 import *
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
from PyQt5.QtWidgets import *

###matplotlib
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from matplotlib.widgets import Cursor

class Main_window(QWidget):

    def __init__(self):
        super().__init__() 
        self.initUI() 

    def initUI(self):

        ### 1 we create the grid
        self.Global = QVBoxLayout(self)
        self.setLayout(self.Global)
        grid= QGridLayout()
        self.Global.addLayout(grid)

        ### a- space for plot
        self.tab = QTabWidget()
        self.figure = Figure()
        self.figure.subplots_adjust(hspace = 0, right=0.95, top=0.94, left=0.15)
        self.win = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.win, self.win)
        grid.addWidget(self.win,0, 0, 1, 3)
        grid.addWidget(self.toolbar, 1, 0, 1, 3)
        self.plot = self.figure.add_subplot(111)
        self.plot.legend()

        #### c- labels
        self.xlabl = QLineEdit('qwdwed')
        grid.addWidget(self.xlabl, 3, 1, 1, 1)
        self.xlabl.textChanged.connect(self.changexlabl)
        self.xlabl.setText('Xlabel')


        self.ylabl = QLineEdit('qwdwed')
        grid.addWidget(self.ylabl, 3, 2, 1, 1)
        self.ylabl.textChanged.connect(self.changeylabl)
        self.ylabl.setText('Ylabel')


        self.win.draw()
        self.show()


    def changeylabl(self):
        try:
            self.plot.set_ylabel(self.ylabl.text())
        except:
            self.plot.set_ylabel('Y-label')
        self.win.draw()

    def changexlabl(self):
        try:
            self.plot.set_xlabel(self.xlabl.text())
        except:
            self.plot.set_xlabel('X-label')

        self.win.draw()


###and start the tui
app = QApplication(sys.argv)
main = Main_window()
main.setFixedSize(730, 1030)
sys.exit(app.exec_())

person Astrom    schedule 01.02.2018    source источник
comment
Я не думаю, что $$ является допустимой строкой MathText. Так что я думаю, что ошибка ожидаема. Как нарисовать рамку? Лучше предоставить минимально воспроизводимый пример.   -  person ImportanceOfBeingErnest    schedule 02.02.2018
comment
Да, я знаю, что «$$» недействителен, в этом суть. Цель состоит в том, чтобы попытаться поймать, когда записывается недопустимая латексная строка. Трассировка дает ValueError: $$, поэтому я подумал, что могу попытаться поймать это и при обнаружении не обновлять график.   -  person Astrom    schedule 02.02.2018
comment
Да, но ошибка возникает во время отрисовки, поэтому у вас нет доступа к вызову, вызвавшему ошибку, и вы в любом случае не хотели бы перехватывать событие отрисовки, так как тогда ничего не отрисовывается (или только половина графика или около того). Решение, безусловно, состоит в том, чтобы убедиться, что любая строка, заданная для легенды , действительно действительна. Конечно, есть некоторые варианты, но я бы хотел, чтобы их опробовал минимальный воспроизводимый пример.   -  person ImportanceOfBeingErnest    schedule 02.02.2018
comment
Я обновил минимальный рабочий пример. Если вы попытаетесь добавить $$ в один из ярлыков, у вас будет такая же ошибка, как описано в посте. Спасибо за вашу помощь!   -  person Astrom    schedule 02.02.2018


Ответы (1)


Проблема сводится к выяснению того, является ли заданная строка корректным математическим текстом или вообще корректна. Поскольку мы не должны помещать сам рисунок в try/except, так как это все равно попытается отобразить фигуру и в случае ошибки остановится на полпути, идея состоит в том, чтобы позволить matplotlib сначала оценить строку, и только в случае успеха, нарисовать фигура.

С этой целью мы установили бы рассматриваемую строку в метку и вызвали функцию метки _get_layout. Если это удается, мы знаем, что отрисовка сохранена, в противном случае ловим ошибку, не отрисовывая холст.

def changelabl(self, which="y"):
    if which=="y":
        lab = self.plot.yaxis.label
        text = self.ylabl.text()
    else:
        lab = self.plot.xaxis.label
        text = self.xlabl.text()
    try:
        lab.set_text(text)
        lab._get_layout(self.figure.canvas.renderer)
    except:
        pass
    else:
        self.win.draw_idle()

Полный пример будет выглядеть так. (Обратите внимание, что здесь я использовал Qt4, он должен работать как есть, заменяя все 4 на 5.)

import sys
######Qt5
from PyQt4 import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
###matplotlib
#import matplotlib
#matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from matplotlib.widgets import Cursor


class Main_window(QWidget):

    def __init__(self):
        QWidget.__init__(self) 
        self.initUI() 

    def initUI(self):
        ### 1 we create the grid
        self.Global = QVBoxLayout(self)
        self.setLayout(self.Global)
        grid= QGridLayout()
        self.Global.addLayout(grid)

        ### a- space for plot
        self.tab = QTabWidget()
        self.figure = Figure()
        self.figure.subplots_adjust(hspace = 0, right=0.95, top=0.94, left=0.15)
        self.win = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.win, self.win)
        grid.addWidget(self.win,0, 0, 1, 3)
        grid.addWidget(self.toolbar, 1, 0, 1, 3)
        self.plot = self.figure.add_subplot(111)
        self.plot.legend()

        #### c- labels
        self.xlabl = QLineEdit('qwdwed')
        grid.addWidget(self.xlabl, 3, 1, 1, 1)
        self.xlabl.textChanged.connect(lambda : self.changelabl("x"))
        self.xlabl.setText('Xlabel')

        self.ylabl = QLineEdit('qwdwed')
        grid.addWidget(self.ylabl, 3, 2, 1, 1)
        self.ylabl.textChanged.connect(lambda : self.changelabl("y"))
        self.ylabl.setText('Ylabel')

        self.win.draw()
        self.show()


    def changelabl(self, which="y"):
        if which=="y":
            lab = self.plot.yaxis.label
            text = self.ylabl.text()
        else:
            lab = self.plot.xaxis.label
            text = self.xlabl.text()
        try:
            lab.set_text(text)
            lab._get_layout(self.figure.canvas.renderer)
        except:
            pass
        else:
            self.win.draw_idle()


app = QApplication(sys.argv)
main = Main_window()
sys.exit(app.exec_())
person ImportanceOfBeingErnest    schedule 02.02.2018