Skip to main content

Ralsina.Me — Roberto Alsina's website

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")
    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 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.


Contents © 2000-2023 Roberto Alsina