Thomas' Blog Tales of a geek globetrotter

4Apr/117

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!
  • Daniel

    The choice of language is quite subjective and depends on the circumstances. You prefer ruby for it is pleasurable for you to write ruby code, on the other hand I choose python.

    I chose python because I am mathematician, so my applications are always mathematical applications. I gave ruby a shot ages ago and I had a lot of fun using it, I also had fun writing python though, I think one can actually use both depending on your  specific needs (come on, it’s programming not religion!). In my case python has an amazing library for linear algebra and other things I usually need (numpy , scipy). And Sage is build using python.

    Sometimes you have to give up coding pleasure for better tools. If I was using a programming language just because of coding please and beauty, I wouldn’t use python nor ruby, I would use Haskell for sure.

    Cheers mate.

    • Daniel

      By the way, sorry if my english sucked, I am not a native speaker.

    • Anonymous

      Hi Daniel,

      Thanks for your comment!
      Don’t worry for your English, I’m not a native English speaker neither and your English is very good.

      I totally agree with you that in real life you have to make choice between “fun” and “work”.
      That’s why in the conclusion I am in favour of Python ;)

  • http://nCdy.org nCdy

    In my opinion Python is better even with a syntax. It’s just Qt integration Ruby syntax looking like a bit better.

  • Alpheus

    I use Python heavily for work, and have used it for projects.  Pending Board approval, I’m likely going to be “forced” to learn Ruby (as if I could be “forced” to learn a new language!  No, I take that back.  There’s always C++ and Perl that I do NOT want to learn.)  I currently have no preference for one language or the other; either is better than PHP.  Based on this article, I think I’m going to be more inclined to like Ruby over Python…

    In my attempts to learn Common Lisp and Haskell, though, I’m convinced that both are better than either Python or Ruby, and because of the flexibility of Common Lisp, I’m inclined to favor CL over Haskell.  But, like Daniel before me, I’m heavily biased:  I’m a mathematician, and these languages have a certain mathematical essence that neither Python nor Ruby will ever have!

    • Anonymous

      Hi Alpheus,

      Thanks for sharing your point of view!I’m curious to know what are your feelings about Ruby after having used Python a lot.
      I have learned Ruby before learning Python so I’m interested in the experiences of people having done the opposite!
      I never tried purely functional programming languages like Haskell but that definitely is on my list of future programming languages to have a look at when I have time…

      And I am not a mathematician but I can understand that you are not satisfied by either Python or Ruby, that do not pretend to be the standard for mathematicians, but I’m sure you can be ecstatic with other languages ;)

  • Justin Malcolm

    Thank you for this write-up Thomas. The conclusion that Ruby is a little more pleasing and Python a little more serious is the same one that I have drawn myself.

    What really strikes me about your write-up is how few real difference there are between the languages themselves. It was the tooling (and performance) that required you to choose one over the other. As a contrast, Java and C# are often considered very similar languages but you would encounter far more difference in rewriting a modern C# in Java than you did moving from Ruby to Python.

    Now, having used “blocks” most seriously in other languages like C# and Scala, I know that sometimes blocks offer more than just the cosmetic differences detailed above, but otherwise it is actually amazing the two languages are so similar.