ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


Introducing REBOL with Amazingly Easy GUI Programming

by Gregg Irwin
10/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.

O'Reilly Emerging Technology Conference.

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 ]

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 ]

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

If you happen to understand French:

Gregg Irwin has been developing software since 1989, with a predilection for human-friendly languages (and interesting words).



Sponsored by: