4

I have the following program which uses sympy and svgmath to render a user's algebraic expression. It nearly works but there are a few issues:

  1. The svg is not actually produced until the program exits so obviously cannot be diplayed.
  2. Is there a way to improve the performance (not looking up 'svgmath.xml' every time etc.)?
  3. Does an actual svg file need to be produced? Can svgmath pass output directly to the QSvgWidget?

Many thanks and best wishes.

from __future__ import division
import sys
import sympy

from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtXml import *
from PySide.QtSvg import *

from xml import sax
from xml.sax.saxutils import XMLGenerator

from svgmath.tools.saxtools import XMLGenerator, ContentFilter
from svgmath.mathhandler import MathHandler, MathNS

from sympy.printing.mathml import mathml
from sympy.abc import x

from sympy.utilities.mathml import c2p

import libxml2

import StringIO

class Form(QDialog):
    def __init__(self, parent=None):
        super(Form, self).__init__(parent)
        self.browser = QTextBrowser()
        self.browser.setCurrentFont(QFont("Courier New",10,QFont.Bold))
        self.lineedit = QLineEdit("please type an expression")
        self.lineedit.selectAll()
        self.svgwidget = QSvgWidget()
        layout = QVBoxLayout()
        layout.addWidget(self.browser)
        layout.addWidget(self.lineedit)
        layout.addWidget(self.svgwidget)
        self.setLayout(layout)
        self.lineedit.setFocus()
        self.connect(self.lineedit, SIGNAL("textChanged (const QString&)"),self.updateUi)

    def updateUi(self):
        text = unicode(self.lineedit.text())
        for z in range(0,9):
            text = text.replace('x'+str(z),'x^'+str(z))
            text = text.replace(')'+str(z),')^'+str(z))
            text = text.replace(str(z)+'x',str(z)+'*x')
            text = text.replace(str(z)+'(',str(z)+'*(')

        try:
            prettytext = sympy.printing.pretty(sympy.sympify(text))
            self.browser.clear()
            self.browser.append(prettytext)

            # Open all files
            output = open("test.svg", "w")
            config = open("svgmath.xml", "r")

            # Create the converter as a content handler. 
            saxoutput = XMLGenerator(output, 'utf-8')
            handler = MathHandler(saxoutput, config)

            # Parse input file with a namespace-aware SAX parser
            parser = sax.make_parser()
            parser.setFeature(sax.handler.feature_namespaces, 1)
            parser.setContentHandler(handler)
            parser.parse(StringIO.StringIO(c2p(mathml(sympy.sympify(text)), simple=True)))
            self.svgwidget.load("test.svg")

        except Exception:
            if text=='': self.browser.clear()

    app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
Geddes
  • 1,191
  • 2
  • 11
  • 25

1 Answers1

2

You should close the file handles, because otherwise the data written may not yet have been flushed to the filesystem.

parser.parse()
output.close()

Alternatively, use a with expression.

with open("test.svg", "w") as output:
    ...
    parser.parse()
load()

Does an actual svg file need to be produced?

I think QSvgWidget offers a load slot, which takes a byte string with the file contents.

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
  • Thanks again! I will give these a go. – Geddes Mar 11 '11 at 08:33
  • The output.close() command works perfectly - thanks! Do you think there is a way to not have to load 'svgmath.xml' each time? (I think it would improve performance). Also, how would you get svgmath to produce a byte string? Many thanks again! – Geddes Mar 11 '11 at 19:05
  • I've never used PyQt, but according to the docs (http://www.pyside.org/docs/pyside/PySide/QtSvg/QSvgWidget.html), you need to pass in a `QByteArray`, which might provide a c'tor taking a `str` or a `bytes` object. – Alexander Gessler Mar 11 '11 at 19:16
  • For the configuration file: I took a quick look at `svgmath`'s source code. Sadly, I didn't find a builtin way to re-use the config file. But if you look at `mathhandler.py:MathHandler:__init__`, it shouldn't be too difficult to fix that and reuse the `MathConfig` instance. – Alexander Gessler Mar 11 '11 at 19:18
  • OK thanks! I'm getting a little out of my depth (I'm just a humble maths teacher) but I am VERY APPRECIATIVE of your help these past couple of days. I will research and think about your latest advice. Bottom line - I am extremely happy to have got it working at all! – Geddes Mar 11 '11 at 19:32