Skip to content

glurp/dsl-gtk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ruiby

Build Status Gem Version AppVeyor

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

Resources

Code: http://github.com/glurp/Ruiby

Doc: Reference+Exemples.

Gem : https://rubygems.org/gems/Ruiby

Based on Ruby-Gmome2 :

Status

NEW : 3.24.0 !! 28-11-2017 : Linear Gradient on canvas draw surfaces primitives

Now, plotters and dashboard can have beautiful background :)

TO DO :

  • improve graphics quality on canvas : linear gradient
  • improve graphics quality on canvas : radial gradient
  • improve graphics quality on canvas : transparency
  • refactoring samples demos with last improve: dynvar/autoslot...
  • resolve 100% gtk3 deprecated warning
  • complete rspec => 99% coverage ?

Abandoned :

  • gadget API

Installation

  1. system

Install Ruby 2.x # x>1

  1. install Ruiby (gem install Ruiby will install ruby-gtk3 which install gtk3 libs)
> gem install Ruiby

Test it:

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

Here a working gem config on windows (25-Sept-2016, Ruby 2.3.3p222) :

  did_you_mean-1.0.0
  pkg-config-1.2.3
  native-package-installer-1.0.4
  cairo-1.15.9
  glib2-3.1.8
  gobject-introspection-3.1.8
  gio2-3.1.8
  atk-3.1.8
  cairo-gobject-3.1.8
  pango-3.1.8
  gdk_pixbuf2-3.1.8
  gdk3-3.1.8
  rsvg2-3.1.8
  gtk3-3.1.8
  Ruiby-3.23.0
  gtksourceview3-3.1.8

Usage

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

By inherit:

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

By include, calling ruiby-component() :

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

Autonomous DSL, for little application (most of demo in samples/ are done with this pattern) :

require  'Ruiby'
Ruiby.app do
	stack do
		. . .
	end
end

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) }

Require

Simple usage with gtk3 :

require 'Ruiby'

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

require 'em-proxy'
require 'Ruiby'

Warning : EM.run 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):

Ruiby.app do
  ....
  after(0) { EventMachine::start_server().. { ... } }
end

See samples/spygui.rb, for example of GUI with EM.

Threading

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 , messages are proc to be instance_eval() in the main window context
  • everywhere, a thread can invoke invoke_gui {ruiby code}. this send to the main queue the proc, which will be evaluated asynchronously

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)
		Thread.new { A.new.run }
    end
	def component()
	  stack do
		sloti(label("Hello, this is Thread test !"))
		stack { @lab=stacki { } }
	  end
	end # endcomponent

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

Ruiby.start { App.new }

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 modify this variable, it must modify the widget, and vice-versa... This is very tiring :)

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 :

  foo=DynVar.new(0)
  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 treatment, you can observe a DynVar :

  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 resource 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 organized by an Ruby Object (a Struct...)

So DynObject create a class, which is organized 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...')
  foo=FooClass.new( "s1" => Time.now.to_s ) # default value of s1 variable is replaced
  ...
  label(" foo: ") ; entry(foo.s1)
  islider(foo.v1)
  islider(foo.v2)
  ....
  button("4x33") { Thread.new { foo.s1.value="s4e33" ; foo.v2.value=33 ; foo.v1.value=4} }
  ....

Dynamic Stock Object

DynObject can be persisted to file system : use make_StockDynObject, and instantiate with an object persistent ID

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

make_StockDynObject do both : Class creation and class instantiation.

  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  
  ....

Component

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

Sometime, this is not good enough :

  • when a component must have many specific methods
  • when component have (model) state : variable member must be used

So Component concept has been added (Fev 2016).It authorize to define a class, child 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:

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 component of class AAA:

module Ruiby_dsl
  def aaa(*args)
    c=install_composant(self,AAA.new(*args))
  end
end

Use the component:

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

A demo is at samples/composant.rb.

TO-DO:

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

License

Ruiby : LGPL, CC BY-SA

fafamfam rasters images : CC Attribution 4.0 http://www.famfamfam.com/

Crystal Clear icon set : LGPL

Farm Fresh icon set : CC Attribution 3.0 License http://www.fatcow.com/free-icons

Exemples

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

About

DSL for make a simple ruby GUI application

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published