diff --git a/common/src/events.mjs b/common/src/events.mjs index f1ec970..4e95db6 100644 --- a/common/src/events.mjs +++ b/common/src/events.mjs @@ -38,7 +38,7 @@ export class BaseEvent { export class BaseEventEmitter { static emits = [] - /** @type {Record} */ + /** @type {Record>} */ #listeners = {} constructor() { @@ -53,8 +53,8 @@ export class BaseEventEmitter { * @param {string} eventType - Name of the event to listen to. Throws an error if this object does not emit this kind of event. * @param {function(BaseEvent)} eventListener - The function to be called back when the event is emitted. */ - addEventListener(eventType, eventListener) { - if(!this.emits.includes(eventType)) { + on(eventType, eventListener) { + if(!this.constructor.emits.includes(eventType)) { const className = this.constructor.name const eventTypes = this.constructor.emits.join(", ") throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`) @@ -70,14 +70,13 @@ export class BaseEventEmitter { * @param {function(BaseEvent)} eventListener - The function previously registered as a listener. * @returns {boolean} True if the listener was removed, false if it was not found. */ - removeEventListener(eventType, eventListener) { - if(!this.emits.includes(eventType)) { + off(eventType, eventListener) { + if(!this.constructor.emits.includes(eventType)) { const className = this.constructor.name const eventTypes = this.constructor.emits.join(", ") throw new Error(`Cannot listen to unknown event ${eventType} in class ${className}. ${className} only emits: ${eventTypes}`) } return this.#listeners[eventType].delete(eventListener) - } /** @@ -88,8 +87,11 @@ export class BaseEventEmitter { emit(e) { if(!(e instanceof BaseEvent)) throw new Error("Cannot emit non event object.") - if(!this.emits.includes(e.name)) - throw new Error(`Cannot emit event '${e.name}' from class ${this.constructor.name}. ${this.constructor.name} can only emits: ${this.constructor.emits.join(", ")}.`) + if(!this.constructor.emits.includes(e.name)) { + const className = this.constructor.name + const eventTypes = this.constructor.emits.join(", ") + throw new Error(`Cannot emit event '${e.name}' from class ${className}. ${className} can only emit: ${eventTypes}`) + } for(const listener of this.#listeners[e.name]) listener(e) } diff --git a/common/src/lib/polyfills/js.mjs b/common/src/lib/polyfills/js.mjs index 9bcf80c..b412716 100644 --- a/common/src/lib/polyfills/js.mjs +++ b/common/src/lib/polyfills/js.mjs @@ -64,10 +64,7 @@ function arrayFlatMap(callbackFn, thisArg) { * @return {String} */ function stringReplaceAll(from, to) { - let str = this - while(str.includes(from)) - str = str.replace(from, to) - return str + return this.split(from).join(to) } diff --git a/common/src/module/canvas.mjs b/common/src/module/canvas.mjs index 2aa14ad..5d42f5e 100644 --- a/common/src/module/canvas.mjs +++ b/common/src/module/canvas.mjs @@ -23,6 +23,7 @@ import { Expression } from "../math/index.mjs" import Latex from "./latex.mjs" import Objects from "./objects.mjs" import History from "./history.mjs" +import Settings from "./settings.mjs" class CanvasAPI extends Module { /** @type {CanvasInterface} */ @@ -84,7 +85,7 @@ class CanvasAPI extends Module { */ get xmin() { if(!this.initialized) throw new Error("Attempting xmin before initialize!") - return this.#canvas.xmin + return Settings.xmin } /** @@ -93,7 +94,7 @@ class CanvasAPI extends Module { */ get xzoom() { if(!this.initialized) throw new Error("Attempting xzoom before initialize!") - return this.#canvas.xzoom + return Settings.xzoom } /** @@ -102,7 +103,7 @@ class CanvasAPI extends Module { */ get ymax() { if(!this.initialized) throw new Error("Attempting ymax before initialize!") - return this.#canvas.ymax + return Settings.ymax } /** @@ -111,7 +112,7 @@ class CanvasAPI extends Module { */ get yzoom() { if(!this.initialized) throw new Error("Attempting yzoom before initialize!") - return this.#canvas.yzoom + return Settings.yzoom } /** @@ -120,7 +121,7 @@ class CanvasAPI extends Module { */ get xlabel() { if(!this.initialized) throw new Error("Attempting xlabel before initialize!") - return this.#canvas.xlabel + return Settings.xlabel } /** @@ -129,7 +130,7 @@ class CanvasAPI extends Module { */ get ylabel() { if(!this.initialized) throw new Error("Attempting ylabel before initialize!") - return this.#canvas.ylabel + return Settings.ylabel } /** @@ -138,7 +139,7 @@ class CanvasAPI extends Module { */ get linewidth() { if(!this.initialized) throw new Error("Attempting linewidth before initialize!") - return this.#canvas.linewidth + return Settings.linewidth } /** @@ -147,7 +148,7 @@ class CanvasAPI extends Module { */ get textsize() { if(!this.initialized) throw new Error("Attempting textsize before initialize!") - return this.#canvas.textsize + return Settings.textsize } /** @@ -156,7 +157,7 @@ class CanvasAPI extends Module { */ get logscalex() { if(!this.initialized) throw new Error("Attempting logscalex before initialize!") - return this.#canvas.logscalex + return Settings.logscalex } /** @@ -165,7 +166,7 @@ class CanvasAPI extends Module { */ get showxgrad() { if(!this.initialized) throw new Error("Attempting showxgrad before initialize!") - return this.#canvas.showxgrad + return Settings.showxgrad } /** @@ -174,7 +175,7 @@ class CanvasAPI extends Module { */ get showygrad() { if(!this.initialized) throw new Error("Attempting showygrad before initialize!") - return this.#canvas.showygrad + return Settings.showygrad } /** @@ -237,9 +238,9 @@ class CanvasAPI extends Module { * @private */ _computeAxes() { - let exprY = new Expression(`x*(${this.#canvas.yaxisstep})`) + let exprY = new Expression(`x*(${Settings.yaxisstep})`) let y1 = exprY.execute(1) - let exprX = new Expression(`x*(${this.#canvas.xaxisstep})`) + let exprX = new Expression(`x*(${Settings.xaxisstep})`) let x1 = exprX.execute(1) this.axesSteps = { x: { diff --git a/common/src/module/index.mjs b/common/src/module/index.mjs index fe2e8d9..2530a00 100644 --- a/common/src/module/index.mjs +++ b/common/src/module/index.mjs @@ -17,6 +17,7 @@ */ import Objects from "./objects.mjs" +import Settings from "./settings.mjs" import ExprParser from "./expreval.mjs" import Latex from "./latex.mjs" import History from "./history.mjs" @@ -26,6 +27,7 @@ import Preferences from "./preferences.mjs" export default { Objects, + Settings, ExprParser, Latex, History, diff --git a/common/src/module/interface.mjs b/common/src/module/interface.mjs index f6e5a13..606ec4d 100644 --- a/common/src/module/interface.mjs +++ b/common/src/module/interface.mjs @@ -78,7 +78,7 @@ export class SettingsInterface extends Interface { showygrad = BOOLEAN } -export class CanvasInterface extends SettingsInterface { +export class CanvasInterface extends Interface { imageLoaders = OBJECT /** @type {function(string): CanvasRenderingContext2D} */ getContext = FUNCTION diff --git a/common/src/module/io.mjs b/common/src/module/io.mjs index e32f2cf..0b4c6fa 100644 --- a/common/src/module/io.mjs +++ b/common/src/module/io.mjs @@ -20,22 +20,20 @@ import { Module } from "./common.mjs" import Objects from "./objects.mjs" import History from "./history.mjs" import Canvas from "./canvas.mjs" +import Settings from "./settings.mjs" import { DialogInterface, RootInterface, SettingsInterface } from "./interface.mjs" class IOAPI extends Module { /** @type {RootInterface} */ #rootElement - /** @type {SettingsInterface} */ - #settings /** @type {{show: function(string)}} */ #alert constructor() { super("IO", { alert: DialogInterface, - root: RootInterface, - settings: SettingsInterface + root: RootInterface }) /** * Path of the currently opened file. Empty if no file is opened. @@ -47,13 +45,11 @@ class IOAPI extends Module { /** * Initializes module with QML elements. * @param {RootInterface} root - * @param {SettingsInterface} settings * @param {{show: function(string)}} alert */ - initialize({ root, settings, alert }) { - super.initialize({ root, settings, alert }) + initialize({ root, alert }) { + super.initialize({ root, alert }) this.#rootElement = root - this.#settings = settings this.#alert = alert } @@ -66,7 +62,7 @@ class IOAPI extends Module { // Add extension if necessary if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1) filename += ".lpf" - this.saveFilename = filename + Settings.set("saveFilename", filename, false) let objs = {} for(let objType in Objects.currentObjects) { objs[objType] = [] @@ -75,19 +71,19 @@ class IOAPI extends Module { } } let settings = { - "xzoom": this.#settings.xzoom, - "yzoom": this.#settings.yzoom, - "xmin": this.#settings.xmin, - "ymax": this.#settings.ymax, - "xaxisstep": this.#settings.xaxisstep, - "yaxisstep": this.#settings.yaxisstep, - "xaxislabel": this.#settings.xlabel, - "yaxislabel": this.#settings.ylabel, - "logscalex": this.#settings.logscalex, - "linewidth": this.#settings.linewidth, - "showxgrad": this.#settings.showxgrad, - "showygrad": this.#settings.showygrad, - "textsize": this.#settings.textsize, + "xzoom": Settings.xzoom, + "yzoom": Settings.yzoom, + "xmin": Settings.xmin, + "ymax": Settings.ymax, + "xaxisstep": Settings.xaxisstep, + "yaxisstep": Settings.yaxisstep, + "xaxislabel": Settings.xlabel, + "yaxislabel": Settings.ylabel, + "logscalex": Settings.logscalex, + "linewidth": Settings.linewidth, + "showxgrad": Settings.showxgrad, + "showygrad": Settings.showygrad, + "textsize": Settings.textsize, "history": History.serialize(), "width": this.#rootElement.width, "height": this.#rootElement.height, @@ -113,24 +109,24 @@ class IOAPI extends Module { if(data.hasOwnProperty("type") && data["type"] === "logplotv1") { History.clear() // Importing settings - this.#settings.saveFilename = filename - this.#settings.xzoom = parseFloat(data["xzoom"]) || 100 - this.#settings.yzoom = parseFloat(data["yzoom"]) || 10 - this.#settings.xmin = parseFloat(data["xmin"]) || 5 / 10 - this.#settings.ymax = parseFloat(data["ymax"]) || 24 - this.#settings.xaxisstep = data["xaxisstep"] || "4" - this.#settings.yaxisstep = data["yaxisstep"] || "4" - this.#settings.xlabel = data["xaxislabel"] || "" - this.#settings.ylabel = data["yaxislabel"] || "" - this.#settings.logscalex = data["logscalex"] === true + Settings.set("saveFilename", filename, false) + Settings.set("xzoom", parseFloat(data["xzoom"]) || 100, false) + Settings.set("yzoom", parseFloat(data["yzoom"]) || 10, false) + Settings.set("xmin", parseFloat(data["xmin"]) || 5 / 10, false) + Settings.set("ymax", parseFloat(data["ymax"]) || 24, false) + Settings.set("xaxisstep", data["xaxisstep"] || "4", false) + Settings.set("yaxisstep", data["yaxisstep"] || "4", false) + Settings.set("xlabel", data["xaxislabel"] || "", false) + Settings.set("ylabel", data["yaxislabel"] || "", false) + Settings.set("logscalex", data["logscalex"] === true, false) if("showxgrad" in data) - this.#settings.showxgrad = data["showxgrad"] + Settings.set("showxgrad", data["showxgrad"], false) if("showygrad" in data) - this.#settings.textsize = data["showygrad"] + Settings.set("showygrad", data["showygrad"], false) if("linewidth" in data) - this.#settings.linewidth = data["linewidth"] + Settings.set("linewidth", data["linewidth"], false) if("textsize" in data) - this.#settings.textsize = data["textsize"] + Settings.set("textsize", data["textsize"], false) this.#rootElement.height = parseFloat(data["height"]) || 500 this.#rootElement.width = parseFloat(data["width"]) || 1000 diff --git a/common/src/module/latex.mjs b/common/src/module/latex.mjs index 8fabee4..a0e8401 100644 --- a/common/src/module/latex.mjs +++ b/common/src/module/latex.mjs @@ -261,7 +261,7 @@ class LatexAPI extends Module { throw new EvalError("Unknown operator " + item.value + ".") } break - case Instruction.IOP3: // Thirdiary operator + case Instruction.IOP3: // Ternary operator n3 = nstack.pop() n2 = nstack.pop() n1 = nstack.pop() diff --git a/common/src/module/settings.mjs b/common/src/module/settings.mjs index 4487dde..44a6593 100644 --- a/common/src/module/settings.mjs +++ b/common/src/module/settings.mjs @@ -18,6 +18,7 @@ import { Module } from "./common.mjs" import { BaseEvent } from "../events.mjs" +import { HelperInterface } from "./interface.mjs" /** @@ -47,20 +48,23 @@ class ChangedEvent extends BaseEvent { class SettingsAPI extends Module { static emits = ["changed"] + #nonConfigurable = ["saveFilename"] + #properties = new Map([ - ['xzoom', 100], - ['yzoom', 10], - ['xmin', .5], - ['ymax', 25], - ['xaxisstep', "4"], - ['yaxisstep', "4"], - ['xlabel', ""], - ['ylabel', ""], - ['linewidth', 1], - ['textsize', 18], - ['logscalex', true], - ['showxgrad', true], - ['showygrad', true], + ["saveFilename", ""], + ["xzoom", 100], + ["yzoom", 10], + ["xmin", .5], + ["ymax", 25], + ["xaxisstep", "4"], + ["yaxisstep", "4"], + ["xlabel", ""], + ["ylabel", ""], + ["linewidth", 1], + ["textsize", 18], + ["logscalex", true], + ["showxgrad", true], + ["showygrad", true] ]) constructor() { @@ -73,32 +77,39 @@ class SettingsAPI extends Module { super.initialize({ helper }) // Initialize default values. for(const key of this.#properties.keys()) { - switch(typeof this.#properties.get(key)) { - case 'boolean': - this.set(key, helper.getSettingBool(key), false) - break - case 'number': - this.set(key, helper.getSettingInt(key), false) - break - case 'string': - this.set(key, helper.getSetting(key), false) - break + if(!this.#nonConfigurable.includes(key)) { + switch(typeof this.#properties.get(key)) { + case "boolean": + this.set(key, helper.getSettingBool("default_graph."+key), false) + break + case "number": + this.set(key, helper.getSettingInt("default_graph."+key), false) + break + case "string": + this.set(key, helper.getSetting("default_graph."+key), false) + break + } } } } /** * Sets a setting to a given value - * - * @param {boolean} byUser - Set to true if the user is at the origin of this change. + * + * @param {string} property + * @param {string|number|boolean} value + * @param {boolean} byUser - Set to true if the user is at the origin of this change. */ set(property, value, byUser) { - if(!this.#properties.has(property)) + if(!this.#properties.has(property)) { throw new Error(`Property ${property} is not a setting.`) + } const oldValue = this.#properties.get(property) const propType = typeof oldValue + if(byUser) + console.debug("Setting", property, "from", oldValue, "to", value, `(${typeof value}, ${byUser})`) if(propType !== typeof value) - throw new Error(`Value of ${property} must be a ${propType}.`) + throw new Error(`Value of ${property} must be a ${propType} (${typeof value} provided).`) this.#properties.set(property, value) this.emit(new ChangedEvent(property, oldValue, value, byUser === true)) } @@ -107,67 +118,70 @@ class SettingsAPI extends Module { * Zoom on the x axis of the diagram. * @returns {number} */ - get xzoom() { return this.#properties.get("xzoom"); } + get xzoom() { return this.#properties.get("xzoom") } /** * Zoom on the y axis of the diagram. * @returns {number} */ - get yzoom() { return this.#properties.get("yzoom"); } + get yzoom() { return this.#properties.get("yzoom") } /** * Minimum x of the diagram. * @returns {number} */ - get xmin() { return this.#properties.get("xmin"); } + get xmin() { return this.#properties.get("xmin") } /** * Maximum y of the diagram. * @returns {number} */ - get ymax() { return this.#properties.get("ymax"); } + get ymax() { return this.#properties.get("ymax") } /** * Step of the x axis graduation (expression). * @note Only available in non-logarithmic mode. * @returns {string} */ - get xaxisstep() { return this.#properties.get("xaxisstep"); } + get xaxisstep() { return this.#properties.get("xaxisstep") } /** * Step of the y axis graduation (expression). * @returns {string} */ - get yaxisstep() { return this.#properties.get("yaxisstep"); } + get yaxisstep() { return this.#properties.get("yaxisstep") } /** * Label used on the x axis. * @returns {string} */ - get xlabel() { return this.#properties.get("xlabel"); } + get xlabel() { return this.#properties.get("xlabel") } /** * Label used on the y axis. * @returns {string} */ - get ylabel() { return this.#properties.get("ylabel"); } + get ylabel() { return this.#properties.get("ylabel") } /** * Width of lines that will be drawn into the canvas. * @returns {number} */ - get linewidth() { return this.#properties.get("linewidth"); } + get linewidth() { return this.#properties.get("linewidth") } /** * Font size of the text that will be drawn into the canvas. * @returns {number} */ - get textsize() { return this.#properties.get("textsize"); } + get textsize() { return this.#properties.get("textsize") } /** * true if the canvas should be in logarithmic mode, false otherwise. * @returns {boolean} */ - get logscalex() { return this.#properties.get("logscalex"); } + get logscalex() { return this.#properties.get("logscalex") } /** * true if the x graduation should be shown, false otherwise. * @returns {boolean} */ - get showxgrad() { return this.#properties.get("showxgrad"); } + get showxgrad() { return this.#properties.get("showxgrad") } /** * true if the y graduation should be shown, false otherwise. * @returns {boolean} */ - get showygrad() { return this.#properties.get("showygrad"); } - + get showygrad() { return this.#properties.get("showygrad") } } + +Modules.Settings = Modules.Settings || new SettingsAPI() +export default Modules.Settings + diff --git a/common/src/preferences/default.mjs b/common/src/preferences/default.mjs index 0fa9531..20d62e3 100644 --- a/common/src/preferences/default.mjs +++ b/common/src/preferences/default.mjs @@ -28,7 +28,7 @@ const XZOOM = new NumberSetting( const YZOOM = new NumberSetting( qsTranslate("Settings", "Y Zoom"), - "default_graph.xzoom", + "default_graph.yzoom", "yzoom", 0.1 ) diff --git a/common/src/utils.mjs b/common/src/utils.mjs index 4f6b0c5..094147d 100644 --- a/common/src/utils.mjs +++ b/common/src/utils.mjs @@ -35,6 +35,19 @@ String.prototype.removeEnclosure = function() { return this.substring(1, this.length - 1) } +/** + * Rounds to a certain number of decimal places. + * From https://stackoverflow.com/a/48764436 + * + * @param {number} decimalPlaces + * @return {number} + */ +Number.prototype.toDecimalPrecision = function(decimalPlaces = 0) { + const p = Math.pow(10, decimalPlaces); + const n = (this * p) * (1 + Number.EPSILON); + return Math.round(n) / p; +} + const powerpos = { "-": "⁻", "+": "⁺", diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml index 38d05be..07b6002 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml @@ -30,104 +30,15 @@ import Qt.labs.platform as Native */ Canvas { id: canvas - anchors.top: separator.bottom + anchors.top: parent.top anchors.left: parent.left height: parent.height - 90 width: parent.width - - /*! - \qmlproperty double LogGraphCanvas::xmin - Minimum x of the diagram, provided from settings. - \sa Settings - */ - property double xmin: 0 - /*! - \qmlproperty double LogGraphCanvas::ymax - Maximum y of the diagram, provided from settings. - \sa Settings - */ - property double ymax: 0 - /*! - \qmlproperty double LogGraphCanvas::xzoom - Zoom on the x axis of the diagram, provided from settings. - \sa Settings - */ - property double xzoom: 10 - /*! - \qmlproperty double LogGraphCanvas::yzoom - Zoom on the y axis of the diagram, provided from settings. - \sa Settings - */ - property double yzoom: 10 - /*! - \qmlproperty string LogGraphCanvas::xaxisstep - Step of the x axis graduation, provided from settings. - \note: Only available in non-logarithmic mode. - \sa Settings - */ - property string xaxisstep: "4" - /*! - \qmlproperty string LogGraphCanvas::yaxisstep - Step of the y axis graduation, provided from settings. - \sa Settings - */ - property string yaxisstep: "4" - /*! - \qmlproperty string LogGraphCanvas::xlabel - Label used on the x axis, provided from settings. - \sa Settings - */ - property string xlabel: "" - /*! - \qmlproperty string LogGraphCanvas::ylabel - Label used on the y axis, provided from settings. - \sa Settings - */ - property string ylabel: "" - /*! - \qmlproperty double LogGraphCanvas::linewidth - Width of lines that will be drawn into the canvas, provided from settings. - \sa Settings - */ - property double linewidth: 1 - /*! - \qmlproperty double LogGraphCanvas::textsize - Font size of the text that will be drawn into the canvas, provided from settings. - \sa Settings - */ - property double textsize: 14 - /*! - \qmlproperty bool LogGraphCanvas::logscalex - true if the canvas should be in logarithmic mode, false otherwise. - Provided from settings. - \sa Settings - */ - property bool logscalex: false - /*! - \qmlproperty bool LogGraphCanvas::showxgrad - true if the x graduation should be shown, false otherwise. - Provided from settings. - \sa Settings - */ - property bool showxgrad: false - /*! - \qmlproperty bool LogGraphCanvas::showygrad - true if the y graduation should be shown, false otherwise. - Provided from settings. - \sa Settings - */ - property bool showygrad: false - /*! \qmlproperty var LogGraphCanvas::imageLoaders Dictionary of format {image: [callback.image data]} containing data for defered image loading. */ property var imageLoaders: {} - /*! - \qmlproperty var LogGraphCanvas::ctx - Cache for the 2D context so that it may be used asynchronously. - */ - property var ctx Component.onCompleted: { imageLoaders = {} @@ -155,7 +66,7 @@ Canvas { Object.keys(imageLoaders).forEach((key) => { if(isImageLoaded(key)) { // Calling callback - imageLoaders[key][0](canvas, ctx, imageLoaders[key][1]) + imageLoaders[key][0](imageLoaders[key][1]) delete imageLoaders[key] } }) diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml index 8545153..51abb46 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogarithmPlotter.qml @@ -42,7 +42,7 @@ ApplicationWindow { width: 1000 height: 500 color: sysPalette.window - title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") + title: "LogarithmPlotter" SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } @@ -145,20 +145,6 @@ ApplicationWindow { width: sidebar.inPortrait ? parent.width : parent.width - sidebar.width//*sidebar.position x: sidebar.width//*sidebar.position - xmin: settings.xmin - ymax: settings.ymax - xzoom: settings.xzoom - yzoom: settings.yzoom - xlabel: settings.xlabel - ylabel: settings.ylabel - yaxisstep: settings.yaxisstep - xaxisstep: settings.xaxisstep - logscalex: settings.logscalex - linewidth: settings.linewidth - textsize: settings.textsize - showxgrad: settings.showxgrad - showygrad: settings.showygrad - property bool firstDrawDone: false onPainted: if(!firstDrawDone) { @@ -264,5 +250,12 @@ ApplicationWindow { Component.onCompleted: { Modules.IO.initialize({ root, settings, alert }) Modules.Latex.initialize({ latex: Latex, helper: Helper }) + Modules.Settings.on("changed", (evt) => { + if(evt.property === "saveFilename") { + const fileName = evt.newValue.split('/').pop().split('\\').pop() + if(fileName !== "") + title = `${fileName}` + } + }) } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml index 2fb45d4..680c310 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/PickLocationOverlay.qml @@ -285,7 +285,7 @@ Item { const axisX = Modules.Canvas.axesSteps.x.value const xpos = Modules.Canvas.px2x(picker.mouseX) if(snapToGridCheckbox.checked) { - if(canvas.logscalex) { + if(Modules.Settings.logscalex) { // Calculate the logged power let pow = Math.pow(10, Math.floor(Math.log10(xpos))) return pow*Math.round(xpos/pow) diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml index afa3e09..a0c030a 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/ExpressionEditor.qml @@ -175,17 +175,17 @@ Item { Icon { id: iconLabel anchors.top: parent.top - anchors.topMargin: icon == "" ? 0 : 3 - source: control.visible && icon != "" ? "../icons/" + control.icon : "" + anchors.topMargin: parent.icon == "" ? 0 : 3 + source: control.visible && parent.icon != "" ? "../icons/" + control.icon : "" width: height - height: icon == "" || !visible ? 0 : 24 + height: parent.icon == "" || !visible ? 0 : 24 color: sysPalette.windowText } Label { id: labelItem anchors.left: iconLabel.right - anchors.leftMargin: icon == "" ? 0 : 5 + anchors.leftMargin: parent.icon == "" ? 0 : 5 anchors.top: parent.top height: parent.height width: Math.max(85, implicitWidth) @@ -231,8 +231,8 @@ Item { onEditingFinished: { if(insertButton.focus || insertPopup.focus) return let value = text - if(value != "" && value.toString() != defValue) { - let expr = parse(value) + if(value != "" && value.toString() != parent.defValue) { + let expr = parent.parse(value) if(expr != null) { control.changed(expr) defValue = expr.toEditableString() @@ -280,10 +280,10 @@ Item { acPopupContent.itemSelected = 0 - if(event.text in openAndCloseMatches && autoClosing) { + if(event.text in parent.openAndCloseMatches && autoClosing) { let start = selectionStart insert(selectionStart, event.text) - insert(selectionEnd, openAndCloseMatches[event.text]) + insert(selectionEnd, parent.openAndCloseMatches[event.text]) cursorPosition = start+1 event.accepted = true } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml index b0a054a..3689dc1 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Setting/TextSetting.qml @@ -37,7 +37,7 @@ Item { Emitted when the value of the text has been changed. The corresponding handler is \c onChanged. */ - signal changed(string newValue) + signal changed(var newValue) /*! \qmlproperty bool TextSetting::isInt diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml index 534556f..b440d07 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml @@ -121,11 +121,6 @@ ScrollView { \sa Settings */ property bool showygrad: Helper.getSettingBool('default_graph.showygrad') - /*! - \qmlproperty bool Settings::saveFilename - Path of the currently opened file. Empty if no file is opened. - */ - property string saveFilename: "" Column { spacing: 10 @@ -136,15 +131,18 @@ ScrollView { id: fdiag onAccepted: { var filePath = fdiag.currentFile.toString().substr(7) - settings.saveFilename = filePath + Modules.Settings.set("saveFilename", filePath) if(exportMode) { Modules.IO.saveDiagram(filePath) } else { Modules.IO.loadDiagram(filePath) - if(xAxisLabel.find(settings.xlabel) == -1) xAxisLabel.model.append({text: settings.xlabel}) - xAxisLabel.editText = settings.xlabel - if(yAxisLabel.find(settings.ylabel) == -1) yAxisLabel.model.append({text: settings.ylabel}) - yAxisLabel.editText = settings.ylabel + // Adding labels. + if(xAxisLabel.find(Modules.Settings.xlabel) === -1) + xAxisLabel.model.append({text: Modules.Settings.xlabel}) + xAxisLabel.editText = Modules.Settings.xlabel + if(yAxisLabel.find(Modules.Settings.ylabel) === -1) + yAxisLabel.model.append({text: Modules.Settings.ylabel}) + yAxisLabel.editText = Modules.Settings.ylabel } } } @@ -158,11 +156,16 @@ ScrollView { min: 0.1 icon: "settings/xzoom.svg" width: settings.settingWidth - value: settings.xzoom.toFixed(2) + onChanged: function(newValue) { - settings.xzoom = newValue + Modules.Settings.set("xzoom", newValue, true) settings.changed() } + + function update(newValue) { + value = Modules.Settings.xzoom.toFixed(2) + maxX.update() + } } Setting.TextSetting { @@ -173,11 +176,16 @@ ScrollView { label: qsTr("Y Zoom") icon: "settings/yzoom.svg" width: settings.settingWidth - value: settings.yzoom.toFixed(2) + onChanged: function(newValue) { - settings.yzoom = newValue + Modules.Settings.set("yzoom", newValue, true) settings.changed() } + + function update(newValue) { + value = Modules.Settings.yzoom.toFixed(2) + minY.update() + } } // Positioning the graph @@ -189,14 +197,18 @@ ScrollView { label: qsTr("Min X") icon: "settings/xmin.svg" width: settings.settingWidth - defValue: settings.xmin + onChanged: function(newValue) { - if(parseFloat(maxX.value) > newValue) { - settings.xmin = newValue - settings.changed() - } else { - alert.show("Minimum x value must be inferior to maximum.") - } + Modules.Settings.set("xmin", newValue, true) + settings.changed() + } + + function update(newValue) { + let newVal = Modules.Settings.xmin + if(newVal > 1e-5) + newVal = newVal.toDecimalPrecision(8) + value = newVal + maxX.update() } } @@ -208,11 +220,16 @@ ScrollView { label: qsTr("Max Y") icon: "settings/ymax.svg" width: settings.settingWidth - defValue: settings.ymax + onChanged: function(newValue) { - settings.ymax = newValue + Modules.Settings.set("ymax", newValue, true) settings.changed() } + + function update() { + value = Modules.Settings.ymax + minY.update() + } } Setting.TextSetting { @@ -223,15 +240,24 @@ ScrollView { label: qsTr("Max X") icon: "settings/xmax.svg" width: settings.settingWidth - defValue: Modules.Canvas.px2x(canvas.width).toFixed(2) + onChanged: function(xvaluemax) { - if(xvaluemax > settings.xmin) { - settings.xzoom = settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) + if(xvaluemax > Modules.Settings.xmin) { + const newXZoom = Modules.Settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) + Modules.Settings.set("xzoom", newXZoom, true) + zoomX.update() settings.changed() } else { alert.show("Maximum x value must be superior to minimum.") } } + + function update() { + let newVal = Modules.Canvas.px2x(canvas.width) + if(newVal > 1e-5) + newVal = newVal.toDecimalPrecision(8) + value = newVal + } } Setting.TextSetting { @@ -242,15 +268,21 @@ ScrollView { label: qsTr("Min Y") icon: "settings/ymin.svg" width: settings.settingWidth - defValue: Modules.Canvas.px2y(canvas.height).toFixed(2) + onChanged: function(yvaluemin) { if(yvaluemin < settings.ymax) { - settings.yzoom = settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) + const newYZoom = Modules.Settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) + Modules.Settings.set("yzoom", newYZoom, true) + zoomY.update() settings.changed() } else { alert.show("Minimum y value must be inferior to maximum.") } } + + function update() { + value = Modules.Canvas.px2y(canvas.height).toDecimalPrecision(8) + } } Setting.TextSetting { @@ -260,12 +292,16 @@ ScrollView { label: qsTr("X Axis Step") icon: "settings/xaxisstep.svg" width: settings.settingWidth - defValue: settings.xaxisstep - visible: !settings.logscalex + onChanged: function(newValue) { - settings.xaxisstep = newValue + Modules.Settings.set("xaxisstep", newValue, true) settings.changed() } + + function update() { + value = Modules.Settings.xaxisstep + visible = !Modules.Settings.logscalex + } } Setting.TextSetting { @@ -275,11 +311,13 @@ ScrollView { label: qsTr("Y Axis Step") icon: "settings/yaxisstep.svg" width: settings.settingWidth - defValue: settings.yaxisstep + onChanged: function(newValue) { - settings.yaxisstep = newValue + Modules.Settings.set("yaxisstep", newValue, true) settings.changed() } + + function update() { value = Modules.Settings.yaxisstep } } Setting.TextSetting { @@ -290,11 +328,13 @@ ScrollView { min: 1 icon: "settings/linewidth.svg" width: settings.settingWidth - defValue: settings.linewidth + onChanged: function(newValue) { - settings.linewidth = newValue + Modules.Settings.set("linewidth", newValue, true) settings.changed() } + + function update() { value = Modules.Settings.linewidth } } Setting.TextSetting { @@ -305,11 +345,13 @@ ScrollView { min: 1 icon: "settings/textsize.svg" width: settings.settingWidth - defValue: settings.textsize + onChanged: function(newValue) { - settings.textsize = newValue + Modules.Settings.set("textsize", newValue, true) settings.changed() } + + function update() { value = Modules.Settings.textsize } } Setting.ComboBoxSetting { @@ -318,24 +360,31 @@ ScrollView { width: settings.settingWidth label: qsTr('X Label') icon: "settings/xlabel.svg" + editable: true model: ListModel { ListElement { text: "" } ListElement { text: "x" } ListElement { text: "ω (rad/s)" } } - currentIndex: find(settings.xlabel) - editable: true + onAccepted: function(){ editText = JS.Utils.parseName(editText, false) - if (find(editText) === -1) model.append({text: editText}) - settings.xlabel = editText + if(find(editText) === -1) model.append({text: editText}) + currentIndex = find(editText) + Modules.Settings.set("xlabel", editText, true) settings.changed() } + onActivated: function(selectedId) { - settings.xlabel = model.get(selectedId).text + Modules.Settings.set("xlabel", model.get(selectedId).text, true) settings.changed() } - Component.onCompleted: editText = settings.xlabel + + function update() { + editText = Modules.Settings.xlabel + if(find(editText) === -1) model.append({text: editText}) + currentIndex = find(editText) + } } Setting.ComboBoxSetting { @@ -344,6 +393,7 @@ ScrollView { width: settings.settingWidth label: qsTr('Y Label') icon: "settings/ylabel.svg" + editable: true model: ListModel { ListElement { text: "" } ListElement { text: "y" } @@ -352,39 +402,52 @@ ScrollView { ListElement { text: "φ (deg)" } ListElement { text: "φ (rad)" } } - currentIndex: find(settings.ylabel) - editable: true + onAccepted: function(){ editText = JS.Utils.parseName(editText, false) - if (find(editText) === -1) model.append({text: editText, yaxisstep: root.yaxisstep}) - settings.ylabel = editText + if(find(editText) === -1) model.append({text: editText}) + currentIndex = find(editText) + Modules.Settings.set("ylabel", editText, true) settings.changed() } + onActivated: function(selectedId) { - settings.ylabel = model.get(selectedId).text + Modules.Settings.set("ylabel", model.get(selectedId).text, true) settings.changed() } - Component.onCompleted: editText = settings.ylabel + + function update() { + editText = Modules.Settings.ylabel + if(find(editText) === -1) model.append({text: editText}) + currentIndex = find(editText) + } } CheckBox { id: logScaleX - checked: settings.logscalex text: qsTr('X Log scale') onClicked: { - settings.logscalex = checked + Modules.Settings.set("logscalex", checked, true) + if(Modules.Settings.xmin <= 0) // Reset xmin to prevent crash. + Modules.Settings.set("xmin", .5) settings.changed() } + + function update() { + checked = Modules.Settings.logscalex + xAxisStep.update() + } } CheckBox { id: showXGrad - checked: settings.showxgrad text: qsTr('Show X graduation') onClicked: { - settings.showxgrad = checked + Modules.Settings.set("showxgrad", checked, true) settings.changed() } + + function update() { checked = Modules.Settings.showxgrad } } CheckBox { @@ -392,9 +455,10 @@ ScrollView { checked: settings.showygrad text: qsTr('Show Y graduation') onClicked: { - settings.showygrad = checked + Modules.Settings.set("showygrad", checked, true) settings.changed() } + function update() { checked = Modules.Settings.showygrad } } Button { @@ -440,10 +504,10 @@ ScrollView { Saves the current canvas in the opened file. If no file is currently opened, prompts to pick a save location. */ function save() { - if(settings.saveFilename == "") { + if(Modules.Settings.saveFilename == "") { saveAs() } else { - Modules.IO.saveDiagram(settings.saveFilename) + Modules.IO.saveDiagram(Modules.Settings.saveFilename) } } @@ -464,4 +528,30 @@ ScrollView { fdiag.exportMode = false fdiag.open() } + + /** + * Initializing the settings + */ + Component.onCompleted: function() { + const matchedElements = new Map([ + ["xzoom", zoomX], + ["yzoom", zoomY], + ["xmin", minX], + ["ymax", maxY], + ["xaxisstep", xAxisStep], + ["yaxisstep", yAxisStep], + ["xlabel", xAxisLabel], + ["ylabel", yAxisLabel], + ["linewidth", lineWidth], + ["textsize", textSize], + ["logscalex", logScaleX], + ["showxgrad", showXGrad], + ["showygrad", showYGrad] + ]) + Modules.Settings.on("changed", (evt) => { + if(matchedElements.has(evt.property)) + matchedElements.get(evt.property).update() + }) + Modules.Settings.initialize({ helper: Helper }) + } } diff --git a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml index e8514d4..7a7072b 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml @@ -17,8 +17,6 @@ */ import QtQuick -import QtQuick.Controls -import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting /*! \qmltype ViewPositionChangeOverlay @@ -81,7 +79,7 @@ Item { property int prevY /*! \qmlproperty double ViewPositionChangeOverlay::baseZoomMultiplier - How much should the zoom be mutliplied/scrolled by for one scroll step (120° on the mouse wheel). + How much should the zoom be multiplied/scrolled by for one scroll step (120° on the mouse wheel). */ property double baseZoomMultiplier: 0.1 @@ -91,15 +89,15 @@ Item { cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor property int positionChangeTimer: 0 - function updatePosition(deltaX, deltaY) { + function updatePosition(deltaX, deltaY, isEnd) { const unauthorized = [NaN, Infinity, -Infinity] - const xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(settingsInstance.xmin)-deltaX)) - const ymax = settingsInstance.ymax + deltaY/canvas.yzoom + const xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(Modules.Settings.xmin)-deltaX)) + const ymax = Modules.Settings.ymax + deltaY/Modules.Settings.yzoom if(!unauthorized.includes(xmin)) - settingsInstance.xmin = xmin + Modules.Settings.set("xmin", xmin, isEnd) if(!unauthorized.includes(ymax)) - settingsInstance.ymax = ymax.toFixed(4) - settingsInstance.changed() + Modules.Settings.set("ymax", ymax.toDecimalPrecision(6), isEnd) + Modules.Canvas.requestPaint() parent.positionChanged(deltaX, deltaY) } @@ -113,9 +111,9 @@ Item { onPositionChanged: function(mouse) { positionChangeTimer++ if(positionChangeTimer == 3) { - let deltaX = mouse.x - prevX - let deltaY = mouse.y - prevY - updatePosition(deltaX, deltaY) + let deltaX = mouse.x - parent.prevX + let deltaY = mouse.y - parent.prevY + updatePosition(deltaX, deltaY, false) prevX = mouse.x prevY = mouse.y positionChangeTimer = 0 @@ -123,35 +121,35 @@ Item { } onReleased: function(mouse) { - let deltaX = mouse.x - prevX - let deltaY = mouse.y - prevY - updatePosition(deltaX, deltaY) + let deltaX = mouse.x - parent.prevX + let deltaY = mouse.y - parent.prevY + updatePosition(deltaX, deltaY, true) parent.endPositionChange(deltaX, deltaY) } onWheel: function(wheel) { // Scrolling let scrollSteps = Math.round(wheel.angleDelta.y / 120) - let zoomMultiplier = Math.pow(1+baseZoomMultiplier, Math.abs(scrollSteps)) + let zoomMultiplier = Math.pow(1+parent.baseZoomMultiplier, Math.abs(scrollSteps)) // Avoid floating-point rounding errors by removing the zoom *after* - let xZoomDelta = (settingsInstance.xzoom*zoomMultiplier - settingsInstance.xzoom) - let yZoomDelta = (settingsInstance.yzoom*zoomMultiplier - settingsInstance.yzoom) + let xZoomDelta = (Modules.Settings.xzoom*zoomMultiplier - Modules.Settings.xzoom) + let yZoomDelta = (Modules.Settings.yzoom*zoomMultiplier - Modules.Settings.yzoom) if(scrollSteps < 0) { // Negative scroll xZoomDelta *= -1 yZoomDelta *= -1 } - let newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(0) - let newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(0) + let newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(0) + let newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(0) // Check if we need to have more precision if(newXZoom < 10) - newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(4) + newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(4) if(newYZoom < 10) - newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(4) + newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(4) if(newXZoom > 0.5) - settingsInstance.xzoom = newXZoom + Modules.Settings.set("xzoom", newXZoom) if(newYZoom > 0.5) - settingsInstance.yzoom = newYZoom - settingsInstance.changed() + Modules.Settings.set("yzoom", newYZoom) + Modules.Canvas.requestPaint() } } }