恢复 Latex 编译错误
问题描述
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 + "}
"
return p
start = "\documentclass[12pt,a4paper,french]{article}
\usepackage[utf8]{inputenc}
"
start = start + preambule('amsmath','graphicx')
start = start + "\begin{document}
"
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"egin{document}",
content,
r"end{document}",
)
return "
".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()
解决方案
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"egin{document}",
content,
r"end{document}",
)
return "
".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
相关文章