diff --git a/.gitignore b/.gitignore index a60c4d3..8802201 100644 --- a/.gitignore +++ b/.gitignore @@ -22,14 +22,15 @@ linux/flatpak/.flatpak-builder **/__pycache__/ .ropeproject .vscode -*.kdev4 -.kdev4 -.coverage build docs/html .directory +*.kdev4 *.lpf *.lgg *.spec +.kdev4 +AccountFree.pro +AccountFree.pro.user *.egg-info/ *.tar.gz diff --git a/LogarithmPlotter/__init__.py b/LogarithmPlotter/__init__.py index b3140ad..9c675a2 100644 --- a/LogarithmPlotter/__init__.py +++ b/LogarithmPlotter/__init__.py @@ -20,13 +20,13 @@ from shutil import which __VERSION__ = "0.6.0" is_release = False + # Check if development version, if so get the date of the latest git patch # and append it to the version string. if not is_release and which('git') is not None: from os.path import realpath, join, dirname, exists from subprocess import check_output from datetime import datetime - # Command to check date of latest git commit cmd = ['git', 'log', '--format=%ci', '-n 1'] cwd = realpath(join(dirname(__file__), '..')) # Root AccountFree directory. @@ -41,5 +41,4 @@ if not is_release and which('git') is not None: if __name__ == "__main__": from .logarithmplotter import run - run() diff --git a/LogarithmPlotter/logarithmplotter.py b/LogarithmPlotter/logarithmplotter.py index ae908f5..1af01d1 100644 --- a/LogarithmPlotter/logarithmplotter.py +++ b/LogarithmPlotter/logarithmplotter.py @@ -87,6 +87,7 @@ def run(): icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom"))) QIcon.setFallbackSearchPaths(icon_fallbacks); + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QApplication(argv) app.setApplicationName("LogarithmPlotter") app.setDesktopFileName("eu.ad5001.LogarithmPlotter.desktop") @@ -103,10 +104,10 @@ def run(): app.installTranslator(translator); # Installing macOS file handler. - macos_file_open_handler = None + macOSFileOpenHandler = None if platform == "darwin": - macos_file_open_handler = native.MacOSFileOpenHandler() - app.installEventFilter(macos_file_open_handler) + macOSFileOpenHandler = native.MacOSFileOpenHandler() + app.installEventFilter(macOSFileOpenHandler) engine = QQmlApplicationEngine() global tmpfile @@ -137,7 +138,7 @@ def run(): chdir(path.dirname(path.realpath(__file__))) if platform == "darwin": - macos_file_open_handler.init_io(js_globals.Modules.IO) + macOSFileOpenHandler.init_io(js_globals.Modules.IO) # Check for LaTeX installation if LaTeX support is enabled if config.getSetting("enable_latex"): diff --git a/LogarithmPlotter/util/config.py b/LogarithmPlotter/util/config.py index d295caa..5c2c653 100644 --- a/LogarithmPlotter/util/config.py +++ b/LogarithmPlotter/util/config.py @@ -21,6 +21,7 @@ from platform import system from json import load, dumps from PySide6.QtCore import QLocale, QTranslator + DEFAULT_SETTINGS = { "check_for_updates": True, "reset_redo_stack": True, @@ -37,7 +38,7 @@ DEFAULT_SETTINGS = { "default_graph": { "xzoom": 100, "yzoom": 10, - "xmin": 5 / 10, + "xmin": 5/10, "ymax": 25, "xaxisstep": "4", "yaxisstep": "4", @@ -53,48 +54,40 @@ DEFAULT_SETTINGS = { # Create config directory CONFIG_PATH = { - "Linux": path.join(environ["XDG_CONFIG_HOME"], "LogarithmPlotter") - if "XDG_CONFIG_HOME" in environ else - path.join(path.expanduser("~"), ".config", "LogarithmPlotter"), + "Linux": path.join(environ["XDG_CONFIG_HOME"], "LogarithmPlotter") if "XDG_CONFIG_HOME" in environ else path.join(path.expanduser("~"), ".config", "LogarithmPlotter"), "Windows": path.join(path.expandvars('%APPDATA%'), "LogarithmPlotter", "config"), "Darwin": path.join(path.expanduser("~"), "Library", "Application Support", "LogarithmPlotter"), }[system()] CONFIG_FILE = path.join(CONFIG_PATH, "config.json") +initialized = False current_config = DEFAULT_SETTINGS -class UnknownNamespaceError(Exception): pass - - def init(): """ Initializes the config and loads all possible settings from the file if needs be. """ - global current_config - current_config = DEFAULT_SETTINGS makedirs(CONFIG_PATH, exist_ok=True) if path.exists(CONFIG_FILE): - cfg_data = load(open(CONFIG_FILE, 'r', -1, 'utf8')) + cfg_data = load(open(CONFIG_FILE, 'r', -1, 'utf8')) for setting_name in cfg_data: if type(cfg_data[setting_name]) == dict: for setting_name2 in cfg_data[setting_name]: - setSetting(setting_name + "." + setting_name2, cfg_data[setting_name][setting_name2]) + setSetting(setting_name+"."+setting_name2, cfg_data[setting_name][setting_name2]) else: setSetting(setting_name, cfg_data[setting_name]) - - -def save(file=CONFIG_FILE): + +def save(): """ Saves the config to the path. """ - write_file = open(file, 'w', -1, 'utf8') + write_file = open(CONFIG_FILE, 'w', -1, 'utf8') write_file.write(dumps(current_config)) write_file.close() - def getSetting(namespace): """ Returns a setting from a namespace. @@ -108,10 +101,9 @@ def getSetting(namespace): setting = setting[name] else: # return namespace # Return original name - raise UnknownNamespaceError(f"Setting {namespace} doesn't exist. Debug: {setting}, {name}") + raise ValueError('Setting ' + namespace + ' doesn\'t exist. Debug: ', setting, name) return setting - def setSetting(namespace, data): """ Sets a setting at a namespace with data. @@ -125,6 +117,6 @@ def setSetting(namespace, data): if name in setting: setting = setting[name] else: - raise UnknownNamespaceError(f"Setting {namespace} doesn't exist. Debug: {setting}, {name}") + raise ValueError('Setting {} doesn\'t exist. Debug: {}, {}'.format(namespace, setting, name)) else: setting[name] = data diff --git a/LogarithmPlotter/util/helper.py b/LogarithmPlotter/util/helper.py index c7c92d8..47e7395 100644 --- a/LogarithmPlotter/util/helper.py +++ b/LogarithmPlotter/util/helper.py @@ -32,9 +32,6 @@ from LogarithmPlotter import __VERSION__ from LogarithmPlotter.util import config -class InvalidFileException(Exception): pass - - class ChangelogFetcher(QRunnable): def __init__(self, helper): QRunnable.__init__(self) @@ -97,16 +94,16 @@ class Helper(QObject): pass elif data[:3] == "LPF": # More recent version of LogarithmPlotter file, but incompatible with the current format - msg = QCoreApplication.translate("This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}.\nPlease update LogarithmPlotter to open this file.") - raise InvalidFileException(msg.format(__VERSION__)) + raise Exception(QCoreApplication.translate("This file was created by a more recent version of LogarithmPlotter and cannot be backloaded in LogarithmPlotter v{}.\nPlease update LogarithmPlotter to open this file.".format(__VERSION__))) else: raise Exception("Invalid LogarithmPlotter file.") except Exception as e: # If file can't be loaded - msg = QCoreApplication.translate('main', 'Could not open file "{}":\n{}') - QMessageBox.warning(None, 'LogarithmPlotter', msg.format(filename, e), QMessageBox.Ok) # Cannot parse file + QMessageBox.warning(None, 'LogarithmPlotter', + QCoreApplication.translate('main', 'Could not open file "{}":\n{}').format(filename, + e), + QMessageBox.Ok) # Cannot parse file else: - msg = QCoreApplication.translate('main', 'Could not open file: "{}"\nFile does not exist.') - QMessageBox.warning(None, 'LogarithmPlotter', msg.format(filename), QMessageBox.Ok) # Cannot parse file + QMessageBox.warning(None, 'LogarithmPlotter', QCoreApplication.translate('main', 'Could not open file: "{}"\nFile does not exist.').format(filename), QMessageBox.Ok) # Cannot parse file try: chdir(path.dirname(path.realpath(__file__))) except NotADirectoryError as e: @@ -134,6 +131,7 @@ class Helper(QObject): @Slot(str, result=float) def getSettingInt(self, namespace): + print('Getting', namespace, config.getSetting(namespace)) return config.getSetting(namespace) @Slot(str, result=bool) @@ -161,8 +159,9 @@ class Helper(QObject): """ Returns the version info about Qt, PySide6 & Python """ - msg = QCoreApplication.translate('main', "Built with PySide6 (Qt) v{} and python v{}") - return msg.format(PySide6_version, sys_version.split("\n")[0]) + return QCoreApplication.translate('main', "Built with PySide6 (Qt) v{} and python v{}").format(PySide6_version, + sys_version.split( + "\n")[0]) @Slot() def fetchChangelog(self): diff --git a/LogarithmPlotter/util/js.py b/LogarithmPlotter/util/js.py index b00c645..4514a42 100644 --- a/LogarithmPlotter/util/js.py +++ b/LogarithmPlotter/util/js.py @@ -18,13 +18,11 @@ from PySide6.QtQml import QJSValue -class InvalidAttributeValueException(Exception): pass class PyJSValue: """ Wrapper to provide easy way to interact with JavaScript values in Python directly. """ - def __init__(self, js_value: QJSValue, parent: QJSValue = None): self.qjs_value = js_value self._parent = parent @@ -39,32 +37,16 @@ class PyJSValue: elif isinstance(value, PyJSValue): # Set property self.qjs_value.setProperty(key, value.qjs_value) - elif isinstance(value, QJSValue): + else: + print('Setting', key, value) self.qjs_value.setProperty(key, value) - elif type(value) in (int, float, str, bool): - self.qjs_value.setProperty(key, QJSValue(value)) - else: - raise InvalidAttributeValueException(f"Invalid value {value} of type {type(value)} being set to {key}.") - - def __eq__(self, other): - if isinstance(other, PyJSValue): - return self.qjs_value.equals(other.qjs_value) - elif isinstance(other, QJSValue): - return self.qjs_value.equals(other) - elif type(other) in (int, float, str, bool): - return self.qjs_value.equals(QJSValue(other)) - else: - return False def __call__(self, *args, **kwargs): - value = None if self.qjs_value.isCallable(): if self._parent is None: - value = self.qjs_value.call(args) + return self.qjs_value.call(args) else: - value = self.qjs_value.callWithInstance(self._parent, args) + return self.qjs_value.callWithInstance(self._parent, args) else: - raise InvalidAttributeValueException('Cannot call non-function JS value.') - if isinstance(value, QJSValue): - value = PyJSValue(value) - return value + raise ValueError('Cannot call non-function JS value.') + diff --git a/LogarithmPlotter/util/latex.py b/LogarithmPlotter/util/latex.py index 6b31359..166e224 100644 --- a/LogarithmPlotter/util/latex.py +++ b/LogarithmPlotter/util/latex.py @@ -66,11 +66,11 @@ class Latex(QObject): self.tempdir = tempdir @Property(bool) - def latexSupported(self) -> bool: + def latexSupported(self): return LATEX_PATH is not None and DVIPNG_PATH is not None @Slot(result=bool) - def checkLatexInstallation(self) -> bool: + def checkLatexInstallation(self): """ Checks if the current latex installation is valid. """ @@ -78,22 +78,19 @@ class Latex(QObject): if LATEX_PATH is None: print("No Latex installation found.") 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) + 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/.")) valid_install = False elif DVIPNG_PATH is None: print("DVIPNG not found.") 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) + QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "DVIPNG was not found. Make sure you include it from your Latex distribution.")) valid_install = False else: try: self.render("", 14, QColor(0, 0, 0, 255)) except Exception as e: - valid_install = False # Should have sent an error message if failed to render + valid_install = False # Should have sent an error message if failed to render return valid_install @Slot(str, float, QColor, result=str) @@ -122,9 +119,9 @@ class Latex(QObject): 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()}' - + @Slot(str, float, QColor, result=str) - def findPrerendered(self, latex_markup: str, font_size: float, color: QColor) -> str: + def findPrerendered(self, latex_markup: str, font_size: float, color: QColor): """ Finds a prerendered image and returns its data if possible, and an empty string if not. """ @@ -134,7 +131,8 @@ class Latex(QObject): img = QImage(export_path) data = f'{export_path}.png,{img.width()},{img.height()}' return data - + + def create_export_path(self, latex_markup: str, font_size: float, color: QColor): """ Standardizes export path for renders. @@ -142,6 +140,7 @@ class Latex(QObject): markup_hash = "render" + str(hash(latex_markup)) export_path = path.join(self.tempdir.name, f'{markup_hash}_{int(font_size)}_{color.rgb()}') return markup_hash, export_path + def create_latex_doc(self, export_path: str, latex_markup: str): """ @@ -184,18 +183,16 @@ class Latex(QObject): """ Runs a subprocess and handles exceptions and messages them to the user. """ - cmd = " ".join(process) 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. if proc.returncode != 0: # Process errored 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.") - 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')}") + 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, output)) + raise Exception("{0} process exited with return code {1}:\n{2}\n{3}".format(" ".join(process), str(proc.returncode), str(out, 'utf8'), str(err, 'utf8'))) except TimeoutExpired as e: # Process timed out proc.kill() @@ -205,14 +202,14 @@ class Latex(QObject): for pkg in PACKAGES: if f'{pkg}.sty' in output: # 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.") - QMessageBox.warning(None, "LogarithmPlotter - Latex", msg.format(pkg, pkg)) + QMessageBox.warning(None, "LogarithmPlotter - Latex", + 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.") + .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.") - QMessageBox.warning(None, "LogarithmPlotter - Latex", msg.format(cmd, output)) - raise Exception(f"{cmd} process timed out:\n{output}") + 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), output)) + raise Exception(" ".join(process) + " process timed out:\n" + output) def cleanup(self, export_path): """ diff --git a/LogarithmPlotter/util/update.py b/LogarithmPlotter/util/update.py index ed2b5ba..dbacbfe 100644 --- a/LogarithmPlotter/util/update.py +++ b/LogarithmPlotter/util/update.py @@ -53,8 +53,9 @@ class UpdateCheckerRunnable(QRunnable): current_version_tuple = self.current_version.split(".") is_version_newer = version_tuple > current_version_tuple if is_version_newer: - msg_text = QCoreApplication.translate("update", "An update for LogarithmPlotter (v{}) is available.") - msg_text = msg_text.format(version) + msg_text = QCoreApplication.translate("update", + "An update for LogarithPlotter (v{}) is available.").format( + version) update_available = True else: show_alert = False @@ -62,11 +63,11 @@ class UpdateCheckerRunnable(QRunnable): except HTTPError as e: msg_text = QCoreApplication.translate("update", - "Could not fetch update information: Server error {}.") - msg_text = msg_text.format(str(e.code)) + "Could not fetch update information: Server error {}.").format( + str(e.code)) except URLError as e: - msg_text = QCoreApplication.translate("update", "Could not fetch update information: {}.") - msg_text = msg_text.format(str(e.reason)) + msg_text = QCoreApplication.translate("update", "Could not fetch update information: {}.").format( + str(e.reason)) self.callback.got_update_info.emit(show_alert, msg_text, update_available) diff --git a/README.md b/README.md index 0745682..7f092e3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,11 @@ For all builds, you need [Python 3](https://python.org) with [PySide6](https://p - To build the snap, you need [snapcraft](https://snapcraft.io) installed. - Run `package-linux.sh`. + +### Linux + +Run `bash linux/install_local.sh` + ## Contribute There are several ways to contribute to LogarithmPlotter. @@ -49,14 +54,6 @@ There are several ways to contribute to LogarithmPlotter. - You can help the development of LogarithmPlotter. In order to get started, take a look at the [wiki](https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/_pages). -## Tests - -To run LogarithmPlotter's test, use the following: - -- Python - - Install `pytest` and `pytest-cov` - - Run `pytest --cov` - ## Legal notice LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions. Copyright (C) 2021-2024 Ad5001 diff --git a/tests/python/test_config.py b/tests/python/test_config.py deleted file mode 100644 index 493b0f1..0000000 --- a/tests/python/test_config.py +++ /dev/null @@ -1,10 +0,0 @@ -import unittest - - -class MyTestCase(unittest.TestCase): - def test_something(self): - self.assertEqual(True, False) # add assertion here - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/python/test_helper.py b/tests/python/test_helper.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/python/test_pyjs.py b/tests/python/test_pyjs.py deleted file mode 100644 index e69de29..0000000