1#!/usr/local/bin/python3.8
2
3from pyUIClass.labelTextEditor import Ui_Dialog
4import PyQt5.QtWidgets as Qw
5import PyQt5.QtSvg as Qs
6import PyQt5.QtGui as Qg
7import PyQt5.QtCore as Qc
8import xasyArgs as xa
9import xasy2asy as x2a
10import subprocess
11import xasyOptions as xo
12import xasyUtils as xu
13import tempfile
14import uuid
15import os
16import io
17
18
19class labelEditor(Qw.QDialog):
20    def __init__(self, text=''):
21        super().__init__()
22        self.ui = Ui_Dialog()
23        self.ui.setupUi(self)
24
25        self.ui.btnAccept.clicked.connect(self.accept)
26        self.ui.btnCancel.clicked.connect(self.reject)
27        self.ui.chkMathMode.stateChanged.connect(self.chkMathModeChecked)
28        self.ui.btnPreview.clicked.connect(self.btnPreviewOnClick)
29        self.ui.btnGetText.clicked.connect(self.btnGetTextOnClick)
30
31        self.svgPreview = None
32        self.initializeText(text)
33
34    def initializeText(self, text: str):
35        if text[0] == '$' and text[-1] == '$':
36            self.ui.chkMathMode.setChecked(True)
37            text = text.strip('$')
38
39            if text.startswith('\\displaystyle{'):
40                self.ui.cmbMathStyle.setCurrentText('Display Style')
41                text = text.rstrip('}')
42                text = text.replace('\\displaystyle{', '', 1)
43            elif text.startswith('\\scriptstyle{'):
44                self.ui.cmbMathStyle.setCurrentText('Script Style')
45                text = text.rstrip('}')
46                text = text.replace('\\scriptstyle{', '', 1)
47
48        self.ui.txtLabelEdit.setPlainText(text)
49
50    def chkMathModeChecked(self, checked):
51        self.ui.cmbMathStyle.setEnabled(checked)
52
53    def getText(self):
54        rawText = self.ui.txtLabelEdit.toPlainText()
55        rawText.replace('\n', ' ')
56        if self.ui.chkMathMode.isChecked():
57            prefix = ''
58            suffix = ''
59            if self.ui.cmbMathStyle.currentText() == 'Display Style':
60                prefix = '\\displaystyle{'
61                suffix = '}'
62            elif self.ui.cmbMathStyle.currentText() == 'Script Style':
63                prefix = '\\scriptstyle{'
64                suffix = '}'
65            return '${0}{1}{2}$'.format(prefix, rawText, suffix)
66        else:
67            return rawText
68
69    def btnPreviewOnClick(self):
70        path = xa.getArgs().asypath
71        if path is None:
72            opt = xo.xasyOptions().load()
73            path = opt['asyPath']
74
75        asyInput = """
76        frame f;
77        label(f, "{0}");
78        write(min(f), newl);
79        write(max(f), newl);
80        shipout(f);
81        """
82
83        self.svgPreview = Qs.QSvgRenderer()
84        with tempfile.TemporaryDirectory(prefix='xasylbl_') as tmpdir:
85            id = str(uuid.uuid4())
86            tmpFile = os.path.join(tmpdir, 'lbl-{0}.svg'.format(id))
87
88            with subprocess.Popen(args=[path, '-fsvg', '-o', tmpFile, '-'], encoding='utf-8', stdin=subprocess.PIPE,
89                                   stdout=subprocess.PIPE) as asy:
90                asy.stdin.write(asyInput.format(self.getText()))
91                asy.stdin.close()
92                out = asy.stdout.read()
93
94            raw_array = out.splitlines()
95
96            bounds_1, bounds_2 = [val.strip() for val in raw_array]
97
98            min_bounds = xu.listize(bounds_1, (float, float))
99            max_bounds = xu.listize(bounds_2, (float, float))
100
101            new_rect = self.processBounds(min_bounds, max_bounds)
102            self.svgPreview.load(tmpFile)
103
104
105
106        self.drawPreview(new_rect)
107
108    def drawPreview(self, naturalBounds):
109        img = Qg.QPixmap(self.ui.lblLabelPreview.size())
110        img.fill(Qg.QColor.fromRgbF(1, 1, 1, 1))
111        if self.svgPreview is None:
112            pass
113        else:
114            with Qg.QPainter(img) as pnt:
115                scale_ratio = self.getIdealScaleRatio(naturalBounds, self.ui.lblLabelPreview.rect())
116
117                pnt.translate(self.ui.lblLabelPreview.rect().center())
118                pnt.scale(scale_ratio, scale_ratio)
119                self.svgPreview.render(pnt, naturalBounds)
120            self.ui.lblLabelPreview.setPixmap(img)
121
122
123    def getIdealScaleRatio(self, rect, boundsRect):
124        assert isinstance(rect, (Qc.QRect, Qc.QRectF))
125        assert isinstance(rect, (Qc.QRect, Qc.QRectF))
126
127        magic_ratio = 0.50
128        idealRatioHeight = (magic_ratio * boundsRect.height()) / rect.height()
129        magicRatioWidth = 0.50
130
131        if idealRatioHeight * rect.width() > magicRatioWidth * boundsRect.width():
132            idealRatioWidth = (magicRatioWidth * boundsRect.width()) / rect.width()
133            idealRatio = min(idealRatioHeight, idealRatioWidth)
134        else:
135            idealRatio = idealRatioHeight
136        return idealRatio
137
138    def processBounds(self, minPt, maxPt):
139        p1x, p1y = minPt
140        p2x, p2y = maxPt
141
142        minPt = Qc.QPointF(p1x, p1y)
143        maxPt = Qc.QPointF(p2x, p2y)
144
145        newRect = Qc.QRectF(minPt, maxPt)
146        return newRect
147
148
149    def btnGetTextOnClick(self):
150        msgbox = Qw.QMessageBox()
151        msgbox.setText('Text Preview:\n' + self.getText())
152        msgbox.setWindowTitle('Text preview')
153        msgbox.show()
154        return msgbox.exec_()
155