Compare commits

..

3 commits

Author SHA1 Message Date
309b0fafb0
Adding tests for debug
Some checks failed
continuous-integration/drone/push Build is failing
2024-09-26 00:01:30 +02:00
f9af0c34dd
Source mapping and debugging 2024-09-25 19:18:04 +02:00
937cb07d0b
Working mjs building 2024-09-24 05:23:12 +02:00
40 changed files with 3233 additions and 54 deletions

16
.gitignore vendored
View file

@ -1,3 +1,4 @@
# Building
build/
dist/
deb_dist/
@ -8,6 +9,11 @@ linux/flatpak/.flatpak-builder
*.snap
*.spec
*.zip
*.tar.gz
*.spec
*.egg-info/
# Runtime data
**/**.qmlc
**/**.jsc
**/**.pyc
@ -20,16 +26,18 @@ linux/flatpak/.flatpak-builder
.DS_Store
**/.DS_Store
**/__pycache__/
# IDE Data
.ropeproject
.vscode
*.kdev4
.kdev4
.coverage
build
docs/html
.directory
*.lpf
*.lgg
*.spec
*.egg-info/
*.tar.gz
# npm
node_modules
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/index.mjs*

View file

@ -43,7 +43,7 @@ if path.realpath(path.join(getcwd(), "..")) not in sys_path:
sys_path.append(path.realpath(path.join(getcwd(), "..")))
from LogarithmPlotter import __VERSION__
from LogarithmPlotter.util import config, native
from LogarithmPlotter.util import config, native, debug
from LogarithmPlotter.util.update import check_for_updates
from LogarithmPlotter.util.helper import Helper
from LogarithmPlotter.util.latex import Latex
@ -155,6 +155,7 @@ def run():
register_icon_directories()
app = create_qapp()
translator = install_translation(app)
debug.setup()
# Installing macOS file handler.
macos_file_open_handler = None

View file

@ -23,7 +23,7 @@ import QtQuick.Layouts 1.12
import QtQuick
// Auto loading all modules.
import "js/autoload.mjs" as ModulesAutoload
import "js/index.mjs" as ModulesAutoload
import eu.ad5001.LogarithmPlotter.History 1.0
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import EditedProperty from "editproperty.mjs"
import EditedProperty from "./editproperty.mjs"
import Objects from "../module/objects.mjs"
export default class ColorChanged extends EditedProperty {

View file

@ -17,7 +17,7 @@
*/
import Objects from "../module/objects.mjs"
import { Action } from "common.mjs"
import { Action } from "./common.mjs"
export default class CreateNewObject extends Action {
// Action used for the creation of an object

View file

@ -17,7 +17,7 @@
*/
import Objects from "../module/objects.mjs"
import CreateNewObject from "create.mjs"
import CreateNewObject from "./create.mjs"
export default class DeleteObject extends CreateNewObject {

View file

@ -19,7 +19,7 @@
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import * as MathLib from "../math/index.mjs"
import { Action } from "common.mjs"
import { Action } from "./common.mjs"
import { DrawableObject } from "../objs/common.mjs"
export default class EditedProperty extends Action {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import EditedProperty from "editproperty.mjs"
import EditedProperty from "./editproperty.mjs"
import Objects from "../module/objects.mjs"

View file

@ -20,7 +20,7 @@ import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import * as MathLib from "../math/index.mjs"
import { escapeHTML } from "../utils.mjs"
import { Action } from "common.mjs"
import { Action } from "./common.mjs"
import { DrawableObject } from "../objs/common.mjs"
export default class EditedPosition extends Action {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import EditedProperty from "editproperty.mjs"
import EditedProperty from "./editproperty.mjs"
import Objects from "../module/objects.mjs"

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Expression, executeExpression } from "expression.mjs"
import { Expression, executeExpression } from "./expression.mjs"
/**
* Main abstract domain class
@ -155,7 +155,7 @@ export class Domain {
return Domain.ZE
break;
default:
return new EmptySet()
return Domain.EmptySet
break;
}
}
@ -182,6 +182,8 @@ export class EmptySet extends Domain {
static import(frm) { return new EmptySet() }
}
Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
/**
* Domain classes for ranges (e.g ]0;3[, [1;2[ ...)
*/

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import * as Expr from "expression.mjs"
import * as Expr from "./expression.mjs"
import * as Utils from "../utils.mjs"
import Latex from "../module/latex.mjs"
import Objects from "../module/objects.mjs"

View file

@ -34,7 +34,7 @@ const evalVariables = {
"false": false
}
export class ExprParserAPI extends Module {
class ExprParserAPI extends Module {
constructor() {
super("ExprParser")
this.currentVars = {}

View file

@ -38,7 +38,6 @@ export class Interface {
const toCheckName = classToCheck.constructor.name
for(const [property, value] of Object.entries(properties))
if(property !== "implement") {
console.log(classToCheck[property], value)
if(!classToCheck.hasOwnProperty(property))
// Check if the property exist
throw new Error(`Property '${property}' (${typeof value}) is present in interface ${interfaceName}, but not in implementation ${toCheckName}.`)
@ -127,4 +126,4 @@ export class HistoryInterface extends Interface {
this.unserialize = FUNCTION
this.serialize = FUNCTION
}
}
}

View file

@ -178,4 +178,4 @@ class IOAPI extends Module {
/** @type {IOAPI} */
Modules.IO = Modules.IO || new IOAPI()
export default Modules.IO
export default Modules.IO

View file

@ -17,17 +17,17 @@
*/
import Objects from "../module/objects.mjs"
import { DrawableObject } from "common.mjs"
import Point from "point.mjs"
import Text from "text.mjs"
import Function from "function.mjs"
import BodeMagnitude from "bodemagnitude.mjs"
import BodePhase from "bodephase.mjs"
import BodeMagnitudeSum from "bodemagnitudesum.mjs"
import BodePhaseSum from "bodephasesum.mjs"
import XCursor from "xcursor.mjs"
import Sequence from "sequence.mjs"
import DistributionFunction from "distribution.mjs"
import { DrawableObject } from "./common.mjs"
import Point from "./point.mjs"
import Text from "./text.mjs"
import Function from "./function.mjs"
import BodeMagnitude from "./bodemagnitude.mjs"
import BodePhase from "./bodephase.mjs"
import BodeMagnitudeSum from "./bodemagnitudesum.mjs"
import BodePhaseSum from "./bodephasesum.mjs"
import XCursor from "./xcursor.mjs"
import Sequence from "./sequence.mjs"
import DistributionFunction from "./distribution.mjs"
/**
* Registers the object obj in the object list.

View file

@ -23,8 +23,8 @@ import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import History from "../module/history.mjs"
import { ExecutableObject } from "common.mjs"
import Function from "function.mjs"
import { ExecutableObject } from "./common.mjs"
import Function from "./function.mjs"
export default class BodeMagnitude extends ExecutableObject {
static type(){return 'Gain Bode'}

View file

@ -21,8 +21,8 @@ import * as P from "../parameters.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { ExecutableObject } from "common.mjs"
import Function from "function.mjs"
import { ExecutableObject } from "./common.mjs"
import Function from "./function.mjs"
export default class BodeMagnitudeSum extends ExecutableObject {

View file

@ -23,7 +23,7 @@ import Objects from "../module/objects.mjs"
import History from "../module/history.mjs"
import Latex from "../module/latex.mjs"
import { ExecutableObject } from "common.mjs"
import { ExecutableObject } from "./common.mjs"
export default class BodePhase extends ExecutableObject {

View file

@ -21,7 +21,7 @@ import * as P from "../parameters.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { ExecutableObject } from "common.mjs"
import { ExecutableObject } from "./common.mjs"
export default class BodePhaseSum extends ExecutableObject {
static type(){return 'Somme phases Bode'}

View file

@ -20,7 +20,7 @@ import * as P from "../parameters.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { ExecutableObject } from "common.mjs"
import { ExecutableObject } from "./common.mjs"
export default class DistributionFunction extends ExecutableObject {

View file

@ -18,7 +18,7 @@
import { textsub } from "../utils.mjs"
import Objects from "../module/objects.mjs"
import { ExecutableObject } from "common.mjs"
import { ExecutableObject } from "./common.mjs"
import { parseDomain, Expression, SpecialDomain } from "../math/index.mjs"
import * as P from "../parameters.mjs"
import Latex from "../module/latex.mjs"

View file

@ -21,7 +21,7 @@ import * as P from "../parameters.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { DrawableObject } from "common.mjs"
import { DrawableObject } from "./common.mjs"
export default class Point extends DrawableObject {

View file

@ -21,8 +21,8 @@ import * as P from "../parameters.mjs"
import Latex from "../module/latex.mjs"
import Objects from "../module/objects.mjs"
import { ExecutableObject } from "common.mjs"
import Function from "function.mjs"
import { ExecutableObject } from "./common.mjs"
import Function from "./function.mjs"
export default class Sequence extends ExecutableObject {

View file

@ -21,7 +21,7 @@ import * as P from "../parameters.mjs"
import Objects from "../module/objects.mjs"
import Latex from "../module/latex.mjs"
import { DrawableObject } from "common.mjs"
import { DrawableObject } from "./common.mjs"
export default class Text extends DrawableObject {

View file

@ -21,7 +21,7 @@ import * as P from "../parameters.mjs"
import Latex from "../module/latex.mjs"
import Objects from "../module/objects.mjs"
import { DrawableObject } from "common.mjs"
import { DrawableObject } from "./common.mjs"
export default class XCursor extends DrawableObject {

View file

@ -18,7 +18,7 @@
import * as Reference from "reference.mjs"
import * as T from "./tokenizer.mjs"
import InputExpression from "common.mjs"
import InputExpression from "./common.mjs"
export const Input = InputExpression
export const TokenType = T.TokenType

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {BoolSetting, ExpressionSetting, NumberSetting, StringSetting} from "common.mjs"
import {BoolSetting, ExpressionSetting, NumberSetting, StringSetting} from "./common.mjs"
const XZOOM = new NumberSetting(

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {BoolSetting, EnumIntSetting} from "common.mjs"
import {BoolSetting, EnumIntSetting} from "./common.mjs"
const AUTOCLOSE_FORMULA = new BoolSetting(
qsTranslate("expression", "Automatically close parenthesises and brackets"),

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {BoolSetting} from "common.mjs"
import {BoolSetting} from "./common.mjs"
import Canvas from "../module/canvas.mjs"
import LatexAPI from "../module/latex.mjs"

View file

@ -0,0 +1,102 @@
"""
* 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 <https://www.gnu.org/licenses/>.
"""
from PySide6.QtCore import QtMsgType, qInstallMessageHandler, QMessageLogContext
from math import ceil, log10
from os import path
CURRENT_PATH = path.dirname(path.realpath(__file__))
SOURCEMAP_PATH = path.realpath(f"{CURRENT_PATH}/../qml/eu/ad5001/LogarithmPlotter/js/index.mjs.map")
SOURCEMAP_INDEX = None
class LOG_COLORS:
GRAY = "\033[90m"
BLUE = "\033[94m"
ORANGE = "\033[38;5;166m"
RED = "\033[e[38;5;204m"
INVERT = "\033[7m"
RESET_INVERT = "\033[27m"
RESET = "\033[0m"
MODES = {
QtMsgType.QtInfoMsg: ['info', LOG_COLORS.BLUE],
QtMsgType.QtWarningMsg: ['warning', LOG_COLORS.ORANGE],
QtMsgType.QtCriticalMsg: ['critical', LOG_COLORS.RED],
QtMsgType.QtFatalMsg: ['critical', LOG_COLORS.RED]
}
DEFAULT_MODE = ['debug', LOG_COLORS.GRAY]
def map_javascript_source(source_file: str, line: str) -> tuple[str, str]:
"""
Maps a line from the compiled javascript to its source.
"""
try:
if SOURCEMAP_INDEX is not None:
token = SOURCEMAP_INDEX.lookup(line, 20)
source_file = source_file[:-len("index.mjs")] + token.src
line = token.src_line
except IndexError:
pass # Unable to find source, leave as is.
return source_file, line
def create_log_terminal_message(mode: QtMsgType, context: QMessageLogContext, message: str):
"""
Parses a qt log message and returns it.
"""
mode = MODES[mode] if mode in MODES else DEFAULT_MODE
line = context.line
source_file = context.file
# Remove source and line from message
if source_file is not None:
if message.startswith(source_file):
message = message[len(source_file) + 1:]
source_file = "LogarithmPlotter/qml/" + source_file.split("/qml/")[-1] # We're only interested in that part.
if line is not None and message.startswith(str(line)):
line_length = ceil(log10((line + 1) if line > 0 else 1))
message = message[line_length + 2:]
# Check MJS
if line is not None and source_file is not None and source_file.endswith("index.mjs"):
source_file, line = map_javascript_source(source_file, line)
prefix = f"{LOG_COLORS.INVERT}{mode[1]}[{mode[0].upper()}]{LOG_COLORS.RESET_INVERT}"
message = message + LOG_COLORS.RESET
context = f"{context.function} at {source_file}:{line}"
return f"{prefix} {message} ({context})"
def log_qt_debug(mode: QtMsgType, context: QMessageLogContext, message: str):
"""
Parses and renders qt log messages.
"""
print(create_log_terminal_message(mode, context, message))
def setup():
global SOURCEMAP_INDEX
try:
with open(SOURCEMAP_PATH, "r") as f:
from sourcemap import loads
SOURCEMAP_INDEX = loads(f.read())
except Exception as e:
log_qt_debug(QtMsgType.QtWarningMsg, QMessageLogContext(),
f"Could not setup JavaScript source mapper in logs: {repr(e)}")
qInstallMessageHandler(log_qt_debug)

View file

@ -15,9 +15,19 @@
You can find more screenshots on the [app's website](https://apps.ad5001.eu/logarithmplotter/).
## Run
## Build & Run
You can simply run LogarithmPlotter using `python3 run.py`.
First, you'll need to install all the required dependencies:
- [Python 3](https://python.org) with [poetry](https://python-poetry.org/), and setup a virtual environment and call
`poetry install`.
- [npm](https://npmjs.com) (or [yarn](https://yarnpkg.com/)), and run `npm install` (or `yarn install`).
You can simply run LogarithmPlotter using `python3 run.py`. It automatically compiles the language files (requires
`lrelease` to be installed and in path), and the JavaScript modules.
If you do not wish do recompile the files again on every run, you can use
`python3 LogarithmPlotter/logarithmplotter.py`.
In order to test translations, you can use the `--lang=<lang code>` commandline option to force the locale.
@ -27,9 +37,7 @@ In order to test translations, you can use the `--lang=<lang code>` commandline
All scripts noted here can be found in the `scripts` directory.
You can generate installers for LogarithmPlotter after installing all the dependencies:
For all builds, you will need [Python 3](https://python.org) with [poetry](https://python-poetry.org/), and
`poetry install --with packaging`.
You can generate installers for LogarithmPlotter after installing all the dependencies.
- Windows installer:
- Run the `build-windows.bat` script (or `build-wine.sh` if you're cross-compiling with wine on Linux) to build an

6
babel.config.json Normal file
View file

@ -0,0 +1,6 @@
{
"presets": ["@babel/preset-env"],
"targets": {
"esmodules": true
}
}

2900
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "logarithmplotter",
"version": "0.6.0",
"description": "2D plotter software to make Bode plots, sequences and distribution functions.",
"main": "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/autoload.mjs",
"scripts": {
"build": "rollup --config rollup.config.mjs"
},
"repository": {
"type": "git",
"url": "https://git.ad5001.eu/Ad5001/LogarithmPlotter"
},
"author": "Ad5001 <mail@ad5001.eu>",
"license": "GPL-3.0-or-later",
"dependencies": {
"@babel/preset-env": "^7.25.4",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"install": "^0.13.0",
"rollup": "^4.22.4"
}
}

13
poetry.lock generated
View file

@ -395,6 +395,17 @@ files = [
{file = "shiboken6-6.7.2-cp39-abi3-win_amd64.whl", hash = "sha256:9024e6afb2af1568ebfc8a5d07e4ff6c8829f40923eeb28901f535463e2b6b65"},
]
[[package]]
name = "sourcemap"
version = "0.2.1"
description = "Parse JavaScript source maps."
optional = false
python-versions = "*"
files = [
{file = "sourcemap-0.2.1-py2.py3-none-any.whl", hash = "sha256:c448a8c48f9482e522e4582106b0c641a83b5dbc7f13927b178848e3ea20967b"},
{file = "sourcemap-0.2.1.tar.gz", hash = "sha256:be00a90185e7a16b87bbe62a68ffd5e38bc438ef4700806d9b90e44d8027787c"},
]
[[package]]
name = "stdeb"
version = "0.10.0"
@ -438,4 +449,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.13"
content-hash = "3db79d8b611fd2e37486fbd8e10c6d454b293cc7156d83b05c1a8459cb9b33e6"
content-hash = "30da53f0a05c06c5f93aa1260217d807ce2ab64debd26f313b47c664931e67c7"

View file

@ -20,3 +20,7 @@ stdeb = "^0.10.0"
pytest = "^8.3.3"
pytest-cov = "^5.0.0"
pytest-qt = "^4.4.0"
[tool.poetry.group.dev.dependencies]
sourcemap = "^0.2.1"

45
rollup.config.mjs Normal file
View file

@ -0,0 +1,45 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import { babel } from "@rollup/plugin-babel"
import terser from "@rollup/plugin-terser"
const path = "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js"
export default {
input: `${path}/autoload.mjs`,
output: {
file: `${path}/index.mjs`,
compact: false,
sourcemap: true,
format: "es"
},
plugins: [
nodeResolve({ browser: true }),
commonjs(),
babel({
babelHelpers: "bundled"
}),
// terser({
// ecma: 2015
// })
]
}

1
run.py
View file

@ -21,6 +21,7 @@ def update_translations():
"""
from os import system, getcwd, chdir, path
pwd = getcwd()
system("npm run build")
chdir(path.join("LogarithmPlotter", "i18n"))
system("./release.sh")
chdir(pwd)

View file

@ -0,0 +1,68 @@
"""
* 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 <https://www.gnu.org/licenses/>.
"""
import pytest
from os.path import exists
from PySide6.QtCore import QtMsgType, QMessageLogContext
from LogarithmPlotter.util import debug
def test_setup():
sourcemap_installed = False
try:
import sourcemap
sourcemap_installed = True
except:
pass
if sourcemap_installed:
file = debug.SOURCEMAP_PATH
debug.SOURCEMAP_PATH = None
debug.setup() # Nothing could be setup.
debug.SOURCEMAP_PATH = file
debug.setup()
assert (sourcemap_installed and exists(debug.SOURCEMAP_PATH)) == (debug.SOURCEMAP_INDEX is not None)
def test_map_source():
sourcemap_available = debug.SOURCEMAP_INDEX is not None
if sourcemap_available:
assert debug.map_javascript_source("js/index.mjs", 21) == ("js/module/interface.mjs", 21)
assert debug.map_javascript_source("js/index.mjs", 100000) == ("js/index.mjs", 100000) # Too long, not found
debug.SOURCEMAP_INDEX = None
assert debug.map_javascript_source("js/index.mjs", 21) == ("js/index.mjs", 21)
def test_log_terminal_message():
msg1 = debug.create_log_terminal_message(
QtMsgType.QtWarningMsg, QMessageLogContext(),
"a long and random message"
)
assert "[WARNING]" in msg1
assert "a long and random message" in msg1
msg2 = debug.create_log_terminal_message(
QtMsgType.QtCriticalMsg,
QMessageLogContext("LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Index.qml", 15, "anotherFunctionName",
"aCategoryDifferent"),
"LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Index.qml:15: a potentially potential error message"
)
assert "[CRITICAL]" in msg2
assert "Index.qml" in msg2
assert "a potentially potential error message" in msg2
assert "anotherFunctionName" in msg2