Improving stability of asynchronous LaTeX renderer.

This commit is contained in:
Ad5001 2024-10-15 03:52:06 +02:00
parent cf73b35a9a
commit 5313428250
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
3 changed files with 18 additions and 7 deletions

View file

@ -30,16 +30,18 @@ class CanvasAPI extends Module {
#canvas = null #canvas = null
/** @type {CanvasRenderingContext2D} */ /** @type {CanvasRenderingContext2D} */
#ctx = null #ctx = null
/** Lock to prevent asynchronous stuff from printing stuff that is outdated. */
#redrawCount = 0
/** @type {{show(string, string, string)}} */ /** @type {{show(string, string, string)}} */
#drawingErrorDialog = null #drawingErrorDialog = null
constructor() { constructor() {
super("Canvas", { super("Canvas", {
canvas: CanvasInterface, canvas: CanvasInterface,
drawingErrorDialog: DialogInterface drawingErrorDialog: DialogInterface
}) })
/** /**
* *
* @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>} * @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>}
@ -207,6 +209,7 @@ class CanvasAPI extends Module {
*/ */
redraw() { redraw() {
if(!this.initialized) throw new Error("Attempting redraw before initialize!") if(!this.initialized) throw new Error("Attempting redraw before initialize!")
this.#redrawCount = (this.#redrawCount + 1) % 10000
this.#ctx = this.#canvas.getContext("2d") this.#ctx = this.#canvas.getContext("2d")
this._computeAxes() this._computeAxes()
this._reset() this._reset()
@ -519,15 +522,18 @@ class CanvasAPI extends Module {
* @param {function(LatexRenderResult|{width: number, height: number, source: string})} callback * @param {function(LatexRenderResult|{width: number, height: number, source: string})} callback
*/ */
renderLatexImage(ltxText, color, callback) { renderLatexImage(ltxText, color, callback) {
const currentRedrawCount = this.#redrawCount
const onRendered = (imgData) => { const onRendered = (imgData) => {
if(!this.#canvas.isImageLoaded(imgData.source) && !this.#canvas.isImageLoading(imgData.source)) { if(!this.#canvas.isImageLoaded(imgData.source) && !this.#canvas.isImageLoading(imgData.source)) {
// Wait until the image is loaded to callback. // Wait until the image is loaded to callback.
this.#canvas.loadImageAsync(imgData.source).then(() => { this.#canvas.loadImageAsync(imgData.source).then(() => {
callback(imgData) if(this.#redrawCount === currentRedrawCount)
callback(imgData)
}) })
} else { } else {
// Callback directly // Callback directly
callback(imgData) if(this.#redrawCount === currentRedrawCount)
callback(imgData)
} }
} }
const prerendered = Latex.findPrerendered(ltxText, this.textsize, color) const prerendered = Latex.findPrerendered(ltxText, this.textsize, color)

View file

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

View file

@ -53,7 +53,12 @@ class PyPromiseRunner(QRunnable):
raise InvalidReturnValue("Must return either a primitive, a valid QObject, JS Value, or None.") raise InvalidReturnValue("Must return either a primitive, a valid QObject, JS Value, or None.")
self.promise.finished.emit(data) self.promise.finished.emit(data)
except Exception as e: except Exception as e:
self.promise.errored.emit(repr(e)) try:
self.promise.errored.emit(repr(e))
except RuntimeError as e2:
# Happens when the PyPromise has already been garbage collected.
# In other words, nothing to report to nowhere.
pass
class PyPromise(QObject): class PyPromise(QObject):