Adding loading screen for rendering LaTeX formula when threaded setting is enabled.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Adsooi 2024-10-27 02:40:42 +02:00
parent 27c9fe0473
commit 727dda2623
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
13 changed files with 168 additions and 34 deletions

View file

@ -95,11 +95,15 @@ export class Action {
if(!Latex.enabled) if(!Latex.enabled)
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.") throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
const imgDepth = History.imageDepth const imgDepth = History.imageDepth
const { source, width, height } = await Latex.requestAsyncRender( const renderArguments = [
latexString, latexString,
imgDepth * (History.fontSize + 2), imgDepth * (History.fontSize + 2),
History.themeTextColor History.themeTextColor
) ]
let render = Latex.findPrerendered(...renderArguments)
if(render === null)
render = await Latex.requestAsyncRender(...renderArguments)
const { source, width, height } = render
return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>` return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
} }

View file

@ -25,7 +25,10 @@ import Objects from "./objects.mjs"
import History from "./history.mjs" import History from "./history.mjs"
import Settings from "./settings.mjs" import Settings from "./settings.mjs"
class CanvasAPI extends Module { class CanvasAPI extends Module {
/** @type {CanvasInterface} */ /** @type {CanvasInterface} */
#canvas = null #canvas = null
/** @type {CanvasRenderingContext2D} */ /** @type {CanvasRenderingContext2D} */

View file

@ -17,6 +17,7 @@
*/ */
import { Module } from "./common.mjs" import { Module } from "./common.mjs"
import { BaseEvent } from "../events.mjs"
import * as Instruction from "../lib/expr-eval/instruction.mjs" import * as Instruction from "../lib/expr-eval/instruction.mjs"
import { escapeValue } from "../lib/expr-eval/expression.mjs" import { escapeValue } from "../lib/expr-eval/expression.mjs"
import { HelperInterface, LatexInterface } from "./interface.mjs" import { HelperInterface, LatexInterface } from "./interface.mjs"
@ -44,6 +45,28 @@ const equivalchars = ["\\pi", "\\infty",
"{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}", "{}_{4}", "{}_{5}", "{}_{6}", "{}_{7}", "{}_{8}", "{}_{9}", "{}_{0}",
] ]
class AsyncRenderStartedEvent extends BaseEvent {
constructor(markup, fontSize, color) {
super("async-render-started")
this.markup = markup
this.fontSize = fontSize
this.color = color
}
}
class AsyncRenderFinishedEvent extends BaseEvent {
constructor(markup, fontSize, color) {
super("async-render-finished")
this.markup = markup
this.fontSize = fontSize
this.color = color
}
}
/** /**
* Class containing the result of a LaTeX render. * Class containing the result of a LaTeX render.
* *
@ -60,6 +83,8 @@ class LatexRenderResult {
} }
class LatexAPI extends Module { class LatexAPI extends Module {
static emits = ["async-render-started", "async-render-finished"]
/** @type {LatexInterface} */ /** @type {LatexInterface} */
#latex = null #latex = null
@ -113,10 +138,14 @@ class LatexAPI extends Module {
async requestAsyncRender(markup, fontSize, color) { async requestAsyncRender(markup, fontSize, color) {
if(!this.initialized) throw new Error("Attempting requestAsyncRender before initialize!") if(!this.initialized) throw new Error("Attempting requestAsyncRender before initialize!")
let render let render
if(this.#latex.supportsAsyncRender) if(this.#latex.supportsAsyncRender) {
console.trace()
this.emit(new AsyncRenderStartedEvent(markup, fontSize, color))
render = await this.#latex.renderAsync(markup, fontSize, color) render = await this.#latex.renderAsync(markup, fontSize, color)
else this.emit(new AsyncRenderFinishedEvent(markup, fontSize, color))
} else {
render = this.#latex.renderSync(markup, fontSize, color) render = this.#latex.renderSync(markup, fontSize, color)
}
const args = render.split(",") const args = render.split(",")
return new LatexRenderResult(...args) return new LatexRenderResult(...args)
} }

View file

@ -47,8 +47,8 @@ class EnableLatex extends BoolSetting {
} }
const ENABLE_LATEX_ASYNC = new BoolSetting( const ENABLE_LATEX_ASYNC = new BoolSetting(
qsTranslate("general", "Enable asynchronous LaTeX renderer"), qsTranslate("general", "Enable threaded LaTeX renderer (experimental)"),
"enable_latex_async", "enable_latex_threaded",
"new" "new"
) )

View file

@ -23,7 +23,7 @@ const DEFAULT_SETTINGS = {
"reset_redo_stack": true, "reset_redo_stack": true,
"last_install_greet": "0", "last_install_greet": "0",
"enable_latex": true, "enable_latex": true,
"enable_latex_async": true, "enable_latex_threaded": true,
"expression_editor": { "expression_editor": {
"autoclose": true, "autoclose": true,
"colorize": true, "colorize": true,
@ -113,4 +113,4 @@ export class MockHelper {
throw new Error(`File not found.`) throw new Error(`File not found.`)
} }
} }

View file

@ -17,10 +17,10 @@
*/ */
import QtQml import QtQml
import QtQuick.Controls
import eu.ad5001.MixedMenu 1.1
import QtQuick.Layouts 1.12
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts 1.12
import eu.ad5001.MixedMenu 1.1
// Auto loading all modules. // Auto loading all modules.
import eu.ad5001.LogarithmPlotter.Common import eu.ad5001.LogarithmPlotter.Common
@ -158,21 +158,17 @@ ApplicationWindow {
Overlay.ViewPositionChange { Overlay.ViewPositionChange {
id: viewPositionChanger id: viewPositionChanger
anchors.fill: parent anchors.fill: parent
canvas: parent
settingsInstance: settings
} }
Overlay.PickLocation { Overlay.PickLocation {
id: positionPicker id: positionPicker
anchors.fill: parent anchors.fill: parent
canvas: parent
} }
}
// Overlay.Loading { Overlay.Loading {
// id: loadingOverlay id: loadingOverlay
// anchors.fill: parent anchors.fill: parent
// canvas: parent
// }
} }
Timer { Timer {

View file

@ -56,7 +56,7 @@ ScrollView {
property var editingRows: [] property var editingRows: []
model: Modules.Objects.currentObjects[objType] model: Modules.Objects.currentObjects[objType]
width: objectsListView.width width: objectsListView.width
implicitHeight: contentItem.childrenRect.height height: contentItem.childrenRect.height + 10
visible: model != undefined && model.length > 0 visible: model != undefined && model.length > 0
interactive: false interactive: false

View file

@ -17,3 +17,114 @@
*/ */
import QtQuick import QtQuick
import QtQuick.Controls
/*!
\qmltype Loading
\inqmlmodule eu.ad5001.LogarithmPlotter.Overlay
\brief Overlay notifiying the user when a file is loading.
Provides an overlay over the canvas that is shown when the user loads a new file, both to lock the ViewPositionChange
overlay and inform the user of what is loading and how much remains.
\sa Common, ViewPositionChange
*/
Item {
id: loadingRoot
opacity: 0
visible: opacity !== 0
clip: true
property int currentlyLoading: 0
property int maxCurrentLoadingSteps: 0
Behavior on opacity { PropertyAnimation {} }
Rectangle {
anchors.fill: parent
color: sysPalette.window
opacity: 0.85
}
Column {
spacing: 5
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
right: parent.right
}
Text {
id: loadingTitle
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 20
color: sysPalette.windowText
}
ProgressBar {
id: progress
anchors.horizontalCenter: parent.horizontalCenter
width: 300
from: 0
value: loadingRoot.maxCurrentLoadingSteps - loadingRoot.currentlyLoading
to: loadingRoot.maxCurrentLoadingSteps
}
Text {
id: lastFinishedStep
anchors.horizontalCenter: parent.horizontalCenter
color: sysPalette.windowText
}
}
MouseArea {
id: picker
anchors.fill: parent
hoverEnabled: parent.visible
cursorShape: Qt.ArrowCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
}
/*!
\qmlmethod void Loading::addedLoadingStep()
Registers one new loading step that will eventually call \c finishedLoadingStep.
*/
function addedLoadingStep() {
if(loadingRoot.maxCurrentLoadingSteps === 1) {
// Only when several ones need to be loaded.
const fileName = Modules.Settings.saveFilename.split('/').pop().split('\\').pop()
loadingTitle.text = qsTr("Loading...")
loadingRoot.opacity = 1
}
loadingRoot.currentlyLoading++
loadingRoot.maxCurrentLoadingSteps++
}
/*!
\qmlmethod void Loading::finishedLoadingStep()
Marks a loading step as finished and displays the message to the user.
*/
function finishedLoadingStep(message) {
loadingRoot.currentlyLoading--
const current = loadingRoot.maxCurrentLoadingSteps - loadingRoot.currentlyLoading
lastFinishedStep.text = `${message} (${current}/${loadingRoot.maxCurrentLoadingSteps})`
if(loadingRoot.currentlyLoading === 0) {
loadingRoot.maxCurrentLoadingSteps = 0
loadingRoot.opacity = 0
}
}
Component.onCompleted: function() {
Modules.Latex.on("async-render-started", (e) => {
addedLoadingStep()
})
Modules.Latex.on("async-render-finished", (e) => {
const markup = e.markup.length > 20 ? e.markup.substring(0, 15)+"..." : e.markup
finishedLoadingStep(qsTr("Finished rendering of %1").arg(markup))
})
}
}

View file

@ -57,16 +57,6 @@ Item {
*/ */
signal endPositionChange(int deltaX, int deltaY) signal endPositionChange(int deltaX, int deltaY)
/*!
\qmlproperty var ViewPositionChangeOverlay::canvas
LogGraphCanvas instance.
*/
property var canvas
/*!
\qmlproperty var ViewPositionChangeOverlay::settingsInstance
Settings instance.
*/
property var settingsInstance
/*! /*!
\qmlproperty int ViewPositionChangeOverlay::prevX \qmlproperty int ViewPositionChangeOverlay::prevX
The x coordinate (on the mousearea) at the last change of the canvas position. The x coordinate (on the mousearea) at the last change of the canvas position.

View file

@ -1,4 +1,5 @@
module eu.ad5001.LogarithmPlotter.Overlay module eu.ad5001.LogarithmPlotter.Overlay
Loading 1.0 Loading.qml
PickLocation 1.0 PickLocation.qml PickLocation 1.0 PickLocation.qml
ViewPositionChange 1.0 ViewPositionChange.qml ViewPositionChange 1.0 ViewPositionChange.qml

View file

@ -28,7 +28,7 @@ DEFAULT_SETTINGS = {
"reset_redo_stack": True, "reset_redo_stack": True,
"last_install_greet": "0", "last_install_greet": "0",
"enable_latex": which("latex") is not None and which("dvipng") is not None, "enable_latex": which("latex") is not None and which("dvipng") is not None,
"enable_latex_async": True, "enable_latex_threaded": True,
"expression_editor": { "expression_editor": {
"autoclose": True, "autoclose": True,
"colorize": True, "colorize": True,

View file

@ -22,9 +22,9 @@ from os import path
from re import compile from re import compile
CURRENT_PATH = path.dirname(path.realpath(__file__)) CURRENT_PATH = path.dirname(path.realpath(__file__))
SOURCEMAP_PATH = path.realpath(f"{CURRENT_PATH}/../qml/eu/ad5001/LogarithmPlotter/js/index.mjs.map") SOURCEMAP_PATH = path.realpath(f"{CURRENT_PATH}/../qml/eu/ad5001/LogarithmPlotter/Common/index.mjs.map")
SOURCEMAP_INDEX = None SOURCEMAP_INDEX = None
INDEX_REG = compile(r"build\/runtime-pyside6\/LogarithmPlotter\/qml\/eu\/ad5001\/LogarithmPlotter\/js\/index.mjs:(\d+)") INDEX_REG = compile(r"build\/runtime-pyside6\/LogarithmPlotter\/qml\/eu\/ad5001\/LogarithmPlotter\/Common\/index.mjs:(\d+)")
class LOG_COLORS: class LOG_COLORS:

View file

@ -91,7 +91,7 @@ class Latex(QObject):
@Property(bool) @Property(bool)
def supportsAsyncRender(self) -> bool: def supportsAsyncRender(self) -> bool:
return config.getSetting("enable_latex_async") return config.getSetting("enable_latex_threaded")
@Slot(result=bool) @Slot(result=bool)
def checkLatexInstallation(self) -> bool: def checkLatexInstallation(self) -> bool: