diff --git a/LogarithmPlotter/__init__.py b/LogarithmPlotter/__init__.py
index 2bd4967..b3140ad 100644
--- a/LogarithmPlotter/__init__.py
+++ b/LogarithmPlotter/__init__.py
@@ -39,3 +39,7 @@ if not is_release and which('git') is not None:
# Date cannot be parsed, not git root?
pass
+if __name__ == "__main__":
+ from .logarithmplotter import run
+
+ run()
diff --git a/LogarithmPlotter/logarithmplotter.py b/LogarithmPlotter/logarithmplotter.py
index bc4d895..d528c02 100644
--- a/LogarithmPlotter/logarithmplotter.py
+++ b/LogarithmPlotter/logarithmplotter.py
@@ -36,8 +36,7 @@ tempdir = TemporaryDirectory()
tmpfile = path.join(tempdir.name, 'graph.png')
pwd = getcwd()
-logarithmplotter_path = path.dirname(path.realpath(__file__))
-chdir(logarithmplotter_path)
+chdir(path.dirname(path.realpath(__file__)))
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
sys_path.append(path.realpath(path.join(getcwd(), "..")))
@@ -92,7 +91,7 @@ def get_platform_qt_style(os) -> str:
def register_icon_directories() -> None:
icon_fallbacks = QIcon.fallbackSearchPaths()
- base_icon_path = path.join(logarithmplotter_path, "qml", "eu", "ad5001", "LogarithmPlotter", "icons")
+ base_icon_path = path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "icons")
paths = [["common"], ["objects"], ["history"], ["settings"], ["settings", "custom"]]
for p in paths:
icon_fallbacks.append(path.realpath(path.join(base_icon_path, *p)))
@@ -107,7 +106,7 @@ def create_qapp() -> QApplication:
app.setDesktopFileName("eu.ad5001.LogarithmPlotter")
app.setOrganizationName("Ad5001")
app.styleHints().setShowShortcutsInContextMenus(True)
- app.setWindowIcon(QIcon(path.realpath(path.join(logarithmplotter_path, "logarithmplotter.svg"))))
+ app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
return app
@@ -116,12 +115,10 @@ def install_translation(app: QApplication) -> QTranslator:
translator = QTranslator()
# Check if lang is forced.
forcedlang = [p for p in argv if p[:7] == "--lang="]
- i18n_path = path.realpath(path.join(logarithmplotter_path, "i18n"))
locale = QLocale(forcedlang[0][7:]) if len(forcedlang) > 0 else QLocale()
- if not translator.load(locale, "lp", "_", i18n_path):
+ if not translator.load(locale, "lp", "_", path.realpath(path.join(getcwd(), "i18n"))):
# Load default translation
- print("Loading default language en...")
- translator.load(QLocale("en"), "lp", "_", i18n_path)
+ translator.load(QLocale("en"), "lp", "_", path.realpath(path.join(getcwd(), "i18n")))
app.installTranslator(translator)
return translator
@@ -136,9 +133,8 @@ def create_engine(helper: Helper, latex: Latex, dep_time: float) -> tuple[QQmlAp
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
engine.rootContext().setContextProperty("StartTime", dep_time)
- qml_path = path.realpath(path.join(logarithmplotter_path, "qml"))
- engine.addImportPath(qml_path)
- engine.load(path.join(qml_path, "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml"))
+ engine.addImportPath(path.realpath(path.join(getcwd(), "qml")))
+ engine.load(path.realpath(path.join(getcwd(), "qml", "eu", "ad5001", "LogarithmPlotter", "LogarithmPlotter.qml")))
return engine, js_globals
diff --git a/LogarithmPlotter/util/helper.py b/LogarithmPlotter/util/helper.py
index d2fe631..c7c92d8 100644
--- a/LogarithmPlotter/util/helper.py
+++ b/LogarithmPlotter/util/helper.py
@@ -24,29 +24,16 @@ from PySide6 import __version__ as PySide6_version
from os import chdir, path
from json import loads
-from sys import version as sys_version, argv
+from sys import version as sys_version
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from LogarithmPlotter import __VERSION__
from LogarithmPlotter.util import config
-SHOW_GUI_MESSAGES = "--test-build" not in argv
-CHANGELOG_VERSION = __VERSION__
-
class InvalidFileException(Exception): pass
-def show_message(msg: str) -> None:
- """
- Shows a GUI message if GUI messages are enabled
- """
- if SHOW_GUI_MESSAGES:
- QMessageBox.warning(None, "LogarithmPlotter", msg, QMessageBox.OK)
- else:
- raise InvalidFileException(msg)
-
-
class ChangelogFetcher(QRunnable):
def __init__(self, helper):
@@ -57,7 +44,7 @@ class ChangelogFetcher(QRunnable):
msg_text = "Unknown changelog error."
try:
# Fetching version
- r = urlopen("https://api.ad5001.eu/changelog/logarithmplotter/?version=" + CHANGELOG_VERSION)
+ r = urlopen("https://api.ad5001.eu/changelog/logarithmplotter/?version=" + __VERSION__)
lines = r.readlines()
r.close()
msg_text = "".join(map(lambda x: x.decode('utf-8'), lines)).strip()
@@ -66,16 +53,21 @@ class ChangelogFetcher(QRunnable):
str(e.code))
except URLError as e:
msg_text = QCoreApplication.translate("changelog", "Could not fetch update: {}.").format(str(e.reason))
- self.helper.changelogFetched.emit(msg_text)
+ self.helper.gotChangelog.emit(msg_text)
class Helper(QObject):
changelogFetched = Signal(str)
+ gotChangelog = Signal(str)
def __init__(self, cwd: str, tmpfile: str):
QObject.__init__(self)
self.cwd = cwd
self.tmpfile = tmpfile
+ self.gotChangelog.connect(self.fetched)
+
+ def fetched(self, changelog: str):
+ self.changelogFetched.emit(changelog)
@Slot(str, str)
def write(self, filename, filedata):
@@ -101,19 +93,20 @@ class Helper(QObject):
if data[:5] == "LPFv1":
# V1 version of the file
data = data[5:]
+ elif data[0] == "{" and "type" in loads(data) and loads(data)["type"] == "logplotv1":
+ pass
elif data[:3] == "LPF":
# More recent version of LogarithmPlotter file, but incompatible with the current format
- msg = QCoreApplication.translate('main',
- "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.")
+ 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__))
else:
- raise InvalidFileException("Invalid LogarithmPlotter file.")
- except InvalidFileException as e: # If file can't be loaded
+ raise Exception("Invalid LogarithmPlotter file.")
+ except Exception as e: # If file can't be loaded
msg = QCoreApplication.translate('main', 'Could not open file "{}":\n{}')
- show_message(msg.format(filename, e)) # Cannot parse file
+ QMessageBox.warning(None, 'LogarithmPlotter', msg.format(filename, e), QMessageBox.Ok) # Cannot parse file
else:
msg = QCoreApplication.translate('main', 'Could not open file: "{}"\nFile does not exist.')
- show_message(msg.format(filename)) # Cannot parse file
+ QMessageBox.warning(None, 'LogarithmPlotter', msg.format(filename), QMessageBox.Ok) # Cannot parse file
try:
chdir(path.dirname(path.realpath(__file__)))
except NotADirectoryError as e:
@@ -137,27 +130,31 @@ class Helper(QObject):
@Slot(str, result=str)
def getSetting(self, namespace):
- return str(config.getSetting(namespace))
+ return config.getSetting(namespace)
@Slot(str, result=float)
def getSettingInt(self, namespace):
- return float(config.getSetting(namespace))
+ return config.getSetting(namespace)
@Slot(str, result=bool)
def getSettingBool(self, namespace):
- return bool(config.getSetting(namespace))
+ return config.getSetting(namespace)
@Slot(str, str)
def setSetting(self, namespace, value):
- return config.setSetting(namespace, str(value))
+ return config.setSetting(namespace, value)
@Slot(str, bool)
def setSettingBool(self, namespace, value):
- return config.setSetting(namespace, bool(value))
+ return config.setSetting(namespace, value)
@Slot(str, float)
def setSettingInt(self, namespace, value):
- return config.setSetting(namespace, float(value))
+ return config.setSetting(namespace, value)
+
+ @Slot(str)
+ def setLanguage(self, new_lang):
+ config.setSetting("language", new_lang)
@Slot(result=str)
def getDebugInfos(self):
@@ -170,6 +167,7 @@ class Helper(QObject):
@Slot()
def fetchChangelog(self):
changelog_cache_path = path.join(path.dirname(path.realpath(__file__)), "CHANGELOG.md")
+ print(changelog_cache_path)
if path.exists(changelog_cache_path):
# We have a cached version of the changelog, for env that don't have access to the internet.
f = open(changelog_cache_path);
diff --git a/LogarithmPlotter/util/update.py b/LogarithmPlotter/util/update.py
index 32017d5..ed2b5ba 100644
--- a/LogarithmPlotter/util/update.py
+++ b/LogarithmPlotter/util/update.py
@@ -89,4 +89,3 @@ def check_for_updates(current_version, window):
runnable = UpdateCheckerRunnable(current_version, update_info)
QThreadPool.globalInstance().start(runnable)
- return update_info
diff --git a/tests/python/test_helper.py b/tests/python/test_helper.py
index 412eac3..c4f6ea7 100644
--- a/tests/python/test_helper.py
+++ b/tests/python/test_helper.py
@@ -17,166 +17,18 @@
"""
import pytest
-from os import getcwd, remove
+from os import getcwd
from os.path import join
from tempfile import TemporaryDirectory
-from json import loads
-from shutil import copy2
-
-from PySide6.QtCore import QObject, Signal, QThreadPool
-from PySide6.QtGui import QImage
-from PySide6.QtWidgets import QApplication
-
-from LogarithmPlotter import __VERSION__ as version
-from LogarithmPlotter.util import config, helper
-from LogarithmPlotter.util.helper import ChangelogFetcher, Helper, InvalidFileException
+from LogarithmPlotter.util import config
pwd = getcwd()
-helper.SHOW_GUI_MESSAGES = False
+
@pytest.fixture()
def temporary():
directory = TemporaryDirectory()
config.CONFIG_PATH = join(directory.name, "config.json")
tmpfile = join(directory.name, "graph.png")
- yield tmpfile, directory
+ yield tmpfile
directory.cleanup()
-
-
-class MockHelperSignals(QObject):
- changelogFetched = Signal(str)
-
- def __init__(self, expect_404):
- QObject.__init__(self)
- self.expect_404 = expect_404
- self.changelogFetched.connect(self.changelog_fetched)
- self.changelog = None
-
- def changelog_fetched(self, changelog):
- self.changelog = changelog
-
-
-class TestChangelog:
-
- def test_exists(self, qtbot):
- helper.CHANGELOG_VERSION = '0.5.0'
- mock_helper = MockHelperSignals(False)
- fetcher = ChangelogFetcher(mock_helper)
- fetcher.run() # Does not raise an exception
- qtbot.waitSignal(mock_helper.changelogFetched, timeout=10000)
- assert type(mock_helper.changelog) == str
- assert '404' not in mock_helper.changelog
-
- def tests_no_exist(self, qtbot):
- mock_helper = MockHelperSignals(True)
- helper.CHANGELOG_VERSION = '1.0.0'
- fetcher = ChangelogFetcher(mock_helper)
- fetcher.run()
- qtbot.waitSignal(mock_helper.changelogFetched, timeout=10000)
- assert type(mock_helper.changelog) == str
- assert '404' in mock_helper.changelog
-
-
-class TestHelper:
- def test_read(self, temporary):
- # Test file reading and information loading.
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- data = obj.load("ci/test1.lpf")
- assert type(data) == str
- data = loads(data)
- assert data['type'] == "logplotv1"
- # Checking data['types'] of valid file.
- # See https://git.ad5001.eu/Ad5001/LogarithmPlotter/wiki/LogarithmPlotter-file-format-v1.0
- assert type(data['width']) == int
- assert type(data['height']) == int
- assert type(data['xzoom']) in (int, float)
- assert type(data['yzoom']) in (int, float)
- assert type(data['xmin']) in (int, float)
- assert type(data['ymax']) in (int, float)
- assert type(data['xaxisstep']) == str
- assert type(data['yaxisstep']) == str
- assert type(data['xaxislabel']) == str
- assert type(data['yaxislabel']) == str
- assert type(data['logscalex']) == bool
- assert type(data['linewidth']) in (int, float)
- assert type(data['showxgrad']) == bool
- assert type(data['showygrad']) == bool
- assert type(data['textsize']) in (int, float)
- assert type(data['history']) == list and len(data['history']) == 2
- assert type(data['history'][0]) == list
- assert type(data['history'][1]) == list
- for action_list in data['history']:
- for action in action_list:
- assert type(action[0]) == str
- assert type(action[1]) == list
- assert type(data['objects']) == dict
- for obj_type, objects in data['objects'].items():
- assert type(obj_type) == str
- assert type(objects) == list
- for obj in objects:
- assert type(obj) == list
-
- def test_read_newer(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- newer_file_path = join(directory.name, "newer.lpf")
- with open(newer_file_path, "w") as f:
- f.write("LPFv2[other invalid data]")
- with pytest.raises(InvalidFileException):
- obj.load(newer_file_path)
-
- def test_read_invalid_file(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- with pytest.raises(InvalidFileException):
- obj.load("./inexistant.lpf")
- with pytest.raises(InvalidFileException):
- obj.load("./pyproject.toml")
-
- def test_write(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- target = join(directory.name, "target.lpf")
- data = "example_data"
- obj.write(target, data)
- with open(target, "r") as f:
- read_data = f.read()
- # Ensure data has been written.
- assert read_data == "LPFv1" + data
-
- def test_tmp_graphic(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- assert obj.gettmpfile() == tmpfile
- obj.copyImageToClipboard()
- clipboard = QApplication.clipboard()
- assert type(clipboard.image()) == QImage
-
- def test_strings(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- assert obj.getVersion() == version
- assert type(obj.getDebugInfos()) == str
- assert type(obj.getSetting("check_for_updates")) == str
- assert type(obj.getSettingInt("check_for_updates")) == float
- assert type(obj.getSettingBool("check_for_updates")) == bool
-
- def test_set_config(self, temporary):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- obj.setSetting("last_install_greet", obj.getSetting("last_install_greet"))
- obj.setSettingBool("check_for_updates", obj.getSettingBool("check_for_updates"))
- obj.setSettingInt("default_graph.xzoom", obj.getSettingInt("default_graph.xzoom"))
-
- def test_fetch_changelog(self, temporary, qtbot):
- tmpfile, directory = temporary
- obj = Helper(pwd, tmpfile)
- copy2("../../CHANGELOG.md", "../../LogarithmPlotter/util/CHANGELOG.md")
- obj.fetchChangelog()
- assert QThreadPool.globalInstance().activeThreadCount() == 0
- qtbot.waitSignal(obj.changelogFetched, timeout=10000)
- remove("../../LogarithmPlotter/util/CHANGELOG.md")
- obj.fetchChangelog()
- assert QThreadPool.globalInstance().activeThreadCount() > 0
- qtbot.waitSignal(obj.changelogFetched, timeout=10000)
\ No newline at end of file
diff --git a/tests/python/test_native.py b/tests/python/test_native.py
deleted file mode 100644
index 6fc2d65..0000000
--- a/tests/python/test_native.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
- * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
- * Copyright (C) 2021-2024 Ad5001
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-"""
-
-import pytest
-from os.path import exists
-
-from PySide6.QtCore import QEvent, QObject, QUrl
-from PySide6.QtGui import QActionEvent, QFileOpenEvent
-
-from LogarithmPlotter.util.native import MacOSFileOpenHandler
-
-
-class LoadDiagramCalledSuccessfully(Exception): pass
-
-
-class MockIO:
- def loadDiagram(self, file_name):
- assert type(file_name) == str
- raise LoadDiagramCalledSuccessfully()
-
-
-class MockFileOpenEvent(QEvent):
- def __init__(self, file):
- QEvent.__init__(self, QEvent.FileOpen)
- self._file = file
-
- def file(self):
- return self._file
-
-
-def test_native():
- event_filter = MacOSFileOpenHandler()
- # Nothing should happen here. The module hasn't been initialized
- event_filter.eventFilter(None, QFileOpenEvent(QUrl.fromLocalFile("ci/test1.lpf")))
- with pytest.raises(LoadDiagramCalledSuccessfully):
- event_filter.init_io(MockIO()) # Now that we've initialized, the loadDiagram function should be called.
- with pytest.raises(LoadDiagramCalledSuccessfully):
- # And now it will do so every time an event is loaded.
- event_filter.eventFilter(None, QFileOpenEvent(QUrl.fromLocalFile("ci/test1.lpf")))
- # Check what happens when a non file open qevent is launched against it.
- event_filter.eventFilter(QObject(), QEvent(QEvent.ActionAdded))
-
diff --git a/tests/python/test_update.py b/tests/python/test_update.py
deleted file mode 100644
index 9f65597..0000000
--- a/tests/python/test_update.py
+++ /dev/null
@@ -1,67 +0,0 @@
-"""
- * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
- * Copyright (C) 2021-2024 Ad5001
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
-"""
-from sys import argv
-
-import pytest
-from PySide6.QtCore import QThreadPool
-
-from LogarithmPlotter import __VERSION__ as version
-from LogarithmPlotter.util.update import UpdateInformation, UpdateCheckerRunnable, check_for_updates
-
-
-class MockWindow:
- def showAlert(self, msg): raise Exception(msg)
- def showUpdateMenu(self, msg): pass
-
-def check_update_callback_type(show_alert, msg_text, update_available):
- assert type(show_alert) == bool
- assert type(msg_text) == str
- assert type(update_available) == bool
-
-def test_update(qtbot):
- def check_older(show_alert, msg_text, update_available):
- check_update_callback_type(show_alert, msg_text, update_available)
- assert update_available
- assert show_alert
-
- def check_newer(show_alert, msg_text, update_available):
- check_update_callback_type(show_alert, msg_text, update_available)
- assert not update_available
- assert not show_alert
-
- update_info_older = UpdateInformation()
- update_info_older.got_update_info.connect(check_older)
- update_info_newer = UpdateInformation()
- update_info_newer.got_update_info.connect(check_newer)
- runnable = UpdateCheckerRunnable('1.0.0', update_info_newer)
- runnable.run()
- qtbot.waitSignal(update_info_newer.got_update_info, timeout=10000)
- runnable = UpdateCheckerRunnable('0.1.0', update_info_older)
- runnable.run()
- qtbot.waitSignal(update_info_older.got_update_info, timeout=10000)
- runnable = UpdateCheckerRunnable('0.5.0+dev0+git20240101', update_info_older)
- runnable.run()
- qtbot.waitSignal(update_info_older.got_update_info, timeout=10000)
-
-def test_update_checker(qtbot):
- update_info = check_for_updates('0.6.0', MockWindow())
- assert QThreadPool.globalInstance().activeThreadCount() == 1
- qtbot.waitSignal(update_info.got_update_info, timeout=10000)
- argv.append("--no-check-for-updates")
- update_info = check_for_updates('0.6.0', MockWindow())
- assert QThreadPool.globalInstance().activeThreadCount() < 2 # No new update checks where added