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