Implementing modules requiring interfaces

This commit is contained in:
Adsooi 2024-09-23 20:55:48 +02:00
parent 8c8964e75e
commit 4c1403c983
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
10 changed files with 312 additions and 131 deletions

View file

@ -212,8 +212,11 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
Modules.History.history = historyObj Modules.History.initialize({
Modules.History.themeTextColor = sysPalette.windowText historyObj,
Modules.History.imageDepth = Screen.devicePixelRatio themeTextColor: sysPalette.windowText.toString(),
imageDepth: Screen.devicePixelRatio,
fontSize: 14
})
} }
} }

View file

@ -120,12 +120,6 @@ Canvas {
*/ */
property bool showygrad: false property bool showygrad: false
/*!
\qmlproperty int LogGraphCanvas::maxgradx
Max power of the logarithmic scaled on the x axis in logarithmic mode.
*/
property int maxgradx: 90
/*! /*!
\qmlproperty var LogGraphCanvas::imageLoaders \qmlproperty var LogGraphCanvas::imageLoaders
Dictionary of format {image: [callback.image data]} containing data for defered image loading. Dictionary of format {image: [callback.image data]} containing data for defered image loading.
@ -139,7 +133,7 @@ Canvas {
Component.onCompleted: { Component.onCompleted: {
imageLoaders = {} imageLoaders = {}
Modules.Canvas.initialize(canvas, drawingErrorDialog) Modules.Canvas.initialize({ canvas, drawingErrorDialog })
} }
Native.MessageDialog { Native.MessageDialog {

View file

@ -128,7 +128,7 @@ ScrollView {
property string saveFilename: "" property string saveFilename: ""
Component.onCompleted: { Component.onCompleted: {
Modules.IO.initialize(root, settings, alert) Modules.IO.initialize({ root, settings, alert })
} }
Column { Column {

View file

@ -24,23 +24,53 @@ import Objects from "./objects.mjs"
import History from "./history.mjs" import History from "./history.mjs"
/**
* @typedef {Settings} Canvas
* @property {object} imageLoaders
* @property {function()} requestPaint
* @property {function(string)} getContext
* @property {function(rect)} markDirty
* @property {function(string)} loadImage
*/
class CanvasAPI extends Module { class CanvasAPI extends Module {
constructor() { constructor() {
super('Canvas', [ super("Canvas", {
Modules.Objects, canvas: {
Modules.History width: "number",
]) height: "number",
xmin: "number",
ymax: "number",
xzoom: "number",
yzoom: "number",
xaxisstep: "number",
yaxisstep: "number",
xlabel: "string",
ylabel: "string",
linewidth: "number",
textsize: "number",
logscalex: "boolean",
showxgrad: "boolean",
showygrad: "boolean",
imageLoaders: "object",
getContext: Function,
markDirty: Function,
loadImage: Function,
requestPaint: Function,
},
drawingErrorDialog: {
showDialog: Function,
}
})
/** @type {HTMLCanvasElement} */ /** @type {Canvas} */
this._canvas = null this._canvas = null
/** @type {CanvasRenderingContext2D} */ /** @type {CanvasRenderingContext2D} */
this._ctx = null this._ctx = null
/** /**
* @type {Object} * @type {{showDialog(string, string, string)}}
* @property {function(string, string, string)} showDialog
* @private * @private
*/ */
this._drawingErrorDialog = null this._drawingErrorDialog = null
@ -62,86 +92,132 @@ class CanvasAPI extends Module {
} }
} }
initialize(canvasObject, drawingErrorDialog) { /**
this._canvas = canvasObject * Initialize the module.
* @param {Canvas} canvas
* @param {{showDialog(string, string, string)}} drawingErrorDialog
*/
initialize({ canvas, drawingErrorDialog }) {
super.initialize({ canvas, drawingErrorDialog })
this._canvas = canvas
this._drawingErrorDialog = drawingErrorDialog this._drawingErrorDialog = drawingErrorDialog
} }
get width() { return this._canvas.width } get width() {
if(!this.initialized) throw new Error("Attempting width before initialize!")
return this._canvas.width
}
get height() { return this._canvas.height } get height() {
if(!this.initialized) throw new Error("Attempting height before initialize!")
return this._canvas.height
}
/** /**
* Minimum x of the diagram, provided from settings. * Minimum x of the diagram, provided from settings.
* @returns {number} * @returns {number}
*/ */
get xmin() { return this._canvas.xmin } get xmin() {
if(!this.initialized) throw new Error("Attempting xmin before initialize!")
return this._canvas.xmin
}
/** /**
* Zoom on the x-axis of the diagram, provided from settings. * Zoom on the x-axis of the diagram, provided from settings.
* @returns {number} * @returns {number}
*/ */
get xzoom() { return this._canvas.xzoom } get xzoom() {
if(!this.initialized) throw new Error("Attempting xzoom before initialize!")
return this._canvas.xzoom
}
/** /**
* Maximum y of the diagram, provided from settings. * Maximum y of the diagram, provided from settings.
* @returns {number} * @returns {number}
*/ */
get ymax() { return this._canvas.ymax } get ymax() {
if(!this.initialized) throw new Error("Attempting ymax before initialize!")
return this._canvas.ymax
}
/** /**
* Zoom on the y-axis of the diagram, provided from settings. * Zoom on the y-axis of the diagram, provided from settings.
* @returns {number} * @returns {number}
*/ */
get yzoom() { return this._canvas.yzoom } get yzoom() {
if(!this.initialized) throw new Error("Attempting yzoom before initialize!")
return this._canvas.yzoom
}
/** /**
* Label used on the x-axis, provided from settings. * Label used on the x-axis, provided from settings.
* @returns {string} * @returns {string}
*/ */
get xlabel() { return this._canvas.xlabel } get xlabel() {
if(!this.initialized) throw new Error("Attempting xlabel before initialize!")
return this._canvas.xlabel
}
/** /**
* Label used on the y-axis, provided from settings. * Label used on the y-axis, provided from settings.
* @returns {string} * @returns {string}
*/ */
get ylabel() { return this._canvas.ylabel } get ylabel() {
if(!this.initialized) throw new Error("Attempting ylabel before initialize!")
return this._canvas.ylabel
}
/** /**
* Width of lines that will be drawn into the canvas, provided from settings. * Width of lines that will be drawn into the canvas, provided from settings.
* @returns {number} * @returns {number}
*/ */
get linewidth() { return this._canvas.linewidth } get linewidth() {
if(!this.initialized) throw new Error("Attempting linewidth before initialize!")
return this._canvas.linewidth
}
/** /**
* Font size of the text that will be drawn into the canvas, provided from settings. * Font size of the text that will be drawn into the canvas, provided from settings.
* @returns {number} * @returns {number}
*/ */
get textsize() { return this._canvas.textsize } get textsize() {
if(!this.initialized) throw new Error("Attempting textsize before initialize!")
return this._canvas.textsize
}
/** /**
* True if the canvas should be in logarithmic mode, false otherwise. * True if the canvas should be in logarithmic mode, false otherwise.
* @returns {boolean} * @returns {boolean}
*/ */
get logscalex() { return this._canvas.logscalex } get logscalex() {
if(!this.initialized) throw new Error("Attempting logscalex before initialize!")
return this._canvas.logscalex
}
/** /**
* True if the x graduation should be shown, false otherwise. * True if the x graduation should be shown, false otherwise.
* @returns {boolean} * @returns {boolean}
*/ */
get showxgrad() { return this._canvas.showxgrad } get showxgrad() {
if(!this.initialized) throw new Error("Attempting showxgrad before initialize!")
return this._canvas.showxgrad
}
/** /**
* True if the y graduation should be shown, false otherwise. * True if the y graduation should be shown, false otherwise.
* @returns {boolean} * @returns {boolean}
*/ */
get showygrad() { return this._canvas.showygrad } get showygrad() {
if(!this.initialized) throw new Error("Attempting showygrad before initialize!")
return this._canvas.showygrad
}
/** /**
* Max power of the logarithmic scaled on the x axis in logarithmic mode. * Max power of the logarithmic scaled on the x axis in logarithmic mode.
* @returns {number} * @returns {number}
*/ */
get maxgradx() { get maxgradx() {
if(!this.initialized) throw new Error("Attempting maxgradx before initialize!")
return Math.min( return Math.min(
309, // 10e309 = Infinity (beyond this land be dragons) 309, // 10e309 = Infinity (beyond this land be dragons)
Math.max( Math.max(
@ -156,6 +232,7 @@ class CanvasAPI extends Module {
// //
requestPaint() { requestPaint() {
if(!this.initialized) throw new Error("Attempting requestPaint before initialize!")
this._canvas.requestPaint() this._canvas.requestPaint()
} }
@ -163,6 +240,7 @@ class CanvasAPI extends Module {
* Redraws the entire canvas * Redraws the entire canvas
*/ */
redraw() { redraw() {
if(!this.initialized) throw new Error("Attempting redraw before initialize!")
this._ctx = this._canvas.getContext("2d") this._ctx = this._canvas.getContext("2d")
this._computeAxes() this._computeAxes()
this._reset() this._reset()
@ -282,7 +360,7 @@ class CanvasAPI extends Module {
// Axis graduation labels // Axis graduation labels
this._ctx.font = `${this.textsize - 4}px sans-serif` this._ctx.font = `${this.textsize - 4}px sans-serif`
let txtMinus = this._ctx.measureText('-').width let txtMinus = this._ctx.measureText("-").width
if(this.showxgrad) { if(this.showxgrad) {
if(this.logscalex) { if(this.logscalex) {
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow += 1) { for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow += 1) {
@ -293,21 +371,21 @@ class CanvasAPI extends Module {
} else { } else {
for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) { for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) {
let drawX = x * this.axesSteps.x.value let drawX = x * this.axesSteps.x.value
let txtX = this.axesSteps.x.expression.simplify(x).toString().replace(/^\((.+)\)$/, '$1') let txtX = this.axesSteps.x.expression.simplify(x).toString().replace(/^\((.+)\)$/, "$1")
let textHeight = this.measureText(txtX).height let textHeight = this.measureText(txtX).height
this.drawVisibleText(txtX, this.x2px(drawX) - 4, axisxpx + this.textsize / 2 + textHeight) this.drawVisibleText(txtX, this.x2px(drawX) - 4, axisxpx + this.textsize / 2 + textHeight)
this.drawVisibleText('-'+txtX, this.x2px(-drawX)-4, axisxpx+this.textsize/2+textHeight) this.drawVisibleText("-" + txtX, this.x2px(-drawX) - 4, axisxpx + this.textsize / 2 + textHeight)
} }
} }
} }
if(this.showygrad) { if(this.showygrad) {
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) { for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
let drawY = y * this.axesSteps.y.value let drawY = y * this.axesSteps.y.value
let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, '$1') let txtY = this.axesSteps.y.expression.simplify(y).toString().replace(/^\((.+)\)$/, "$1")
textWidth = this._ctx.measureText(txtY).width textWidth = this._ctx.measureText(txtY).width
this.drawVisibleText(txtY, axisypx - 6 - textWidth, this.y2px(drawY) + 4 + (10 * (y === 0))) this.drawVisibleText(txtY, axisypx - 6 - textWidth, this.y2px(drawY) + 4 + (10 * (y === 0)))
if(y !== 0) if(y !== 0)
this.drawVisibleText('-'+txtY, axisypx-6-textWidth-txtMinus, this.y2px(-drawY)+4) this.drawVisibleText("-" + txtY, axisypx - 6 - textWidth - txtMinus, this.y2px(-drawY) + 4)
} }
} }
this._ctx.fillStyle = "#FFFFFF" this._ctx.fillStyle = "#FFFFFF"
@ -363,7 +441,7 @@ class CanvasAPI extends Module {
* @param {number} height * @param {number} height
*/ */
drawVisibleImage(image, x, y, width, height) { drawVisibleImage(image, x, y, width, height) {
this._canvas.markDirty(Qt.rect(x, y, width, height)); this._canvas.markDirty(Qt.rect(x, y, width, height))
this._ctx.drawImage(image, x, y, width, height) this._ctx.drawImage(image, x, y, width, height)
} }
@ -380,7 +458,7 @@ class CanvasAPI extends Module {
theight += defaultHeight theight += defaultHeight
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
} }
return {'width': twidth, 'height': theight} return { "width": twidth, "height": theight }
} }
/** /**
@ -448,10 +526,10 @@ class CanvasAPI extends Module {
* @param {number} y2 * @param {number} y2
*/ */
drawLine(x1, y1, x2, y2) { drawLine(x1, y1, x2, y2) {
this._ctx.beginPath(); this._ctx.beginPath()
this._ctx.moveTo(x1, y1); this._ctx.moveTo(x1, y1)
this._ctx.lineTo(x2, y2); this._ctx.lineTo(x2, y2)
this._ctx.stroke(); this._ctx.stroke()
} }
/** /**
@ -463,9 +541,9 @@ class CanvasAPI extends Module {
* @param {number} dashPxSize * @param {number} dashPxSize
*/ */
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) { drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
this._ctx.setLineDash([dashPxSize/2, dashPxSize]); this._ctx.setLineDash([dashPxSize / 2, dashPxSize])
this.drawLine(x1, y1, x2, y2) this.drawLine(x1, y1, x2, y2)
this._ctx.setLineDash([]); this._ctx.setLineDash([])
} }
/** /**
@ -496,8 +574,13 @@ class CanvasAPI extends Module {
// Context methods // Context methods
// //
get font() { return this._ctx.font } get font() {
set font(value) { return this._ctx.font = value } return this._ctx.font
}
set font(value) {
return this._ctx.font = value
}
/** /**
* Draws an act on the canvas centered on a point. * Draws an act on the canvas centered on a point.
@ -521,9 +604,9 @@ class CanvasAPI extends Module {
* @param {number} radius * @param {number} radius
*/ */
disc(x, y, radius) { disc(x, y, radius) {
this._ctx.beginPath(); this._ctx.beginPath()
this._ctx.arc(x, y, radius, 0, 2 * Math.PI) this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
this._ctx.fill(); this._ctx.fill()
} }
/** /**

View file

@ -24,22 +24,44 @@ export class Module {
/** /**
* *
* @param {string} name - Name of the API * @param {string} name - Name of the API
* @param {(Module|undefined)[]} requires - List of APIs required to initialize this one. * @param {Object.<string, (Object.<string, string|object>|string[]|string|object)>} initializationParameters - List of parameters for the initialize function.
*/ */
constructor(name, requires = []) { constructor(name, initializationParameters = {}) {
console.log(`Loading module ${name}...`) console.log(`Loading module ${name}...`)
this.__check_requirements(requires, name) this.__name = name
this.__initializationParameters = initializationParameters
this.initialized = false
} }
/** /**
* Checks if all requirements are defined. * Checks if all requirements are defined.
* @param {(Module|undefined)[]} requires * @param {Object.<string, any>} options
* @param {string} name
*/ */
__check_requirements(requires, name) { initialize(options) {
for(let requirement of requires) { if(this.initialized)
if(requirement === undefined) throw new Error(`Cannot reinitialize module ${this.__name}.`)
throw new Error(`Requirement ${requires.indexOf(requirement)} of ${name} has not been initialized.`) for(const [name, value] of Object.entries(this.__initializationParameters)) {
} if(!options.hasOwnProperty(name))
throw new Error(`Option '${name}' of initialize of module ${this.__name} does not exist.`)
if(typeof value === "object") {
if(value instanceof Array)
for(const k in value)
if(!options[name].hasOwnProperty(k))
throw new Error(`Option '${name}' of initialize of module ${this.__name} does not have the property '${k}'.`)
else
for(const [k, v] in Object.entries(value)) {
if(!options[name].hasOwnProperty(k))
throw new Error(`Option '${name} of initialize of module ${this.__name} does not have the property '${k}'.`)
else if(typeof (v) === "string" && typeof (options[name][k]) !== v)
throw new Error(`Property '${k}' of initialize option ${name} of module ${this.__name}'s type is not '${v}'.`)
else if(typeof (v) === "object" && !(options[name][k] instanceof v))
throw new Error(`Property '${k}' of initialize option ${name} of module ${this.__name} is not a '${v}'.`)
}
} else if(typeof value === "string" && typeof options[name] !== value)
throw new Error(`Option '${name}' of initialize of module ${this.__name} is not a '${value}' (${typeof options[name]}).`)
}
this.initialized = true
} }
} }

View file

@ -36,10 +36,7 @@ const evalVariables = {
export class ExprParserAPI extends Module { export class ExprParserAPI extends Module {
constructor() { constructor() {
super("ExprParser", [ super("ExprParser")
/** @type {ObjectsAPI} */
Modules.Objects
])
this.currentVars = {} this.currentVars = {}
this._parser = new Parser() this._parser = new Parser()

View file

@ -22,22 +22,64 @@ import Latex from "./latex.mjs"
class HistoryAPI extends Module { class HistoryAPI extends Module {
constructor() { constructor() {
super('History', [ super("History", {
Modules.Latex historyObj: {
]) undo: Function,
redo: Function,
clear: Function,
addToHistory: Function,
unserialize: Function,
serialize: Function
},
themeTextColor: "string",
imageDepth: "number",
fontSize: "number"
})
// History QML object // History QML object
this.history = null; this.history = null
this.themeTextColor = "#ff0000"; this.themeTextColor = "#FF0000"
this.imageDepth = 2; this.imageDepth = 2
this.fontSize = 14; this.fontSize = 28
} }
undo() { this.history.undo() } initialize({ historyObj, themeTextColor, imageDepth, fontSize }) {
redo() { this.history.redo() } super.initialize({ historyObj, themeTextColor, imageDepth, fontSize })
clear() { this.history.clear() } console.log("Initializing history...")
addToHistory(action) { this.history.addToHistory(action) } this.history = historyObj
unserialize(...data) { this.history.unserialize(...data) } this.themeTextColor = themeTextColor
serialize() { return this.history.serialize() } this.imageDepth = imageDepth
this.fontSize = fontSize
}
undo() {
if(!this.initialized) throw new Error("Attempting undo before initialize!")
this.history.undo()
}
redo() {
if(!this.initialized) throw new Error("Attempting redo before initialize!")
this.history.redo()
}
clear() {
if(!this.initialized) throw new Error("Attempting clear before initialize!")
this.history.clear()
}
addToHistory(action) {
if(!this.initialized) throw new Error("Attempting addToHistory before initialize!")
this.history.addToHistory(action)
}
unserialize(...data) {
if(!this.initialized) throw new Error("Attempting unserialize before initialize!")
this.history.unserialize(...data)
}
serialize() {
if(!this.initialized) throw new Error("Attempting serialize before initialize!")
return this.history.serialize()
}
} }
/** @type {HistoryAPI} */ /** @type {HistoryAPI} */

View file

@ -21,13 +21,55 @@ import Objects from "./objects.mjs"
import History from "./history.mjs" import History from "./history.mjs"
import Canvas from "./canvas.mjs" import Canvas from "./canvas.mjs"
/**
* @typedef Settings
* @property {number} width
* @property {number} height
* @property {number} xmin
* @property {number} ymax
* @property {number} xzoom
* @property {number} yzoom
* @property {number} xaxisstep
* @property {number} yaxisstep
* @property {string} xlabel
* @property {string} ylabel
* @property {number} linewidth
* @property {number} textsize
* @property {boolean} logscalex
* @property {boolean} showxgrad
* @property {boolean} showygrad
*/
class IOAPI extends Module { class IOAPI extends Module {
constructor() { constructor() {
super("IO", [ super("IO", {
Modules.Objects, root: {
Modules.History width: "number",
]) height: "number",
updateObjectsLists: Function,
},
alert: {
show: Function
},
settings: {
width: "number",
height: "number",
xmin: "number",
ymax: "number",
xzoom: "number",
yzoom: "number",
xaxisstep: "number",
yaxisstep: "number",
xlabel: "string",
ylabel: "string",
linewidth: "number",
textsize: "number",
logscalex: "boolean",
showxgrad: "boolean",
showygrad: "boolean"
}
})
/** /**
* Path of the currently opened file. Empty if no file is opened. * Path of the currently opened file. Empty if no file is opened.
* @type {string} * @type {string}
@ -37,12 +79,13 @@ class IOAPI extends Module {
/** /**
* Initializes module with QML elements. * Initializes module with QML elements.
* @param {{width: number, height: number, updateObjectsLists: function()}} rootElement * @param {{width: number, height: number, updateObjectsLists: function()}} root
* @param {Settings} settings * @param {Settings} settings
* @param {{show: function(string)}} alert * @param {{show: function(string)}} alert
*/ */
initialize(rootElement, settings, alert) { initialize({ root, settings, alert }) {
this.rootElement = rootElement super.initialize({ root, settings, alert })
this.rootElement = root
this.settings = settings this.settings = settings
this.alert = alert this.alert = alert
} }
@ -52,6 +95,7 @@ class IOAPI extends Module {
* @param {string} filename * @param {string} filename
*/ */
saveDiagram(filename) { saveDiagram(filename) {
if(!this.initialized) throw new Error("Attempting saveDiagram before initialize!")
// Add extension if necessary // Add extension if necessary
if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1) if(["lpf"].indexOf(filename.split(".")[filename.split(".").length - 1]) === -1)
filename += ".lpf" filename += ".lpf"
@ -93,11 +137,13 @@ class IOAPI extends Module {
* @param {string} filename * @param {string} filename
*/ */
loadDiagram(filename) { loadDiagram(filename) {
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
let basename = filename.split("/").pop() let basename = filename.split("/").pop()
this.alert.show(qsTranslate("io", "Loading file '%1'.").arg(basename)) this.alert.show(qsTranslate("io", "Loading file '%1'.").arg(basename))
let data = JSON.parse(Helper.load(filename)) let data = JSON.parse(Helper.load(filename))
let error = "" let error = ""
if(Object.keys(data).includes("type") && data["type"] === "logplotv1") { if(data.hasOwnProperty("type") && data["type"] === "logplotv1") {
History.clear() History.clear()
// Importing settings // Importing settings
this.settings.saveFilename = filename this.settings.saveFilename = filename
@ -129,7 +175,7 @@ class IOAPI extends Module {
// Another way would be to change the reference as well, but I feel like the code would be less clean. // Another way would be to change the reference as well, but I feel like the code would be less clean.
} }
for(let objType in data["objects"]) { for(let objType in data["objects"]) {
if(Object.keys(Objects.types).indexOf(objType) > -1) { if(Object.keys(Objects.types).includes(objType)) {
Objects.currentObjects[objType] = [] Objects.currentObjects[objType] = []
for(let objData of data["objects"][objType]) { for(let objData of data["objects"][objType]) {
/** @type {DrawableObject} */ /** @type {DrawableObject} */
@ -157,7 +203,7 @@ class IOAPI extends Module {
} }
if(error !== "") { if(error !== "") {
console.log(error) console.log(error)
this.alert.show(qsTranslate("io", "Could not save file: ") + error) this.alert.show(qsTranslate("io", "Could not load file: ") + error)
// TODO: Error handling // TODO: Error handling
return return
} }

View file

@ -58,10 +58,7 @@ class LatexRenderResult {
class LatexAPI extends Module { class LatexAPI extends Module {
constructor() { constructor() {
super("Latex", [ super("Latex")
/** @type {ExprParserAPI} */
Modules.ExprParser
])
/** /**
* true if latex has been enabled by the user, false otherwise. * true if latex has been enabled by the user, false otherwise.
*/ */

View file

@ -22,10 +22,7 @@ import DefaultGraph from "../preferences/default.mjs"
class PreferencesAPI extends Module { class PreferencesAPI extends Module {
constructor() { constructor() {
super('Preferences', [ super('Preferences')
Modules.Canvas,
Modules.Latex
])
this.categories = { this.categories = {
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General, [QT_TRANSLATE_NOOP('settingCategory', 'general')]: General,