Posts about programming (old posts, page 23)

2009-02-21 20:19

Planeta PyAr, or how to generate multiple planets with rawdog

I just finished implementing my first public planet, Planeta PyAr using rawdog. It contains the blogs of members of Python Argentina

To avoid the classical argument about whether this is "the planet of people in PyAr" or "posta about python from people in PyAr", I decided to hack rawdog into producing both.

I didn't want to have two separate rawdog configurations, so I started looking at plugins.

The basis was the selectfeeds plugin and the rss plugin.

Then I merged selectfeeds into rss to make things simpler.

Here is my hacked version which takes the selectfeed argument and uses only those feeds when creating the HTML, FOAF, OPML and RSS files.

The bad side of this is that selectfeed requires you post the feed URLs all in one line!

With just a couple dozen blogs in the planet, that means several hundred characters. And then when you decide to change one URL... ick.

So, I wrote a tiny script to take separate feed files and create separate config files,

Now, I create a feeds-full file with the non-python-specific feeds in rawdog format:

# Feeds full

# Anthony Lenton
feed 20m
# Rivendel
feed 20m
# Facundo Batista
feed 20m

And the same for a feeds-python:

# Feeds solo python

# Anthony Lenton
feed 20m
# Rivendel
# No tiene RSS solo python
# Facundo Batista
# No tiene RSS solo python
# Gabriel Patiño
feed 20m

Put all the common configurations in config-base.

And produces a config, a config-full and a config-python.

Then run rawdog like this:

cd ~/.rawdog ; LANG=es_ES rawdog -c config-full -w ; LANG=es_ES rawdog -c config-python -w

And that's it, two planets for the cost of one and a half.I wonder why other planets don't provide this.

2009-02-17 12:26

uRSSus 0.2.12 released!


Announcing release 0.2.12 or uRSSus a RSS/Atom aggregator.

This release fixes the big crashy updater bug in 0.2.11, and makes some minor improvements, like nicer date display, and a xdg resource installer.

2009-02-13 22:23

Indeed screw all gui builders... for java!

Reading DZone I ran into an interesting post titled Screw all GUI builders which advocates dropping all GUI builders and instead coding your UI by hand.

I always advocate using Qt Designer instead of coding by hand, so I wanted to see, even it's talking about Java, I thought, "why don't I feel that way"?

My conclusion? I don't see it because in PyQt we are just lucky because our tools don't suck quite as much.

It gives several arguments:

  • You don't know how exactly the generated code works. You don't need to. You start not to care and GUI application development becomes a process of drawing and adding simple event handlers here and there.

To that, my reaction was yes, indeed I don't care, as long as it works, which it has 99.99% of the time. When it didn't, I did understand the generated code, though.

The reason for this will be more obvious once you see the code in both cases, I think.

  • Most GUI builders force you to use single class for single window, so generated classes tend to have thousands of lines of code.

Hmmm... I really don't know what this means in context, but that's just my java ignorance. OTOH, yes, one class per window, but almost never thousands of lines of code.

For example, the UI for uRSSus main window is quite complex, and it is only 530 lines of code. Here's how it looks, so you see it's not a trivial window:

urssus23 * Most GUI builders don't want you to modify the generated code. And if you do, they either break or rewrite your code.

Indeed Designer's code is not meant to be modified. That's what inheritance is there for. Overload whatever you want changed. Maybe this is easier in PyQt because Python is more dynamic than Java? Not sure.

  • GUI builders force you to use an IDE, mostly one you started coding with. So if you start with NetBeans, you most likely be forced to stay with it for the whole project.

Just not true for Designer. I can see how that would suck, though.

  • The generated code is far from being optimal. It's not resize-friendly, not dynamic enough, it has many hard-coded values, refactoring is most likely impossible, because builder would not allow that.

Designer does generate resize-friendly dialogs if you use it correctly. The hard-coded values are runtime-editable, refactoring is pretty simple (take whatever you want, create a widget with it?)

And since I wanted to compare apples to apples...

Here is his dialog, done via PyQt and Designer:


No, I didn't bother inserting text in the dialog so it loks the same ;-)

The "close" button closes the window, the URL opens in your system browser.

The text widget actually supports a subset of HTML, so there is a valid HTML document there, instead of just plain text.

Also, another thing is ... Designer's (or rather pyuic's) generated code is straightorward stuff.

Here's the code, which I think is roughly equivalent to his, only it's just 123 LOC, instead of 286 (his generated version) or 279 (his hand-made version). And I didn't delete the comments.

If I were doing this for real, all widgets would have descriptive names instead of PushButton1 and whatever.

Also, it's i18n-ready, unlike the Java versions, unless I missed something.

You can also get hawkscope.ui from this site to play with, it's done with Designer from Qt 4.4.

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

# Form implementation generated from reading ui file 'hawkscope.ui'
# Created: Fri Feb 13 22:39:47 2009
#      by: PyQt4 UI code generator 4.4.4
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.resize(444, 357)
        self.verticalLayout_2 = QtGui.QVBoxLayout(Dialog)
        self.horizontalLayout_2 = QtGui.QHBoxLayout()
        self.label_2 = QtGui.QLabel(Dialog)
        self.verticalLayout = QtGui.QVBoxLayout()
        self.label_3 = QtGui.QLabel(Dialog)
        font = QtGui.QFont()
        self.label_4 = QtGui.QLabel(Dialog)
        self.gridLayout = QtGui.QGridLayout()
        self.label_5 = QtGui.QLabel(Dialog)
        font = QtGui.QFont()
        self.gridLayout.addWidget(self.label_5, 0, 0, 1, 1)
        self.label_10 = QtGui.QLabel(Dialog)
        self.gridLayout.addWidget(self.label_10, 0, 1, 1, 1)
        self.label_9 = QtGui.QLabel(Dialog)
        font = QtGui.QFont()
        self.gridLayout.addWidget(self.label_9, 1, 0, 1, 1)
        self.label_8 = QtGui.QLabel(Dialog)
        self.gridLayout.addWidget(self.label_8, 1, 1, 1, 1)
        self.label_7 = QtGui.QLabel(Dialog)
        font = QtGui.QFont()
        self.gridLayout.addWidget(self.label_7, 2, 0, 1, 1)
        self.label_6 = QtGui.QLabel(Dialog)
        self.gridLayout.addWidget(self.label_6, 2, 1, 1, 1)
        self.label = QtGui.QLabel(Dialog)
        font = QtGui.QFont()
        self.textBrowser = QtGui.QTextBrowser(Dialog)
        self.horizontalLayout = QtGui.QHBoxLayout()
        spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
        self.pushButton = QtGui.QPushButton(Dialog)
        self.pushButton_2 = QtGui.QPushButton(Dialog)

        QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL("clicked()"), Dialog.accept)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
        self.label_3.setText(QtGui.QApplication.translate("Dialog", "Hawkscope", None, QtGui.QApplication.UnicodeUTF8))
        self.label_4.setText(QtGui.QApplication.translate("Dialog", "Access anything with single click!", None, QtGui.QApplication.UnicodeUTF8))
        self.label_5.setText(QtGui.QApplication.translate("Dialog", "Version:", None, QtGui.QApplication.UnicodeUTF8))
        self.label_10.setText(QtGui.QApplication.translate("Dialog", "0.4.1", None, QtGui.QApplication.UnicodeUTF8))
        self.label_9.setText(QtGui.QApplication.translate("Dialog", "Released:", None, QtGui.QApplication.UnicodeUTF8))
        self.label_8.setText(QtGui.QApplication.translate("Dialog", "2009-02-06", None, QtGui.QApplication.UnicodeUTF8))
        self.label_7.setText(QtGui.QApplication.translate("Dialog", "Homepage:", None, QtGui.QApplication.UnicodeUTF8))
        self.label_6.setText(QtGui.QApplication.translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Droid Sans\'; font-size:8pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"\"><span style=\" text-decoration: underline; color:#3c7dbe;\"></span></a></p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
        self.label.setText(QtGui.QApplication.translate("Dialog", "Environment", None, QtGui.QApplication.UnicodeUTF8))
        self.textBrowser.setHtml(QtGui.QApplication.translate("Dialog", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Droid Sans\'; font-size:8pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Text goes here</p>\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"></p></body></html>", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("Dialog", "C&opy To Clipboard", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setShortcut(QtGui.QApplication.translate("Dialog", "Alt+O", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton_2.setText(QtGui.QApplication.translate("Dialog", "&Close", None, QtGui.QApplication.UnicodeUTF8))

2009-02-13 13:37

Counting unread items is HARD

I mean, if google can get it wrong, I have an excuse, right?


See how at the same time you see:

  • At the left: "All items (8)"
  • At the top-center: "8 new items"
  • In the tree: 8 items
  • In the list: 14 new items

Each of those numbers can get out of sync with each other if you do something the "wrong" way.

But what the heck, it seems as of r678 urssus does it right. The list of unread posts even updates when you have it open adding the new posts in the right places without disturbing you, and keeping the right numbers everywhere, AFAICS!


So here, in uRSSUs:

  • 13 unread articles in the "Unread Articles" item
  • 13 in "All Feeds"
  • 13 adding each folder
  • 13 in the article list

Sadly, this is all post-0.2.11 release.

2009-02-13 11:55

uRSSus 0.2.11 is out!

Since I did some neat coding on it yesterday and today, I decided it was a good moment to release uRSSus 0.2.11 into the wild.

It doesn't look much different from 0.2.9 but it works much, much better.

Gone are the crazy disappearing/duplicate feeds when dragging/dropping in the feed tree.

Gone is the broken update scheduler, and now feeds update smoothly.

Gone are the inconsistent displays (like All feeds <> Unread items)

Gone is the CPU guzzling

Gone seems to be the DB locking and IO churning.

Sadly, gone is the DB schema migration because I can't get miruku to work with current alchemy/migrate/Elixir. Since I don't remember if the DB schema changed since 0.2.9 this may be bad ... or not. Backup, friends. Export to OPML and back. Whatever, this is alpha stuff ;-)

So, head to the uRSSus homepage and take a look. Maybe you will like it!

2009-02-12 19:04

Urssus update: 12/2/2009

Yes, after months of doing nothing, I hve made some changes in uRSSus, my RSS aggregator.

Basically, I removed the Qt MVC tree for feeds and replaced it with a old, reliable item-based tree.

And now, magically you can drop feeds into folders, sort feeds by unread count, and it all seems to work.

I am probably the only uRSSus user in the world, so I am not bothering with a release until I kick the beast into shape with at least some nice features, but it's something.

2009-02-05 22:40

Back to my own dogfood.

So, I must confess something. I have not been using uRSSus for a couple of months. It all started on my vacations. Since I had only my eee, I ran into some problems and was forced to use google reader.

And it was NOT terrible. Mind you, it was not great, either, but really, not terrible at all.

Since switching RSS apps is a pain (because you need to REALLY catchup on your reading before switching), it took me a long time to get back.

And now I find all sorts of problems in uRSSus which I will have to fix. Which means I better start cranking.

Not that it looks bad, it's a handsome program!


I don't expect to do anything like last year's "A release of something every friday" madness, but I will do something.

Also, I need to rewrite BartleBlog, the app that generates this blog you are reading. It's broken in many ways.

2009-02-05 15:22

Back from the dead.

Yes, again, two months passed and nothing on the blog. What can I say, my life takes a lot of my time.

Anyway: I will try to actually write my PyQt tutorial based on what I showed at LatinoWare (I said "will write it in a few days". Ja!), and start pushing things out every once in a while.

The only FLOSS I did was some bugfixes for rst2pdf which is desperately needing a new release.

Hopefully march will mean a bit more time for free software and writing.

On other areas:

  • KDE 4.2 is really good. Beats 3.5 for me, like for most anyone else that was not convinced yet.
  • I want to try my hand at a couple of plasmoids (yes, I do have at least one semi-original idea)

So, see you soon, dear readers. Maybe. Probably.

2008-11-01 10:16

Blogging from Latinoware again

Last night while eating one of the Python Brazil guys told me about the Lightning Talks session taking place today.

Since I am basically insane, I was convinced to do my first lightning talk:

  • In spanish in front of a purely brazilian audience
  • Creating it while having breakfast 1 hour before the event
  • After exceeding the allotted 50! minutes 2 days ago

So, in short, I was terrified. I got there, sat at the back, start listening.

I was 4th in order.

1: doctests (nice)

2: pronounceable passwords (nice)

3: MVC in gtk... I didn't understand a word, and ran out of time.

So, I am now thinking nobody will understand me at all, so I will slow down, run out of time, and will then give the worst lightning talk ever.

I'm next up. My topic: how to build a spreadsheet in 5 minutes.

Amazingly, it worked rather well, the "MC" asked me for a copy of the code, and said it was a neat example. Noone looked horrified or ROFLing, so I am at least somewhat happy about it.

Here is the code for you, too: ssin5min.tgz

I would do it again. But if I am doing it again here, I am learning portuguese first.

Contents © 2000-2019 Roberto Alsina