Build Status Gem Version

A DSL for building simple GUI ruby application. Based on gtk.



Doc: Reference+Exemples.

Gem :


NEW : 3.14.0 !! 24-05-2016 : * stop EM at app destroy * ruiby_require working: if a gem is not installed, ask to user if installation, do it, and reload it * Stock icones and famfamfam correction: label/button/image are now working with stock names and famfamfam names * Samples/icones.rb creation: search/show an incon in Gtk Stock/embeded famfamfam * force gtk 3.0.7 ( 3.0.8 has a big bug on DrawingArea resize, so all gadgets scratchs...). if 3.0.8 is needed , intall it manually (3.0.8 has callback with mouse position working on DrawingArea, better demos, ...) * travis pass ...!


  • [x] Stock and famfamfam bug for default icones
  • [x] Create a icone selector for helping to find good icone
  • [x] corrections in ruiby_require
  • [x] complete treeview and tree_grid,
  • [x] EM integration test
  • [ ] Gadget api: actual gadgets are cairo drawing => to be encapsuled
  • [ ] refactor samples demos with last improve: dynvar/autoslot...
  • [ ] resolve 100% gtk3 deprecated warning
  • [ ] complete rspec => 99% coverage ?


1) system

Install Ruby 2.x

2) install Ruiby (Ruiby install ruby-gtk3 which install gtk3 libs)

> gem install Ruiby

> ruiby_demo             # check good installation with gtk3 (default)
> ruiby_sketchi          # write and test ruiby code

3) if you need video/gstreamer, install gst/clutter : > gem install gstreamer > gem install clutter-gtk > gem install clutter-gstreamer

Here a working gem config on windows (17-Feb-2016, ruby 2.0.0p0) : cairo-1.15.2 glib2-3.0.7 gobject-introspection-3.0.7 gio2-3.0.7 atk-3.0.7 pango-3.0.7 gdk_pixbuf2-3.0.7 cairo-gobject-3.0.7 gdk3-3.0.7 rsvg2-3.0.7 gtk3-3.0.7 gtksourceview3-3.0.7


DSL is usable via inherit, include, bloc, or one-liner command.

By inherit:

class Application < Ruiby_gtk
    def initialize(t,w,h)
    def component()
      stack do
    .....your code....
Ruiby.start {"application title",350,10) }

By include, calling ruiby_component() :

class Win < Gtk::Window
    include Ruiby
    def initialize(t,w,h)
        add(, 2))
    def add_a_ruiby_button()
        ruiby_component do
            append_to(@vb) do
                button("Hello Word #{@vb.children.size}") {
Ruiby.start {"application title",350,10) }

Autonomous DSL, for little application :

require  'Ruiby' do
    stack do
        . . .

And, for very little application ('~' are replaced by guillemet):

> ruiby   button(~Continue ? ~) "{  exit!(0) }"
> ruiby   fields([%w{a b},%w{b c},%w{c d}]) { "|a,b,c|" p [a,b,c] if a; exit!(a ?0:1) }
> ruiby -width 100  -height 300 -title "Please, select a file" \
             l=list(~Files :~);l.set_data Dir.glob(~*~) ;  \
             buttoni(~Selected~) { puts l.selection ; exit!(0) } ;\
             buttoni(~Annul~) { exit!(1) }


Simple usage with gtk3 :

require 'Ruiby'

Usage with Event Machine: preload event-machine before Ruiby :

require 'em-proxy'
require 'Ruiby'

Warning : is done when starting mainloop, after creation of window(s). So, if you need initialization of event-machine callback, do it in component(), in a after(0): do
  after(0) { EventMachine::start_server().. { ... } }

See samples/spygui.rb, for exemple of gui with EM.


Ruiby does not have confidence in gtk multi threading, so all Ruiby commands must be done in main thread context. A Ruiby delegate is provided in Kernel module for support multi-threading

A Queue is polled by main-window thread : * main window poll Queue , messagers are proc to be instance_eval() in the main window context * everywere, a thread can invoke invoke_gui {ruiby code}. this send to the main queue the proc, which will be evaluated asynchroniously

instance_eval is avoided in ruiby. He is used only for thread invoker : gui_invoke().

require_relative '../lib/Ruiby'
class App < Ruiby_gtk
    def initialize
        super("Testing Ruiby for Threading",150,0)
        threader(10) { }
    def component()
      stack do
        sloti(label("Hello, this is Thread test !"))
        stack { @lab=stacki { } }
    end # endcomponent

class A
    def run
        loop do
            sleep(1) # thread...
            gui_invoke { append_to(@lab) { sloti(
                    label( )  # ! instance_eval on main window
            )  } }
    def aaa()  end

Ruiby.start { }


Ruiby is not realy object : most of DSL words are simple method in Ruby_dsl module.

Sometime, this is not good enaugh : * when a compenent must have many specifique methods * when component have (model) state : variable member must be used

So Component concept has been added (Fev 2016).It authorise to define a class, childr of AbstractComponent, which can be used by a dsl Word.

Components code seem very close to a Ruiby window : free constructor, define component() method for draw the widgets

Create a component: ruby class AAA < AbstractComposant def initialize(name) @name= name @state=1 end def component() framei("Component Comp:#{@name}") do label_clickable("B#{@name}...") { @state=2 } entry(@name,4) end end def get_state() @state end end

Define a word which instantiate a composant of class AAA: ruby module Ruiby_dsl def aaa(*args) c=install_composant(self,*args)) end end

Use the component: ruby c=nil stack { c=aaa "foo" flowi { aaa 1; aaa 2 } } button("?") { alert( c.get_state() ) }

A demo is at samples/composant.rb.

TODO: * Canvas and Plot must be converted to Component, soon :) * Define destroy() * Hook for auto-generate dsl word * Test Stock, Dynvar, threading, * Tests, tests, test...

Observed Object/Variable

Dynamic variable

Often, a widget (an entry, a label, a slider...) show the value of a ruby variable. each time a code mofify this variable, it must modify the widget, and vice-versa... This is very tyring :)

With data binding, this notifications are done by the framework

So DynVar can be used for representing a value variable which is dynamics, ie. which must notify widgets which show the variable state.

So we can do : ruby entry(foo) islider(foo) .... foo.value=43 ....

That works ! the entry and the slider will be updated.

A move on slider will update foo.value and the entry. Idem for a key in the entry : slider and foo.value will be updated.

if you want to be notified for your own traitment, you can observ a DynVar : ruby foo.observ { |v| @socket.puts(v.to_s) rescue nil }

Here, a modification of foo variable will be send on the network...

Warning !! the block will always be executed in the main thread context (mainloop gtk context). So DynVar is a ressource internal to Ruiby framework.

Widget which accept DynVar are : entry, ientry, islider, label, check_button,

must be extend to button, togglebutton, combo, radio_button ... list, grid,...

Dynamic Object

Often, this kind of Dyn variables are members of a 'record', which should be organised by an Ruby Object (a Struct...)

So DynObject create a class, which is organised by a hash : * packet of variable name * put initial value for each * each variable will be a DynVar

  FooClass=make_DynClass("v1" => 1 , "v2" => 2, "s1" => 'Hello...') "s1" => ) # default value of s1 variable is replaced
  label(" foo: ") ; entry(foo.s1)
  button("4x33") { { foo.s1.value="s4e33" ; foo.v2.value=33 ; foo.v1.value=4} }

Dynamic Stock Object

DynObject can be persisted to filesystem : use make_StockDynObject, and instantiate with an object persistant ID

  FooClass=make_StockDynClass("v1"=> 1 , "v2" => 2, "s1" => 'Hello...') "foo1" , "s1" => ) "foo2" , "s1" => ( )
  button("Exit") { ruiby_exit} # on exit, foo1 and foo2 will been saved to {tmpdir}/<$0>.storage  

make_StockDynObject do both : Class creation and class instanciation.

  foo=make_StockDynObject("v1"=> 1 , "v2" => 2, "s1" => 'Hello...')
  button(foo.s1) { foo.s1.value= prompt("new S1 value ?")}
  button("Exit") { ruiby_exit} # on exit, foo1 and foo2 will been saved to {tmpdir}/<$0>.storage  




see samples in "./samples" directory (run all.rb) cd sampSee at end of Doc reference : Ex.