« Ruby Glimmer Days 2021, January 26th to January 29th - 4 Days of Ruby (Desktop) Gems

Day 3 - glimmer-dsl-tk Gem - Script Widgets the Declarative Way (Say What, Not How) - Tk - The Best-Kept Secret and Evergreen Classic Now in v8.5 with Native Look ‘n’ Feel on Mac, Windows, and Linux

Written by AndyObtiva Andy Maleh

Software Engineering Expert from Montreal, Quebec. Creator of Glimmer and Abstract Feature Branch. Speaker at RailsConf, RubyConf, AgileConf, EclipseCon, EclipseWorld. Master in Software Engineering, DePaul University, Chicago. Blogs at Code Mastery Takes Commitment To Bold Coding Adventures. Snowboarder and Drummer.

What is Tk?

Tcl’s Tk has evolved into a practical desktop graphical user interface toolkit due to gaining truely native looking widgets on Mac, Windows, and Linux in Tk version 8.5.

Additionally, ruby 3.0 ractor (formerly known as guilds) supports truly parallel multi-threading, making both classic ruby and Tk finally viable for support in Glimmer (a language construction kit) as an alternative to Glimmer for the Standard Widget Toolkit (SWT) running on the java virtual machine (JVM).

The trade-off is that while the Standard Widget Toolkit (SWT) from Eclipse provides a plethora of high quality reusable widgets for the Enterprise (such as Nebula), Tk enables very fast app startup time and a small memory footprint via classic ruby.

Goals

Glimmer for Tk aims to provide a domain-specific language similar to the Glimmer for the Standard Widget Toolkit (SWT) to enable more productive desktop development in ruby with:

Girb - Glimmer Interactive Ruby (IRB)

You can run the girb command:

girb

This gives you irb with the glimmer-dsl-tk gem loaded and the Glimmer module mixed into the main object for easy experimentation with scripting widgets.

Glimmer Scripting Domain-Specific Language (DSL) Concepts

The Glimmer script language provides a declarative syntax for Tk that:

The Glimmer script follows these simple concepts in mapping from Tk syntax:

Example of an app written in Tk imperative (“say how”) syntax:

root = TkRoot.new
root.title = 'Hello, Tab!'

notebook = ::Tk::Tile::Notebook.new(root).grid

tab1 = ::Tk::Tile::Frame.new(notebook).grid
notebook.add tab1, text: 'English'
label1 = ::Tk::Tile::Label.new(tab1).grid
label1.text = 'Hello, World!'

tab2 = ::Tk::Tile::Frame.new(notebook).grid
notebook.add tab2, text: 'French'
label2 = ::Tk::Tile::Label.new(tab2).grid
label2.text = 'Bonjour, Univers!'

root.mainloop

Example of the same app written in Glimmer declarative (“say what”) syntax:

root {
  title 'Hello, Tab!'

  notebook {
    frame(text: 'English') {
      label {
        text 'Hello, World!'
      }
    }

    frame(text: 'French') {
      label {
        text 'Bonjour, Univers!'
      }
    }
  }
}.open

Two-Way Data-Binding

Glimmer supports bidirectional two-way data-binding via the bind keyword, which takes a model and an attribute.

Combo Data-Binding

Example:

This assumes a Person model with a country attribute representing their current country and a country_options attribute representing available options for the country attribute.

  combobox {
    state 'readonly'
    text bind(person, :country)
  }

That code sets the values of the combobox to the country_options property on the person model (data-binding attribute + “_options” by convention). It also binds the text selection of the combobox to the country property on the person model.

It automatically handles all the Tk plumbing behind the scenes, such as using TkVariable and setting combobox values from person.country_options by convention (attribute_name + “_options”).

More details can be found in the Hello, Combo! sample in the README..

List Single Selection Data-Binding

Tk does not support a native themed listbox, so Glimmer implements its own list widget on top of Tk::Tile::Treeview. It is set to single selection via selectmode ‘browse’.

Example:

This assumes a Person model with a country attribute representing their current country and a country_options attribute representing available options for the country attribute.

  list {
    selectmode 'browse'
    text bind(person, :country)
  }

That code binds the items text of the list to the country_options property on the person model (data-binding attribute + “_options” by convention). It also binds the selection text of the list to the country property on the person model.

It automatically handles all the Tk plumbing behind the scenes.

More details can be found in the Hello, List Single Selection! in the README.

List Multi Selection Data-Binding

Tk does not support a native themed listbox, so Glimmer implements its own list widget on top of Tk::Tile::Treeview. It is set to multi selection by default.

Example:

This assumes a Person model with a provinces attribute representing their current country and a provinces_options attribute representing available options for the provinces attribute.

  list {
    text bind(person, :provinces)
  }

That code binds the items text of the list to the provinces_options property on the person model (data-binding attribute + “_options” by convention). It also binds the selection text of the list to the provinces property on the person model.

It automatically handles all the Tk plumbing behind the scenes.

More details can be found in the Hello, List Multi Selection! sample in the README.

Label Data-Binding

Example:

This assumes a Person model with a country attribute.

  label {
    text bind(person, :country)
  }

That code binds the textvariable value of the label to the country property on the person model.

It automatically handles all the Tk plumbing behind the scenes.

More details can be found in the Hello, Computed! sample below.

Entry Data-Binding

Example:

This assumes a Person model with a country attribute.

  entry {
    text bind(person, :country)
  }

That code binds the textvariable value of the entry to the country property on the person model.

It automatically handles all the Tk plumbing behind the scenes.

More details can be found in the Hello, Computed! sample below.

Command Observer

Buttons can set a command option to trigger when the user clicks the button. This may be done with the command keyword, passing in a block directly (no need for proc as per Tk)

Example:

  button {
    text "Reset Selection"
    command {
      person.reset_country
    }
  }

This resets the person country.

More details can be found in the Hello, Combo! sample in the README.

Samples

Hello, World!

Glimmer code (from samples/hello/hello_world.rb):

include Glimmer

root {
  label {
    text 'Hello, World!'
  }
}.open

Run (with the glimmer-dsl-tk gem installed):

ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"

Glimmer app:

Hello, Computed!

Glimmer code (from samples/hello/hello_computed.rb):

# ... more code precedes
    root {
      title 'Hello, Computed!'

      frame {
        grid column: 0, row: 0, padx: 5, pady: 5

        label {
          grid column: 0, row: 0, sticky: 'w'
          text 'First Name: '
        }
        entry {
          grid column: 1, row: 0
          width 15
          text bind(@contact, :first_name)
        }

        label {
          grid column: 0, row: 1, sticky: 'w'
          text 'Last Name: '
        }
        entry {
          grid column: 1, row: 1
          width 15
          text bind(@contact, :last_name)
        }

        label {
          grid column: 0, row: 2, sticky: 'w'
          text 'Year of Birth: '
        }
        entry {
          grid column: 1, row: 2
          width 15
          text bind(@contact, :year_of_birth)
        }

        label {
          grid column: 0, row: 3, sticky: 'w'
          text 'Name: '
        }
        label {
          grid column: 1, row: 3, sticky: 'w'
          text bind(@contact, :name, computed_by: [:first_name, :last_name])
        }

        label {
          grid column: 0, row: 4, sticky: 'w'
          text 'Age: '
        }
        label {
          grid column: 1, row: 4, sticky: 'w'
          text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
        }
      }
    }.open
# ... more code follows

Run (with the glimmer-dsl-tk gem installed):

ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_computed.rb'"

Glimmer app:

Find Out More

References

Built with Ruby (running Jekyll) on 2021-07-25 15:15:02 +0000 in 0.371 seconds.
Hosted on GitHub Pages. </> Source on GitHub. (0) Dedicated to the public domain.