Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about pyqt (old posts, page 8)

Are we really this clueless about software costs?

Here's what Ohloh has to say about the cost of de­vel­op­ing Mar­ave

Re­al­ly, Mar­ave is maybe a month of part-­time pro­gram­ming. How could that pos­si­ble be U$S71355, or "1 Per­son Years"?

Is this garbage the best we have to es­ti­mate cost­s? If that's the case, then when­ev­er you see some­thing about "Open source pro­gram X would take Y years and cost Z dol­lars to write", cut it down by a fac­tor of 10 or more.

Here's what Mar­ave re­al­ly cost­ed to de­vel­op:

  • Noth­ing.

Ok, here's what it would have cost­ed if I had charged for it:

I am guess­ing 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%.

OTO­H, for that much free­lance work I would not charge you the full rate, I would prob­a­bly end charg­ing you more like U$S20/hour which would make Ohlo­h's guess over 3000% too high.

In con­clu­sion: if you like my code (and hey, you can see it for your­self), hire me, I am in­cred­i­bly cheap, or amaz­ing­ly fast!

Marave 0.6 is out

Ver­sion 0.6 of Mar­ave, my peace­ful, fullscreen text ed­i­tor is now avail­able at the usu­al place: http://­mar­ave.­google­code.­com

New stuff:

  • Syn­­tax high­­­lighter

  • Plug­ins

  • Bugs fixed

  • Nicer an­i­­ma­­tions

  • Code cleanup

Gra­tu­itous screen­shot:

The aha! moment

I had a small task to­day in Mar­ave. The goal was:

  1. Fade in a wid­get

  2. Set a var­i­able

  3. Fade in an­oth­er one

It's im­por­tant that things are done in that or­der and it's al­so im­por­tant that the app re­mains in­ter­ac­tive.

And here's the code to do that (sim­pli­fied):

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")
    if thendo:

And this is how you use it:

def later():

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 oth­er lan­guages can do this, and in Javascript it's one of the most com­mon trick­s, but con­sid­er that PyQt is a wrap­per for a C++ li­brary!

I think this kind of us­age shows the re­al added val­ue PyQt brings to the table, it's not just that python avoids the bor­ing com­piles, or that you have the awe­some stan­dard li­brary to use, but that the lan­guage it­self en­ables you to do things that are not prac­ti­cal 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.

Pro­gram­ming is like a slap in the face some­times... you re­al­ize that things you use with­out even notic­ing are far from triv­ial.

So, re­mem­ber young padawan: you can choose you tool­s. Choose wise­ly.

Extending Marave

Mar­ave is a text ed­i­tor. If there's one thing that's true of most text ed­i­tors, it's this: they lack the ex­act fea­tures you need.

So, the so­lu­tion, in the an­cient tra­di­tion of Emacs and Vim is... make it ex­ten­si­ble.

I am a big fan of pro­grams that can be ex­tend­ed by users.

So... here's the anato­my of a Mar­ave plug­in as it stands right now on SVN trunk, which of course can change any minute.

Creating a plugin

You just need to cre­ate a .py file in the plug­ins fold­er.

Here's the most ba­sic plug­in, which does noth­ing:

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

from plugins import Plugin
class Smarty(Plugin):
    description='Smart quote and dash replacement'

De­fault val­ues for any­thing con­fig­urable (in this case, "mod­e") is just added to the class.

The manda­to­ry field­s:

  • short­­­cut: a key­board short­­­cut that trig­gers this plug­in

  • name: a short name

  • de­scrip­­tion: a one-­­line de­scrip­­tion of what it does

What does it do? It adds the plug­in to the plug­in list in the prefs di­alog, and you can open its con­fig­u­ra­tion di­alog, where you can change the short­cut:


If you en­able this plug­in, when­ev­er the short­cut is used the "run" method of the plug­in is called.

Making the Plugin Configurable

This plug­in sup­ports dif­fer­ent modes of op­er­a­tion. To make this reach­able to the user, you need to im­ple­ment a few ex­tra meth­od­s.

The ad­d­Con­fig­Wid­gets method takes a di­a­log ar­gu­ment and adds what­ev­er you want there:

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

And then the con­fig di­a­log will look like this:


But then you need to save those op­tions some­where, which you do reim­ple­ment­ing save­Con­fig:

def saveConfig(self, dialog):

    self.settings.setValue('plugin-''-shortcut', self.shortcut)

    if self.q.isChecked():
    if self.b.isChecked():
    if self.d.isChecked():
    if self.e.isChecked():


And you need to load those set­tings and put them in your class, too:

def loadConfig(self):
    print 'SMARTY loadconfig', self.settings
    if self.settings:
        if sc.isValid():
        if mode.isValid():

Making it Work

And yes, you need to make it do some­thing use­ful. The plug­in has ac­cess to a "clien­t" which is Mar­ave's main win­dow. Ev­ery­thing is avail­able there, some­where ;-)

def run(self):
    print 'running smarty plugin'
    prog=QtGui.QProgressDialog("Applying smarty"),
    for i,l in enumerate(text):

And there it is, if you en­able the smar­ty plug­in, you can "fix" your quotes, dash­es and el­lip­sis with a key com­bi­na­tion :-)

Full source code here: http://­code.­google.­com/p/­mar­ave/­source/browse/trunk­/­mar­ave/­plu­g­in­s/s­mar­

Still to be done: oth­er ways to in­te­grate plug­ins in­to the UI, but­ton­s, pan­el­s, etc.

Yak Shavings for February 16, 2010

yak shaving

(id­iomat­ic) Any ap­par­ent­ly use­less ac­tiv­i­ty which, by al­low­ing you to over­come in­ter­me­di­ate dif­fi­cul­ties, al­lows you to solve a larg­er prob­lem.

A while ago, I wrote how I im­ple­ment­ed a gener­ic syn­tax high­lighter for PyQt us­ing Pyg­ments.

I got a re­quest for such a fea­ture in Mar­ave, so I digged that code and... it's freak­ing use­less. It's just too slow for rea­son­able use.

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

The goal is a way to high­light syn­tax in a QPlain­TextE­d­it that:

  • Does­n't re­quire pro­­gram­ming to add a new high­­­lighter

  • Does­n't re­quire pro­­gram­ming to add a new col­or scheme

  • Does­n't re­quire me to spend a year writ­ing high­­­lighters for ex­ist­ing lan­guages

  • Is fast enough

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

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

Here it is: http://­mar­ave.­google­code.­com/svn/trunk­/­mar­ave/high­light/

And here's a screen­shot of the de­mo pro­gram run­ning, show­ing it­self in its en­tire­ty:

You can cre­ate a col­or scheme us­ing CSS, a lan­guage def­i­ni­tion is a text file, there are a bazil­lion al­ready writ­ten, it seems to be fast enough.

So, an­oth­er yak shaved, an­oth­er fea­ture (not fin­ished!) for Mar­ave

Contents © 2000-2020 Roberto Alsina