How to create a custom signal in Ruby-GTK2
I'm playing with the GTK2 bindings for Ruby. When coding GUI things, I usually like to build my own higher level widgets by subclassing containers and populating them with other widgets.
For example, I'm creating an image browser and I like to make an ImageList class that subclasses a Gtk::ScrolledWindow and builds the TreeView inside it:
class ImageList < Gtk::ScrolledWindow def initialize super @files = Gtk::ListStore.new(String, String, Gdk::Pixbuf) @fileList = Gtk::TreeView.new(@files) renderer = Gtk::CellRendererPixbuf.new col = Gtk::TreeViewColumn.new("Thumb", renderer, :pixbuf => 2) @fileList.append_column(col) renderer = Gtk::CellRendererText.new col = Gtk::TreeViewColumn.new("File name", renderer, :text => 1) @fileList.append_column(col) self.add @fileList end def readDir (root) Dir.foreach root do |dir| file = root+'/'+dir if !FileTest.directory?(file) && dir =~ /\.jpe?g$/ && FileTest.readable?(file) thumb = getThumbnail(file) iter = @files.append iter[0] = file iter[1] = dir iter[2] = Gdk::Pixbuf.new thumb File.delete(thumb) end end end end
When working like this, I find myself very soon in need of creating custom signals: in this case I want to make a signal that ImageList emits when the user select an image from the TreeView, like this:
fileList.signal_connect("selected") do |filename| imageDetails.load(filename) end
Now, how do I implement that extra 'selected' signal in my ImageList? Ruby documentation is usually very good and easy to find, but this time Google had little to say to me.
When the going gets tough, the tough gets to IRC::
enrico> Hi. http://ruby-gnome2.sourceforge.jp/?News_20031116_1 says that it is possible to create custom signals, however I could not find any documentation on how to do it. Anyone has a link or a short explanation? pterjan> glib/sample in the source IIRC pterjan> # define new signal "hoge" pterjan> signal_new("hoge", # name pterjan> GLib::Signal::RUN_FIRST, # flags pterjan> nil, # accumulator (XXX: not supported yet) pterjan> nil, # return type (void == nil) pterjan> Integer, Integer # parameter types pterjan> ) pterjan> in glib/sample/type-register.rb enrico> ooh! enrico> pterjan: thanks! enrico> I'll blog about it, it might make it easier for others to google this answer
Thanks pterjan, and oh, coolness! So there are nice examples that I was silly
enough to miss. Are they included in the Debian packages? Oh, yes! Right in
/usr/share/doc/libglib2-ruby/examples/type-register.rb
.
Here's the ImageList class with the new signal::
class ImageList < Gtk::ScrolledWindow type_register signal_new("selected", # name GLib::Signal::RUN_FIRST, # flags nil, # accumulator (XXX: not supported yet) nil, # return type (void == nil) String # parameter types ) def initialize super @files = Gtk::ListStore.new(String, String, Gdk::Pixbuf) @fileList = Gtk::TreeView.new(@files) renderer = Gtk::CellRendererPixbuf.new col = Gtk::TreeViewColumn.new("Thumb", renderer, :pixbuf => 2) @fileList.append_column(col) renderer = Gtk::CellRendererText.new col = Gtk::TreeViewColumn.new("File name", renderer, :text => 1) @fileList.append_column(col) self.add @fileList sel = @fileList.selection sel.signal_connect("changed") do |sel| if iter = sel.selected puts "Double-clicked row contains name #{iter[0]}!" self.signal_emit("selected", iter[0]) end end end def signal_do_selected(file) puts "Selected " + file #p caller end def readDir (root) Dir.foreach root do |dir| file = root+'/'+dir if !FileTest.directory?(file) && dir =~ /\.jpe?g$/ && FileTest.readable?(file) thumb = getThumbnail(file) iter = @files.append iter[0] = file iter[1] = dir iter[2] = Gdk::Pixbuf.new thumb File.delete(thumb) end end end end
And here's the controller code that binds the signal to some effect::
imageList.signal_connect("selected") do |imagelist, filename| puts "File #{filename} was clicked!" image.load(filename) end
It's quite easy, and (finally!) it does what I want without needing to write thousands of lines of code. YAY! And I finally fulfull my dream of connecting closures instead of callbacks to GTK signals.
Yes, I could have done it in Perl. Yes I could have done it in Python. But Ruby is soo cute!