Yak Shavings for September 22, 2009

yak shaving
(idiomatic) Any apparently useless activity which, by allowing you to overcome intermediate difficulties, allows you to solve a larger problem.

This yak is starting to look better.

For my second pile of yak shavings: turning QPlainTextEdit into a decent editing widget for programmers.

As work advanced in my rst2pdf editor (BTW: need a name!), it became obvious that the piece of the UI the user will use most is just a couple of plain text editors.

Qt comes with a widget for that, of course, called QPlainTextEdit. However, it's a very, very bad widget for programmers.

Here's the least I wanted:

  1. Syntax highlighting for two languages: restructured text and javascript. This yak is already shaved.
  2. Line numbers
  3. Current line highlight
  4. Error highlight when it makes sense (like, in the stylesheet)

One way to achieve this would be to dump QPlainTextEdit and use QSciScintilla which is the basis for the code editor in eric and (another version) in scite.

However, I experienced a bad bug in QSciScintilla, where I can't type accented characters! Without that, (decent) spanish is impossible, and the bug seems to be at least two years old, so... no go.

So, did I get those features? I say yes!

Here is the video (yes, I am getting addicted to making these, since qt-recordmydesktop makes them so easy ;-):

The basis for this is the Code Editor example that comes with Qt itself, plus a bit of my own handywork.

First, I ported Code Editor from C++ to Python, which was very simple and took a few minutes. That takes care of points 2 and 3.

Then, the syntax highlight was plugged in, which was point 1.

Then, how about realtime javascript validation? Easy using simplejson! Just make sure to run this whenever you want validation (I do it on every keystroke).

Replace self.ui.style.toPlainText with whatever your widget is called, of course:

def validateStyle(self):
    style=unicode(self.ui.style.toPlainText())
    #no point in validating an empty string
    if not style.strip():
        return
    pos=None
    try:
        json.loads(style)
    except ValueError, e:
        s=str(e)
        print s
        if s == 'No JSON object could be decoded':
            pos=0
        elif s.startswith('Expecting '):
            pos=int(s.split(' ')[-1][:-1])
        else:
            print 'UNKNOWN ERROR'

    # This makes a red bar appear in the line
    # containing position pos
    self.ui.style.highlightError(pos)

highlightError(pos) simply stores pos in the Code Editor, which will draw a red bar in that line, the same way it highlights the current line.

And that's it. Here is the code for codeeditor.py

Comments

Comments powered by Disqus