diff --git a/common/src/events.mjs b/common/src/events.mjs index 4e95db6..f1ec970 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. */ - on(eventType, eventListener) { - if(!this.constructor.emits.includes(eventType)) { + addEventListener(eventType, eventListener) { + if(!this.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,13 +70,14 @@ 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. */ - off(eventType, eventListener) { - if(!this.constructor.emits.includes(eventType)) { + removeEventListener(eventType, eventListener) { + if(!this.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) + } /** @@ -87,11 +88,8 @@ export class BaseEventEmitter { emit(e) { if(!(e instanceof BaseEvent)) throw new Error("Cannot emit non event object.") - 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}`) - } + 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(", ")}.`) 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 b412716..9bcf80c 100644 --- a/common/src/lib/polyfills/js.mjs +++ b/common/src/lib/polyfills/js.mjs @@ -64,7 +64,10 @@ function arrayFlatMap(callbackFn, thisArg) { * @return {String} */ function stringReplaceAll(from, to) { - return this.split(from).join(to) + let str = this + while(str.includes(from)) + str = str.replace(from, to) + return str } diff --git a/common/src/module/canvas.mjs b/common/src/module/canvas.mjs index 5d42f5e..2aa14ad 100644 --- a/common/src/module/canvas.mjs +++ b/common/src/module/canvas.mjs @@ -23,7 +23,6 @@ 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} */ @@ -85,7 +84,7 @@ class CanvasAPI extends Module { */ get xmin() { if(!this.initialized) throw new Error("Attempting xmin before initialize!") - return Settings.xmin + return this.#canvas.xmin } /** @@ -94,7 +93,7 @@ class CanvasAPI extends Module { */ get xzoom() { if(!this.initialized) throw new Error("Attempting xzoom before initialize!") - return Settings.xzoom + return this.#canvas.xzoom } /** @@ -103,7 +102,7 @@ class CanvasAPI extends Module { */ get ymax() { if(!this.initialized) throw new Error("Attempting ymax before initialize!") - return Settings.ymax + return this.#canvas.ymax } /** @@ -112,7 +111,7 @@ class CanvasAPI extends Module { */ get yzoom() { if(!this.initialized) throw new Error("Attempting yzoom before initialize!") - return Settings.yzoom + return this.#canvas.yzoom } /** @@ -121,7 +120,7 @@ class CanvasAPI extends Module { */ get xlabel() { if(!this.initialized) throw new Error("Attempting xlabel before initialize!") - return Settings.xlabel + return this.#canvas.xlabel } /** @@ -130,7 +129,7 @@ class CanvasAPI extends Module { */ get ylabel() { if(!this.initialized) throw new Error("Attempting ylabel before initialize!") - return Settings.ylabel + return this.#canvas.ylabel } /** @@ -139,7 +138,7 @@ class CanvasAPI extends Module { */ get linewidth() { if(!this.initialized) throw new Error("Attempting linewidth before initialize!") - return Settings.linewidth + return this.#canvas.linewidth } /** @@ -148,7 +147,7 @@ class CanvasAPI extends Module { */ get textsize() { if(!this.initialized) throw new Error("Attempting textsize before initialize!") - return Settings.textsize + return this.#canvas.textsize } /** @@ -157,7 +156,7 @@ class CanvasAPI extends Module { */ get logscalex() { if(!this.initialized) throw new Error("Attempting logscalex before initialize!") - return Settings.logscalex + return this.#canvas.logscalex } /** @@ -166,7 +165,7 @@ class CanvasAPI extends Module { */ get showxgrad() { if(!this.initialized) throw new Error("Attempting showxgrad before initialize!") - return Settings.showxgrad + return this.#canvas.showxgrad } /** @@ -175,7 +174,7 @@ class CanvasAPI extends Module { */ get showygrad() { if(!this.initialized) throw new Error("Attempting showygrad before initialize!") - return Settings.showygrad + return this.#canvas.showygrad } /** @@ -238,9 +237,9 @@ class CanvasAPI extends Module { * @private */ _computeAxes() { - let exprY = new Expression(`x*(${Settings.yaxisstep})`) + let exprY = new Expression(`x*(${this.#canvas.yaxisstep})`) let y1 = exprY.execute(1) - let exprX = new Expression(`x*(${Settings.xaxisstep})`) + let exprX = new Expression(`x*(${this.#canvas.xaxisstep})`) let x1 = exprX.execute(1) this.axesSteps = { x: { diff --git a/common/src/module/index.mjs b/common/src/module/index.mjs index 2530a00..fe2e8d9 100644 --- a/common/src/module/index.mjs +++ b/common/src/module/index.mjs @@ -17,7 +17,6 @@ */ 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" @@ -27,7 +26,6 @@ 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 606ec4d..f6e5a13 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 Interface { +export class CanvasInterface extends SettingsInterface { imageLoaders = OBJECT /** @type {function(string): CanvasRenderingContext2D} */ getContext = FUNCTION diff --git a/common/src/module/io.mjs b/common/src/module/io.mjs index 0b4c6fa..e32f2cf 100644 --- a/common/src/module/io.mjs +++ b/common/src/module/io.mjs @@ -20,20 +20,22 @@ 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 + root: RootInterface, + settings: SettingsInterface }) /** * Path of the currently opened file. Empty if no file is opened. @@ -45,11 +47,13 @@ class IOAPI extends Module { /** * Initializes module with QML elements. * @param {RootInterface} root + * @param {SettingsInterface} settings * @param {{show: function(string)}} alert */ - initialize({ root, alert }) { - super.initialize({ root, alert }) + initialize({ root, settings, alert }) { + super.initialize({ root, settings, alert }) this.#rootElement = root + this.#settings = settings this.#alert = alert } @@ -62,7 +66,7 @@ class IOAPI extends Module { // Add extension if necessary if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1) filename += ".lpf" - Settings.set("saveFilename", filename, false) + this.saveFilename = filename let objs = {} for(let objType in Objects.currentObjects) { objs[objType] = [] @@ -71,19 +75,19 @@ class IOAPI extends Module { } } let settings = { - "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, + "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, "history": History.serialize(), "width": this.#rootElement.width, "height": this.#rootElement.height, @@ -109,24 +113,24 @@ class IOAPI extends Module { if(data.hasOwnProperty("type") && data["type"] === "logplotv1") { History.clear() // Importing settings - 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) + 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 if("showxgrad" in data) - Settings.set("showxgrad", data["showxgrad"], false) + this.#settings.showxgrad = data["showxgrad"] if("showygrad" in data) - Settings.set("showygrad", data["showygrad"], false) + this.#settings.textsize = data["showygrad"] if("linewidth" in data) - Settings.set("linewidth", data["linewidth"], false) + this.#settings.linewidth = data["linewidth"] if("textsize" in data) - Settings.set("textsize", data["textsize"], false) + this.#settings.textsize = data["textsize"] 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 a0e8401..8fabee4 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: // Ternary operator + case Instruction.IOP3: // Thirdiary 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 44a6593..4487dde 100644 --- a/common/src/module/settings.mjs +++ b/common/src/module/settings.mjs @@ -18,7 +18,6 @@ import { Module } from "./common.mjs" import { BaseEvent } from "../events.mjs" -import { HelperInterface } from "./interface.mjs" /** @@ -48,23 +47,20 @@ class ChangedEvent extends BaseEvent { class SettingsAPI extends Module { static emits = ["changed"] - #nonConfigurable = ["saveFilename"] - #properties = new Map([ - ["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] + ['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() { @@ -77,39 +73,32 @@ class SettingsAPI extends Module { super.initialize({ helper }) // Initialize default values. for(const key of this.#properties.keys()) { - 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 - } + 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 } } } /** * Sets a setting to a given value - * - * @param {string} property - * @param {string|number|boolean} value - * @param {boolean} byUser - Set to true if the user is at the origin of this change. + * + * @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} (${typeof value} provided).`) + throw new Error(`Value of ${property} must be a ${propType}.`) this.#properties.set(property, value) this.emit(new ChangedEvent(property, oldValue, value, byUser === true)) } @@ -118,70 +107,67 @@ 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 20d62e3..0fa9531 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.yzoom", + "default_graph.xzoom", "yzoom", 0.1 ) diff --git a/common/src/utils.mjs b/common/src/utils.mjs index 094147d..4f6b0c5 100644 --- a/common/src/utils.mjs +++ b/common/src/utils.mjs @@ -35,19 +35,6 @@ 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 07b6002..38d05be 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/LogGraphCanvas.qml @@ -30,15 +30,104 @@ import Qt.labs.platform as Native */ Canvas { id: canvas - anchors.top: parent.top + anchors.top: separator.bottom 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 = {} @@ -66,7 +155,7 @@ Canvas { Object.keys(imageLoaders).forEach((key) => { if(isImageLoaded(key)) { // Calling callback - imageLoaders[key][0](imageLoaders[key][1]) + imageLoaders[key][0](canvas, ctx, 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 51abb46..8545153 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" + title: "LogarithmPlotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename.split('/').pop() : "") + (history.saved ? "" : "*") SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } @@ -145,6 +145,20 @@ 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) { @@ -250,12 +264,5 @@ 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 680c310..2fb45d4 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(Modules.Settings.logscalex) { + if(canvas.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 a0c030a..afa3e09 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: parent.icon == "" ? 0 : 3 - source: control.visible && parent.icon != "" ? "../icons/" + control.icon : "" + anchors.topMargin: icon == "" ? 0 : 3 + source: control.visible && icon != "" ? "../icons/" + control.icon : "" width: height - height: parent.icon == "" || !visible ? 0 : 24 + height: icon == "" || !visible ? 0 : 24 color: sysPalette.windowText } Label { id: labelItem anchors.left: iconLabel.right - anchors.leftMargin: parent.icon == "" ? 0 : 5 + anchors.leftMargin: 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() != parent.defValue) { - let expr = parent.parse(value) + if(value != "" && value.toString() != defValue) { + let expr = parse(value) if(expr != null) { control.changed(expr) defValue = expr.toEditableString() @@ -280,10 +280,10 @@ Item { acPopupContent.itemSelected = 0 - if(event.text in parent.openAndCloseMatches && autoClosing) { + if(event.text in openAndCloseMatches && autoClosing) { let start = selectionStart insert(selectionStart, event.text) - insert(selectionEnd, parent.openAndCloseMatches[event.text]) + insert(selectionEnd, 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 3689dc1..b0a054a 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(var newValue) + signal changed(string 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 b440d07..534556f 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/Settings.qml @@ -121,6 +121,11 @@ 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 @@ -131,18 +136,15 @@ ScrollView { id: fdiag onAccepted: { var filePath = fdiag.currentFile.toString().substr(7) - Modules.Settings.set("saveFilename", filePath) + settings.saveFilename = filePath if(exportMode) { Modules.IO.saveDiagram(filePath) } else { Modules.IO.loadDiagram(filePath) - // 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 + 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 } } } @@ -156,16 +158,11 @@ ScrollView { min: 0.1 icon: "settings/xzoom.svg" width: settings.settingWidth - + value: settings.xzoom.toFixed(2) onChanged: function(newValue) { - Modules.Settings.set("xzoom", newValue, true) + settings.xzoom = newValue settings.changed() } - - function update(newValue) { - value = Modules.Settings.xzoom.toFixed(2) - maxX.update() - } } Setting.TextSetting { @@ -176,16 +173,11 @@ ScrollView { label: qsTr("Y Zoom") icon: "settings/yzoom.svg" width: settings.settingWidth - + value: settings.yzoom.toFixed(2) onChanged: function(newValue) { - Modules.Settings.set("yzoom", newValue, true) + settings.yzoom = newValue settings.changed() } - - function update(newValue) { - value = Modules.Settings.yzoom.toFixed(2) - minY.update() - } } // Positioning the graph @@ -197,18 +189,14 @@ ScrollView { label: qsTr("Min X") icon: "settings/xmin.svg" width: settings.settingWidth - + defValue: settings.xmin onChanged: function(newValue) { - 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() + if(parseFloat(maxX.value) > newValue) { + settings.xmin = newValue + settings.changed() + } else { + alert.show("Minimum x value must be inferior to maximum.") + } } } @@ -220,16 +208,11 @@ ScrollView { label: qsTr("Max Y") icon: "settings/ymax.svg" width: settings.settingWidth - + defValue: settings.ymax onChanged: function(newValue) { - Modules.Settings.set("ymax", newValue, true) + settings.ymax = newValue settings.changed() } - - function update() { - value = Modules.Settings.ymax - minY.update() - } } Setting.TextSetting { @@ -240,24 +223,15 @@ 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 > 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() + if(xvaluemax > settings.xmin) { + settings.xzoom = settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point) 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 { @@ -268,21 +242,15 @@ 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) { - 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.yzoom = settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point) 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 { @@ -292,16 +260,12 @@ ScrollView { label: qsTr("X Axis Step") icon: "settings/xaxisstep.svg" width: settings.settingWidth - + defValue: settings.xaxisstep + visible: !settings.logscalex onChanged: function(newValue) { - Modules.Settings.set("xaxisstep", newValue, true) + settings.xaxisstep = newValue settings.changed() } - - function update() { - value = Modules.Settings.xaxisstep - visible = !Modules.Settings.logscalex - } } Setting.TextSetting { @@ -311,13 +275,11 @@ ScrollView { label: qsTr("Y Axis Step") icon: "settings/yaxisstep.svg" width: settings.settingWidth - + defValue: settings.yaxisstep onChanged: function(newValue) { - Modules.Settings.set("yaxisstep", newValue, true) + settings.yaxisstep = newValue settings.changed() } - - function update() { value = Modules.Settings.yaxisstep } } Setting.TextSetting { @@ -328,13 +290,11 @@ ScrollView { min: 1 icon: "settings/linewidth.svg" width: settings.settingWidth - + defValue: settings.linewidth onChanged: function(newValue) { - Modules.Settings.set("linewidth", newValue, true) + settings.linewidth = newValue settings.changed() } - - function update() { value = Modules.Settings.linewidth } } Setting.TextSetting { @@ -345,13 +305,11 @@ ScrollView { min: 1 icon: "settings/textsize.svg" width: settings.settingWidth - + defValue: settings.textsize onChanged: function(newValue) { - Modules.Settings.set("textsize", newValue, true) + settings.textsize = newValue settings.changed() } - - function update() { value = Modules.Settings.textsize } } Setting.ComboBoxSetting { @@ -360,31 +318,24 @@ 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}) - currentIndex = find(editText) - Modules.Settings.set("xlabel", editText, true) + if (find(editText) === -1) model.append({text: editText}) + settings.xlabel = editText settings.changed() } - onActivated: function(selectedId) { - Modules.Settings.set("xlabel", model.get(selectedId).text, true) + settings.xlabel = model.get(selectedId).text settings.changed() } - - function update() { - editText = Modules.Settings.xlabel - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - } + Component.onCompleted: editText = settings.xlabel } Setting.ComboBoxSetting { @@ -393,7 +344,6 @@ ScrollView { width: settings.settingWidth label: qsTr('Y Label') icon: "settings/ylabel.svg" - editable: true model: ListModel { ListElement { text: "" } ListElement { text: "y" } @@ -402,52 +352,39 @@ 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}) - currentIndex = find(editText) - Modules.Settings.set("ylabel", editText, true) + if (find(editText) === -1) model.append({text: editText, yaxisstep: root.yaxisstep}) + settings.ylabel = editText settings.changed() } - onActivated: function(selectedId) { - Modules.Settings.set("ylabel", model.get(selectedId).text, true) + settings.ylabel = model.get(selectedId).text settings.changed() } - - function update() { - editText = Modules.Settings.ylabel - if(find(editText) === -1) model.append({text: editText}) - currentIndex = find(editText) - } + Component.onCompleted: editText = settings.ylabel } CheckBox { id: logScaleX + checked: settings.logscalex text: qsTr('X Log scale') onClicked: { - Modules.Settings.set("logscalex", checked, true) - if(Modules.Settings.xmin <= 0) // Reset xmin to prevent crash. - Modules.Settings.set("xmin", .5) + settings.logscalex = checked settings.changed() } - - function update() { - checked = Modules.Settings.logscalex - xAxisStep.update() - } } CheckBox { id: showXGrad + checked: settings.showxgrad text: qsTr('Show X graduation') onClicked: { - Modules.Settings.set("showxgrad", checked, true) + settings.showxgrad = checked settings.changed() } - - function update() { checked = Modules.Settings.showxgrad } } CheckBox { @@ -455,10 +392,9 @@ ScrollView { checked: settings.showygrad text: qsTr('Show Y graduation') onClicked: { - Modules.Settings.set("showygrad", checked, true) + settings.showygrad = checked settings.changed() } - function update() { checked = Modules.Settings.showygrad } } Button { @@ -504,10 +440,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(Modules.Settings.saveFilename == "") { + if(settings.saveFilename == "") { saveAs() } else { - Modules.IO.saveDiagram(Modules.Settings.saveFilename) + Modules.IO.saveDiagram(settings.saveFilename) } } @@ -528,30 +464,4 @@ 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 7a7072b..e8514d4 100644 --- a/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml +++ b/runtime-pyside6/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/ViewPositionChangeOverlay.qml @@ -17,6 +17,8 @@ */ import QtQuick +import QtQuick.Controls +import eu.ad5001.LogarithmPlotter.Setting 1.0 as Setting /*! \qmltype ViewPositionChangeOverlay @@ -79,7 +81,7 @@ Item { property int prevY /*! \qmlproperty double ViewPositionChangeOverlay::baseZoomMultiplier - How much should the zoom be multiplied/scrolled by for one scroll step (120° on the mouse wheel). + How much should the zoom be mutliplied/scrolled by for one scroll step (120° on the mouse wheel). */ property double baseZoomMultiplier: 0.1 @@ -89,15 +91,15 @@ Item { cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor property int positionChangeTimer: 0 - function updatePosition(deltaX, deltaY, isEnd) { + function updatePosition(deltaX, deltaY) { const unauthorized = [NaN, Infinity, -Infinity] - const xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(Modules.Settings.xmin)-deltaX)) - const ymax = Modules.Settings.ymax + deltaY/Modules.Settings.yzoom + const xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(settingsInstance.xmin)-deltaX)) + const ymax = settingsInstance.ymax + deltaY/canvas.yzoom if(!unauthorized.includes(xmin)) - Modules.Settings.set("xmin", xmin, isEnd) + settingsInstance.xmin = xmin if(!unauthorized.includes(ymax)) - Modules.Settings.set("ymax", ymax.toDecimalPrecision(6), isEnd) - Modules.Canvas.requestPaint() + settingsInstance.ymax = ymax.toFixed(4) + settingsInstance.changed() parent.positionChanged(deltaX, deltaY) } @@ -111,9 +113,9 @@ Item { onPositionChanged: function(mouse) { positionChangeTimer++ if(positionChangeTimer == 3) { - let deltaX = mouse.x - parent.prevX - let deltaY = mouse.y - parent.prevY - updatePosition(deltaX, deltaY, false) + let deltaX = mouse.x - prevX + let deltaY = mouse.y - prevY + updatePosition(deltaX, deltaY) prevX = mouse.x prevY = mouse.y positionChangeTimer = 0 @@ -121,35 +123,35 @@ Item { } onReleased: function(mouse) { - let deltaX = mouse.x - parent.prevX - let deltaY = mouse.y - parent.prevY - updatePosition(deltaX, deltaY, true) + let deltaX = mouse.x - prevX + let deltaY = mouse.y - prevY + updatePosition(deltaX, deltaY) parent.endPositionChange(deltaX, deltaY) } onWheel: function(wheel) { // Scrolling let scrollSteps = Math.round(wheel.angleDelta.y / 120) - let zoomMultiplier = Math.pow(1+parent.baseZoomMultiplier, Math.abs(scrollSteps)) + let zoomMultiplier = Math.pow(1+baseZoomMultiplier, Math.abs(scrollSteps)) // Avoid floating-point rounding errors by removing the zoom *after* - let xZoomDelta = (Modules.Settings.xzoom*zoomMultiplier - Modules.Settings.xzoom) - let yZoomDelta = (Modules.Settings.yzoom*zoomMultiplier - Modules.Settings.yzoom) + let xZoomDelta = (settingsInstance.xzoom*zoomMultiplier - settingsInstance.xzoom) + let yZoomDelta = (settingsInstance.yzoom*zoomMultiplier - settingsInstance.yzoom) if(scrollSteps < 0) { // Negative scroll xZoomDelta *= -1 yZoomDelta *= -1 } - let newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(0) - let newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(0) + let newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(0) + let newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(0) // Check if we need to have more precision if(newXZoom < 10) - newXZoom = (Modules.Settings.xzoom+xZoomDelta).toDecimalPrecision(4) + newXZoom = (settingsInstance.xzoom+xZoomDelta).toFixed(4) if(newYZoom < 10) - newYZoom = (Modules.Settings.yzoom+yZoomDelta).toDecimalPrecision(4) + newYZoom = (settingsInstance.yzoom+yZoomDelta).toFixed(4) if(newXZoom > 0.5) - Modules.Settings.set("xzoom", newXZoom) + settingsInstance.xzoom = newXZoom if(newYZoom > 0.5) - Modules.Settings.set("yzoom", newYZoom) - Modules.Canvas.requestPaint() + settingsInstance.yzoom = newYZoom + settingsInstance.changed() } } }