diff --git a/LogarithmPlotter/util/latex.py b/LogarithmPlotter/util/latex.py index 2c650ce..6b31359 100644 --- a/LogarithmPlotter/util/latex.py +++ b/LogarithmPlotter/util/latex.py @@ -18,12 +18,13 @@ from PySide6.QtCore import QObject, Slot, Property, QCoreApplication from PySide6.QtGui import QImage, QColor -from PySide6.QtWidgets import QMessageBox +from PySide6.QtWidgets import QApplication, QMessageBox from os import path, remove from string import Template from tempfile import TemporaryDirectory from subprocess import Popen, TimeoutExpired, PIPE +from platform import system from shutil import which from sys import argv @@ -35,7 +36,6 @@ If not found, it will send an alert to the user. LATEX_PATH = which('latex') DVIPNG_PATH = which('dvipng') PACKAGES = ["calligra", "amsfonts", "inputenc"] -SHOW_GUI_MESSAGES = "--test-build" not in argv DEFAULT_LATEX_DOC = Template(r""" \documentclass[]{minimal} @@ -54,20 +54,6 @@ $$$$ $markup $$$$ """) -def show_message(msg: str) -> None: - """ - Shows a GUI message if GUI messages are enabled - """ - if SHOW_GUI_MESSAGES: - QMessageBox.warning(None, "LogarithmPlotter - Latex", msg) - - -class MissingPackageException(Exception): pass - - -class RenderError(Exception): pass - - class Latex(QObject): """ Base class to convert Latex equations into PNG images with custom font color and size. @@ -91,20 +77,22 @@ class Latex(QObject): valid_install = True if LATEX_PATH is None: print("No Latex installation found.") - msg = 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/.") - show_message(msg) + if "--test-build" not in argv: + msg = 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", msg) valid_install = False elif DVIPNG_PATH is None: print("DVIPNG not found.") - msg = QCoreApplication.translate("latex", - "DVIPNG was not found. Make sure you include it from your Latex distribution.") - show_message(msg) + if "--test-build" not in argv: + msg = QCoreApplication.translate("latex", + "DVIPNG was not found. Make sure you include it from your Latex distribution.") + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", msg) valid_install = False else: try: self.render("", 14, QColor(0, 0, 0, 255)) - except MissingPackageException: + except Exception as e: valid_install = False # Should have sent an error message if failed to render return valid_install @@ -117,17 +105,20 @@ class Latex(QObject): if self.latexSupported and not path.exists(export_path + ".png"): print("Rendering", latex_markup, export_path) # Generating file - latex_path = path.join(self.tempdir.name, str(markup_hash)) - # If the formula is just recolored or the font is just changed, no need to recreate the DVI. - if not path.exists(latex_path + ".dvi"): - self.create_latex_doc(latex_path, latex_markup) - self.convert_latex_to_dvi(latex_path) - self.cleanup(latex_path) - # Creating four pictures of different sizes to better handle dpi. - self.convert_dvi_to_png(latex_path, export_path, font_size, 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+"@4", font_size*4, color) + try: + latex_path = path.join(self.tempdir.name, str(markup_hash)) + # If the formula is just recolored or the font is just changed, no need to recreate the DVI. + if not path.exists(latex_path + ".dvi"): + self.create_latex_doc(latex_path, latex_markup) + self.convert_latex_to_dvi(latex_path) + self.cleanup(latex_path) + # Creating four pictures of different sizes to better handle dpi. + self.convert_dvi_to_png(latex_path, export_path, font_size, 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+"@4", font_size*4, color) + except Exception as e: # One of the processes failed. A message will be sent every time. + raise e 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()}' @@ -156,6 +147,7 @@ class Latex(QObject): """ 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.close() @@ -201,10 +193,10 @@ class Latex(QObject): output = str(out, 'utf8') + "\n" + str(err, 'utf8') msg = 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.") - show_message(msg.format(cmd, proc.returncode, output)) - raise RenderError( - f"{cmd} process exited with return code {str(proc.returncode)}:\n{str(out, 'utf8')}\n{str(err, 'utf8')}") - except TimeoutExpired: + msg = msg.format(cmd, proc.returncode, output) + QMessageBox.warning(None, "LogarithmPlotter - Latex", msg) + raise Exception(f"{cmd} 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() @@ -215,12 +207,12 @@ class Latex(QObject): # Package missing. msg = QCoreApplication.translate("latex", "Your LaTeX installation does not include some required packages:\n\n- {} (https://ctan.org/pkg/{})\n\nMake sure said package is installed, or disable the LaTeX rendering in LogarithmPlotter.") - show_message(msg.format(pkg, pkg)) - raise MissingPackageException("Latex: Missing package " + pkg) + QMessageBox.warning(None, "LogarithmPlotter - Latex", msg.format(pkg, pkg)) + raise Exception("Latex: Missing package " + pkg) msg = 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.") - show_message(msg.format(cmd, output)) - raise RenderError(f"{cmd} process timed out:\n{output}") + QMessageBox.warning(None, "LogarithmPlotter - Latex", msg.format(cmd, output)) + raise Exception(f"{cmd} process timed out:\n{output}") def cleanup(self, export_path): """ diff --git a/ci/drone.yml b/ci/drone.yml index 5760b95..de4165e 100644 --- a/ci/drone.yml +++ b/ci/drone.yml @@ -14,8 +14,7 @@ steps: - name: Tests image: ad5001/ubuntu-pyside6-xvfb:noble-6.7.2 commands: - - apt install -y texlive-base dvipng texlive-latex-extra # Install latex dependencies. - - bash scripts/run-tests.sh + - pytest --cov --cov-report term-missing - xvfb-run python3 run.py --test-build --no-check-for-updates - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test1.lpf - xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test2.lpf diff --git a/poetry.lock b/poetry.lock index cf83dea..e874544 100644 --- a/poetry.lock +++ b/poetry.lock @@ -315,25 +315,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[[package]] -name = "pytest-qt" -version = "4.4.0" -description = "pytest support for PyQt and PySide applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-qt-4.4.0.tar.gz", hash = "sha256:76896142a940a4285339008d6928a36d4be74afec7e634577e842c9cc5c56844"}, - {file = "pytest_qt-4.4.0-py3-none-any.whl", hash = "sha256:001ed2f8641764b394cf286dc8a4203e40eaf9fff75bf0bfe5103f7f8d0c591d"}, -] - -[package.dependencies] -pluggy = ">=1.1" -pytest = "*" - -[package.extras] -dev = ["pre-commit", "tox"] -doc = ["sphinx", "sphinx-rtd-theme"] - [[package]] name = "pywin32-ctypes" version = "0.2.3" @@ -421,4 +402,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "4693a671e927103ceeb946f688b84fdc56b8b39b2cd772d8d32475e1236d8a07" +content-hash = "8ce304f6a3fbab24428232c1a7d0b59ea412094e82d6b8ce47e4d93462cc235a" diff --git a/pyproject.toml b/pyproject.toml index bf23573..0aff704 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,5 +15,8 @@ PySide6-Essentials = "^6.7.2" pyinstaller = "^6.10.0" pytest = "^8.3.3" pytest-cov = "^5.0.0" -pytest-qt = "^4.4.0" stdeb = "^0.10.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh deleted file mode 100644 index b1a1ec4..0000000 --- a/scripts/run-tests.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -cd "$(dirname "$(readlink -f "$0" || realpath "$0")")/.." - -# Run python tests -pytest --cov --cov-report term-missing - - diff --git a/tests/python/test_latex.py b/tests/python/test_latex.py deleted file mode 100644 index 8f1d8a2..0000000 --- a/tests/python/test_latex.py +++ /dev/null @@ -1,68 +0,0 @@ -import pytest -from tempfile import TemporaryDirectory -from shutil import which -from os.path import exists -from re import match -from PySide6.QtGui import QColor - -from LogarithmPlotter.util import latex - -latex.SHOW_GUI_MESSAGES = False - - -@pytest.fixture() -def latex_obj(): - directory = TemporaryDirectory() - obj = latex.Latex(directory) - if not obj.checkLatexInstallation(): - raise Exception("Cannot run LaTeX tests without a proper LaTeX installation. Make sure to install a LaTeX distribution, DVIPNG, and the calligra package, and run the tests again.") - yield obj - directory.cleanup() - - -class TestLatex: - def test_check_install(self, latex_obj: latex.Latex) -> None: - assert latex_obj.latexSupported == True - assert latex_obj.checkLatexInstallation() == True - bkp = [latex.DVIPNG_PATH, latex.LATEX_PATH] - # Check what happens when one is missing. - latex.DVIPNG_PATH = None - assert latex_obj.latexSupported == False - assert latex_obj.checkLatexInstallation() == False - latex.DVIPNG_PATH = bkp[0] - latex.LATEX_PATH = None - assert latex_obj.latexSupported == False - assert latex_obj.checkLatexInstallation() == False - # Reset - [latex.DVIPNG_PATH, latex.LATEX_PATH] = bkp - - def test_render(self, latex_obj: latex.Latex) -> None: - result = latex_obj.render(r"\frac{d\sqrt{\mathrm{f}(x \times 2.3)}}{dx}", 14, QColor(0, 0, 0, 255)) - # Ensure result format - assert type(result) == str - [path, width, height] = result.split(",") - assert exists(path) - assert match(r"\d+", width) - assert match(r"\d+", height) - # Ensure it returns errors on invalid latex. - with pytest.raises(latex.RenderError): - latex_obj.render(r"\nonexistant", 14, QColor(0, 0, 0, 255)) - # Replace latex bin with one that returns errors - bkp = latex.LATEX_PATH - latex.LATEX_PATH = which("false") - with pytest.raises(latex.RenderError): - latex_obj.render(r"\mathrm{f}(x)", 14, QColor(0, 0, 0, 255)) - latex.LATEX_PATH = bkp - - def test_prerendered(self, latex_obj: latex.Latex) -> None: - args = [r"\frac{d\sqrt{\mathrm{f}(x \times 2.3)}}{dx}", 14, QColor(0, 0, 0, 255)] - latex_obj.render(*args) - prerendered = latex_obj.findPrerendered(*args) - assert type(prerendered) == str - [path, width, height] = prerendered.split(",") - assert exists(path) - assert match(r"\d+", width) - assert match(r"\d+", height) - prerendered2 = latex_obj.findPrerendered(args[0], args[1]+2, args[2]) - assert prerendered2 == "" -