Skip to content

Using blocks as slots in QtRuby/Korundum

Thursday, 14 September 2006  |  richard dale

On the #qtruby IRC channel kelko thought up a simple way of adding blocks as targets to Qt::connect() calls. After some more discussion rickdangerous suggested also adding a simple method that would work like 'signal_connect()' in ruby-gnome, which just takes a single signal name argument and a block. I've just added the code to both the Qt3 and Qt4 versions of QtRuby. There are three variants that allow you to replace the usual SLOT(:foobar) with a block.

First, the simplest version that works like ruby-gnome's signal_connect(), looks like this:

quit = Qt::PushButton.new('Quit') quit.connect(SIGNAL(:clicked)) { puts 'quit pressed' }

The code in the block is just called in the context of where the connect() statement is called from (not in the context of the instance emitting the signal). With this form of connect() you can call any arbitrary method (which needn't been in a subclass of Qt::Object), like this: class MyApp def initialize @button = Qt::PushButton.new('Quit') @button.connect(SIGNAL(:clicked), &method(:quit_app)) end

def quit_app
    p "quiting"
end

end

The second form of connect() with a block can be called like this from within a Qt::Object:

class MyButton < Qt::Button def initialize(text) super(text) connect(self, SIGNAL(:clicked)) { puts 'button clicked' } end ...

The block is called in the context of the instance calling connect(), a Qt::PushButton in this case. The third form of connect() a block looks like this:

Qt::Object.connect(quit, SIGNAL(:clicked), app) { puts 'quit clicked' }

The block is executed in the context of the third argument, 'app' in this example. It was a bit tricky implementing this one as the block needs to be invoked on 'app', with any arguments that were emitted by the signal. However, the Object#instance_eval method in Ruby 1.8 doesn't allow you to pass any arguments to the block. In Ruby 1.9 there is a new method Object#instance_exec which does take arguments, and after a bit of searching with Google I found out that Rails has a version implemented in pure Ruby 1.8. See here for a discussion of instance_exec(). I was going to paste the code here but I get a 'Terminated request because of suspicious input data.' from the kdedevelopers.org server when I try - it must think the code is a bit too clever for its own good or something :-).

Well, that's it - I'm looking forward to seeing what interesting uses people will find for this new functionality. I found when I added optional block to QtRuby constructors, it made a surprisingly large difference to the way the programs looked.