Hacked on kuatia for a couple of hours...

2010-03-03 19:28:21

As mentioned previously, I am hacking a bit on a proof-of-concept word processor. Right now, it's hosted on googlecode and called kuatia.

Now, it is far from being useful for anything, but... it can do nested itemized and bulleted lists.

Here's a screenie of the editor and the PDF output it produces via reStructured Text:

editando2

Personally I think that's not too bad.

Marave 0.7 released

2010-02-25 11:16:12

I just uploaded version 0.7 of Marave, my fullscreen text editor to http://marave.googlecode.com

Marave is a "relaxing" text editor inspired by ommwriter, DarkRoom and many others. It combines a spartan fullscreen UI with a vanishing UI, which gets out of the way of your text.

It supports syntax highlighting, inine spellchecking, background music, audible keyboard feedback, themes, is extensible via plugins, and much more.

Here's a screenshot:

marave18

There are no major new features in 0.7, but there are important internal changes and some major bugs fixed:

  • Fixed bug that broke opening files if you had no spellchecker
  • Implemented basic RTL language support
  • Several other minor fixes
  • Refactored the editor component so it can be reused

A teaser for an idea

2010-02-24 14:13:30

I have been thinking on what I really really want in a word processor. And then what would it take to create such a thing.

A few minutes of playing have led me the way of this teaser (video here if you can't see it):

Could something come out of it? Who knows.

Editor: a better QTextEdit

2010-02-23 14:23:06

Writing an editor is reinventing the wheel. I know that. I tell myself Marave is a fine wheel, with distinct features, and I think that is true, but, if you are reinventing the wheel, there's no need to reinvent the axle and the spoke, too.

So, I refactored the stuff that I think a text editor must provide into a nice library, so the next time someone must invent a wheel, he can use Marave's neat spokes and axles:

So, introducing Editor, the most-obviously named class ever! It's a text editing widget for PyQt with extra features, which you can use as a drop-in replacement for a QTextEdit or QPlainTextEdit.

Right now, it lives inside Marave's SVN but it may even move out someday.

Here are its features:

  • Syntax highlighting

    And I don't mean "in theory", like QTextEdit and company do! Editor can highlight a bunch of languages, because it uses GNU source highlight via Lorenzo Bettini's Source Highlight Qt.

  • Spell checking

    If you have PyEnchant installed and the right dictionaries, it will do online spellchecking.

  • Search and Search+Replace widgets

    The Editor class can give you nice widgets for search or search and replace already hooked with the editor widget, so you can add them to your app's UI easily.

  • new/open/save/saveas methods:

    Don't implement opening/saving, etc yourself! That's always the same code!

Hopefully this will be helpful for someone else :-)

Preview chapter of "Grok 1.0 Web Development"

2010-02-23 12:03:28

The nice fellows at Packt Publishing have sent me a copy of Grok 1.0 Web Development by Carlos de la Guardia to review.

I am reading it and expect to write about it in a few days (BTW: nice so far! My proposed slogan: it's like zope, without the Java [1]), and here is a sample chapter so you can see what it's about. It's easy to read standalone, too:

Chapter 5: Forms

Having had to deal this week with the brokenness that's Django oldforms (legacy site, don't even ask), the Grok way of dealing with forms is refreshing ;-)

[1] Yes, of course I know Zope had no Java in it, it just felt like Java ;-)

Are we really this clueless about software costs?

2010-02-22 00:54:03

Here's what Ohloh has to say about the cost of developing Marave

Really, Marave is maybe a month of part-time programming. How could that possible be U$S71355, or "1 Person Years"?

Is this garbage the best we have to estimate costs? If that's the case, then whenever you see something about "Open source program X would take Y years and cost Z dollars to write", cut it down by a factor of 10 or more.

Here's what Marave really costed to develop:

  • Nothing.

Ok, here's what it would have costed if I had charged for it:

I am guessing about 100 hours of my time. At my "I hope they pay me this much" rate of U$S 40/hour , that's U$S 4000, which means Ohloh is off by 1600%.

OTOH, for that much freelance work I would not charge you the full rate, I would probably end charging you more like U$S20/hour which would make Ohloh's guess over 3000% too high.

In conclusion: if you like my code (and hey, you can see it for yourself), hire me, I am incredibly cheap, or amazingly fast!

Marave 0.6 is out

2010-02-21 21:49:36

Version 0.6 of Marave, my peaceful, fullscreen text editor is now available at the usual place: http://marave.googlecode.com

New stuff:

  • Syntax highlighter
  • Plugins
  • Bugs fixed
  • Nicer animations
  • Code cleanup

Gratuitous screenshot:

The aha! moment

2010-02-19 22:07:00

I had a small task today in Marave. The goal was:

  1. Fade in a widget
  2. Set a variable
  3. Fade in another one

It's important that things are done in that order and it's also important that the app remains interactive.

And here's the code to do that (simplified):

def fadein(thing, target=1., thendo=None):
    """
    * thing is a QWidget
    * thing.proxy is a QGraphicsWidget
    * thendo is callable
    * target is the desired opacity
    """

    thing.anim=QtCore.QPropertyAnimation(thing.proxy, "opacity")
    thing.anim.setDuration(200)
    thing.anim.setStartValue(thing.proxy.opacity())
    thing.anim.setEndValue(target)
    thing.anim.start()
    thing.anim.finished.connect(thing.anim.deleteLater)
    if thendo:
        thing.anim.finished.connect(thendo)

And this is how you use it:

def later():
    avar=avalue
    fadein(widget2)

fadein(widget1, thendo=later)

Isn't that lovely? Having functions as first class objects means I can just take later as a closure, along with widget2 and avar, which need only be defined in the local scope, and the call chain will work just as I wanted!

Yes, many other languages can do this, and in Javascript it's one of the most common tricks, but consider that PyQt is a wrapper for a C++ library!

I think this kind of usage shows the real added value PyQt brings to the table, it's not just that python avoids the boring compiles, or that you have the awesome standard library to use, but that the language itself enables you to do things that are not practical in C++.

In C++ the only way I can think of is creating a slot that's the equivalent of later, then chaining the signals... which means that this throwaway later becomes part of the interface of a class!

I would have to define later somewhere else on the file, separate from its only usage (maybe even inlined in the header file).

Even then, that's not equivalent: avalue may be something that was only avalable before the first call to fadein, (for example, the time of the first fadein): I would have to create a place to store it, make it reachable by later... and wht happens if you try to do this again while the first fadein is in progress?... it gets hairy.

Programming is like a slap in the face sometimes... you realize that things you use without even noticing are far from trivial.

So, remember young padawan: you can choose you tools. Choose wisely.

Extending Marave

2010-02-18 18:50:20

Marave is a text editor. If there's one thing that's true of most text editors, it's this: they lack the exact features you need.

So, the solution, in the ancient tradition of Emacs and Vim is... make it extensible.

I am a big fan of programs that can be extended by users.

So... here's the anatomy of a Marave plugin as it stands right now on SVN trunk, which of course can change any minute.

Creating a plugin

You just need to create a .py file in the plugins folder.

Here's the most basic plugin, which does nothing:

# -*- coding: utf-8 -*-

from plugins import Plugin
class Smarty(Plugin):
    name='smarty'
    shortcut='Ctrl+.'
    description='Smart quote and dash replacement'
    mode="qBde"

Default values for anything configurable (in this case, "mode") is just added to the class.

The mandatory fields:

  • shortcut: a keyboard shortcut that triggers this plugin
  • name: a short name
  • description: a one-line description of what it does

What does it do? It adds the plugin to the plugin list in the prefs dialog, and you can open its configuration dialog, where you can change the shortcut:

maraveplugin1

If you enable this plugin, whenever the shortcut is used the "run" method of the plugin is called.

Making the Plugin Configurable

This plugin supports different modes of operation. To make this reachable to the user, you need to implement a few extra methods.

The addConfigWidgets method takes a dialog argument and adds whatever you want there:

@classmethod
def addConfigWidgets(self, dialog):
    print 'Adding widgets to smarty config'
    l=dialog.ui.layout
    self.q=QtGui.QCheckBox(dialog.tr('Replace normal quotes'))
    if 'q' in self.mode:
        self.q.setChecked(True)
    self.b=QtGui.QCheckBox(dialog.tr('Replace backtick-style quotes (` and ``)'))
    if 'B' in self.mode:
        self.b.setChecked(True)
    self.d=QtGui.QCheckBox(dialog.tr('Replace -- by en-dash, --- by em-dash'))
    if 'd' in self.mode:
        self.d.setChecked(True)
    self.e=QtGui.QCheckBox(dialog.tr('Replace ellipses'))
    if 'e' in self.mode:
        self.e.setChecked(True)
    l.addWidget(self.q)
    l.addWidget(self.b)
    l.addWidget(self.d)
    l.addWidget(self.e)

And then the config dialog will look like this:

maraveplugin2

But then you need to save those options somewhere, which you do reimplementing saveConfig:

@classmethod
def saveConfig(self, dialog):

    self.shortcut=unicode(dialog.ui.shortcut.text())
    self.settings.setValue('plugin-'+self.name+'-shortcut', self.shortcut)

    newmode=""
    if self.q.isChecked():
        newmode+='q'
    if self.b.isChecked():
        newmode+='B'
    if self.d.isChecked():
        newmode+='d'
    if self.e.isChecked():
        newmode+='e'
    self.mode=newmode

    self.settings.setValue('plugin-smarty-mode',self.mode)
    self.settings.sync()

And you need to load those settings and put them in your class, too:

@classmethod
def loadConfig(self):
    print 'SMARTY loadconfig', self.settings
    if self.settings:
        sc=self.settings.value('plugin-'+self.name+'-shortcut')
        if sc.isValid():
            self.shortcut=unicode(sc.toString())
        mode=self.settings.value('plugin-smarty-mode')
        if mode.isValid():
            self.mode=unicode(mode.toString())

Making it Work

And yes, you need to make it do something useful. The plugin has access to a "client" which is Marave's main window. Everything is available there, somewhere ;-)

def run(self):
    print 'running smarty plugin'
    text=unicode(self.client.editor.toPlainText()).splitlines()
    prog=QtGui.QProgressDialog(self.client.tr("Applying smarty"),
                               self.client.tr("Cancel"),
                               0,len(text),
                               self.client)
    prog.show()
    output=[]
    for i,l in enumerate(text):
        output.append(unescape(smartyPants(l,self.mode)))
        prog.setValue(i)
        QtGui.QApplication.instance().processEvents()
    prog.hide()
    self.client.editor.setPlainText('\n'.join(output))

And there it is, if you enable the smarty plugin, you can "fix" your quotes, dashes and ellipsis with a key combination :-)

Full source code here: http://code.google.com/p/marave/source/browse/trunk/marave/plugins/smarty.py

Still to be done: other ways to integrate plugins into the UI, buttons, panels, etc.

Yak Shavings for February 16, 2010

2010-02-16 19:20:42
yak shaving
(idiomatic) Any apparently useless activity which, by allowing you to overcome intermediate difficulties, allows you to solve a larger problem.

A while ago, I wrote how I implemented a generic syntax highlighter for PyQt using Pygments.

I got a request for such a feature in Marave, so I digged that code and... it's freaking useless. It's just too slow for reasonable use.

So, that yak's hair is all grown up again, and I just got this new pair of scissors!

The goal is a way to highlight syntax in a QPlainTextEdit that:

  • Doesn't require programming to add a new highlighter
  • Doesn't require programming to add a new color scheme
  • Doesn't require me to spend a year writing highlighters for existing languages
  • Is fast enough

A quick google shows that for C++ you can use Source highlight qt which is based on GNU source highlight.

Alas, no python binding that I could find. So, let's write one!

Here it is: http://marave.googlecode.com/svn/trunk/marave/highlight/

And here's a screenshot of the demo program running, showing itself in its entirety:

You can create a color scheme using CSS, a language definition is a text file, there are a bazillion already written, it seems to be fast enough.

So, another yak shaved, another feature (not finished!) for Marave


View My Stats