Ruby Tk

Recently I wrote a lot code with Ruby and Tk. I just need to write some old fashioned GUI application. And being somewhere in 1/3 way with this work, what I can say?

This is my first time I’m using Tk so I’m newcomer to this land, and my first impressions are quite positive (YMMV). Maybe Tk powered GUI looks & feels a bit old-school, but its use is quite straightforward. Nevertheless, I had few struggles with some simple things, so I decided to start here new category Ruby/Tk and put here few examples. I do this since I had troubles with finding good examples to Ruby/Tk.

Maybe a book for a start?

First if You don’t have any prior experience with Tcl/Tk think about purchasing some good book about it. For it have worked very well. I went for Tcl/Tk: A Developer’s Guide by Clif Flynt, and was happy with this. I don’t need Tcl part, and Tk is described in big detail. Of course – this is Tcl/Tk description, not it’s Ruby bindings.

Ruby and Tk – real example

So let’s start with something simple. Window which will allow run ruby code. I needed this to inspect my application status, it was much simpler than using debugger or implementing DRb client.

Code goes here:

require 'tk'

$root = TkRoot.new( :title => "Tk Example", 
	:width => 300, :height => 100)

Tk.root.bind( TkVirtualEvent.new('Control-c', 'Control-q'), 
	proc{Tk.exit})

label = TkLabel.new{ text "Command:" }
label.place(:x => 10, :y => 10)
label.font= TkFont.new(:size => 6)

entry = TkEntry.new
var = TkVariable.new("puts 'test'")

entry.textvariable(var)
entry.place(:x => 10, :y => 30)
entry.bind("FocusOut") { eval var.value }

button = TkButton.new { text "Run" }
button.place(:x => 10, :y => 52)
button.bind("ButtonPress") { eval var.value }

Tk.mainloop

In lines 3-4 we create root window. Code is self explanatory ;) In line 6 and 7 we create virtual event and associating it with lambda calling Tk.exit, or in other words destroys Tk application. TkVirtualEvent allows DRY, since it binds to Control-c or Control-q sequences.

Lines 9-11 create label with word Command on it. Most of objects can be initialized like $root in line 3 or like label in line 9 – code block or function call with hash of args. Line 9 in hash version would be:

label = TkLabel.new( :text => "Command:" )

In line 11 we set non-default font size. It is possible (I mean no error warning, because duck still quacks) to try label.font.size = 6, but it wont give expected result. You need to create new font object with size property.

I use place method to put object on window, most examples uses pack. The latter arranges objects with some built-in magic/logic, but in my application I needed strict control over layout and overlapping objects. This can provide only place method.

In lines 13-14 we create two objects – entry for type in command and variable which will hold it contents. Of course You could create just entry object and have possibility to write something in it, but I don’t know any way to get contents from this entry without TkVariable associated with entry.

This association is made in line 16. Then we place it on window (17) and assign code when entry loses focus. It just evals code typed in this entry. Simple. But can be tricky. Run example, write some code in entry, or just leave default, place cursor in entry field and switch activie window to other application. Surprised? Well… Technically, when You have switched windows entry filed lost focus so code was evaluated…

So maybe we need other way of evaluation – after pressing button with Run. This button is created, placed and code evaluation is assigned in lines 20-22. Nothing new here. Almost. ButtonPress needs more attention. bind can take as argument:

  • alphanumeric – single character (or punctuation, digit, other) which defines what key need to be pressed to fire code binded with this statement
  • virtual event – defined with TkVirtualEvent (we have done it on begging when we wanted provide alternate exit)
  • mod-type-detail or just strict definition what combination events need to happen to fire code. mod – could be B1-5 (mouse buttons), Alt, Control, Shift, Double, Triple. Type could be ButtonPress, ButtonRelease, Motion, FocusIn/Out, KeyPress, KreyRelease. Detail depends on type field. Not all combination are valid. For example change ButtonPress in line 22 for Double-Shift-ButtonPress and You will need to double-click Run button with shift pressed to activate code from entry. ButtonPress itself fires with any mouse button pressed. When we want double-right-click with shift we need to use code>Double-Shift-ButtonPress-3 (button 2 is middle button)

More about arguments for bind You can read in documentation for Active State Tcl/Tk implementation (which is free BTW, so You know where to go for Tk implementation if You want to give it a try).

And last but not least in line 24 we start Tk main loop, from now our GUI is alive and kicking. This example is quite simple but demonstrates few issues I had problem with. Maybe it will be useful for other.

Stay tuned, in future posts – drag&drop layout changes, schedule command execution in future and more ;)

Join the Conversation

1 Comment

  1. Pingback: Super City

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.