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!