From 861bb001c98c134cf4729bd986a9848266584f46 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Thu, 28 Mar 2024 22:52:14 +0100 Subject: [PATCH] Changing version, making Helper objects globals, adding their polyfills. --- LogarithmPlotter/__init__.py | 4 +- LogarithmPlotter/logarithmplotter.py | 9 +- .../LogarithmPlotter/LogarithmPlotter.qml | 11 +-- .../LogarithmPlotter/js/history/common.mjs | 2 +- .../LogarithmPlotter/js/lib/qmlpolyfills.mjs | 27 +++++- .../ad5001/LogarithmPlotter/js/math/latex.mjs | 7 +- LogarithmPlotter/util/latex.py | 88 ++++++++----------- 7 files changed, 77 insertions(+), 71 deletions(-) diff --git a/LogarithmPlotter/__init__.py b/LogarithmPlotter/__init__.py index 4b060a5..d8bb713 100644 --- a/LogarithmPlotter/__init__.py +++ b/LogarithmPlotter/__init__.py @@ -17,8 +17,8 @@ """ from shutil import which -__VERSION__ = "0.5.0" -is_release = True +__VERSION__ = "0.5.1" +is_release = False # Check if development version, if so get the date of the latest git patch diff --git a/LogarithmPlotter/logarithmplotter.py b/LogarithmPlotter/logarithmplotter.py index 28affed..f8670bd 100644 --- a/LogarithmPlotter/logarithmplotter.py +++ b/LogarithmPlotter/logarithmplotter.py @@ -112,9 +112,12 @@ def run(): global tmpfile helper = Helper(pwd, tmpfile) latex = Latex(tempdir) - engine.globalObject().setProperty('Runtime', engine.newObject()) - engine.rootContext().setContextProperty("Helper", helper) - engine.rootContext().setContextProperty("Latex", latex) + modules = engine.newObject() + engine.globalObject().setProperty('Runtime', modules) + engine.globalObject().setProperty('Helper', engine.newQObject(helper)) + engine.globalObject().setProperty("Latex", engine.newQObject(latex)) + # engine.rootContext().setContextProperty("Helper", helper) + # engine.rootContext().setContextProperty("Latex", latex) engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv) engine.rootContext().setContextProperty("StartTime", dep_time) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml index 1715967..4c9a7b4 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -44,16 +44,7 @@ ApplicationWindow { color: sysPalette.window title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") - SystemPalette { - id: sysPalette; colorGroup: SystemPalette.Active - - Component.onCompleted: { - // LatexJS initialization. - Runtime.Latex.enabled = Helper.getSettingBool("enable_latex") - Runtime.Latex.Renderer = Latex - Runtime.Latex.defaultColor = sysPalette.windowText - } - } + SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } menuBar: appMenu.trueItem diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs index d9a3bc8..78476ca 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/history/common.mjs @@ -108,7 +108,7 @@ export class Action { if(!Latex.enabled) throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") let imgDepth = Runtime.History.imageDepth - let [src, width, height] = Latex.Renderer.render( + let [src, width, height] = Latex.render( latexString, imgDepth * (Runtime.History.fontSize + 2), Runtime.History.themeTextColor diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs index d3214f7..a940967 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/lib/qmlpolyfills.mjs @@ -27,4 +27,29 @@ qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); } /** @type {function(string, string): string} */ QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); } /** @type {function(string|boolean|int): string} */ -String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } \ No newline at end of file +String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } + +/** Typehints for Helper. */ +const Helper = { + /** @type {function(string): boolean} */ + getSettingBool: (setting) => true, + /** @type {function(string): int} */ + getSettingInt: (setting) => 0, + /** @type {function(string): string} */ + getSetting: (setting) => '', + /** @type {function(string, boolean)} */ + setSettingBool: (setting, value) => {}, + /** @type {function(string, int)} */ + setSettingInt: (setting, value) => 0, + /** @type {function(string, string)} */ + setSetting: (setting, value) => '', + /** @type {function(string, string)} */ + write: (filename, data) => {}, + /** @type {function(string): string} */ + load: (filename) => '', +} + +const Latex = { + /** @type {function(string, number, string): string} */ + render: (latex_markup, font_size, color) => '', +} \ No newline at end of file diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs index c7975e7..ef1f862 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.mjs @@ -50,11 +50,12 @@ class LatexAPI extends RuntimeAPI { /** * true if latex has been enabled by the user, false otherwise. */ - this.enabled = false + this.enabled = Helper.getSettingBool("enable_latex") /** - * LaTeX python backend QObject. + * Mirror method for Python object. + * @type {function(string, number, string): string}. */ - this.Renderer = null + this.render = Latex.render } /** diff --git a/LogarithmPlotter/util/latex.py b/LogarithmPlotter/util/latex.py index ce22bb7..d519e18 100644 --- a/LogarithmPlotter/util/latex.py +++ b/LogarithmPlotter/util/latex.py @@ -51,17 +51,19 @@ $$$$ $markup $$$$ \end{document} """) - + + class Latex(QObject): """ Base class to convert Latex equations into PNG images with custom font color and size. It doesn't have any python dependency, but requires a working latex installation and dvipng to be installed on the system. """ + def __init__(self, tempdir: TemporaryDirectory): QObject.__init__(self) self.tempdir = tempdir - + def check_latex_install(self): """ Checks if the current latex installation is valid. @@ -69,22 +71,23 @@ class Latex(QObject): if LATEX_PATH is None: print("No Latex installation found.") if "--test-build" not in argv: - QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "No Latex installation found.\nIf you already have a latex distribution installed, make sure it's installed on your path.\nOtherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/.")) + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", + QCoreApplication.translate("latex", "No Latex installation found.\nIf you already have a latex distribution installed, make sure it's installed on your path.\nOtherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/.")) elif DVIPNG_PATH is None: print("DVIPNG not found.") if "--test-build" not in argv: - QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "DVIPNG was not found. Make sure you include it from your Latex distribution.")) - + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "DVIPNG was not found. Make sure you include it from your Latex distribution.")) + @Property(bool) def latexSupported(self): return LATEX_PATH is not None and DVIPNG_PATH is not None - + @Slot(str, float, QColor, result=str) def render(self, latex_markup: str, font_size: float, color: QColor) -> str: """ Prepares and renders a latex string into a png file. """ - markup_hash = "render"+str(hash(latex_markup)) + markup_hash = "render" + str(hash(latex_markup)) export_path = path.join(self.tempdir.name, f'{markup_hash}_{int(font_size)}_{color.rgb()}') if self.latexSupported and not path.exists(export_path + ".png"): print("Rendering", latex_markup, export_path) @@ -101,21 +104,21 @@ class Latex(QObject): # self.convert_dvi_to_png(latex_path, export_path+"@2", font_size*2, color) # self.convert_dvi_to_png(latex_path, export_path+"@3", font_size*3, color) # self.convert_dvi_to_png(latex_path, export_path+"@4", font_size*4, color) - except Exception as e: # One of the processes failed. A message will be sent every time. + except Exception as e: # One of the processes failed. A message will be sent every time. raise e - img = QImage(export_path); + img = QImage(export_path) # Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded return f'{export_path}.png,{img.width()},{img.height()}' - + def create_latex_doc(self, export_path: str, latex_markup: str): """ Creates a temporary latex document with base file_hash as file name and a given expression markup latex_markup. """ ltx_path = export_path + ".tex" f = open(export_path + ".tex", 'w') - f.write(DEFAULT_LATEX_DOC.substitute(markup = latex_markup)) + f.write(DEFAULT_LATEX_DOC.substitute(markup=latex_markup)) f.close() - + def convert_latex_to_dvi(self, export_path: str): """ Converts a TEX file to a DVI file. @@ -124,7 +127,7 @@ class Latex(QObject): LATEX_PATH, export_path + ".tex" ]) - + def convert_dvi_to_png(self, dvi_path: str, export_path: str, font_size: float, color: QColor): """ Converts a DVI file to a PNG file. @@ -135,61 +138,44 @@ class Latex(QObject): depth = int(font_size * 72.27 / 100) * 10 self.run([ DVIPNG_PATH, - '-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space. - '--truecolor', # Make sure it's rendered in 24 bit colors. - '-D',f'{depth}', # Depth of the image - '-bg', 'Transparent', # Transparent background - '-fg',f'{fg}', # Foreground of the wanted color. - f'{dvi_path}.dvi', # Input file - '-o',f'{export_path}.png', # Output file + '-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space. + '--truecolor', # Make sure it's rendered in 24 bit colors. + '-D', f'{depth}', # Depth of the image + '-bg', 'Transparent', # Transparent background + '-fg', f'{fg}', # Foreground of the wanted color. + f'{dvi_path}.dvi', # Input file + '-o', f'{export_path}.png', # Output file ]) - + def run(self, process: list): """ Runs a subprocess and handles exceptions and messages them to the user. """ proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir.name) try: - out, err = proc.communicate(timeout=2) # 2 seconds is already FAR too long. + out, err = proc.communicate(timeout=2) # 2 seconds is already FAR too long. if proc.returncode != 0: # Process errored - QMessageBox.warning(None, "LogarithmPlotter - Latex", - QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' ended with a non-zero return code {}:\n\n{}\nPlease make sure your latex installation is correct and report a bug if so.") - .format(" ".join(process), proc.returncode, str(out, 'utf8')+"\n"+str(err,'utf8'))) - raise Exception(" ".join(process) + " process exited with return code " + str(proc.returncode) + ":\n" + str(out, 'utf8')+"\n"+str(err,'utf8')) + QMessageBox.warning(None, "LogarithmPlotter - Latex", + QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' ended with a non-zero return code {}:\n\n{}\nPlease make sure your latex installation is correct and report a bug if so.") + .format(" ".join(process), proc.returncode, + str(out, 'utf8') + "\n" + str(err, 'utf8'))) + raise Exception( + " ".join(process) + " process exited with return code " + str(proc.returncode) + ":\n" + str(out, + 'utf8') + "\n" + str( + err, 'utf8')) except TimeoutExpired as e: # Process timed out proc.kill() out, err = proc.communicate() QMessageBox.warning(None, "LogarithmPlotter - Latex", QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' took too long to finish:\n{}\nPlease make sure your latex installation is correct and report a bug if so.") - .format(" ".join(process), str(out, 'utf8')+"\n"+str(err,'utf8'))) - raise Exception(" ".join(process) + " process timed out:\n" + str(out, 'utf8')+"\n"+str(err,'utf8')) - - def cleanup(self, export_path): + .format(" ".join(process), str(out, 'utf8') + "\n" + str(err, 'utf8'))) + raise Exception(" ".join(process) + " process timed out:\n" + str(out, 'utf8') + "\n" + str(err, 'utf8')) + + def cleanup(self, export_path): """ Removes auxiliary, logs and Tex temporary files. """ for i in [".tex", ".aux", ".log"]: remove(export_path + i) - - """ - @Slot(str, float, QColor, result=str) - def render_legacy(self, latexstring, font_size, color = True): - exprpath = path.join(self.tempdir.name, f'{hash(latexstring)}_{font_size}_{color.rgb()}.png') - print("Rendering", latexstring, exprpath) - if not path.exists(exprpath): - fg = color.convertTo(QColor.Rgb) - fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}' - preview('$${' + latexstring + '}$$', viewer='file', filename=exprpath, dvioptions=[ - "-T", "tight", - "-z", "0", - "--truecolor", - f"-D {int(font_size * 72.27 / 100) * 10}", # See https://linux.die.net/man/1/dvipng#-D for convertion - "-bg", "Transparent", - "-fg", fg], - euler=False) - img = QImage(exprpath); - # Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded - return f'{exprpath},{img.width()},{img.height()}' - """