Changing version, making Helper objects globals, adding their polyfills.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Adsooi 2024-03-28 22:52:14 +01:00
parent 08fea34366
commit 861bb001c9
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
7 changed files with 77 additions and 71 deletions

View file

@ -17,8 +17,8 @@
""" """
from shutil import which from shutil import which
__VERSION__ = "0.5.0" __VERSION__ = "0.5.1"
is_release = True is_release = False
# Check if development version, if so get the date of the latest git patch # Check if development version, if so get the date of the latest git patch

View file

@ -112,9 +112,12 @@ def run():
global tmpfile global tmpfile
helper = Helper(pwd, tmpfile) helper = Helper(pwd, tmpfile)
latex = Latex(tempdir) latex = Latex(tempdir)
engine.globalObject().setProperty('Runtime', engine.newObject()) modules = engine.newObject()
engine.rootContext().setContextProperty("Helper", helper) engine.globalObject().setProperty('Runtime', modules)
engine.rootContext().setContextProperty("Latex", latex) 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("TestBuild", "--test-build" in argv)
engine.rootContext().setContextProperty("StartTime", dep_time) engine.rootContext().setContextProperty("StartTime", dep_time)

View file

@ -44,16 +44,7 @@ ApplicationWindow {
color: sysPalette.window color: sysPalette.window
title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*")
SystemPalette { SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
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: sysPaletteIn; colorGroup: SystemPalette.Disabled } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled }
menuBar: appMenu.trueItem menuBar: appMenu.trueItem

View file

@ -108,7 +108,7 @@ export class Action {
if(!Latex.enabled) if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
let imgDepth = Runtime.History.imageDepth let imgDepth = Runtime.History.imageDepth
let [src, width, height] = Latex.Renderer.render( let [src, width, height] = Latex.render(
latexString, latexString,
imgDepth * (Runtime.History.fontSize + 2), imgDepth * (Runtime.History.fontSize + 2),
Runtime.History.themeTextColor Runtime.History.themeTextColor

View file

@ -27,4 +27,29 @@ qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
/** @type {function(string, string): string} */ /** @type {function(string, string): string} */
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); } QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
/** @type {function(string|boolean|int): string} */ /** @type {function(string|boolean|int): string} */
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); } 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) => '',
}

View file

@ -50,11 +50,12 @@ class LatexAPI extends RuntimeAPI {
/** /**
* true if latex has been enabled by the user, false otherwise. * 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
} }
/** /**

View file

@ -51,17 +51,19 @@ $$$$ $markup $$$$
\end{document} \end{document}
""") """)
class Latex(QObject): class Latex(QObject):
""" """
Base class to convert Latex equations into PNG images with custom font color and size. 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 It doesn't have any python dependency, but requires a working latex installation and
dvipng to be installed on the system. dvipng to be installed on the system.
""" """
def __init__(self, tempdir: TemporaryDirectory): def __init__(self, tempdir: TemporaryDirectory):
QObject.__init__(self) QObject.__init__(self)
self.tempdir = tempdir self.tempdir = tempdir
def check_latex_install(self): def check_latex_install(self):
""" """
Checks if the current latex installation is valid. Checks if the current latex installation is valid.
@ -69,22 +71,23 @@ class Latex(QObject):
if LATEX_PATH is None: if LATEX_PATH is None:
print("No Latex installation found.") print("No Latex installation found.")
if "--test-build" not in argv: 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: elif DVIPNG_PATH is None:
print("DVIPNG not found.") print("DVIPNG not found.")
if "--test-build" not in argv: 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) @Property(bool)
def latexSupported(self): def latexSupported(self):
return LATEX_PATH is not None and DVIPNG_PATH is not None return LATEX_PATH is not None and DVIPNG_PATH is not None
@Slot(str, float, QColor, result=str) @Slot(str, float, QColor, result=str)
def render(self, latex_markup: str, font_size: float, color: QColor) -> str: def render(self, latex_markup: str, font_size: float, color: QColor) -> str:
""" """
Prepares and renders a latex string into a png file. 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()}') 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"): if self.latexSupported and not path.exists(export_path + ".png"):
print("Rendering", latex_markup, export_path) 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+"@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+"@3", font_size*3, color)
# self.convert_dvi_to_png(latex_path, export_path+"@4", font_size*4, 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 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 # 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()}' return f'{export_path}.png,{img.width()},{img.height()}'
def create_latex_doc(self, export_path: str, latex_markup: str): 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. Creates a temporary latex document with base file_hash as file name and a given expression markup latex_markup.
""" """
ltx_path = export_path + ".tex" ltx_path = export_path + ".tex"
f = open(export_path + ".tex", 'w') 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() f.close()
def convert_latex_to_dvi(self, export_path: str): def convert_latex_to_dvi(self, export_path: str):
""" """
Converts a TEX file to a DVI file. Converts a TEX file to a DVI file.
@ -124,7 +127,7 @@ class Latex(QObject):
LATEX_PATH, LATEX_PATH,
export_path + ".tex" export_path + ".tex"
]) ])
def convert_dvi_to_png(self, dvi_path: str, export_path: str, font_size: float, color: QColor): 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. Converts a DVI file to a PNG file.
@ -135,61 +138,44 @@ class Latex(QObject):
depth = int(font_size * 72.27 / 100) * 10 depth = int(font_size * 72.27 / 100) * 10
self.run([ self.run([
DVIPNG_PATH, DVIPNG_PATH,
'-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space. '-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. '--truecolor', # Make sure it's rendered in 24 bit colors.
'-D',f'{depth}', # Depth of the image '-D', f'{depth}', # Depth of the image
'-bg', 'Transparent', # Transparent background '-bg', 'Transparent', # Transparent background
'-fg',f'{fg}', # Foreground of the wanted color. '-fg', f'{fg}', # Foreground of the wanted color.
f'{dvi_path}.dvi', # Input file f'{dvi_path}.dvi', # Input file
'-o',f'{export_path}.png', # Output file '-o', f'{export_path}.png', # Output file
]) ])
def run(self, process: list): def run(self, process: list):
""" """
Runs a subprocess and handles exceptions and messages them to the user. Runs a subprocess and handles exceptions and messages them to the user.
""" """
proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir.name) proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir.name)
try: 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: if proc.returncode != 0:
# Process errored # Process errored
QMessageBox.warning(None, "LogarithmPlotter - Latex", 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.") 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'))) .format(" ".join(process), proc.returncode,
raise Exception(" ".join(process) + " process exited with return code " + str(proc.returncode) + ":\n" + str(out, 'utf8')+"\n"+str(err,'utf8')) 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: except TimeoutExpired as e:
# Process timed out # Process timed out
proc.kill() proc.kill()
out, err = proc.communicate() out, err = proc.communicate()
QMessageBox.warning(None, "LogarithmPlotter - Latex", 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.") 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'))) .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')) raise Exception(" ".join(process) + " process timed out:\n" + str(out, 'utf8') + "\n" + str(err, 'utf8'))
def cleanup(self, export_path): def cleanup(self, export_path):
""" """
Removes auxiliary, logs and Tex temporary files. Removes auxiliary, logs and Tex temporary files.
""" """
for i in [".tex", ".aux", ".log"]: for i in [".tex", ".aux", ".log"]:
remove(export_path + i) 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()}'
"""