Tales of a GeekTrotter Binary Logbook

4Apr/1114

Python VS Ruby through a concrete GUI example (Qt)

In a previous post, I was justifying my choice to start learning Ruby instead of Python, and in another previous post I was praising QtRuby.

Now it's time to challenge QtRuby (and Ruby in general) with PyQt/PySide (and Python in general).

Due to some reasons, I had to write the same program in both languages.
Thanks to that, I better understood myself the pros and cons of both languages and will try to share my thoughts with you.

A very brief summary would be: Ruby is the winner for programming pleasure and private applications while Python is the winner when it gets serious.
For more information, read the whole post!

Background

I wanted to write a new GUI program, basically uploading pictures to Twitpic (the real goal is beyond that but let's make it easy).
Being a Ruby fan, I started to write my code in Ruby 1.9 with the "qtbindings" gems.
For my application, I also needed other gems like the "zip/zipfilesystem", "digest/md5" or "json".
As always with Ruby, everything went fine and after about 16 hours I had my application working.

ddl-ruby-settings

Came the moment to send the program to beta testers.
To make it simple for them, as not all of them are familiar with programming, I wanted to bundle the program in an executable with all the required DLLs, as I used to do in C++ with Qt.

Lucky me, there is something called “RubyScript2Exe” (or “AllInOneRuby”) that can do it for you!
Unlucky me, it’s not working at all! It always raises a fatal error, even with the simplest QtRuby script…
A “can’t modify frozen string (RunTimeError)” that many people seem to have and no fix available yet…

Well, there is another one called OCRA (One-Click Ruby Application Builder) that looks more serious.
It works with basic QtRuby scripts, but unfortunately not when we require some dependencies, like “zip/zipfilesystem”. It posted on their Google Groups and hopefully a solution will be found, but so far… nothing!

Update: Thanks to Lars, a simple --no-autoload made OCRA work perfectly!
So now I can easily bundle my QtRuby applications too!

That’s good to be able to quickly write programs that do their job, but it’s very frustrating when you cannot distribute them to others!
For beta testers, I could tell them to install Ruby 1.9, the required gems, Qt 4.7, and send them directly the source code, but it’s not convenient at all for them, especially if they don’t really have time to waste…

Another solution was to switch to Ruby 1.8 instead of Ruby 1.9 that I was using and hope that it works.
However, I am simply unable to run my code under Ruby 1.8 as the qtbindings are not recognized by Ruby 1.8 (even if of course I install the gem… :s).
I posted the problem on stack overflow, and, hopefully I will be able to edit this post and write that I managed to make it work on Ruby 1.8, and, with even a lot of luck, to bundle the QtRuby package with Ruby 1.8!
But for now, trying to bundle my QtRuby application just fails enormously…

So I decided to re-write the program in Python, as Python and Ruby are often compared; Python being more accepted among professionals.

Switching to Python

In Python, there are two different Qt4 bindings: PyQt4 and PySide.
They are very similar, except the licence.
PySide being supported by Nokia (which now owns Qt), I chose PySide.

Before anything else, I first tried to compile a very basic PySide program into a binary (an .exe for Windows).
There is a tool called py2exe so I tried that one and… it works very well! (Unlike RubyScript2Exe…)
I added some “import” and made some basic calls and… py2exe just worked well.

Accepted! I was ready to write my program again, but in Python!
The good thing is that I already had the “brain” of my program and new how to do it.
I just had to change the syntax from Ruby to Python, which was done quite smoothly.

It took me again about 16 hours to re-write it and add some extra features and error control.

The result is looking just exactly the same:

ddl-python-settings

The only differences are because I did not copy exactly the position and size of all the elements…

But the code is very different so let see the differences between Ruby and Python!

Similarities between Python and Ruby

First let’s talk about the similarities.

Indentation

A lot of people talk about the “whitespaces” in Python, that force you to indent your code otherwise Python does not understand it… Honestly, it is so transparent that you just forget it after a while!
I mean, when you write code in C++ or Java or whatever, you indent your code (or your IDE does it for you). It is just so natural that there is just nothing to say about it.
My Ruby code had an indentation of 4 whitespaces, and my Python code too.
I didn’t feel any difference between the two languages as for the indentation…

Object Programming

Ruby is 100% object programming, Python… is?
It seems that recent Python is also fully object oriented and I did not feel any problem with “int” or “str” that are well handled in both languages.

Libraries

For the needs of my project, both languages had the required libraries, built-in or through easy to install libraries or gems.
It is said that Python has more libraries, and I am sure it is true, but Ruby is very likely to have enough libraries to meet your needs.

Differences between Python and Ruby

Pleasure of programming

The first difference is very subjective.
I enjoy much more writing some Ruby code than Python code.
First of all, I hate the ":" in Python… I just find it too ugly (ok, very subjective…).

I also don’t like the fact that you always need to add the parentheses to the method calls to really call the method. This is also very subjective again and many people won’t agree and say it’s better to differentiate the method call and reference, but I prefer a clean code without parentheses like in Ruby (you still can add the parentheses in Ruby if you want though…).

Then, the keyword self in Python: you just have it everywhere!
All the method calls first argument are always “self”, and you always repeat “self” when you call a local class instance variable… Among 391 lines of code, I have 286 times the word “self” (more than one “self” in some lines of course), that’s too many!
I definitely prefer the @var in Ruby that some Pythonists laugh at.

Finally, the Python __init__, __str__ and other underscored methods/functions…
What the…! No, seriously, someone has to to something for that!
OK, the __str__ function that can change any object (anything?) into a string is very useful.
But please find another name…
For example __init__ and __del__ are respectively called initialize and define_finalizer in Ruby.
It’s less ugly, isn’t it?

To conclude, it’s just nicer to read and write some Ruby code than Python code (for me!).

The Blocks Power of Ruby

Here we are. The Ruby Blocks ( { } ).
They are just so convenient that once you code with it, you’re addicted!
It is possible to achieve such features in Python too, but it’s definitely better implemented in Ruby where it’s very very easy to use.

Let’s take a concrete example of a Qt signal connection.
In Python you have to pass a function as parameter of the call, even if you only have one action to do when the signal is received.

In Python, you need to define the receiving slot separately (note the “…”):

        saveSettingsButton = QtGui.QPushButton('Save Settings')
        saveSettingsButton.clicked.connect(self.saveSettings)
        ...
    @QtCore.Slot()
    def saveSettings(self):
        self.settings.setValue('author',self.author())
        QtGui.QMessageBox.information(self, 'Settings', 'Settings saved!') 

In Ruby, you can just use the blocks to write the action on-click right after:

    # Save settings
    saveSettingsButton = Qt::PushButton.new(tr('Save settings'))
    saveSettingsButton.connect(SIGNAL :clicked) {
        @settings.setValue("author", Qt::Variant.new(@author))
        Qt::MessageBox.new(Qt::MessageBox::Information, 'Settings', 'Settings saved!').exec
    }

When you will perform the action only once, or for very short actions, it is definitely better to write it directly after the connection inside the blocks, like in Ruby.

It is easier to read and maintain.

For actions to perform that can be use somewhere else in the code by another object, of course it is better to separate it, and Ruby can also do that. But for very short call, this Ruby shortcut using blocks is very convenient!

Definition of the slots/signals

In Python, you need to add a @QtCore.Slot(type1, type2,…) before the functions that you want to declare as a slot, like the previous "saveSettings(self)" method.

In Ruby, you can just add one line at the top of your class listing all the slots, like you would do in Qt/C++: slots :saveSettings, :mySlot2, :mySlot3

Same for the signals with the keyword "signals", whereas in Python you need to declare the signals at the top of your class, outside __init__

Python:

class myClass(QtCore.QObject):
    errorSignal         = QtCore.Signal(str)
    infoSignal          = QtCore.Signal(str)
    def __init__(self):
        ...
    @QtCore.Slot()
    def saveSettings(self):
        ...
    

Ruby:

class myClass < Qt::Object
  slots :saveSettings
  signals :errorSignal, :infoSignal

And finally, you can just have no slot at all in Ruby if you add your code when a signal is received inside blocks, like shown in the previous code example!

Easiness of distribution

Yes, both of them are multi-platform, and it is possible to run the same code on Windows, Linux and Mac.

However, not all the clients want to install Ruby or Python and the required libraries to run the program.

So you need to bundle the application for them, and Python is far better on this point!

I have talked about it in more details in the background introduction as it was the reason why I wrote the same program in both languages.

Refer to the first part for more details.

Performance

I was surprised reading on Internet that Ruby was performing better than Python.

I don’t know if it’s my computer or not, but for me PySide is performing far better than QtRuby…

The PySide GUI is almost immediately displayed when the program is started whereas the QtRuby GUI takes about 10 seconds to display!

The difference is even bigger when I run the debugger… Python debugger runs very smoothly whereas Ruby debugger is very slow, so slow I can’t use it… (and I used the ruby “fastdebug” :s)

My computer is not recent (an almost 3 years old AMD X2 laptop that rediscovered life when it met an SSD) so I tried on a Quad Core running Windows XP and of course the difference was smaller…

But on old machines, I beleive the PySide GUI is starting faster than the QtRuby one (and the performance of the program once launched is similar).

Conclusion

In this article, I have shown the pros and cons of Ruby and Python on my concrete example, with my subjective point of view.

Yes, I prefer Ruby as a language itself and as I already said, I enjoy much more programming in Ruby than Python.

However, in the real world, for real projects and real clients that want a bundled application that just runs smoothly, I’m afraid Python is better.

It is even far better that after justifying Ruby during all this post, I will continue further development of this program in Python… Because yes, the easiness of distribution is a crucial point in the real world and the differences between Ruby and Python are more about self-pleasure than language features.

I hope Ruby will soon become easier to bundle so I can switch back to my lovely functional blocks!

Update: Thanks to Lars, a simple --no-autoload made OCRA work perfectly!
So now I can easily bundle my QtRuby applications too!
  • Pingback: the truth about six pack abs review()

  • Pingback: penis advantage()

  • Pingback: Cody Shappell()

  • Pingback: Honey Curson()

  • Pingback: Thalia Vogds()

  • Pingback: Thalia Vogds()

  • Marvin Roger

    En Python, il est également possible de faire des fonctions anonymes (les “blocs” de Ruby). Il s’agit des fonctions lambda, un petit exemple :

    ####Python

    def executer(fonction):
    fonction()

    executer(lambda: (
    print(‘1ère instruction’),
    print(‘2ème instruction’),
    print(‘…’)
    ))
    ####

    C’est un peu particulier comme syntaxe, mais le fait est que ça existe.

    • ThomasDalla

      Effectivement, c’est possible, bien que la syntaxe soit quelque peu moins commode.
      Merci de le préciser.

  • Phil Howell

    This is a great write up! I personally come from a C++/C# background and I’ve been working with Python and Ruby for quite a while now (mainly for some of my customers who require rapidly built Amazon API based apps who don’t have the time or knowledge to get into the .NET languages). I’m in agreement with you though, I personally enjoy writing code in Ruby more – I find it flows more naturally. That said (aside from the syntactic nastiness that __init__ etc) I find Python to be a breeze, and it’s extremely readable for non-programmers too – who are largely my target audience at the moment (the rise of the Dev-Ops is a blessing a curse sometimes). It’s good to see that the conclusion you were drawing 3 years ago hold true in 2014.

  • jdd

    Qu’en est-il en 2015 ? Est-il toujours aussi difficile de distribuer du QtRuby ?

    • ThomasDalla

      Très bonne question. Je ne programme ni en Python ni en Ruby depuis plusieurs années donc à vous de mettre cet article à jour 🙂