Introducing REBOL with Amazingly Easy GUI Programming
by Gregg Irwin10/30/2003
Editor's Note: In the discussion on What I Hate About Your Programming Language, several people brought up less widely-used programming languages. While space precludes covering every language under the sun, studying how different languages approach similar problems is often very enlightening.
I like REBOL for many reasons. In this article I'll focus on one, the integrated GUI system, and I'll show you how to build a front end for a small email-based survey and feedback system.
Why an email rather than a web system?
- You don't have to set anything up on a server (or even have a server available).
- Users don't have to go to a web site and fill out an HTML form.
- It's a convenient way to do polls on mailing lists. How many times have you been part of a mailing list thread that reached the "let's take a vote" stage, and people responded, but nothing was done with the results?
Here's how such a system works. You send mail containing a script that the user runs. That script sends mail back to you with the results. A little POP reader consumes incoming mail, identifies responses, and writes out the data for processing. Finally, a local script runs against that data, tallying results. REBOL can be used to build all those pieces, but we'll only cover the user script here.
To run the code from this article, you will need to have REBOL/View installed on your system. It can be downloaded from the REBOL Technologies View Download Page.
There are some code examples in the article that you can type directly in the REBOL console; they are preceded by the standard REBOL prompt (>>). For example:
>> help print
>> help +
>> help req
>> help function!
From the View Desktop
you can get to the console by clicking on the Console icon in the bookmarks
panel on the left. To return to the desktop, type desktop in the
console.
The interactive console and the help function are valuable
tools for learning REBOL. Rather than explain each REBOL function or word as I
go, I'll leave it to you to use help in the console to get
information on those you're unfamiliar with.
GUIs in REBOL
REBOL GUIs are built with a specialized dialect called VID (the Visual
Interface Dialect). As a dialect--sometimes called a domain specific or
embedded, language--VID inherits much of the flavor of REBOL while
providing a more convenient syntax for its intended purpose. Here's how it
works. You define your GUI, including actions and custom styles, using VID.
You then call the layout function which parses your GUI
specification and creates the GUI structure. Finally, you call the
view function to display it and start handling events.
The following code will display a window with a button that says "Hide". Clicking the button will hide the window.
>> view layout [button "Hide" [unview]]
You could also have the button shut down the program completely, like this:
>> view layout [button "Quit" [quit]]
Note that if you call quit from a layout created in a console
session, as we did here, it will shut down your console session, just as if you
had typed quit or q at the prompt.
VID is extremely flexible, allowing you to create almost any kind of look and feel you want; the underlying graphics engine in REBOL is a full 24-bit compositing system with alpha-channel support. You can create custom styles (a.k.a. widgets or controls) easily, even writing your own GUI dialect to replace or enhance VID. Just as importantly, VID is designed so you can omit unnecessary details in many cases, making GUI specs small and easy to define.
Having the GUI system, networking protocols, and other features built in makes it possible to write useful one-liners and work easily in the console, without having to import various support modules. It also makes it much easier to deploy scripts because you don't have to worry about what your users have installed, and they don't have to worry about downloading other things to make your code work. That's important for an application like this.
VID uses a textual description of a graphical layout, which can be a big advantage. For example, in our mail-based survey system you can include the entire script, GUI definition and all, in the body of the message. That's handy for mailing lists that don't allow attachments. If you need binary data, such as images, it's easy to embed them in a REBOL script as Base-64 encoded data and decode them when the script runs.
REBOL doesn't have a visual layout tool built in, but for those of you that think you can't live without a graphical layout editor, I moved to REBOL after 11 years as a VB specialist, for whatever that's worth. There is a fairly complete layout editor example, written by REBOL's designer, available in the script library that shows what about 36K of REBOL can do.
Everyone I know writes VID specs directly. There are at least two good reasons for this. First, VID is smart about aligning and spacing things automatically, so you don't have to fill your layout spec with fixed offsets for faces. This can make your specs smaller and more flexible than what a visual layout editor might generate. Second, it's easy to generate dynamic layouts in REBOL. A script that uses data to generate a layout opens up a whole new world of possibilities, as you'll see shortly.
The User Script
The survey script we're going to send out should be a complete program. It needs to contain all the data and functionality necessary to display a GUI to the user, collect the data they enter, and mail it back to us.
Following are two GUI examples, one static and one dynamic, that could be adapted for almost any subject just by changing the data the user sees.
The Common Pieces
Some pieces of code and data are used by both GUIs: where to send the responses, what items you want listed for the user to choose from, a function to send responses, and a function to view what data will be sent back.
; Where we want the responses sent. You can decide if you
; want to put the address in directly, do a slight tweak to
; confuse harvesters, or use full-blown encryption.
address: head reverse gro.mca@niwriggerg
; Our sample surveys deal with programming languages,
; so we'll define a list of them for later use.
languages: [
REBOL Java VB Perl Python Ruby Tcl PHP C C++ C-sharp
Delphi Smalltalk Lisp COBOL
]
; This is the function that actually sends us the reply.
reply: func [address survey-name data] [
if error? set/any 'err try [
send/subject
address
data
join survey-name " survey response!"
return true
][
alert mold disarm err
return false
]
]
Our reply function uses the REBOL send function,
which handles all the SMTP functionality for us. SMTP support, along with most
other standard Internet protocols, is built into REBOL.
; VIEW-DATA is mainly for testing, but if you want to
; let the users preview what's being sent back, you can
; include it for them as well.
view-data: does [
view/new layout [
area 500 mold gather-data
button "Close" [unview]
]
]
Static GUI Version
There are two common but different elements in both systems. The first is the name of the survey. The second is the routine that gathers data from the screen.
; We need to give our survey a name so we can identify
; responses for it.
survey-name: "Static GUI Survey"
; This gathers information from the screen.
gather-data: does [
reduce [lang-list/picked comments/text]
]
Now we get to the GUI itself. In the static version, we'll display a list that lets users select one or more languages (to indicate which ones they use) and a text box for comments. Parsing and evaluating those comments is something REBOL is well suited for but is beyond the scope of this article.
view layout [
H3 400 {
Just a little informal survey to see what
languages you use and how you feel about them.
}
text 400 brick {
Select the languages you use below (ctrl+click),
enter comments in the text box, and press Send.
} guide
lang-list: text-list 85x245 data languages
return guide
comments: area 310
text 310 navy {
Comments can be things such as: "I like lang-x",
"I have to use lang-x", "lang-x is insanely
great!", "lang-x is really the best", or "I love
lang-x"
} return
across
pad 0x5
button "Send" [reply address survey-name gather-data]
button "Quit" [quit]
button "View Data" [view-data]
]
This simple layout uses several VID styles (H3,
text, text-list, area, and
button), a few layout attributes and spacing commands
(return, guide, across,
pad), overrides a couple default colors and sizes, loads our list
of languages into the text-list, binds action blocks to the buttons, and sets
lang-list and comments to refer to UI elements for
data gathering purposes. Not bad for about 20 lines of code.
Here's the result:
![[ the static GUI ]](/onlamp/2003/10/30/graphics/static-gui.gif)
Figure 1 — the static GUI.
Dynamic GUI Version
Static GUIs are fine in many cases, but sometimes you must create dynamic screens based on data you define or from search results. REBOL is terrific for data-driven work, whether you're building VID layouts as we are here, or in a Web context. We'll next build a GUI that has a label and two sliders for each language in our list; one slider for how often you use a language, and one for how much you like it.
I'll explain each piece as we go, but I'll also warn you now that this is a bit advanced. That should come as no surprise if you've ever built a dynamic GUI in any language.
The survey name is almost the same as before, but make-lbl-name
is a new function.
; We should give our survey a name so we can identify
; responses for it.
survey-name: "Dynamic GUI Survey"
; This is a support function that helps reduce redundancy
; in the dynamic name-generation aspects of our GUI.
make-lbl-name: func [lang [word!] type [datatype!]][
to type join 'lbl- lang
]
make-lbl-name is easy to understand on its own, but how we use
it may not be so clear. As we build our GUI dynamically, we'll "name" certain
elements so we can refer to them later; we're basically creating named
variables on-the-fly. That's the job of make-lbl-name. We give it
the name of a language from our list and the type of word to create. A
set-word! type will set the value of the variable and a
word! type will evaluate (get) the value of the variable. We need
to set the values when building the GUI and get them when gathering data.
You can type or paste make-lbl-name into the console to see how
it works.
>> make-lbl-name 'REBOL set-word!
== lbl-REBOL:
>> make-lbl-name 'REBOL word!
== lbl-REBOL
gather-data is a bit trickier here than in the static version.
When we generate the GUI, each programming language will have a text label and
two sliders associated with it, giving us three UI elements (faces in
REBOL parlance). During UI generation, the label gets a name, which we use to
find it again later. To gather data, we find the face that is the label for a
language, and we know that the next two faces are the sliders for that
language.
; This gathers information from the screen. Our GUI has
; many dynamically generated elements; this approach uses
; a named face as a marker and finds related faces from
; its position.
gather-data: has [result pos sld-1 sld-2] [
result: copy []
foreach lang languages [
pos: find lay/pane get make-lbl-name lang word!
sld-1: first next pos
sld-2: first next next pos
append result reduce [lang sld-1/data sld-2/data]
]
result
]
This GUI is not entirely dynamic; it's a hybrid, with three phases to its
development. First, we define static labels at the top. Next, we generate the
dynamic labels and sliders for each language. Finally, we tack on the static
part defining the buttons. The important thing to note here is that our layout
spec is just a block of data passed to the layout function. We can
manipulate it at will before calling layout. Once
layout renders it, we can still change things, but by accessing
the faces created instead of the spec that defined them.
lay-spec: [
across space 4x4
backdrop effect compose [
gradient 1x0 (sky - 40) (water)
grid 0x46 0x14 (sky - 40) 0x2
]
hdr: h3 400 {
Just a little informal survey to see what
languages you use and how you feel about them.
}
return
text 115 "How much do you..."
text 125 center "Use it"
text 125 center "Like it"
return pad 0x5
style sld slider 125x16
]
; Dynamic labels and sliders (using sld style above)
foreach lang languages [
append lay-spec compose [
(make-lbl-name lang set-word!) text 115 (form lang)
sld sld return
]
]
; Static buttons
append lay-spec [
pad 0x5
button "Send" [reply address survey-name gather-data]
button "Quit" [quit]
button "View Data" [view-data]
]
;print mold lay-spec ;<< Uncomment to see generated spec
view lay: layout lay-spec
Here's the result:
![[ the dynamic GUI ]](/onlamp/2003/10/30/graphics/dynamic-gui.gif)
Figure 2 — the dynamic GUI
Compared to our static GUI, we now have about 50 faces on the screen which
give us more specific information about each item in our survey. This costs
about 15 lines of code more than the the static GUI. Four of those extra lines
are for the arguably gratuitous backdrop to make it look a little more
interesting. make-lbl-name and gather-data account
for almost all the rest. Lines of code is a bad metric though; REBOL is a
free-form language, and some of the formatting here was done to match the
ONLamp format. The point is that the extra work is almost all design related,
not code.
Going Further
Both scripts can be run by anyone who has REBOL/View installed, on any supported operating system, without the need for any external UI toolkits or OS-specific modules. You could extend or enhance them by using other REBOL styles such as check boxes, radio, and toggle buttons, or rotary selectors to suit your needs. With REBOL Technologies SDK you can also turn your scripts into standalone EXEs requiring no installation, at the cost of binding them to the REBOL interpreter-meaning they are about the same size as REBOL itself.
The other pieces of an email-based survey system could easily be written in REBOL as well. With support for SMTP and POP built in and terrific text and data processing abilities, it should be a snap.
Conclusion
REBOL/View is a quick download (about 340K as a ZIP archive), easy to install (just put the EXE somewhere and run it), and runs on a wide range of platforms. Its built-in GUI system and support for networking protocols make it a good choice for net-related applications where size and easy deployment are factors.
Related Links
- http://www.rebol.com
- http://www.rebol.org
- http://www.rebolforces.com
- http://www.compkarori.com/vanilla/
- http://www.shlik.org
If you happen to understand French:
Gregg Irwin has been developing software since 1989, with a predilection for human-friendly languages (and interesting words).