Compare commits
No commits in common. "ec90779912b81f59707cb938d8db2f26864b647b" and "650e43894cccba6ccd5edb16c46034cd0442e43b" have entirely different histories.
ec90779912
...
650e43894c
11 changed files with 72 additions and 200 deletions
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
|
start_time = time()
|
||||||
|
|
||||||
from PySide2.QtWidgets import QApplication
|
from PySide2.QtWidgets import QApplication
|
||||||
from PySide2.QtQml import QQmlApplicationEngine
|
from PySide2.QtQml import QQmlApplicationEngine
|
||||||
from PySide2.QtCore import Qt, QTranslator, QLocale
|
from PySide2.QtCore import Qt, QTranslator, QLocale
|
||||||
|
@ -27,9 +29,6 @@ from tempfile import TemporaryDirectory
|
||||||
from os import getcwd, chdir, environ, path, remove, close
|
from os import getcwd, chdir, environ, path, remove, close
|
||||||
from platform import release as os_release
|
from platform import release as os_release
|
||||||
from sys import platform, argv, version as sys_version, exit
|
from sys import platform, argv, version as sys_version, exit
|
||||||
from sys import path as sys_path
|
|
||||||
|
|
||||||
start_time = time()
|
|
||||||
|
|
||||||
# Create the temporary directory for saving copied screenshots and latex files
|
# Create the temporary directory for saving copied screenshots and latex files
|
||||||
tempdir = TemporaryDirectory()
|
tempdir = TemporaryDirectory()
|
||||||
|
@ -38,6 +37,7 @@ pwd = getcwd()
|
||||||
|
|
||||||
chdir(path.dirname(path.realpath(__file__)))
|
chdir(path.dirname(path.realpath(__file__)))
|
||||||
|
|
||||||
|
from sys import path as sys_path
|
||||||
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
|
if path.realpath(path.join(getcwd(), "..")) not in sys_path:
|
||||||
sys_path.append(path.realpath(path.join(getcwd(), "..")))
|
sys_path.append(path.realpath(path.join(getcwd(), "..")))
|
||||||
|
|
||||||
|
@ -85,11 +85,11 @@ def run():
|
||||||
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom")))
|
icon_fallbacks.append(path.realpath(path.join(base_icon_path, "settings", "custom")))
|
||||||
QIcon.setFallbackSearchPaths(icon_fallbacks);
|
QIcon.setFallbackSearchPaths(icon_fallbacks);
|
||||||
|
|
||||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
||||||
app = QApplication(argv)
|
app = QApplication(argv)
|
||||||
app.setApplicationName("LogarithmPlotter")
|
app.setApplicationName("LogarithmPlotter")
|
||||||
app.setOrganizationName("Ad5001")
|
app.setOrganizationName("Ad5001")
|
||||||
app.styleHints().setShowShortcutsInContextMenus(True)
|
app.styleHints().setShowShortcutsInContextMenus(True)
|
||||||
|
app.setAttribute(Qt.AA_EnableHighDpiScaling)
|
||||||
app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
|
app.setWindowIcon(QIcon(path.realpath(path.join(getcwd(), "logarithmplotter.svg"))))
|
||||||
|
|
||||||
# Installing translators
|
# Installing translators
|
||||||
|
@ -109,7 +109,7 @@ def run():
|
||||||
engine = QQmlApplicationEngine()
|
engine = QQmlApplicationEngine()
|
||||||
global tmpfile
|
global tmpfile
|
||||||
helper = Helper(pwd, tmpfile)
|
helper = Helper(pwd, tmpfile)
|
||||||
latex = Latex(tempdir)
|
latex = Latex(tempdir, app.palette())
|
||||||
engine.rootContext().setContextProperty("Helper", helper)
|
engine.rootContext().setContextProperty("Helper", helper)
|
||||||
engine.rootContext().setContextProperty("Latex", latex)
|
engine.rootContext().setContextProperty("Latex", latex)
|
||||||
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
engine.rootContext().setContextProperty("TestBuild", "--test-build" in argv)
|
||||||
|
@ -135,8 +135,6 @@ def run():
|
||||||
if platform == "darwin":
|
if platform == "darwin":
|
||||||
macOSFileOpenHandler.init_graphics(engine.rootObjects()[0])
|
macOSFileOpenHandler.init_graphics(engine.rootObjects()[0])
|
||||||
|
|
||||||
latex.check_latex_install()
|
|
||||||
|
|
||||||
# Check for updates
|
# Check for updates
|
||||||
if config.getSetting("check_for_updates"):
|
if config.getSetting("check_for_updates"):
|
||||||
check_for_updates(__VERSION__, engine.rootObjects()[0])
|
check_for_updates(__VERSION__, engine.rootObjects()[0])
|
||||||
|
|
|
@ -200,7 +200,7 @@ Canvas {
|
||||||
// Reset
|
// Reset
|
||||||
ctx.fillStyle = "#FFFFFF"
|
ctx.fillStyle = "#FFFFFF"
|
||||||
ctx.strokeStyle = "#000000"
|
ctx.strokeStyle = "#000000"
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
ctx.font = `${canvas.textsize-2}px sans-serif`
|
||||||
ctx.fillRect(0,0,width,height)
|
ctx.fillRect(0,0,width,height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,12 +257,12 @@ Canvas {
|
||||||
var axisxpx = y2px(0) // Y coordinate of X axis
|
var axisxpx = y2px(0) // Y coordinate of X axis
|
||||||
// Labels
|
// Labels
|
||||||
ctx.fillStyle = "#000000"
|
ctx.fillStyle = "#000000"
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
ctx.font = `${canvas.textsize+2}px sans-serif`
|
||||||
ctx.fillText(ylabel, axisypx+10, 24)
|
ctx.fillText(ylabel, axisypx+10, 24)
|
||||||
var textSize = ctx.measureText(xlabel).width
|
var textSize = ctx.measureText(xlabel).width
|
||||||
ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5)
|
ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5)
|
||||||
// Axis graduation labels
|
// Axis graduation labels
|
||||||
ctx.font = `${canvas.textsize-4}px sans-serif`
|
ctx.font = `${canvas.textsize-2}px sans-serif`
|
||||||
|
|
||||||
var txtMinus = ctx.measureText('-').width
|
var txtMinus = ctx.measureText('-').width
|
||||||
if(showxgrad) {
|
if(showxgrad) {
|
||||||
|
|
|
@ -99,7 +99,7 @@ ScrollView {
|
||||||
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.
|
||||||
\sa Settings
|
\sa Settings
|
||||||
*/
|
*/
|
||||||
property double textsize: 18
|
property double textsize: 14
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty bool Settings::logscalex
|
\qmlproperty bool Settings::logscalex
|
||||||
true if the canvas should be in logarithmic mode, false otherwise.
|
true if the canvas should be in logarithmic mode, false otherwise.
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
.import "common.js" as C
|
.import "common.js" as C
|
||||||
.import "expression.js" as Expr
|
.import "expression.js" as Expr
|
||||||
.import "../utils.js" as Utils
|
.import "../utils.js" as Utils
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents mathematical object for sequences.
|
* Represents mathematical object for sequences.
|
||||||
|
@ -34,13 +32,9 @@ class Sequence extends Expr.Expression {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.baseValues = baseValues
|
this.baseValues = baseValues
|
||||||
this.calcValues = Object.assign({}, baseValues)
|
this.calcValues = Object.assign({}, baseValues)
|
||||||
this.latexValues = Object.assign({}, baseValues)
|
|
||||||
for(var n in this.calcValues)
|
for(var n in this.calcValues)
|
||||||
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
if(['string', 'number'].includes(typeof this.calcValues[n]))
|
||||||
let parsed = C.parser.parse(this.calcValues[n].toString()).simplify()
|
this.calcValues[n] = C.parser.parse(this.calcValues[n].toString()).simplify().evaluate(C.evalVariables)
|
||||||
this.latexValues[n] = Latex.expressionToLatex(parsed.tokens)
|
|
||||||
this.calcValues[n] = parsed.evaluate(C.evalVariables)
|
|
||||||
}
|
|
||||||
this.valuePlus = parseInt(valuePlus)
|
this.valuePlus = parseInt(valuePlus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,15 +75,4 @@ class Sequence extends Expr.Expression {
|
||||||
).join('; ')
|
).join('; ')
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
toLatexString(forceSign=false) {
|
|
||||||
var str = this.latexMarkup
|
|
||||||
if(str[0] != '-' && forceSign) str = '+' + str
|
|
||||||
var subtxt = '_{n' + (this.valuePlus == 0 ? '' : '+' + this.valuePlus) + '}'
|
|
||||||
var ret = `\\begin{array}{l}${Latex.variable(this.name)}${subtxt} = ${str}${this.latexValues.length == 0 ? '' : "\n"}\\\\`
|
|
||||||
ret += Object.keys(this.latexValues).map(
|
|
||||||
n => `${this.name}_{${n}} = ${this.latexValues[n]}`
|
|
||||||
).join('; ') + "\\end{array}"
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,19 @@
|
||||||
.import "../mathlib.js" as MathLib
|
.import "../mathlib.js" as MathLib
|
||||||
.import "../historylib.js" as HistoryLib
|
.import "../historylib.js" as HistoryLib
|
||||||
.import "../parameters.js" as P
|
.import "../parameters.js" as P
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class PhaseBode extends Common.ExecutableObject {
|
class PhaseBode extends Common.ExecutableObject {
|
||||||
static type(){return 'Phase Bode'}
|
static type(){return 'Phase Bode'}
|
||||||
static displayType(){return qsTr('Bode Phase')}
|
static displayType(){return qsTr('Bode Phase')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
||||||
|
/*static properties() {return {
|
||||||
|
'om_0': new P.ObjectType('Point'),
|
||||||
|
'phase': 'Expression',
|
||||||
|
'unit': new P.Enum('°', 'deg', 'rad'),
|
||||||
|
'labelPosition': new P.Enum('above', 'below', 'left', 'right', 'above-left', 'above-right', 'below-left', 'below-right'),
|
||||||
|
'labelX': 'number'
|
||||||
|
}}*/
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||||
[QT_TRANSLATE_NOOP('prop','phase')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','phase')]: 'Expression',
|
||||||
|
@ -76,10 +82,6 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
|
return `${this.name}: ${this.phase.toString(true)}${this.unit} at ${this.om_0.name} = ${this.om_0.x}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
|
||||||
return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textrm{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}`
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(x=1) {
|
execute(x=1) {
|
||||||
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
if(typeof x == 'string') x = MathLib.executeExpression(x)
|
||||||
if(x < this.om_0.x) {
|
if(x < this.om_0.x) {
|
||||||
|
@ -118,7 +120,37 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
|
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
var text = this.getLabel()
|
||||||
|
ctx.font = `${canvas.textsize}px sans-serif`
|
||||||
|
var textSize = canvas.measureText(ctx, text)
|
||||||
|
var posX = canvas.x2px(this.labelX)
|
||||||
|
var posY = canvas.y2px(this.execute(this.labelX))
|
||||||
|
switch(this.labelPosition) {
|
||||||
|
case 'above':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY-textSize.height)
|
||||||
|
break;
|
||||||
|
case 'below':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX-textSize.width/2, posY+textSize.height)
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height/2)
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height/2)
|
||||||
|
break;
|
||||||
|
case 'above-left':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY-textSize.height)
|
||||||
|
break;
|
||||||
|
case 'above-right':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX, posY-textSize.height)
|
||||||
|
break;
|
||||||
|
case 'below-left':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX-textSize.width, posY+textSize.height)
|
||||||
|
break;
|
||||||
|
case 'below-right':
|
||||||
|
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
.import "function.js" as F
|
.import "function.js" as F
|
||||||
.import "../mathlib.js" as MathLib
|
.import "../mathlib.js" as MathLib
|
||||||
.import "../parameters.js" as P
|
.import "../parameters.js" as P
|
||||||
.import "../math/latex.js" as Latex
|
|
||||||
|
|
||||||
|
|
||||||
class Sequence extends Common.ExecutableObject {
|
class Sequence extends Common.ExecutableObject {
|
||||||
|
@ -77,14 +76,11 @@ class Sequence extends Common.ExecutableObject {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return this.sequence.toString()
|
return this.sequence.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
|
||||||
return this.sequence.toLatexString()
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(x % 1 == 0)
|
if(x % 1 == 0)
|
||||||
return this.sequence.execute(x)
|
return this.sequence.execute(x)
|
||||||
|
@ -107,17 +103,7 @@ class Sequence extends Common.ExecutableObject {
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
case 'null':
|
case 'null':
|
||||||
return ''
|
return ''
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLatexLabel() {
|
|
||||||
switch(this.labelContent) {
|
|
||||||
case 'name':
|
|
||||||
return `(${Latex.variable(this.name)}_n)`
|
|
||||||
case 'name + value':
|
|
||||||
return this.getLatexString()
|
|
||||||
case 'null':
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,8 +111,7 @@ class Sequence extends Common.ExecutableObject {
|
||||||
F.Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? MathLib.Domain.NE : MathLib.Domain.N, MathLib.Domain.R, this.drawPoints, this.drawDashedLines)
|
F.Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? MathLib.Domain.NE : MathLib.Domain.N, MathLib.Domain.R, this.drawPoints, this.drawDashedLines)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
var text = this.getLabel()
|
||||||
/*var text = this.getLabel()
|
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
ctx.font = `${canvas.textsize}px sans-serif`
|
||||||
var textSize = canvas.measureText(ctx, text)
|
var textSize = canvas.measureText(ctx, text)
|
||||||
var posX = canvas.x2px(this.labelX)
|
var posX = canvas.x2px(this.labelX)
|
||||||
|
@ -156,7 +141,7 @@ class Sequence extends Common.ExecutableObject {
|
||||||
case 'below-right':
|
case 'below-right':
|
||||||
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
canvas.drawVisibleText(ctx, text, posX, posY+textSize.height)
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,156 +16,23 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PySide2.QtCore import QObject, Slot, Property, QCoreApplication
|
from PySide2.QtCore import QObject, Slot
|
||||||
from PySide2.QtGui import QImage, QColor
|
from PySide2.QtGui import QImage, QColor
|
||||||
from PySide2.QtWidgets import QApplication, QMessageBox
|
from PySide2.QtWidgets import QApplication
|
||||||
|
|
||||||
from os import path, remove
|
from os import path
|
||||||
from string import Template
|
from sympy import preview
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from subprocess import Popen, TimeoutExpired, PIPE
|
|
||||||
from platform import system
|
|
||||||
from shutil import which
|
|
||||||
|
|
||||||
"""
|
|
||||||
Searches for a valid Latex and DVIPNG (http://savannah.nongnu.org/projects/dvipng/)
|
|
||||||
installation and collects the binary path in the DVIPNG_PATH variable.
|
|
||||||
If not found, it will send an alert to the user.
|
|
||||||
"""
|
|
||||||
LATEX_PATH = which('latex')
|
|
||||||
DVIPNG_PATH = which('dvipng')
|
|
||||||
#subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
|
|
||||||
|
|
||||||
DEFAULT_LATEX_DOC = Template(r"""
|
|
||||||
\documentclass[]{minimal}
|
|
||||||
\usepackage[utf8]{inputenc}
|
|
||||||
\usepackage{calligra}
|
|
||||||
\usepackage{amsfonts}
|
|
||||||
|
|
||||||
\title{}
|
|
||||||
\author{}
|
|
||||||
|
|
||||||
\begin{document}
|
|
||||||
|
|
||||||
$$$$ $markup $$$$
|
|
||||||
|
|
||||||
\end{document}
|
|
||||||
""")
|
|
||||||
|
|
||||||
class Latex(QObject):
|
class Latex(QObject):
|
||||||
"""
|
def __init__(self, tempdir: str, palette):
|
||||||
Base class to convert Latex equations into PNG images with custom font color and size.
|
|
||||||
It doesn't have any python dependency, but requires a working latex installation and
|
|
||||||
dvipng to be installed on the system.
|
|
||||||
"""
|
|
||||||
def __init__(self, tempdir: TemporaryDirectory):
|
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
self.tempdir = tempdir
|
self.tempdir = tempdir
|
||||||
|
self.palette = palette
|
||||||
def check_latex_install(self):
|
fg = self.palette.windowText().color().convertTo(QColor.Rgb)
|
||||||
"""
|
|
||||||
Checks if the current latex installation is valid.
|
|
||||||
"""
|
|
||||||
if LATEX_PATH is None:
|
|
||||||
QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "No Latex installation found.\nIf you already have a latex distribution installed, make sure it's installed on your path.\nOtherwise, you can download a Latex distribution like TeX Live at https://tug.org/texlive/."))
|
|
||||||
elif DVIPNG_PATH is None:
|
|
||||||
QMessageBox.warning(None, "LogarithmPlotter - Latex setup", QCoreApplication.translate("latex", "DVIPNG was not found. Make sure you include it from your Latex distribution."))
|
|
||||||
|
|
||||||
@Property(bool)
|
|
||||||
def latexSupported(self):
|
|
||||||
return LATEX_PATH is not None and DVIPNG_PATH is not None
|
|
||||||
|
|
||||||
@Slot(str, float, QColor, result=str)
|
@Slot(str, float, QColor, result=str)
|
||||||
def render(self, latex_markup: str, font_size: float, color: QColor = True) -> str:
|
def render(self, latexstring, font_size, color = True):
|
||||||
"""
|
|
||||||
Renders a latex string into a png file.
|
|
||||||
"""
|
|
||||||
export_path = path.join(self.tempdir.name, f'{hash(latex_markup)}_{font_size}_{color.rgb()}')
|
|
||||||
print(export_path)
|
|
||||||
if self.latexSupported and not path.exists(export_path + ".png"):
|
|
||||||
# Generating file
|
|
||||||
try:
|
|
||||||
self.create_latex_doc(export_path, latex_markup)
|
|
||||||
self.convert_latex_to_dvi(export_path)
|
|
||||||
self.convert_dvi_to_png(export_path, font_size, color)
|
|
||||||
self.cleanup(export_path)
|
|
||||||
except Exception as e: # One of the processes failed. A message will be sent every time.
|
|
||||||
raise e
|
|
||||||
img = QImage(export_path + ".png");
|
|
||||||
# Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded
|
|
||||||
return f'{export_path}.png,{img.width()},{img.height()}'
|
|
||||||
|
|
||||||
def create_latex_doc(self, export_path: str, latex_markup: str):
|
|
||||||
"""
|
|
||||||
Creates a temporary latex document with base file_hash as file name and a given expression markup latex_markup.
|
|
||||||
"""
|
|
||||||
ltx_path = export_path + ".tex"
|
|
||||||
f = open(export_path + ".tex", 'w')
|
|
||||||
f.write(DEFAULT_LATEX_DOC.substitute(markup = latex_markup))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def convert_latex_to_dvi(self, export_path: str):
|
|
||||||
"""
|
|
||||||
Converts a DVI file to a PNG file.
|
|
||||||
"""
|
|
||||||
self.run([
|
|
||||||
LATEX_PATH,
|
|
||||||
export_path + ".tex"
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def convert_dvi_to_png(self, export_path: str, font_size: float, color: QColor):
|
|
||||||
"""
|
|
||||||
Converts a DVI file to a PNG file.
|
|
||||||
Documentation: https://linux.die.net/man/1/dvipng
|
|
||||||
"""
|
|
||||||
fg = color.convertTo(QColor.Rgb)
|
|
||||||
fg = f'rgb {fg.redF()} {fg.greenF()} {fg.blueF()}'
|
|
||||||
depth = int(font_size * 72.27 / 100) * 10
|
|
||||||
self.run([
|
|
||||||
DVIPNG_PATH,
|
|
||||||
'-T', 'tight', # Make sure image borders are as tight around the equation as possible to avoid blank space.
|
|
||||||
'--truecolor', # Make sure it's rendered in 24 bit colors.
|
|
||||||
'-D',f'{depth}', # Depth of the image
|
|
||||||
'-bg', 'Transparent', # Transparent background
|
|
||||||
'-fg',f'{fg}', # Foreground of the wanted color.
|
|
||||||
f'{export_path}.dvi', # Input file
|
|
||||||
'-o',f'{export_path}.png', # Output file
|
|
||||||
])
|
|
||||||
|
|
||||||
def run(self, process: list):
|
|
||||||
"""
|
|
||||||
Runs a subprocess and handles exceptions and messages them to the user.
|
|
||||||
"""
|
|
||||||
proc = Popen(process, stdout=PIPE, stderr=PIPE, cwd=self.tempdir.name)
|
|
||||||
try:
|
|
||||||
out, err = proc.communicate(timeout=5) # 5 seconds is already FAR too long.
|
|
||||||
if proc.returncode != 0:
|
|
||||||
# Process errored
|
|
||||||
QMessageBox.warning(None, "LogarithmPlotter - Latex",
|
|
||||||
QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' ended with a non-zero return code {}:\n{}\nPlease make sure your latex installation is correct and report a bug if so.")
|
|
||||||
.format(" ".join(process), proc.returncode, str(out, 'utf8')+"\n"+str(err,'utf8')))
|
|
||||||
raise Exception(" ".join(process) + " process exited with return code " + str(proc.returncode) + ":\n" + str(out, 'utf8')+"\n"+str(err,'utf8'))
|
|
||||||
print(out)
|
|
||||||
except TimeoutExpired as e:
|
|
||||||
# Process timed out
|
|
||||||
proc.kill()
|
|
||||||
out, err = proc.communicate()
|
|
||||||
QMessageBox.warning(None, "LogarithmPlotter - Latex",
|
|
||||||
QCoreApplication.translate("latex", "An exception occured within the creation of the latex formula.\nProcess '{}' took too long to finish:\n{}\nPlease make sure your latex installation is correct and report a bug if so.")
|
|
||||||
.format(" ".join(process), str(out, 'utf8')+"\n"+str(err,'utf8')))
|
|
||||||
raise Exception(" ".join(process) + " process timed out:\n" + str(out, 'utf8')+"\n"+str(err,'utf8'))
|
|
||||||
|
|
||||||
def cleanup(self, export_path):
|
|
||||||
"""
|
|
||||||
Removes Tex, auxiliary, logs and DVI temporary files.
|
|
||||||
"""
|
|
||||||
for i in [".tex", ".dvi", ".aux", ".log"]:
|
|
||||||
remove(export_path + i)
|
|
||||||
|
|
||||||
|
|
||||||
@Slot(str, float, QColor, result=str)
|
|
||||||
def render_legacy(self, latexstring, font_size, color = True):
|
|
||||||
exprpath = path.join(self.tempdir.name, f'{hash(latexstring)}_{font_size}_{color.rgb()}.png')
|
exprpath = path.join(self.tempdir.name, f'{hash(latexstring)}_{font_size}_{color.rgb()}.png')
|
||||||
print("Rendering", latexstring, exprpath)
|
print("Rendering", latexstring, exprpath)
|
||||||
if not path.exists(exprpath):
|
if not path.exists(exprpath):
|
||||||
|
@ -182,3 +49,9 @@ class Latex(QObject):
|
||||||
img = QImage(exprpath);
|
img = QImage(exprpath);
|
||||||
# Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded
|
# Small hack, not very optimized since we load the image twice, but you can't pass a QImage to QML and expect it to be loaded
|
||||||
return f'{exprpath},{img.width()},{img.height()}'
|
return f'{exprpath},{img.width()},{img.height()}'
|
||||||
|
|
||||||
|
@Slot(str)
|
||||||
|
def copyLatexImageToClipboard(self, latexstring):
|
||||||
|
global tempfile
|
||||||
|
clipboard = QApplication.clipboard()
|
||||||
|
clipboard.setImage(self.render(latexstring))
|
||||||
|
|
|
@ -14,7 +14,7 @@ steps:
|
||||||
- name: Linux test
|
- name: Linux test
|
||||||
image: ad5001/ubuntu-pyside2-xvfb:hirsute-5.15.2
|
image: ad5001/ubuntu-pyside2-xvfb:hirsute-5.15.2
|
||||||
commands:
|
commands:
|
||||||
- apt install texlive-latex-base dvipng
|
- pip3 install sympy
|
||||||
- xvfb-run python3 run.py --test-build --no-check-for-updates
|
- xvfb-run python3 run.py --test-build --no-check-for-updates
|
||||||
- xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test1.lpf
|
- xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test1.lpf
|
||||||
- xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test2.lpf
|
- xvfb-run python3 run.py --test-build --no-check-for-updates ./ci/test2.lpf
|
||||||
|
@ -25,6 +25,7 @@ steps:
|
||||||
image: ad5001/ubuntu-pyside2-xvfb-wine:win7-5.15.2
|
image: ad5001/ubuntu-pyside2-xvfb-wine:win7-5.15.2
|
||||||
commands:
|
commands:
|
||||||
- # For some reason, launching GUI apps with wine, even with xvfb-run, fails.
|
- # For some reason, launching GUI apps with wine, even with xvfb-run, fails.
|
||||||
|
- pip install sympy
|
||||||
- xvfb-run python run.py --test-build --no-check-for-updates
|
- xvfb-run python run.py --test-build --no-check-for-updates
|
||||||
- xvfb-run python run.py --test-build --no-check-for-updates ./ci/test1.lpf
|
- xvfb-run python run.py --test-build --no-check-for-updates ./ci/test1.lpf
|
||||||
- xvfb-run python run.py --test-build --no-check-for-updates ./ci/test2.lpf
|
- xvfb-run python run.py --test-build --no-check-for-updates ./ci/test2.lpf
|
||||||
|
|
|
@ -3,7 +3,7 @@ Source: logarithmplotter
|
||||||
Version: 0.1.9
|
Version: 0.1.9
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Ad5001 <mail@ad5001.eu>
|
Maintainer: Ad5001 <mail@ad5001.eu>
|
||||||
Depends: python3, python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), texlive-latex-base, dvipng
|
Depends: python3, python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), python3-sympy
|
||||||
|
|
||||||
Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools, python3-all-dev (>=3.6)
|
Build-Depends: debhelper (>=11~), dh-python, dpkg-dev (>= 1.16.1~), python-setuptools, python3-all-dev (>=3.6)
|
||||||
Section: science
|
Section: science
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), texlive-latex-base, dvipng
|
python3-pip, qml-module-qtquick-controls2 (>= 5.12.0), qml-module-qtmultimedia (>= 5.12.0), qml-module-qtgraphicaleffects (>= 5.12.0), qml-module-qtquick2 (>= 5.12.0), qml-module-qtqml-models2 (>= 5.12.0), qml-module-qtquick-controls (>= 5.12.0), python3-pyside2.qtcore (>= 5.12.0), python3-pyside2.qtqml (>= 5.12.0), python3-pyside2.qtgui (>= 5.12.0), python3-pyside2.qtquick (>= 5.12.0), python3-pyside2.qtwidgets (>= 5.12.0), python3-pyside2.qtmultimedia (>= 5.12.0), python3-pyside2.qtnetwork (>= 5.12.0), python3-sympy
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -127,7 +127,7 @@ if sys.platform == 'linux':
|
||||||
os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg')
|
os.remove(os.environ["PREFIX"] + '/icons/hicolor/scalable/apps/logplotter.svg')
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
install_requires=([] if "FLATPAK_INSTALL" in os.environ else ["PySide2"]),
|
install_requires=([] if "FLATPAK_INSTALL" in os.environ else ["PySide2", "sympy"]),
|
||||||
python_requires='>=3.8',
|
python_requires='>=3.8',
|
||||||
|
|
||||||
name='logarithmplotter',
|
name='logarithmplotter',
|
||||||
|
|
Loading…
Reference in a new issue