Recovering Latex Compilation Errors

﹥>﹥吖頭↗ 提交于 2020-07-16 07:36:50

问题


I have a program to write a text and when I click on the 'compile' button, it compiles to latex, converts to pdf and displays it on my application. The problem is that when I have a compilation error, the application bugs and that's it. I'd like to know if it's possible to recover compilation errors without the application crashing. I tried with try/expect but it's not a python error so it doesn't work.

You will need PDF.js to view the pdf https://mozilla.github.io/pdf.js/getting_started/#download

Ui :

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(841, 481)
        self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setMinimumSize(QtCore.QSize(0, 200))
        self.textEdit.setObjectName("textEdit")
        self.gridLayout.addWidget(self.textEdit, 0, 0, 1, 1)
        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        self.buttonBox.setOrientation(QtCore.Qt.Vertical)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName("buttonBox")
        self.gridLayout.addWidget(self.buttonBox, 0, 1, 1, 1)
        #self.textEdit_2 = QtWidgets.QTextEdit(Dialog)
        #self.textEdit_2.setObjectName("textEdit_2")
        #self.gridLayout.addWidget(self.textEdit_2, 1, 0, 1, 1)
        self.horizontalLayout.addLayout(self.gridLayout)

        self.retranslateUi(Dialog)
        self.buttonBox.accepted.connect(Dialog.accept)
        self.buttonBox.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))

app:

import os
import sys
from PyQt5.QtWidgets import QDialog, QPushButton, QWidget, QApplication
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from sympy import Symbol
import untitled

x=Symbol('x')
class Test(QDialog, untitled.Ui_Dialog):
    def __init__(self):
        super(Test, self).__init__()
        self.setupUi(self)

        self.bouton= QPushButton('compile',self)
        self.horizontalLayout.addWidget(self.bouton)
        self.bouton.clicked.connect(self.crertest)

        self.widget = QWidget(self)
        self.gridLayout.addWidget(self.widget, 1, 0, 1, 1)
        self.t = Window()
        lay = QtWidgets.QVBoxLayout(self.widget)
        lay.addWidget(self.t)

    def crertest(self):
        try :
            def preambule(*packages):
                p = ""
                for i in packages:
                    p = p + "\\usepackage{" + i + "}\n"
                return p
            start = "\\documentclass[12pt,a4paper,french]{article}\n\\usepackage[utf8]{inputenc}\n"
            start = start + preambule('amsmath','graphicx')
            start = start + "\\begin{document}\n"
            end = "\\end{document}"
            body = self.textEdit.toPlainText()

            container = start + body + end
            file = "mypdf.tex"
            if os.path.exists(file):
                os.remove(file)
            fichier = open("mypdf.tex", "x")  #
            fichier.write(container)
            fichier.close()

            instructions = "pdflatex " + file
            os.system(instructions)
            readpdf = "START " + file[:-4] + ".pdf"
            self.t.loadd()
        except:
            print('Fail')

PDFJS = 'file:///C:/Users/pdf_js/web/viewer.html' #Path too viewer.htlm in your pddf_js folder
PDF = 'file:///C:/Users/mypdf.pdf' #Path to your pdf


class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self):
        super(Window, self).__init__()
        self.loadd()
    try:
        def loadd(self):
            self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))
    except:
        print ('Fail 2')
if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Test()
    main.show()
    app.exec_()

I use the code taken from this post for the pdf display : How to render PDF using pdf.js viewer in PyQt?

Exactly like a latex editor like Texmaker does.

Edit.

I put the part of the program that compiles in latex and displays the pdf in another module.

I call the module on my main file, except that I have to compile the program twice to get an update of the display. Sometimes it's after compiling 3/4 times that the bug appeared. I need to add something to run it once ?

compile : test.py

import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
TMP_DIR = os.path.join(CURRENT_DIR, "tmp")
PDFJS = os.path.join(CURRENT_DIR, "3rdParty/pdfjs-2.2.228-dist/web/viewer.html")


def create_document(content, packages):
    lines = (
        r"\documentclass[12pt,a4paper,french]{article}",
        r"\usepackage[utf8]{inputenc}",
        *(r"\usepackage {%s}" % (package,) for package in packages),
        r"\begin{document}",
        content,
        r"\end{document}",
    )
    return "\n".join(lines)


class PdfViewer(QtWebEngineWidgets.QWebEngineView):
    def load_pdf(self, filename):
        url = QtCore.QUrl.fromUserInput(
            "%s?file=%s" % (QtCore.QUrl.fromLocalFile(PDFJS).toString(), filename)
        )
        self.load(url)


class Compile:
    def __init__(self, parent):
        super().__init__()
        self.process = QtCore.QProcess(parent)
        self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
        self.process.finished.connect(self.on_finished)
        self.process.setProgram("pdflatex")

        self.basename = "mypdf"

    def compile(self,body):
        doc = create_document(body, ("amsmath", "graphicx"))
        QtCore.QDir().mkpath(TMP_DIR)
        self.qfile = QtCore.QFile(os.path.join(TMP_DIR, self.basename + ".tex"))
        if self.qfile.open(QtCore.QIODevice.WriteOnly):
            self.qfile.write(doc.encode())
            self.qfile.close()

        self.process.setArguments(
            [
                "-interaction=nonstopmode",
                "-jobname={}".format(self.basename),
                "-output-directory={}".format(TMP_DIR),
                self.qfile.fileName(),
            ]
        )
        self.process.start()

    def on_readyReadStandardOutput(self):
        print(self.process.readAllStandardOutput().data().decode(), end='')

    def on_finished(self):
        self.qfile.remove()

call with :

import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
import test


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.input = QtWidgets.QTextEdit()
        self.input.setText('.')

        self.a = test.Compile(self)
        self.a.compile('.')
        self.b = test.PdfViewer()
        self.b.load_pdf(your PDF path)

        compile_button = QtWidgets.QPushButton(self.tr("Compile"))

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(compile_button, 0, 0, 1, 2)
        lay.addWidget(self.input, 1, 0)
        lay.addWidget(self.b, 1, 1)
        lay.setColumnStretch(0, 1)
        lay.setColumnStretch(1, 1)

        compile_button.clicked.connect(self.call)

    def call(self):
        self.a.compile(self.input.toPlainText())
        self.b.load_pdf(your PDF path)


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.showMaximized()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

回答1:


It is difficult to know the error of your problem since there can be many causes so I have created my example from scratch. To do this you must have the following file structure: At the side of the script there must be a folder called 3rdParty where you must unzip the file downloaded from PDF.js.

├── 3rdParty
│   └── pdfjs-2.2.228-dist
│       ├── build
│       │   ├── pdf.js
│       │   ├── pdf.js.map
│       │   ├── pdf.worker.js
│       │   └── pdf.worker.js.map
│       ├── LICENSE
│       └── web
│           ├── cmaps
│           ├── compressed.tracemonkey-pldi-09.pdf
│           ├── debugger.js
│           ├── images
│           ├── locale
│           ├── viewer.css
│           ├── viewer.html
│           ├── viewer.js
│           └── viewer.js.map
├── main.py
└── tmp

Instead of using os.system () it is better to use a method that gives feedback on the state of the conversion, in this case use QProcess, in addition to adding more options such as "-interaction=nonstopmode", "-jobname=FILENAME" and "-output-directory=DIR" that allow that there is no interactive mode, point to the folder Output and name of pdf:

import os
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
TMP_DIR = os.path.join(CURRENT_DIR, "tmp")
PDFJS = os.path.join(CURRENT_DIR, "3rdParty/pdfjs-2.2.228-dist/web/viewer.html")


def create_document(content, packages):
    lines = (
        r"\documentclass[12pt,a4paper,french]{article}",
        r"\usepackage[utf8]{inputenc}",
        *(r"\usepackage {%s}" % (package,) for package in packages),
        r"\begin{document}",
        content,
        r"\end{document}",
    )
    return "\n".join(lines)


class PdfViewer(QtWebEngineWidgets.QWebEngineView):
    def load_pdf(self, filename):
        url = QtCore.QUrl.fromUserInput(
            "%s?file=%s" % (QtCore.QUrl.fromLocalFile(PDFJS).toString(), filename)
        )
        self.load(url)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.input = QtWidgets.QTextEdit()
        self.output = PdfViewer()

        compile_button = QtWidgets.QPushButton(self.tr("Compile"))

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(compile_button, 0, 0, 1, 2)
        lay.addWidget(self.input, 1, 0)
        lay.addWidget(self.output, 1, 1)
        lay.setColumnStretch(0, 1)
        lay.setColumnStretch(1, 1)

        compile_button.clicked.connect(self.compile)

        self.process = QtCore.QProcess(self)
        self.process.readyReadStandardError.connect(self.on_readyReadStandardError)
        self.process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)
        self.process.finished.connect(self.on_finished)
        self.process.setProgram("pdflatex")

        self.basename = "mypdf"

    def compile(self):
        doc = create_document(self.input.toPlainText(), ("amsmath", "graphicx"))
        QtCore.QDir().mkpath(TMP_DIR)
        self.qfile = QtCore.QFile(os.path.join(TMP_DIR, self.basename + ".tex"))
        if self.qfile.open(QtCore.QIODevice.WriteOnly):
            self.qfile.write(doc.encode())
            self.qfile.close()

        self.process.setArguments(
            [
                "-interaction=nonstopmode",
                "-jobname={}".format(self.basename),
                "-output-directory={}".format(TMP_DIR),
                self.qfile.fileName(),
            ]
        )
        self.process.start()

    def on_readyReadStandardError(self):
        print(self.process.readAllStandardError().data().decode(), end='')

    def on_readyReadStandardOutput(self):
        print(self.process.readAllStandardOutput().data().decode(), end='')

    def on_finished(self):
        self.qfile.remove()
        pdf_filename = os.path.join(TMP_DIR, self.basename + ".pdf")
        self.output.load_pdf(pdf_filename)


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.showMaximized()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Note: try-except only handles the exceptions (not errors) thrown by the python code, not by other programs such as pdflatex. In the case of QProcess the readyReadStandardOutput and readyReadStandardError signal notifies if there is stdout and stderr information from the program(in this case pdflatex), respectively and can be retrieved using the readAllStandardOutput() and readAllStandardError() methods, respectively. That information is printed on the console



来源:https://stackoverflow.com/questions/60503862/recovering-latex-compilation-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!