Rebol Programming Tutorial - A Concise Overview
By: Nick Antonaccio Updated: 3-26-2008 Learn to program quickly, easily, and productively in the Rebol development environment. Be sure to see the Rebol YouTube video tutorial at http://musiclessonz.com/rebol_video_links.html
Contents:
1. Introducing Rebol
1.1 Quick Overview
1.2 An Amazing Tour of Rebol Features and Benefits
2. Getting Started: Installing Rebol, Hello World, and 5 Short Examples
3. A Quick Summary of the Rebol Language
3.1 Built-In Functions and Basic Syntax
3.2 More Basics: Word Assignment, I/O, Files, Built-In Data Types and Native Protocols
3.3 Built-in Help and Resources
3.4 GUIs
3.5 Blocks and Series
3.6 Conditions
3.7 Loops
3.8 User Defined Functions
4. More Useful Topics
4.1 Binary Embedding and Compression
4.2 Running Command Line Applications
4.3 Saving and Running Rebol Scripts
4.4 "Compiling" Rebol Programs - Distributing Packaged .EXE Files
4.5 Responding to Special Events in a GUI
4.6 Common Errors
5. Examples
5.1 Little Email Client
5.2 Simple Web Page Editor
5.3 Simple Menu Example
5.4 FTP Chat Room
5.5 Image Effector
5.6 Guitar Chord Diagram Maker
5.7 Listview Database Front End
5.8 Peer-to-Peer Instant Messenger
5.9 Textris
6. Additional Topics
6.1 2D Drawing, Graphics, and Animation
6.2 Using Animated GIF Images
6.3 3D Graphics with r3D
6.4 Multitasking
6.5 Using DLLs and Shared Code Files in Rebol
6.6 Web Programming and the CGI Interface
6.7 Rebol as a Browser Plugin
6.8 MySQL and databases
6.9 Parsing
6.10 Objects
6.11 Rebcode
6.12 Useful Rebol Tools
6.13 6 Rebol Flavors
6.14 Learning More About Rebol
1. Introducing Rebol
1.1 Quick Overview
What is Rebol? What does it do? Who might benefit from learning it? What's so great about it? Here's a quick overview:
- Rebol can be thought of as: (1) a rich development environment for the creation of desktop, web site, and distributed network applications (2) a light scripting language, easily learned by any average computer user, and (3) a simple multi-platform utility application that looks and operates the same way on over 40 operating systems.
- Rebol can be used as a framework for desktop software development and for distributed network application development, for web site development using the CGI interface and/or PHP-like server pages, and even to create applications that run in a browser, using one of several plugin versions of the interpreter (i.e., like flash). To accomplish tasks that typically require widely varied tools such as Visual Basic, PHP, Perl, Javascript, Flash, C, C++, Python, Java, Ruby, wxWidgets, and a variety of utility applications, Rebol covers the same ground with one simple and consistent paradigm. Despite its broad usefulness, from system development to web page scripting, Rebol is easier to implement than any other comparable tool.
- Rebol can be used immediately, without installation, as a lightweight file manager, text editor, calculator, email client, ftp client, news reader, image viewer/editor, and more.
- Rebol includes GUI, network, graphics, sound, image editing, data management, math, parsing, compression, CGI decoding, secure network services, and other functions built-in. No external modules, tool kits, or IDEs are required for any essential functionality. Other features such as a built-in text/code editor and a graphical browser for Rebol's online repository of open source applications are also built-in.
- Rebol is ultra compact. Its uncompressed file size is about 1/2 Meg on most platforms. It can be downloaded, installed, and put to use on all supported operating systems in less than a minute. No other comparably powerful tool is as small or as easy to get working.
- Rebol is easy enough for absolute beginners and average computer users to operate immediately, but powerful and rich enough for a wide variety of complex professional work.
- Rebol has useful built-in help for all available functions and language constructs.
- Rebol is available in both free and supported commercial versions. The free version can be used to create commercial applications, with very few license restrictions.
- Rebol has a facility for ultra fast performance using "Rebcode", which can be optimized like assembly language, but works the same way across all supported hardware and operating systems (using the exact same code).
- Rebol is a modern tool for application development, supporting elements of object oriented design and other organizational approaches. Do to its dialecting approach, Rebol code is typically much shorter and more readable than language implementations in other development environments. Rebol is often dramatically more productive than other development tools. There's absolutely no simpler solution for cross-platform GUI creation, anywhere (you can create complete, functional graphic applications with 1 line of code). But that just scratches the surface. Rebol's ability to simplify other difficult computing tasks with dramatically small and usable code dialects is applicable to virtually every area of computing. Only the most basic understanding of Rebol language syntax, and very short code snippets, are required to accomplish difficult objectives using high level code dialects. Because the syntax required to use potential Rebol dialects is so simple, the learning curve required to get real work done in Rebol is much easier than other development environments.
- The storage/manipulation/transfer of all data is managed by a single, simple, ubiquitous code structure (arrays, lists, tables, and other structures of text and binary data are all simply stored as "blocks"). Common network protocols and data values are also natively usable in Rebol without any preparation by the programmer. No other language has such straightforward mechanisms for accomplising the most basic work of computers - manipulating, storing and retrieving data.
- Rebol is small, practical, portable, extremely productive, and different than the typical mess of modern computing tools.
1.2 An Amazing Tour of Rebol Features and Benefits
(Skip this section if you want to jump right into coding with Rebol. The following paragraphs simply explain in detail what Rebol is, and what the main reasons are to learn and use it).
Rebol is an extraordinary tool for software development and for general computing productivity, created by Carl Sassenrath. It provides a single, simple, multi-platform solution for a wide variety of common computing tasks. Absolute beginners can use the Rebol interpreter immediately as a lightweight file manager, text editor, calculator, email client, ftp client, news reader, image viewer/editor, and more. Average computer users can learn Rebol's super-simple scripting language to quickly create small customized programs that accomplish personally useful and interesting computing tasks. Professionals can use Rebol as a robust and uncompromisingly productive development environment for demanding commercial work. Rebol can be used to build real-world desktop and web site applications of all types, with modern graphics, network functionality, database connectivity, and much more. With unique versatility, Rebol performs equally well as a desktop development environment for PC, Mac, Linux, BSD, Windows CE, QNX, Solaris, and other operating systems (use it to create typical windowed user programs), as an architecture for distributed LAN software (use it to create software that communicates across a network, allowing multiple users to work with shared data, to create networked games, etc.), as a tool for rich in-browser application development (the Rebol browser plugin allows complete graphical desktop applications to run immediately in a web browser (i.e., as part of a web page)), and as a CGI solution for web site development on just about any platform (use it to make your web site dynamically interactive, to process and display data as needed by each unique visitor, to manage email, to keep pages updated, etc.). Learning to write simple Rebol scripts requires about as much effort as learning to use a typical piece of modern user software. But with greater potential functionality than any single user application, Rebol can easily replace a hard drive full of bloated software with a single lightweight program that allows you to do things with your computer, your network, and your web site, when you can't find the required functionality in existing pre-made software packages. Using Rebol to achieve specific personal computing goals leverages your future effort and time by enabling a powerful development tool that is more exactly configurable to your unique productivity requirements and creative interests. It's a great way to get started with software development, and it's a uniquely productive and versatile tool for experienced coders.
Rebol's blend of capability, compact size, ease of use, and cross-platform functionality make it a remarkably versatile solution for a broad scope of computing problems. Even more significant is the efficient and effective way it embodies and implements those solutions. The free Rebol interpreter is trivially small to download (<600k on most operating systems) and can be used immediately, with or without installation (it's also available as a plugin for popular browsers). Rebol code works identically on over 40 operating systems, and with fewer gotchas than other cross-platform computing solutions. Rebol's multimedia functions are easy to use, and it's built-in graphic tools are powerful enough to even handle 3D animation without any external requirements (no OpenGL, DirectX, etc. needed). Common network protocols and data types are natively usable in Rebol without any preparation by the programmer, and the storage/manipulation/transfer of all data is managed by a single, simple, ubiquitous code structure (arrays, lists, tables, and other structures of text and binary data are all simply stored as "blocks"). No other language has such a straightforward mechanism for accomplising the most basic work of computers - manipulating data. Data processing of all types is easy to perform with Rebol's versatile, native list management functions. Built-in help is available for any language construct, and the interpreter itself functions as a graphic client to Rebol's online repository of shared code (hundreds of useful applications and their source codes are immediately available directly within the interpreter).
Rebol handles familiar, traditional programming techniques, and adds some concise coding facilities that dramatically increase user productivity. Rebol is designed to allow for a new paradigm in natural language dialect ("DSL") creation, which is a powerful approach to simplifying common tasks with short syntax patterns. The built-in parsing, drawing, image editing, and GUI dialects are uniquely elegant and simple-to-use realizations of that ability. In fact, there's absolutely no simpler solution for cross-platform GUI creation, anywhere (you can create complete, functional graphic applications with 1 line of code). But that just scratches the surface. Rebol's ability to simplify other difficult computing tasks with dramatically small and usable code dialects is applicable to virtually every area of computing. Only the most basic understanding of Rebol language syntax, and very short code snippets, are required to accomplish difficult objectives using high level code dialects. Detailed control of underlying code is always accessible to advanced users. As a result of the ability to build upon layers of complexity using unusually simple syntax structures, Rebol code for just about any given task tends to be much shorter and more readable than that of other development tools.
Rebol is among the easiest scripting languages to get started with, but despite the quick learning curve and compact file size, it is not a limited learning environment or a simple software toy. It is a deep, rich, and powerful tool, suitable for a broad scope of industrial work. At the far end of the usability spectrum, Rebol's low level "rebcode" VM acts as a sort of cross-platform assembly language that can dramatically improve the processing speed of computational tasks which benefit from tight optimization. Rebol allows for elements of object oriented (prototype based) design and other modern organizational approaches, but it's really the practical, useful value of Rebol's whole package of features that is it's greatest strength. In many cases, Rebol is versatile and powerful enough to gracefully replace such favorite tools as Python, Perl, Visual Basic, PHP, Java, Ruby, C, C++, Javascript, Flash, wxWidgets and related frameworks, as well as a broad variety of utility applications for users. Because Rebol can work on just about any machine as a client or server framework, as a user interface framework, as an in-browser application framework, as a web site scripting solution, etc., it covers much of the same ground as those other tools. Yet, all its features exist inside one tiny downloadable executable that anyone can get running, on just about any computer, in less than a minute. Because so many essential computing elements all interact natively and naturally without requiring any third party toolkits, even average computer users with absolutely no coding experience can learn to create real, useful and powerful Rebol applications in a day.
To be productive with just one simple paradigm that efficiently handles virtually all modern computing problem domains, Rebol is a unique and refreshingly straightforward solution. Whether you're new to programming, or if you've struggled to be productive with other development tools, or if you've ever spent too much time integrating numerous pieces of software just to complete a simple computing task, or if you've ever found the language and/or data syntax of other development environments to be unwieldy, or if you've ever been frustrated by the slow development cycle, lack of portability, large download size, or diverse bloated IDEs of other modern tools, then you'll love Rebol. If the functional features don't pique your curiosity, Rebol is also interesting to study simply because it's ingenious, thought provoking, and different than other modern development technology. Rebol is easy to learn and use, small, practical, portable, and productive. Especially for simple projects that should be easy to complete, to get real computing jobs done, Rebol just works.
This tutorial demonstrates essential elements of Rebol programming in a concise format. For those who are interested, this document provides a working knowledge of Rebol's main features in less than 100 pages. If you're an absolute beginner who can't imagine "programming" your own applications, see Rebol Programming for the Absolute Beginner. It covers many of the same examples as this text, but with more explanation of each topic and a less technical learning curve. That tutorial also includes an in-depth section of case studies intended to teach beginning coders how to think more fundamentally about approaching any general programming task. If you're not sure where to start, try this text first, then pick up the absolute beginners tutorial above if you need additional guidance and perspective.
2. Getting Started: Installing Rebol, Hello World, and 5 Short Examples
The Rebol interpreter is a program that runs on your computer. It translates written text in the Rebol language syntax ("source code") to instructions the computer understands. To get the free Rebol interpreter for Microsoft Windows, go to http://rebol.com/view-platforms.html and download the view.exe file for Windows - just click it with your mouse and save it to your hard drive. If you want to run Rebol on any other operating system (Macintosh, Linux, etc.), just select, download and run the correct file for your computer. It works the same way on every operating system. You can use the stand-alone versions on just about any desktop machine, and also on your web server. Upload the correct interpreter version to your web server and you can execute Rebol programs directly on your web site. You can also install a plugin version to run Rebol code directly on pages in your web browser.
Once you've got the Rebol interpreter downloaded and running on your computer, click the "Console" icon, and you're ready to start typing in Rebol programs. To create your first program, type the following line into the Rebol interpreter, and then press the [Enter] (return) key on your keyboard:
alert "Hello world!"
Before going any further, give it a try. Download Rebol, and type in the code above to see how it works. It's simple and it only takes a moment. To benefit from this tutorial, type or paste each code example into the Rebol interpreter to see what happens.
To whet your appetite, here are 5 tiny GUI programs that demonstrate just how potent Rebol code is. The first example is a fully functional web page editor. You can use it to edit html pages and other text files on any web site ftp server:
view layout [
h1 "Enter your ftp info, then click 'load' to download and edit a file:"
p: field 600 "ftp://user:pass@website.com/public_html/filename.html"
h: area 600x440 across
btn "Load" [h/text: read (to-url p/text) show h]
btn "Save" [write (to-url p/text) h/text]
]
Here's a classic graphic sliding tile game:
view center-face layout [
origin 0x0 space 0x0 across
style p button 60x60 [
if not find [0x60 60x0 0x-60 -60x0]
face/offset - empty/offset [exit]
temp: face/offset face/offset: empty/offset
empty/offset: temp
]
p "A" p "B" p "C" p "D" return p "E" p "F" p "G" p "H" return
p "I" p "J" p "K" p "L" return p "M" p "N" p "O"
empty: p 200.200.200 edge [size: 0]
]
Here's little painting program:
view layout [
h1 "Paint with the mouse:"
scrn: box black 400x400 feel [
engage: func [face action event] [
if find [down over] action [
append scrn/effect/draw event/offset show scrn
]
if action = 'up [append scrn/effect/draw 'line]
]
] effect [draw [line]]
btn "Save" [
save/png %/c/painting.png to-image layout [
origin 0x0 box black 400x400 effect pick get scrn 9
] alert "Saved to C:\painting.png"
]
btn "Clear" [scrn/effect/draw: copy [line] show scrn]
]
Here's a short program that uses Rebol's parsing and networking abilities to display the current WAN and LAN IP addresses of your computer:
parse read http://whatsmyip.org/ [thru <title> copy my-ip to </title>]
parse my-ip [thru "Your IP is " copy stripped-ip to end]
alert to-string rejoin ["WAN: " stripped-ip " ---- LAN: "
read join dns:// read dns://]
Here's a little email client you can use to read and send emails to/from any pop/smtp server (edit the first line so it contains your personal email account info):
set-net [user:pass@website.com smtp.website.com pop.website.com]
view layout[
h1 "Send:" a: field "user@website.com" s: field "Subject" b: area
btn "Send"[
send/subject to-email a/text b/text s/text alert "Sent"
]
h1 "Read:" m: field "pop://user:pass@website.com"
btn "Read"[editor read to-url m/text]
]
Try pasting those examples into the Rebol interpreter to see what just a little Rebol code can do (those programs take up a total of less than 1 printed page of code). Before the end of this tutorial you'll know exactly how they all work, and much more...
3. A Quick Summary of the Rebol Language
3.1 Built-In Functions and Basic Syntax
To use Rebol, you need to learn how to use "functions". Functions are words that perform actions. Function words are followed by data parameters ("arguments"). Paste these functions into the Rebol interpreter to see how they work:
alert "Alert is a function. This text is its parameter."
request "Are you having fun yet?"
editor "Edit this text."
browse http://rebol.com
Some functions don't require any data parameters, but do produce "return" values. Try these functions in the interpreter. They each return a value entered by the user:
request-pass
request-date
request-color
request-file
Many functions have optional or limited parameters/return values. These options ("refinements") are specified by the "/" symbol:
request-pass/only
request-pass/user "username"
request-pass/title "The 'title' refinement sets this header text."
request-pass/offset/title 10x100 "'offset' repositions the requester."
Some functions take multiple arguments. The "rejoin" function returns the joined ("concatenated") text arguments inside brackets:
rejoin ["Hello " "there" "!"]
IMPORTANT: Return values from one function can be used as arguments for other functions. Here, the value returned by the "rejoin" function is passed as a parameter to the "alert" function:
alert rejoin ["Hello " "there" "!"]
Here, the value returned by the "request-text" function is passed as an argument to the "editor" function:
editor request-text
Any number of functions can be written on a single line, with return values cascaded from one function to the next:
alert rejoin ["You chose: " request "Choose one:"]
Parentheses can be used to clarify which expressions are evaluated and passed as parameters to other functions. The parenthesized line below is exactly the same as the line above:
alert (rejoin ["You chose: " (request "Choose one:")])
Understanding the above line is important - it's typical of Rebol language syntax. There are three functions in that line: "alert", "rejoin", and "request". The "alert" function is the first to be executed ("evaluated"). It's purpose is to display a message, and that message is contained in a parameter that immediately follows the word "alert". It can't complete its evaluation, though, until the necessary parameter value is returned by the rejoin function, which in turn needs a value returned by the request function. So, when you paste the above code into the interpreter, the first thing you see is a request box (the last function in that line of code). The return value from that function is required before any other evaluations can be completed. After you respond to the request, the selected response is rejoined with the text "You chose: ", and the joined text is then displayed as an alert message. The parentheses in the above example are not required - they are only for human readability. Rebol knows how many parameters to expect for any function, and it will simply move along a line of code, grouping together each function word with its appropriate parameters, evaluating functions from left to right on the line.
If that seems confusing, just remember to keep an eye out for function words and their associated parameters. Learning to recognise and use Rebol's built-in function words is an important part of the initial learning curve. Getting used to using several function words in a row is also an important part of the process. Keep it that simple.
Rebol does not require any line terminators between expressions (functions, parameters, etc.), and you can insert empty white space (tabs, spaces, newlines, etc.) as desired into code. Text after a semicolon and before a new line is treated as a comment (ignored entirely by the interpreter). The code below works exactly the same as the previous example:
alert rejoin [
"You chose: " ; 1st piece of joined data
(request "Choose one:") ; 2nd piece of joined data
]
ONE CAVEAT: parameters for any given function need to be included on the same line, so the following will not work (the rejoin arguments' opening brackets must be on the same line as the rejoin function):
alert rejoin
[
"You chose: " ; 1st piece of joined data
(request "Choose one:") ; 2nd piece of joined data
]
Math expressions are evaluated from left to right like all other functions. To force a specific order of evaluation, enclose the functions in parentheses:
print 10 + 12 / 2 ; 11
print 10 + (12 / 2) ; 16
If you're completely new to programming, this may all seem a bit technical. Just follow along and paste every example into the Rebol interpreter. Seeing the code work is essential. Rebol is simpler to learn than any other programming language, and using functions is an important part of its language style. Whether you're new to programming, or just new to Rebol, you'll get used to the syntax very quickly. If you get done with this tutorial and still feel lost, take a look at Rebol Programming for the Absolute Beginner. It's much more descriptive and explanatory for average computer users and beginners.
3.2 More Basics: Word Assignment, I/O, Files, Built-In Data Types and Native Protocols
In Rebol, the colon (":") symbol is used to assign word labels ("variables") to values. In the example below, the word "filename" is assigned to the value returned by the request-file function:
filename: request-file
Now, the word label "filename" can be used anywhere, to represent the file selected above:
alert rejoin ["You chose " filename]
The "ask" function is a simple way to get some text data from a user:
name: (ask "What is your name? ")
The "print" function is a simple way to display text data at the interpreter's command line:
print rejoin ["Good to meet you " name]
The "prin" function prints consecutive text elements right next to each other (not on consecutive lines):
prin "All " prin "on " prin "one " print "line." print "On another."
Multi-line text is enclosed in curly braces ("{}"), instead of quotes:
print {
Line 1
Line 2
Line 3
}
The "write" function saves data to a file. It takes two parameters: a file name to write to, and some data to write to that file.
write %/C/YOURNAME.txt name
NOTE: in Rebol, the percent character ("%") is used to represent local files. Because Rebol can be used on many operating systems, and because those operating systems all use different syntax to refer to drives, paths, etc., Rebol uses the universal format: %/drive/path/path/.../file.ext . For example, "%/c/windows/notepad.exe" refers to "C:\Windows\Notepad.exe" in Windows. Rebol converts that syntax to the appropriate operating system format, so that your code can be written once and used on every operating system, without alteration.
You can write data to a web site using the exact same write syntax (be sure to use an appropriate username and password for your web site ftp account):
write ftp://user:pass@website.com/name.txt name
The "read" function reads data from a file:
print (read %/C/YOURNAME.txt)
Rebol has a built-in text editor that can also read, write, and manipulate text data:
editor %/c/YOURNAME.txt
You can read data straight from a web server, an ftp account, an email account, etc. using the same format. Many Internet protocols are built right into the Rebol interpreter. They're understood natively, and Rebol knows exactly how to connect to them without any preparation by the programmer:
editor read http://rebol.com
editor read ftp://user:pass@website.com/public_html/index.html
print read dns://msn.com
print read whois://yahoo.com@rs.internic.net
editor read pop://user:pass@website.com ; reads all emails in
; the inbox
editor read clipboard:// ; reads data that has
; been copied/pasted to
; the OS clipboard
Transferring data between devices connected by any supported protocol is easy:
; read data from a web site, and paste it into the local clipboard:
write clipboard:// (read http://rebol.com) ; afterward, try pasting into
; your favorite text editor
; read a page from one web site, and write it to another:
write ftp://user:pass@website2.com (read http://website1.com)
Sending email is just as easy, using a similar syntax:
send user@website.com "Hello"
send user@website.com (read file.txt) ; sends an email, with
; file.txt as the body
The "/binary" modifier is used to read or write binary (non-text) data:
write/binary %/c/bay.jpg read/binary http://rebol.com/view/bay.jpg
For clarification, the first parameter of the function above is "%/c/bay.jpg". The second parameter is the binary data read from http://rebol.com/view/bay.jpg:
write/binary (%/c/bay.jpg) (read/binary http://rebol.com/view/bay.jpg)
The "load" and "save" functions read, write, and in the process, automatically format certain data types for use in Rebol:
picture: load http://rebol.com/view/bay.jpg
save/png %/c/picture.png picture
view layout [image load %/c/picture.png]
Rebol also automatically knows how to perform appropriate computations on times, dates, IP addresses, coordinate values, and other common types of data:
print 3:30am + 00:07:19 ; increment time values properly
print now ; print current date and time
print now + :30 ; print 30 minutes from now
print now - 10 ; print 10 days ago
print 23x54 + 19x31 ; easily add coordinate pairs
print 192.168.1.1 + 000.000.000.37 ; easily increment ip addresses
Data types can be specifically assigned ("cast") using "to-(type)" functions:
; this example creates and adds two coordinate pairs
; using the "to-pair" function:
x: 12 y: 33 q: 18 p: 7
pair1: to-pair rejoin [x "x" y] ; 12x33
pair2: to-pair rejoin [q "x" p] ; 18x7
print pair1 + pair2 ; 12x33 + 18x7 = 30x40
; this example builds and manipulates a time value
; using the "to-time" function:
hour: 3
minute: 45
second: 00
the-time: to-time rejoin [hour ":" minute ":" second] ; 3:45am
later-time: the-time + 3:00:15
print rejoin ["3 hours and 15 seconds after 3:45 is " later-time]
Built-in network protocols, native data types, and consistent language syntax for reading, writing, and manipulating data allow you to perform common coding chores easily and intuitively in Rebol.
3.3 Built-in Help and Resources
The "help" function displays required syntax for any Rebol function:
help print
The "what" function lists all built-in words:
what
Together, those two words provide a built-in reference guide for the entire core Rebol language.
"Help system" displays the contents of the Rebol system object, which contains many important settings and values. You can explore each level of the system object using path notation, like this:
editor system/view/VID/vid-styles
Additionally, the Rebol desktop that appears by default when you run the view.exe interpreter can be used as a gateway into a world of "Rebsites" that developers use to share useful code. Surfing the public rebsites is a great way to explore the language more deeply. All of the code in the rebol.org archive, and much more, is available on the rebsites. When typing at the interpreter console, the "desktop" function brings up the Rebol desktop:
desktop
Click the "REBOL" or "Public" folders to see hundreds of interesting demos and useful examples. Source code for every example is available by right-clicking individual program icons and selecting "edit". You can learn volumes about the Rebol language using only the resources built directly into the 600k interpreter!
For detailed, categorized, and cross-referenced information about built-in functions, see the Rebol Dictionary rebsite, found in the Rebol desktop folder REBOL->Tools (an html version is also available at http://www.rebol.com/docs/dictionary.html).
3.4 GUIs
Graphic user interfaces ("GUI"s) are easier to create in Rebol than in any other language. The functions "view" and "layout" are used together to display GUIs. The parameters passed to the layout function are enclosed in brackets. Those brackets can include identifiers for all types of GUI elements ("widgets"):
view layout [btn] ; creates a GUI with a button
view layout [field] ; a text input field
view layout [text "Rebol is really pretty easy to program"]
view layout [text-list] ; a selection list
view layout [
button
field
text "Rebol is really pretty easy to program."
text-list
check
]
You can adjust the characteristics of a widget by following it with appropriate modifiers:
view layout [
button red "Click Me"
field "Enter some text here"
text font-size 16 "Rebol is really pretty easy to program." purple
text-list 400x300 "line 1" "line 2" "another line"
check yellow
]
A variety of functions are available to control the alignment, spacing, and size of elements in a GUI layout:
view layout/size [
across
btn "side" btn "by" btn "side"
return
btn "on the next line"
tab
btn "over a bit"
tab
btn "over more"
below
btn 160 "underneath" btn 160 "one" btn 160 "another"
at 359x256
btn "at 359x256"
] 500x350
IMPORTANT: You can have widgets perform functions when clicked, or when otherwise activated. Just put the functions inside another set of brackets after the widget. This is how you get your GUIs to 'do something' (using the fundamentals introduced in the previous section):
view layout [button "click me" [alert "You clicked the button."]]
view layout [btn "Display Rebol.com Html" [editor read http://rebol.com]]
view layout [btn "Write current time to HD" [write %time.txt now/time]]
; The word "value" refers to data contained in a currently activated
; widget:
view layout [
text "Some action examples. Try using each widget:"
button red "Click Me" [alert "You clicked the red button."]
field 400 "Type some text here, then press [Enter] on your keyboard." [
alert value
]
text-list 400x300 "Select this line" "Then this line" "Now this line" [
alert value
]
check yellow [alert "You clicked the yellow check box."]
button "Quit" [quit]
]
To react to right-button mouse clicks on a widget, put the functions to be performed inside a second set of brackets after the widget:
view layout [
btn "Right Click Me" [alert "left click"][alert "right click"]
]
You can assign keyboard shortcuts (keystrokes) to any widget, so that pressing the key reacts the same way as activating the GUI widget:
view layout [
btn "Click me or press the 'A' key on your keyboard" #"a" [
alert "You just clicked the button OR pressed the 'A' key"
]
]
You can assign a word label to any widget, and refer to data and properties of that widget by its label. The "text" property is especially useful:
view layout [
page-to-read: field "http://rebol.com"
; page-to-read/text now refers to the text contained in that field
btn "Display Html" [editor read (to-url page-to-read/text)]
]
You can also set various properties of a widget using its label. When the "Edit HTML Page" button is clicked below, the text of the multi-line area widget is set to contain the text read from the given url. The "show" function in the example below is very important. It must be used to update the GUI display any time a widget property is changed (if you ever create a GUI that doesn't seem to respond properly, the first thing to check is that you've used a "show" function to properly update any changes on screen):
view layout [
page-to-read: field "http://rebol.com"
the-html: area 600x440
btn "Download Html Page" [
the-html/text: read (to-url page-to-read/text)
; try commenting out the following line to see what happens:
show the-html
]
]
The "offset" of a widget holds its coordinate position. It's another useful property, especially for GUIs which involve movement:
view layout/size [
jumper: button "click me" [
jumper/offset: random 580x420
; The "random" function creates a random value within
; a specified range. In this example, it creates a
; random coordinate pair within the range 580x420,
; every time the button is clicked. That random value
; is assigned to the position of the button.
]
] 600x440
The "style" function is very powerful. It allows you to assign a specific widget definition, including all its properties and actions, to any word label you choose. Any instance of that word label is thereafter treated as a replication of the entire widget definition:
view layout/size [
style my-btn btn green "click me" [
face/offset: random 580x420
]
; "my-btn" now refers to all the above code
at 254x84 my-btn
at 19x273 my-btn
at 85x348 my-btn
at 498x12 my-btn
at 341x385 my-btn
] 600x440
Rebol is great at dealing with all types of common data - not just text. You can easily display photos and other graphics in your GUIs, play sounds, display web pages, etc. Here's some code that downloads an image from a web server and displays it in a GUI - notice the "view layout" functions again:
view layout [image (load http://rebol.com/view/bay.jpg)]
The "image" widget inside the brackets displays a picture (.bmp, .jpg, .gif., .png) in the GUI. The "load" function downloads the image to be displayed.
Rebol can apply many built-in effects to images:
view layout [image load http://rebol.com/view/bay.jpg effect [Grayscale]]
view layout [image load http://rebol.com/view/bay.jpg effect [Emboss]]
view layout [image load http://rebol.com/view/bay.jpg effect [Flip 1x1]]
; there are many more built-in effects
Here are some other GUI elements used in Rebol's "VID" layout language:
view layout [
backcolor white
h1 "More GUI Examples:"
box red 500x2
bar: progress
slider 200x16 [bar/data: value show bar]
area "Type here"
drop-down
across
toggle "Click" "Here" [print value]
rotary "Click" "Again" "And Again" [print value]
choice "Choose" "Item 1" "Item 2" "Item 3" [print value]
radio radio radio
led
arrow
return
text "Normal"
text "Bold" bold
text "Italic" italic
text "Underline" underline
text "Bold italic underline" bold italic underline
text "Serif style text" font-name font-serif
text "Spaced text" font [space: 5x0]
return
h1 "Heading 1"
h2 "Heading 2"
h3 "Heading 3"
h4 "Heading 4"
tt "Typewriter text"
code "Code text"
below
text "Big" font-size 32
title "Centered title" 200
across
vtext "Normal"
vtext "Bold" bold
vtext "Italic" italic
vtext "Underline" underline
vtext "Bold italic underline" bold italic underline
vtext "Serif style text" font-name font-serif
vtext "Spaced text" font [space: 5x0]
return
vh1 "Video Heading 1"
vh2 "Video Heading 2"
vh3 "Video Heading 3"
vh4 "Video Heading 3"
label "Label"
below
vtext "Big" font-size 32
banner "Banner" 200
]
That's just the tip of the iceberg. With Rebol, even absolute beginners can create nice looking, powerful graphic interfaces in minutes. See http://rebol.com/docs/easy-vid.html and http://rebol.com/docs/view-guide.html for more information. Here's a little game that demonstrates common GUI techniques (this whole program was presented earlier, in a more compact format without any comments. It's tiny.):
; Create a GUI that's centered on the user's screen:
view center-face layout [
; Define some basic layout parameters. "origin 0x0"
; starts the layout in the upper left corner of the
; GUI window. "space 0x0" dictates that there's no
; space between adjacent widgets, and "across" lays
; out consecutive widgets next to each other:
origin 0x0 space 0x0 across
; The section below creates a newly defined button
; style called "piece", with an action block that
; swaps the current button's position with that of
; the adjacent empty space. That action is run
; whenever one of the buttons is clicked:
style piece button 60x60 [
; The line below checks to see if the clicked button
; is adjacent to the empty space. The "offset"
; refinement contains the position of the given
; widget. The word "face" is used to refer to the
; currently clicked widget. The "empty" button is
; defined later (at the end of the GUI layout).
; It's ok that the empty button is not yet defined,
; because this code is not evaluated until the
; the entire layout is built and "view"ed:
if not find [0x60 60x0 0x-60 -60x0
] (face/offset - empty/offset) [exit]
; In English, that reads 'subtract the position of
; the empty space from the position of the clicked
; button (the positions are in the form of
; Horizontal x Vertical coordinate pairs). If that
; difference isn't 60 pixels on one of the 4 sides,
; then don't do anything.' (60 pixels is the size of
; the "piece" button defined above.)
; The next three lines swap the positions of the
; clicked button with the empty button.
; First, create a variable to hold the current
; position of the clicked button:
temp: face/offset
; Next, move the button's position to that of the
; current empty space:
face/offset: empty/offset
; Last, move the empty space (button), to the old
; position occupied by the clicked button:
empty/offset: temp
]
; The lines below draw the "piece" style buttons onto
; the GUI display. Each of these buttons contains all
; of the action code defined for the piece style above:
piece "1" piece "2" piece "3" piece "4" return
piece "5" piece "6" piece "7" piece "8" return
piece "9" piece "10" piece "11" piece "12" return
piece "13" piece "14" piece "15"
; Here's the empty space. Its beveled edge is removed
; to make it look less like a movable piece, and more
; like an empty space:
empty: piece 200.200.200 edge [size: 0]
]
3.5 Blocks and Series
In Rebol, all data and code is stored in "blocks". Blocks are delineated by starting and ending brackets ("[]"). Data items in blocks are separated by white space. Like any other variable data, blocks can be assigned word labels:
some-names: ["John" "Bill" "Tom" "Mike"]
print some-names
Blocks were snuck in earlier as arguments passed to the "rejoin" function, and as brackets used to delineate GUI code passed to the 'view layout' functions. Like any other blocks, GUI code can be labeled and referred to by the assigned word:
gui-layout1: [button field text-list]
view layout gui-layout1
Blocks of user data can also be easily displayed in GUI widgets:
view layout [text-list data some-names]
view layout [area rejoin some-names]
The "append" function is used to add items to a block:
append some-names "Lee"
print some-names
append gui-layout1 [text "This text was appended to the GUI block."]
view layout gui-layout1
The "foreach" function is used to do something to/with each item in a block:
foreach item some-names [alert item]
Here's a slightly more complex foreach example:
some-names: ["John" "Bill" "Tom" "Mike"]
count: 0
foreach name some-names [
count: count + 1
print rejoin ["Item " count ": " name]
]
The "remove-each" function can be used to remove items from a block that match a certain criteria:
remove-each name some-names [find name "i"]
Empty data blocks are created with the "copy" function. "Copy" assures that blocks are erased and defined without any previous content:
empty-block: copy []
Here's an example in which an empty block is created and data is appended using a foreach function. The data is then converted to a text string and displayed in a GUI:
some-names: ["John" "Bill" "Tom" "Mike"]
data-block: copy []
count: 0
foreach name some-names [
count: count + 1
append data-block rejoin ["Item " count ": " name newline]
]
view layout [area to-string data-block]
In Rebol, blocks can be automatically treated as lists of data, called "series", and manipulated using built-in functions that enable searching, sorting, and otherwise organizing the blocked data:
some-names: ["John" "Bill" "Tom" "Mike"]
sortednames: sort some-names ; sort alphabetically/ordinally
print first sortednames ; display the first item ("Bill")
print sortednames/1 ; display the first item ("Bill")
print pick sortednames 1 ; display the first item ("Bill")
find some-names "John" ; search for "John" in the block,
; set a position marker after that
; item
remove sortednames ; remove the first item in the block
remove find sortednames "Mike" ; find the "Mike" item in the block
; and remove it
length? sortednames ; count items in the block
head sortednames ; set a position marker at the
; beginning of the block
next sortednames ; set a position marker at the next
; item in the block
last sortednames ; set a position marker at the last
; item in the block
back sortednames ; set a position marker at the
; previous item in the block
tail sortednames ; set a position marker after the
; last item in the block
insert sortednames "Lee" ; add the name "Lee" at the current
; position in the block
write %/c/names.txt some-names ; write the block to the hard drive
; as raw text data
save %/c/namess.txt some-names ; write the block to the hard drive
; as native Rebol formatted code
IMPORTANT: In Rebol, blocks can contain mixed data of ANY type (text and binary items, embedded lists of items (other blocks), variables, etc.):
some-items: ["item1" "item2" "item3" "item4"]
an-image: load http://rebol.com/view/bay.jpg
append some-items an-image
; "some-items" now contains 4 text strings, and an image!
view layout [image fifth some-items]
Take a moment to examine the example above. Rebol's block structure works in a way that is dramatically easy to use compared to other languages and data management solutions. It's is a very flexible, simple, and powerful way to store data in code! The fact that blocks can hold all types of data using one simple syntactic structure is a fundamental reason it's easier to use than other programming languages and computing tools. All programming, and computing in general, after all, at its core, is essentially about storing, organizing, manipulating, and transferring data of some sort. Rebol makes working with all types of data very easy.
Blocks are also used as the fundamental structure for organizing Rebol code. Brackets are used throughout the Rebol language syntax to separate and delineate groups of functions, as well as collections of data parameters. The "compose" function allows variables in parentheses to be evaluated and inserted as if they'd been typed explicitly into a code block:
length-of-block: length? some-items
view layout compose [image some-items/(length-of-block)]
The last line in the example above appears to the interpreter as if the following had been typed:
view layout [image some-items/5]
The "reduce" function can be used to produce the same type of evaluation. Function words in a reduced block should begin with the tick (') symbol:
view layout reduce ['image some-items/(length-of-block)]
Another way to use variable values explicitly is with the ":" format below. This code evaluates the same as the previous two examples:
view layout [image some-items/:length-of-block]
Here's an example that displays variable image data contained in a block, using a foreach loop. The compose function is used to include dynamically changeable data (image representations), as if that data had been typed directly into the code:
photo1: load http://rebol.com/view/bay.jpg
photo2: load http://rebol.com/view/demos/palms.jpg
photo-block: compose [(photo1) (photo2)]
foreach photo photo-block [view layout [image photo]]
The practical application of block structures will be presented by example throughout this tutorial. For more detailed information about using blocks and series functions see http://www.rebol.com/docs/core23/rebolcore-6.html.
3.6 Conditions
3.6.1 If
Conditions are used to manage program flow. The most basic conditional evaluation is "if":
if (this expression is true) [do this block of code]
; parentheses are not required
Math operators are typically used to perform conditional evaluations: = < > <> (equal, less-than, greater-than, not-equal):
if now/time > 12:00 [alert "It's after noon."]
; get a username and password:
userpass: request-pass/title "Type 'username' and 'password'"
; test it and provide a response if correct:
if (userpass = ["username" "password"]) [alert "Welcome back!"]
3.6.2 Either
"Either" is an if/then/else evaluation that chooses between two blocks to evaluate, based on whether the given condition is true or false. Its syntax is:
either (condition) [
block to perform if the condition is true
][
block to perform if the condition is false
]
For example:
either now/time > 8:00am [
alert "It's time to get up!"
][
alert "You can keep on sleeping."
]
userpass: request-pass
either userpass = ["username" "password"] [
alert "Welcome back!"
][
alert "Incorrect user/password combination!"
]
3.6.3 Switch
The "switch" evaluation chooses between numerous functions to perform, based on multiple evaluations. Its syntax is:
switch/default (main value) [
(value 1) [block to execute if value 1 = main value
(value 2) [block to execute if value 2 = main value]
(value 3) [block to execute if value 3 = main value]
; etc...
] [default block of code to execute if none of the values match]
You can compare as many values as you want against the main value, and run a block of code for each matching value:
favorite-day: request-text/title "What's your favorite day of the week?"
switch/default favorite-day [
"Monday" [alert "Monday is the worst! Just the start of the week..."]
"Tuesday" [alert "Tuesdays and Thursdays are both ok, I guess..."]
"Wednesday" [alert "The hump day - the week is halfway over!"]
"Thursday" [alert "Tuesdays and Thursdays are both ok, I guess..."]
"Friday" [alert "Yay! TGIF!"]
"Saturday" [alert "Of course, the weekend!"]
"Sunday" [alert "Of course, the weekend!"]
] [alert "You didn't type in the name of a day!"]
3.7 Loops
3.7.1 Forever
"Loop" structures provide programmatic ways to methodically repeat actions, manage program flow, and automate lengthy data processing activities. The "forever" function creates a simple repeating loop. Its syntax is:
forever [block of actions to repeat]
The following code uses a forever loop to continually check the time. It alerts the user when 60 seconds has passed. Notice the "break" function, used to stop the loop:
alarm-time: now/time + :00:60
forever [if now/time = alarm-time [alert "1 minute has passed" break]]
Here's a more interactive version using some info provided by the user. Notice how the forever loop, if evaluation, and alert arguments are indented to clarify the grouping of related parameters:
event-name: request-text/title "What do you want to be reminded of?"
seconds: to-integer request-text/title "Seconds to wait?"
alert rejoin [
"It's now " now/time ", and you'll be alerted in "
seconds " seconds."
]
alarm-time: now/time + seconds
forever [
if now/time = alarm-time [
alert rejoin [
"It's now "alarm-time ", and " seconds
" seconds have passed. It's time for: " event-name
]
break
]
]
Here's a forever loop that displays/updates the current time in a GUI:
view layout [
timer: field
button "Start" [
forever [
set-face timer now/time
wait 1
]
]
]
3.7.2 For
"For" loops allow you to control repetition patterns that involve consecutively changing values. You specify a start value, end value, incremental value, and a variable name to hold the current value during the loop. Here's the "for" loop syntax:
for {variable word to hold current value} {starting value} {ending value} {incremental value} [block of code to perform, which can use the current variable value]
For example:
for counter 1 10 1 [print counter]
; starts on 1 and counts to 10 by increments of 1
for counter 10 1 -1 [print counter]
; starts on 10 and counts backwards to 1 by increments of -1
for counter 10 100 10 [print counter]
; starts on 10 and counts to 100 by increments of 10
for counter 1 5 .5 [print counter]
; starts on 1 and counts to 5 by increments of .5
for timer 8:00 9:00 0:05 [print timer]
; starts at 8:00am and counts to 9:00am by increments of 5 minutes
for dimes $0.00 $1.00 $0.10 [print dimes]
; starts at 0 cents and counts to 1 dollar by increments of a dime
for date 1-dec-2005 25-jan-2006 8 [print date]
; starts at December 12, 2005 and counts to January 25, 2006
; and by increments of 8 days
for alphabet #"a" #"z" 1 [prin alphabet]
; starts at the character a and counts to z by increments of 1 letter
Notice that Rebol properly increments dates, money, time, etc.
This "for" loop displays the first 5 file names in the current folder on your hard drive:
files: read %.
for count 1 5 1 compose [print files/(count)]
Notice the "compose" word used in the for loop. "files/1" represents the first item in the file list, "files/2" represents the second, and so on. The first time though the loop, the code reads as if [print files/1] had been typed in manually, etc.
3.7.3 Foreach
The "foreach" function lets you easily loop through a block of data. Its syntax is:
foreach {variable name referring to each consecutive item in the given block} [given block] [block of functions to be executed upon each item in the given block, using the variable name to refer to each successive item]
This example prints the name of every file in the current directory on your hard drive:
folder: read %.
foreach file folder [print file]
This line reads and prints each successive message in a user's email box:
foreach mail (read pop://user:pass@website.com) [print mail]
3.7.4 While
The "while" function repeatedly evaluates a block of code while the given condition is true. While loops are formatted as follows:
while [condition] [
block of functions to be executed while the condition is true
]
This example counts to 5:
x: 1 ; create an initial counter value
while [x <= 5] [
alert to-string x
x: x + 1
]
In English, that code reads:
"x" initially equals 1.
While x is less than or equal to 5, display the value of x,
then add 1 to the value of x and repeat.
Some additional "while" loop examples:
while [not request "End the program now?"] [
alert "Select YES to end the program."
]
; "not" reverses the value of data received from
; the user (i.e., yes becomes no and visa versa)
alert "Please select today's date"
while [request-date <> now/date] [
alert join "Please select TODAY's date. It's " [now/date]
]
while [request-pass <> ["username" "password"]] [
alert "The username is 'username' and the password is 'password'"
]
The example below uses several loops to alert the user to feed the cat, every 6 hours between 8am and 8pm. It uses a for loop to increment the times to be alerted, a while loop to continually compare the incremented times with the current time, and a forever loop to do the same thing every day, continuously. Notice the indentation:
forever [
for timer 8:00am 8:00pm 6:00 [
while [now/time <= timer] [wait :00:01]
alert rejoin ["It's now " now/time ". Time to feed the cat."]
]
]
3.7.5 A Real World Example Using Loops, Conditions, and Blocks:
This longer example uses a variety of loops and conditions to process data in typical ways. It also demonstrates how a simple block can be used to store a simple database of information:
Rebol [Title: "User Database Looping Example"]
users: [
["John" "Smith" "123 Tomline Lane"
"Forest Hills, NJ" "555-1234"]
["Paul" "Thompson" "234 Georgetown Place"
"Peanut Grove, AL" "555-2345"]
["Jim" "Persee" "345 Pickles Pike"
"Orange Grove, FL" "555-3456"]
["George" "Jones" "456 Topforge Court"
"Mountain Creek, CO" ""]
["Tim" "Paulson" ""
"" "555-5678"]
]
a-line: copy []
loop 65 [append a-line "-"]
a-line: trim to-string a-line
print-all: does [
foreach user users [
print a-line
print rejoin ["User: " user/1 " " user/2]
print a-line
print rejoin ["Address: " user/3 " " user/4]
print rejoin ["Phone: " user/5]
print newline
]
]
forever [
prin "^(1B)[J" ; clear the screen
print "Here are the current users in the database:^/"
print a-line
foreach user users [prin rejoin [user/1 " " user/2 " "]]
print "" print a-line
print "Type the name of a user below (part of a name will perform search):^/"
print "Type 'all' for a complete database listing."
print "Press [Enter] to quit.^/"
answer: ask {What person would you like info about? }
print newline
switch/default answer [
"all" [print-all]
"" [ask "Goodbye! Press any key to end." quit]
][
found: false
foreach user users [
if find rejoin [user/1 " " user/2] answer [
print a-line
print rejoin ["User: " user/1 " " user/2]
print a-line
print rejoin ["Address: " user/3 " " user/4]
print rejoin ["Phone: " user/5]
print newline
found: true
]
]
if found <> true [
print "That user is not in the database!^/"
]
]
ask "Press [ENTER] to continue"
]
For some perspective, here's a GUI version of the same program that demonstrates how GUI and command line programming styles differ:
Rebol [title: "User Database GUI Example"]
users: [
["John" "Smith" "123 Tomline Lane"
"Forest Hills, NJ" "555-1234"]
["Paul" "Thompson" "234 Georgetown Place"
"Peanut Grove, AL" "555-2345"]
["Jim" "Persee" "345 Pickles Pike"
"Orange Grove, FL" "555-3456"]
["George" "Jones" "456 Topforge Court"
"Mountain Creek, CO" ""]
["Tim" "Paulson" ""
"" "555-5678"]
]
user-list: copy []
foreach user users [append user-list user/1]
user-list: sort user-list
view display-gui: layout [
h2 "Click a user name to display their information:"
across
list-users: text-list 200x400 data user-list [
current-info: []
foreach user users [
if find user/1 value [
current-info: rejoin [
"FIRST NAME: " user/1 newline newline
"LAST NAME: " user/2 newline newline
"ADDRESS: " user/3 newline newline
"CITY/STATE: " user/4 newline newline
"PHONE: " user/5
]
]
]
display/text: current-info
show display show list-users
]
display: area "" 300x400 wrap
]
More powerful and specific looping structures and conditions such as "forskip", "any", and "all" add to Rebol's flexibility. For more information, see the Rebol Dictionary in the Rebol desktop folder REBOL->Tools.
3.8 User Defined Functions
Rebol's built-in functions satisfy many fundamental needs. To achieve more complex or specific computations, you can create your own function definitions.
Data and function words contained in blocks can be evaluated (their actions performed and their data values assigned) using the "do" word. Because of this, any block of code can essentially be treated as a function. That's a powerful key element of the Rebol language design:
some-actions: [
alert "Here is one action."
print "Here's a second action."
write %/c/anotheraction.txt "Here's a third action."
]
do some-actions
New function words can also be defined using the "does" and "func" words. "Does" is included directly after a word label definition, and forces a block to be evaluated every time the word is encountered:
more-actions: does [
alert "4"
alert "5"
alert "6"
]
more-actions
Here's a useful function to clear the command line screen in the Rebol interpreter.
cls: does [prin "^(1B)[J"]
cls
The "func" word creates an executable block in the same way as "does", but additionally allows you to pass your own specified parameters to the newly defined function word. The first block in a func definition contains the name(s) of the variable(s) to be passed. The second block contains the actions to be taken. Here's the "func" syntax:
func [names of variables to be passed] [
actions to be taken with those variables
]
This function definition:
sqr-var: func [anumber] [print square-root (4 + anumber)]
Can be used as follows:
sqr-var 12 ; prints "4", the square root of 12+4 (16)
sqr-var 96 ; prints "10", the square root of 96+4 (100)
Here's a simple function to display images:
display: func [filename] [view layout [image load filename]]
display (to-file request-file)
You can "do" a module of code contained in any text file, as long as it contains the minimum header "rebol[]" (this includes html files and any other files that can be read via Rebol's built-in protocols). For example, if you save the previous functions in a text file called "myfunctions.r":
Rebol []
sqr-var: func [anumber] [print square-root (4 + anumber)]
display: func [filename] [view layout [image load filename]]
cls: does [prin "^(1B)[J"]
You can import and use them in your current code, as follows:
do %myfunctions.r
sqr-var
display
cls
Here's an example function that plays a .wave sound file. Save this code as C:\play_sound.r:
Rebol [title: "play-sound"] ; you can add a title to the header
play-sound: func [sound-file] [
wait 0
ring: load sound-file
sound-port: open sound://
insert sound-port ring
wait sound-port
close sound-port
]
Then run the code below to import the function and play selected .wav files:
do %/c/play_sound.r
play-sound %/C/WINDOWS/Media/chimes.wav
play-sound to-file request-file/file %/C/WINDOWS/Media/tada.wav
4. More Useful Topics
Before typing in or pasting any more code, adjust the following option in the Rebol interpreter: click the "User" menu in the graphic "Viewtop" that opens by default with Rebol, and uncheck "Open Desktop On Startup". That'll save you the trouble of clicking the "Console" button every time you start Rebol.
4.1 Binary Embedding and Compression
The following program can be used to encode external files (images, sounds, .exe files, etc.) so that they can be included within the text of your program code. Use "load (data)" to make use of any text data created by this program:
Rebol [Title: "Simple Binary Embedder"]
system/options/binary-base: 64
file: to-file request-file/only
data: read/binary file
editor data
The example below uses a text representation of the image at http://musiclessonz.com/test.png, encoded with the program above:
picture: load 64#{
iVBORw0KGgoAAAANSUhEUgAAAFUAAABkCAIAAAB4sesFAAAAE3RFWHRTb2Z0d2Fy
ZQBSRUJPTC9WaWV3j9kWeAAAAU1JREFUeJztlzEOgzAQBHkaT7s2ryZUUZoYRz4t
e9xsSzTjEXIktqP3trsPcPPo7z36e4/+3qO/9y76t/qjn3766V/oj4jBb86nUyZP
lM7kidKZPFE6kydq/Pjxq/nSElGv3qv50vj/o59++hNQM6Z93+P3zqefAw12Fyqh
v/ToX+4Pt0ubiNKZPFE6Ux5q/O/436lkh6affvrpp38ZRT/99Ov6+f4tPPqX+8Ps
/meidCZPlM7kidKZPFE6kydKZ/JE6UyeKJ3JE6UzeaJ0Jk+UzuSJ0pk8UTMmvn8L
j/7l/nC7tIkonekLdXm9dafSmeinn376D/rpp5/+vv1GqBkT37+FR/9yf7hd2kSU
zuSJ0pk8UTqTJ0pn8kTpTJ4onckTpTN5onQmT5TO5InSmTxROpMnasbE92/h0b/Q
//jR33v09x79vUd/73XvfwNmVzlr+eOLmgAAAABJRU5ErkJggg==
}
view layout [image picture]
The program below allows you to compress and embed binary files in your code:
REBOL [Title: "Rebol Binary Embedder"]
system/options/binary-base: 64
file: to-file request-file/only
if not file [quit]
uncompressed: read/binary file
compressed: compress to-string uncompressed
editor compressed
alert rejoin ["Uncompressed size: " length? uncompressed
" bytes. Compressed size: " length? compressed " bytes."]
To use the compressed version of data created by the program above, use the following code:
to-binary decompress {compressed data}
For example:
image-compressed: load to-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtL/GRgYdTiYgKzm7Z9WACnhEteIkuD8tJLyxKJU
hiBXJ38f/bDM1PL+m2IVDAzsFz1dHEMq5ry9u3GijKcAy0Fh3kVzn/0XmRW5WXGV
sUF25EOmKwrSjrrF9v89o//u+cs/IS75763Tv7ZO/5qt//p63LX1e9fEV0fu/7ap
7m0qZRIJf+2DmGZoVER5MQiz+ntzJix6kKnJ6CNio6va0Nm0fCmLQeCHLVMY1Ljm
TRM64HLwMpGK/334Hf4n+vkn+1pr9md7jAVsYv+X8Z3Z+M/yscIX/j32H7sl/0j3
KK+of/CX8/X63sV1w51WqNj1763MjOS/xcccX8hzzFtXDwyXL9f/P19/f0vxz4f2
OucaHfmZDwID+P7Hso/5snw8m+qevH1030pG4kr8fhNC4f/34Z89ov+vHe4vAeut
SsdqX8T/OYUCv9iblr++f67R8pp9ukzLv8YHL39tL07o+3pekn1h/dDVBgzLU/d3
9te/Lki4cNgBmA6/lO+J/RPdzty8Rr5y94/tfOxsX6/r8xJK0/UW9vlH93/9oAzR
e09yKIUBVbT9/br/U/m7x6CU98VAAJS2ZPPF/197eEDhtfs9vX9rDzc6/v3qzUyo
nJA/dz76Y77tHw+w3gXlbEMpDKihza/+7/o/c3+DU54tDwsobR2/fXR/qYXBiV8T
t3eDEmpA/d9LDASK0y/tnz+H/Ynmt78E1vti7lAKA6pouxz/X7v+uR045ZFdRE6x
1q21pG7NiSzx1f5R40pvvdNn+oB1P4Onq5/LOqeEJgCemFy1KQgAAA==
}
view layout [image image-compressed]
4.2 Running Command Line Applications
The "call" function executes commands in your computer's operating system (i.e., DOS and Unix commands). The example below opens Windows' Notepad to edit the "C:\YOURNAME.txt" text file created earlier:
call "notepad.exe c:\YOURNAME.txt"
This next example opens Windows' Paint program to edit an image we downloaded earlier in the tutorial:
call "mspaint.exe c:\bay.jpg"
Here's an example that embeds an executable program into the code, decompresses, and writes the program to the hard drive, and then runs it with the call function:
program: load to-binary decompress 64#{
eJztF11sU2X03K4VqJsrkZJp6OzchhFJsx8qDB9od1fHdIO6ds7AgJX2jttyey/p
vWUjJuNnmNhMibzwaCSLi+EBE1ziGIkBGh0BSYTwwAMme9Dk4kgkgSiKcj3nu7es
QrKFhMUQOcn5+c7fd875+vXe27FJAg4AbIiGAQwWIwZMEbqTcmODN5xRdmRi6aoy
Z83YogngLlaNtV+s6kV7q9KelHeu9LYqQTXt7e/v97UqLcLuqKJIvriShnAIoJ0r
gXvPn+StlDAF5dyzHLwAdlw4TZ1Mm7oQvWDu7jKLslsxBc4KQ30bb9bMHF3F/D5j
MFAHEIbHD+cwb88s9riSEIjvK7EKogZs//bxAvQmYlqM5JsOUwHPWFgEAYDTvqTp
eYdy1Fn5Sh/O96h9nLrrDcD4IpQm7UOkWL/nt6MlqMvxrkl+GVWS7xqWalzDzqGz
9rbyD5ehpmnl+ezt3M/RSPe7Q9/ajeh5+9Ztm3vKh9xoM7SaimLUR18C2JKf+Kg2
APoJwzDOuiAF+hHU/pHXryObdLyP+y2kEhx7UaLfo0gq/RJa60/n88Ndrpz7FmqG
u5bk3L8zwdWXc0+jdOYXkn4lnYfW++/qOPLyDz7BfH3jTXVnplx949inhPvnSgw/
8RSIHM7P8PdSUYtxlxSkONE+o/u7EkNElMbpcuRKUhTjmLH/iHbDQQ7DHqL77zbh
oQxeRa9duBQHkRj+HnIdr7y/e178AvmmnHt5VQAmaNo59/EZ8QSJAY7EURJvMu2x
KipYj2CaEToYve2eYYiwl4rWY6jN8RWF5XtsuWSyhO7aJG8XXQFkNdWYIqIHK8nH
8FOSFJMoteEfZfQEo1SNCPCW2/BTjWK1uXkp9dDDegjrDqpkAUtiJhNp4ma3qUrx
MG6dqkyFMQ2ExQmaxgU2c/07D2ZJsCz3Q68Xh76Cvac2pZwi8jCO8rIZd4jielmc
uHxmsEMe1vMBZJf0YY8Pda95yH5p+tWrI86XMZbTE5a1gVlXFKyryeowp0Cy4Wf+
hdSrWGp26N008hW4XnS6/OBS7MnUVHoK0osoTV+22qF56c95qKdtZBzB66J/imSc
/Rmsg/KDdHFbA9O3RrZWByD/qPf1KTCwze3y2KCbn9vnP4ExoItiwr11zvncqq6+
oXGV//XVa5qCzXxL6M3ZfBfMZyFPBvywgD3FGDjLnGVl83o4T+HJAZ/PFxWTqrcj
GxerHljRqyL9sWXxqU2/nkHki1H4HDkvJeM7vZooeLdnNU2R10K34G1XdgveTmE7
vmv7fNDcFY1u3ABpNa5J6rZd9MouqGpjw6z1GLXn6vDxV/s9o1cYvcroNUanGP2J
UZ3RG4zeZPQ2o3cY/YtRqCdqZ3Qho6WMuhitYHQZ0pr6mRr21Zvv03VFuuMoX0Gd
VqT7BlupKFoXw8eo/8yynUR+HvEa4g3EPxEXYuwSxOWIaxADiGHEBKKGeADxCOIx
a1wXkE81zH/ut0OdG0LtjQ2+hCSBzLUKWoeSyErC+pickIQgfAmhgaSG319xPEvo
ioQ6Ld9D0CL04ddZQuknaxA4W1hRtXeySa0DXWM7BHjDFhHkhLUKYs2cJTcrA0H4
mmtXYgk+m1GVTBBOsVVbXJGDsNTWKexIqpqQ4aWYqgbps4LPCDFNMPcLYXQpldrC
g0bcVHcKcQ220DqyB4PTHYKWScZVgCGsw/LBEgHWsjYLZR2zRTMxWZUwfaFwOAot
SXVXTIuLM9V/ZeuSMw/UxW/s4KOF6W2GNjmp8Uo6rci8ImsZRVLxG+1hZWhgrlv6
/4F/ABcSIgQAEAAA
}
write/binary %program.exe program
call %program.exe
4.3 Saving and Running Rebol Scripts
Remember, whenever you save a Rebol program to a text file, the code must begin with the following bit of text:
REBOL []
That text tells the Rebol interpreter that the file contains a valid Rebol program. The code below is a web cam viewer program. Type in or copy/paste the complete code source below into a text editor such as Windows Notepad or the Rebol built-in text editor ({editor ""} at the Rebol console prompt). Save the text as a file called "webcam.r" on your C:\ drive.
Rebol [Title: "Webcam Viewer"]
; try http://www.webcam-index.com/USA/ for more webcam links.
temp-url: "http://209.165.153.2/axis-cgi/jpg/image.cgi"
while [true] [
webcam-url: to-url request-text/title/default trim {
Enter the web cam URL:} temp-url
either attempt [webcam: load webcam-url]
[break]
[either request [trim {
That webcam is not currently available.} trim {
Try Again} "Quit"]
[temp-url: to-string webcam-url]
[quit]
]
]
resize-screen: func [size] [
webcam/size: to-pair size
window/size: (to-pair size) + 40x72
show window
]
window: layout [
across
btn "Stop" [webcam/rate: none show webcam]
btn "Start" [
webcam/rate: 0
webcam/image: load webcam-url
show webcam
]
rotary "320x240" "640x480" "160x120" [
resize-screen to-pair value
]
btn "Exit" [quit] return
webcam: image load webcam-url 320x240
with [
rate: 0
feel/engage: func [face action event][
switch action [
time [face/image: load webcam-url show face]
]
]
]
]
view center-face window
Once you've saved the webcam.r program to C:\, you can run it in any one of a variety of ways:
- If you've already installed the Rebol interpreter in Windows, just find the C:\webcam.r file icon in your file explorer and double click it (i.e., click My Computer -> C: -> webcam.r). The Rebol interpreter automatically executes the script. By default, during Rebol's initial installation, all files with an ".r" extension are associated with the interpreter. They can be clicked and run as if they're executable programs, just like ".exe" files. This is the most common way to run Rebol scripts, and it works the same way on all major graphic operating systems.
- Type "do %/c/webcam.r" into the Rebol interpreter.
- Use the built-in editor in Rebol by typing "editor %/c/webcam.r" at the prompt. Pressing F5 in the editor will automatically run the script.
- Scripts can be run at the command line. In Windows, copy rebol.exe and webcam.r to the same folder (C:\), then click Start -> Run, and type "C:\rebol.exe C:\webcam.r" Those commands will start the Rebol interpreter and do the webcam.r code.
- At the Windows command prompt (in a Windows DOS box), type "C:\rebol.exe C:\webcam.r"
- Create a text file called webcam.bat, containing the text "C:\rebol.exe C:\webcam.r" . Click on the webcam.bat file in Windows, and it'll run those commands.
- Use a program such as XpackerX to package and distribute the program. XpackerX allows you to wrap the Rebol interpreter and webcam.r program into a single executable file that has a clickable icon, and automatically runs both files. That allows you to create a single file executable Windows program that can be distributed and run like any other application. Just click it and run...
- Buy the commercial "SDK" version of Rebol, which provides the best method for packaging Rebol applications.
IMPORTANT: To turn off the default security requester that continually asks permission to read/write the hard drive, type "secure none" in the Rebol interpreter, and then run the program with "do {filename}". Running "C:\rebol.exe -s {filename}" does the same thing . The "-s" launches the Rebol interpreter without any security features turned on, making it behave like a typical Windows program.
4.4 "Compiling" Rebol Programs - Distributing Packaged .EXE Files
By packaging the Rebol.exe interpreter, your Rebol script(s), and any supporting data file(s) into a single executable with an icon of your choice, XpackerX works like a Rebol compiler that produces regular Windows programs that look and act just like those created by other compiled languages. To do that, you'll need to create a text file in the following format (save it as "template.xml"):
<?xml version="1.0"?>
<xpackerdefinition>
<general>
<!--shown in taskbar -->
<appname>your_program_name</appname>
<exepath>your_program_name.exe</exepath>
<showextractioninfo>false</showextractioninfo>
<!-- <iconpath>c:\icon.ico</iconpath> -->
</general>
<files>
<file>
<source>your_rebol_script.r</source>
<destination>your_rebol_script.r</destination>
</file>
<file>
<source>C:\Program Files\rebol\view\Rebol.exe</source>
<destination>rebol.exe</destination>
</file>
<!--put any other data files here -->
</files>
<!-- $FINDEXE, $TMPRUN, $WINDIR, $PROGRAMDIR, $WINSYSDIR -->
<onrun>$TMPRUN\rebol.exe -si $TMPRUN\your_rebol_script.r</onrun>
</xpackerdefinition>
Just download the free XpackerX program and alter the above template so that it contains the filenames you've given to your script(s) and file(s), and the correct path to your Rebol interpreter. Run XpackerX, and it'll spit out a beautifully packaged .exe file that requires no installation. Your users do not need to have Rebol installed to run this type of executable. To them it appears and runs just like any other native compiled Windows program.
To create a self-extracting Rebol executable for Linux, first create a .tgz file containing all the files you want to distribute (the Rebol interpreter, your script(s), any external binary files, etc.). For the purposes of this example, name that bundle "rebol_files.tgz". Next, create a text file containing the following code, and save it as "sh_commands":
#!/bin/sh
SKIP=`awk '/^__REBOL_ARCHIVE__/ { print NR + 1; exit 0; }' $0`
tail +$SKIP $0 | tar xz
exit 0
__REBOL_ARCHIVE__
Finally, use the following command to combine the above script file with the bundled .tgz file:
cat sh_commands rebol_files.tgz > rebol_program.sh
The above line will create a single executable file named "rebol_program.sh" that can be distributed and run by end users. The user will have to set the file permissions for rebol_program.sh to executable before running it ("chmod +x rebol_program.sh"), or execute it using the syntax "sh rebol_program.sh". For more information about using this technique to create self-extracting Linux executables, see the article at http://linux.org.mt/article/selfextract.
4.5 Responding to Special Events in a GUI
Rebol's simple GUI syntax makes it easy for widgets to respond to mouse clicks. As you've seen, you can simply put the block of code you want evaluated immediately after the widget that activates it:
view layout [btn "Click me" [alert "Thank you for the click :)"]]
But what if you want your GUI to respond to events other than a mouse click directly on a widget? What if, for example, you want the program to react whenever a user clicks anywhere on the GUI screen (in a paint program, for example), or if you want a widget to do something after a certain amount of time has passed, or if you want to capture clicks on the GUI close button so that the user can't accidentally shut down an important data screen. That's what the "feel" object and "insert-event-func" function are used for.
Here's an example of the basic feel syntax:
view layout [
text "Click, right-click, and drag the mouse over this text." feel [
engage: func [face action event] [
print action
print event/offset
]
]
]
The above code is often shortened using "f a e" to represent "face action event":
view layout [
text "Mouse me." feel [
engage: func [f a e] [
print a
print e/offset
]
]
]
You can respond to specific events as follows:
view layout [
text "Mouse me." feel [
engage: func [f a e] [
if a = 'up [print "You just released the mouse."]
]
]
]
You can also assign timer events to any widget, as follows:
view layout [
text "This text has a timer event attached." rate 00:00:00.5 feel [
engage: func [f a e] [
if a = 'time [print "1/2 second has passed."]
]
]
]
Here's a button with a time event attached (a rate of "0" means don't wait at all). Every 0 seconds, when the timer event is detected, the offset (position) of the button is updated. This creates animation:
view layout/size [
mover: btn rate 0 feel [
engage: func [f a e] [
if a = 'time [
mover/offset: mover/offset + 5x5
show mover
]
]
]
] 400x400
By updating the offset of a widget every time it's clicked, you can enable drag-and-drop operations:
view layout/size [
text "Click and drag this text" feel [
; remember f="face", a="action", e="event":
engage: func [f a e] [
; first, record the coordinate at which the mouse is
; initially clicked:
if a = 'down [initial-position: e/offset]
; if the mouse is moved while holding down the button,
; move the position of the clicked widget the same amount
; (the difference between the intial clicked coordinate
; recorded above, and the new current coordinate determined
; whenever a mouse move event occurs):
if find [over away] a [
f/offset: f/offset + (e/offset - initial-position)
]
show f
]
]
] 600X440
Feel objects and event functions can be included right inside a style definition. The definition below allows you to easily create multiple GUI widgets that can be dragged around the screen. "movestyle" is defined as a block of code that's later passed to a widget's "feel" object, and is therefore included in the overall style definition (the remove and append functions have been added here to place the moved widget on top of other widgets in the GUI (i.e., to bring the dragged widget to the visual foreground)). You can add this "feel movestyle" code to any GUI widget to make it drag-able:
movestyle: [
engage: func [f a e] [
if a = 'down [
initial-position: e/offset
remove find f/parent-face/pane f
append f/parent-face/pane f
]
if find [over away] a [
f/offset: f/offset + (e/offset - initial-position)
]
show f
]
]
view layout/size [
style moveable-object box 20x20 feel movestyle
; "random 255.255.255" represents a different random
; color for each piece:
at random 600x400 moveable-object (random 255.255.255)
at random 600x400 moveable-object (random 255.255.255)
at random 600x400 moveable-object (random 255.255.255)
at random 600x400 moveable-object (random 255.255.255)
at random 600x400 moveable-object (random 255.255.255)
text "This text and all the boxes are movable" feel movestyle
] 600x440
To handle global events in a GUI such as resizing and closing, "insert-event-func" is useful. The following example checks for resize events:
insert-event-func [
either event/type = 'resize [
alert "I've been resized"
none ; return this value when you don't want to
; do anything else with the event.
][
event ; return this value if the specified event
; is not found
]
]
view/options layout [text "Resize this window."] [resize]
You can use that technique to adjust the window layout, and specifically reposition widgets when a screen is resized:
insert-event-func [
either event/type = 'resize [
stay-here/offset:
stay-here/parent-face/size - stay-here/size - 20x20
show stay-here
none ; return this value when you don't want to
; do anything else with the event.
][
event ; return this value if the specified event
; is not found
]
]
view/options layout [
stay-here: text "Resize this window."
] [resize]
To remove an installed event handler, use "remove-event-func". The following example captures three consecutive close events, and then removes the event handler, allowing you to close the GUI on the 4th try:
count: 1
evtfunc: insert-event-func [
either event/type = 'close [
if count = 3 [remove-event-func :evtfunc]
count: count + 1
none
][
event
]
]
view layout [text "Try to close this window 4 times."]
For more information about handling events see http://www.rebol.com/how-to/feel.html, http://www.codeconscious.com/rebol/view-notes.html, and http://www.rebol.com/docs/view-system.html.
4.6 Common Errors
Listed below are solutions to a variety of common errors you'll run into when first experimenting with Rebol:
1) "** Syntax Error: Script is missing a REBOL header" - Whenever you "do" a script that's saved as a file, it must contain at least a minimum required header at the top of the code. Just include the following text at the beginning of the script:
Rebol []
2) "** Syntax Error: Missing ] at end-of-script" - You'll get this error if you don't put a closing bracket at the end of a block. You'll see a similar error for unclosed parentheses and strings. The code below will give you an error, because it's missing a "]" at the end of the block:
fruits: ["apple" "orange" "pear" "grape"
print fruits
Instead it should be:
fruits: ["apple" "orange" "pear" "grape"]
print fruits
Indenting blocks helps to find and eliminate these kinds of errors.
3) "** Script Error: request expected str argument of type: string block object none" - This type of error occurs when you try to pass the wrong type of value to a function. The code below will give you an error, because Rebol automatically interprets the website variable as a url, and the "alert" function requires a string value:
website: http://rebol.com
alert website
The code below solves the problem by converting the url value to a string before passing it to the alert function:
website: to-string http://rebol.com
alert website
4) "** Script Error: word has no value" - Miss-spellings will elicit this type of error. You'll run into it any time you try to use a word that isn't defined (either natively in the Rebol interpreter, or by you, in previous code):
wrod: "Hello world"
print word
5) If an error occurs in a "view layout" block, and the GUI becomes unresponsive, type "unview" at the interpreter command line and the broken GUI will be closed. To break out of an endless loop, or to otherwise stop the execution of errant code, just hit the [Esc] key on your keyboard.
6) Here's a quirk of Rebol that doesn't elicit an error, but which can cause confusing results, especially if you're familiar with other languages:
unexpected: [
empty-variable: ""
append empty-variable "*"
print empty-variable
]
do unexpected
do unexpected
do unexpected
The line:
empty-variable: ""
doesn't re-initialize the variable to an empty state. Instead, every time the block is run, "empty-variable" contains the previous value. In order to set the variable back to empty, as intended, use the word "copy" as follows:
expected: [
empty-variable: copy ""
append empty-variable "*"
print empty-variable
]
do expected
do expected
do expected
7) Load/Save, Read/Write, Mold, Reform, etc. - another point of confusion you may run into initially with Rebol has to do with various words that read, write, and format data. When saving data to a file on your hard drive, for example, you can use either of the words "save" or "write". "Save" is used to store data in a format more directly usable by Rebol. "Write" saves data in a raw, 'unRebolized' form. "Load" and "read" share a comparable relationship. "Load" reads data in a way that is more automatically understood and put to use in Rebol code. "Read" opens data in exactly the format it's saved, byte for byte. Generally, data that is "save"d should also be "load"ed, and data that's "write"ed should be "read". For more information, see the following Rebol dictionary entries:
http://rebol.com/docs/words/wload.html
http://rebol.com/docs/words/wsave.html
http://rebol.com/docs/words/wread.html
http://rebol.com/docs/words/wwrite.html
Other built-in words such as "mold" and "reform" help you deal with text in ways that are either more human-readable or more natively readable by the Rebol interpreter. For a helpful explanation, see http://www.rebol.net/cookbook/recipes/0015.html.
8) Order of precedence - Rebol expressions are always evaluated from left to right, regardless of the operations involved. If you want specific mathematical operators to be evaluated first, they should either be enclosed in parenthesis or put first in the expression. For example, to the Rebol interpreter:
2 + 4 * 6
is the same as:
(2 + 4) * 6 ; the left side is evaluated first
== 6 * 6
== 36
This is contrary to other familiar evaluation rules. In many languages, for example, multiplication is typically handled before addition. So, the same expression:
2 + 4 * 6
is treated as:
2 + (4 * 6) ; the multiplication operator is evaluated first
== 2 + 24
== 26
Just remember, evaluation is always left to right, without exception.
4.6.1 Trapping Errors
There are several simple ways to keep your program from crashing when an error occurs. The words "error?" and "try" together provide a way to check for and handle expected error situations. For example, if no Internet connection is available, the code below will crash abruptly with an error:
html: read http://rebol.com
The adjusted code below will handle the error more gracefully:
if error? try [html: read http://rebol.com] [
alert "Unavailable."
]
The word "attempt" is an alternative to the "error? try" routine. It returns the evaluated contents of a given block if it succeeds. Otherwise it returns "none":
if not attempt [html: read http://rebol.com] [
alert "Unavailable."
]
To clarify, "error? try [block]" evaluates to true if the block produces an error, and "attempt [block]" evaluates to false if the block produces an error.
For a complete explanation of Rebol error codes, see: http://www.rebol.com/docs/core23/rebolcore-17.html.
5. Examples
The examples in this section demonstrate how Rebol code is put together to create complete programs. The code is heavily commented to provide line-by-line explanations of how each element works. The recommended way to run the examples is to install Rebol on your computer, paste the code for each program into a text editor, save the code file as "(program).r" and then double click the icon for the text file you've created. With Rebol installed, any file with a ".r" extension will automatically run as if it's an .exe program. You can also use XpackerX to package and distribute the code file as a real .exe, or use any of the other methods described earlier. A downloadable zip file containing screen shots, XpackerX instructions, and .exe files of these examples and others from this tutorial is available at:
http://musiclessonz.com/rebol_tutorial_examples.zip
Be sure to check out the hundreds of additional code examples available directly from rebsites on the desktop of the Rebol interpreter!
5.1 Little Email Client
The first example is a complete graphical email client that can be used to read and send messages:
Rebol [Title: "Little Email Client"]
; (every program requires a minimum header)
view layout [
; The line above creates the GUI layout.
h1 "Send Email:"
; The second line adds a text label to the GUI.
address: field "recipient@website.com"
; This line creates a text entry field, containing
; the default text "recipient@website.com". It assigns
; the variable word "address" to the text entered here.
subject: field "Subject"
; another text entry field for the email subject line
body: area "Body"
; This creates a larger, multi-line text entry area for
; the body text of the email.
btn "Send" [
; A button with the word "send". The functions
; inside this action block are executed whenever
; the button is clicked.
send/subject to-email address/text body/text subject/text
; This line does most of the work. It uses the
; built-in Rebol word "send" to send the email. The
; send function, with its "/subject" refinement
; accepts three parameters. It's passed the current
; text contained in each field labeled above
; (referred to as "address/text" "body/text" and
; "subject/text"). The built-in "to-email" function
; ensures that the address text is treated as an
; email data value.
alert "Message Sent."
; alerts the user when the previous line is complete.
]
h1 "Read Email:"
; Another text label
mailbox: field "pop://user:pass@website.com"
; Another text entry field. The user's email account
; info is entered here.
btn "Read" [
; An additional button, this time with an action
; block that reads messages from a specified mailbox.
; It only takes one line:
editor read to-url mailbox/text
; The built-in "to-url" function ensures that the
; text in the mailbox field is treated as a url.
; The contents of the mailbox are read and displayed
; in the built-in Rebol editor.
]
]
Here's the same code, without comments - it's very simple. Try pasting it directly into the Rebol interpreter:
Rebol [Title: "Little Email Client"]
view layout [
h1 "Send Email:"
address: field "recipient@website.com"
subject: field "Subject"
body: area "Body"
btn "Send" [
send/subject to-email address/text body/text subject/text
alert "Message Sent."
]
h1 "Read Email:"
mailbox: field "pop://user:pass@website.com"
btn "Read" [
editor read to-url mailbox/text
]
]
5.2 Simple Web Page Editor
The following program can be used to load, edit, and save html files (or any other text file) directly to/from a live web server or to/from a drive on your local computer. It requires 14 lines of code:
Rebol [Title: "Web Page Editor"] ; required header
view layout [
; Create a text entry field containing a generic url address for
; the page to be edited. Assign the label "page-to-read" to the
; text entered here:
page-to-read: field 600 "ftp://user:pass@website.com/path/page.html"
; Create a multi-line text field to hold and edit the html
; downloaded from the above url. Assign the label "the-html" to it:
the-html: area 600x440
; Layout the next three buttons on the same line:
across
; Create a button to download and display the html at the url given
; above.
btn "Download Html Page" [
; Whenever the button is clicked, download the html at the url
; above, insert it into the multi-line text area (by setting the
; text property of that field to the downloaded text), and update
; the display:
the-html/text: read (to-url page-to-read/text)
show the-html
]
; Create another button to read and display html from a local file:
btn "Load Local Html File" [
; Whenever the button is clicked, read the html from a file
; selected by the user, insert it into the multi-line text area,
; and update the display:
the-html/text: read (to-file request-file)
show the-html
]
; Create another button to write the edited contents of the multi-
; line text area back to the url:
btn "Save Changes to Web Site" [
write (to-url page-to-read/text) the-html/text
]
; Create another button to write the edited contents of the multi-
; line text area to a local file selected by the user:
btn "Save Changes to Local File" [
write (to-file request-file/save) the-html/text
]
]
5.3 Simple Menu Example
A module that produces full blown menus with all the bells and whistles, animated icons, appropriate look-and-feel for various operating systems, and every possible display option is available at http://www.rebol.org/library/scripts/menu-system.r. Here's a simpler homemade example that can be included in your programs to provide basic menu functionality. It's constructed using only raw, native Rebol GUI components:
Rebol [Title: "Simple Menu Example"]
view center-face gui: layout/size [
at 100x100 H3 "You selected:"
display: field
; Here's the menu. Make sure it goes AFTER other GUI code.
; If you put it before other code, the menu will appear be-
; hind other widgets in the GUI. The menu is basically just
; a text-list widget, which is initially hidden off-screen
; at position -200x-200. When an item in the list is
; clicked upon, the action block for the text-list runs
; through a conditional switch structure, to decide what to
; do for the chosen item. The code for each option first
; re-hides the menu by repositioning it off screen (at
; -200x-200 again). For use in your own programs, you can
; put as many items as you want in the list, and the action
; block for each item can perform any actions you want.
; Here, each option just updates the text in the "display"
; text entry field, created above. Change, add to, or
; delete the "item1" "item2" and "quit" elements to suit
; your needs:
origin 2x2 space 5x5 across
at -200x-200 file-menu: text-list "item1" "item2" "quit" [
switch value [
"item1" [
face/offset: -200x-200
show file-menu
; PUT YOUR CODE HERE:
set-face display "File / item1"
]
"item2" [
face/offset: -200x-200
show file-menu
; PUT YOUR CODE HERE:
set-face display "File / item2"
]
"quit" [quit]
]
]
; The menu initially just appears as some text choices at
; the top of the GUI. When the "File" menu is clicked,
; the action block of that text widget repositions the
; text-list above, so that it appears directly underneath
; the File menu ("face/offset" is the location of the
; currently selected text widget). It disappears when
; clicked again - the code checks to see if the text-list
; is positioned beneath the menu. If so, it repositions
; it out of sight.
at 2x2
text bold "File" [
either (face/offset + 0x22) = file-menu/offset [
file-menu/offset: -200x-200
show file-menu
][
file-menu/offset: (face/offset + 0x22)
show file-menu
]
]
; Here's an additional top level menu option. It provides
; just a single choice. Instead of opening a text-list
; widget with multiple options, it simply ensures that the
; other menu is closed (re-hidden), and then runs some code.
text bold "Help" [
file-menu/offset: -200x-200
show file-menu
; PUT YOUR CODE HERE:
set-face display "Help"
]
] 400x300
5.4 FTP Chat Room
This example is a simple chat application that lets users send instant text messages back and forth across the Internet. It includes password protected access for administrators to erase chat contents. It also allows users to pause activity momentarily, and requires a username/password to continue ["secret" "password"]. The chat "rooms" are created by dynamically creating, reading, appending, and saving text files via ftp (to use the program, you'll need access to an available ftp server: ftp address, username, and password. Nothing else needs to be configured on the server).
Rebol [title: "FTP Chat Room"] ; required header
webserver: to-url request-text/title/default trim {
Web Server Address:} {ftp://user:pass@website.com/chat.txt}
; get the url of a webserver text file to use for the chat.
; The ftp username, password, domain, and filename must be
; entered in the format shown.
name: request-text/title "Enter your name:"
; get the user's name
cls: does [prin "^(1B)[J"]
; "cls" is assigned a function definition that clears the screen.
write/append webserver join now [
": " name " has entered the room." newline
]
; The line above writes some text to the webserver.
; The "/append" refinement adds it to the existing
; text in the webserver file (as opposed to erasing
; what's already there). Using "join", the text
; written to the webserver is the combined value of
; {the user's name}, some static text, the current
; date and time, and a carriage return.
forever [
current-chat: read webserver
; read the messages that are currently on the webserver,
; and assign the variable word "current-chat"
cls ; clear the screen using the word defined above
print rejoin [
"--------------------------------------------------"
newline {You are logged in as: } name newline
{Type "room" to switch chat rooms.} newline
{Type "lock" to pause/lock your chat.} newline
{Type "quit" to end your chat.} newline
{Type "clear" to erase the current chat.} newline
{Press [ENTER] to periodically update the display.} newline
"--------------------------------------------------" newline]
; displays a greeting and some instructions
print join "Here's the current chat text at: " [webserver newline]
print current-chat
sent-message: copy join name [
" says: " entered-text: ask "You say: "
]
; get the text to send, then check for commands below
; ("quit", "clear", "room", "lock", and [ENTER])
; The built-in word "ask" requests some info within
; the interpreter.
switch/default entered-text [
"quit" [break]
; if the user typed in "quit",
; stop the forever loop (exit the program)
"clear" [
if/else request-pass = ["secret" "password"] [
write webserver ""
; "if/else" is the same as "either"
][
alert trim {
You must know the administrator
password to clear the room!}
; if the user typed in "clear", erase the
; current text chat. But first, ask user
; for the administrator username/password
]
]
"room" [
write/append webserver join now [
": " name " has left the room." newline]
webserver: to-url request-text/title/default {New Web
Server Address:} to-string webserver
write/append webserver join now [
": " name " has entered the room." newline
; if the user typed in "room", request a new
; webserver address, and run some code that was
; presented earlier in the program,
; using the newly entered "webserver" variable,
; to effectively change chat "rooms".
]
]
"lock" [
alert trim {The program will now pause for 5 seconds.
You'll need the correct username and password
to continue.
}
pause-time: now/time + 5
; assign a variable to the time 5 seconds from now
forever [if now/time = pause-time [
; wait 5 seconds
while [request-pass <> ["secret" "password"]] [
alert "Incorrect password - look in the source!"
]
; don't go on until the user gets the password right.
break
]
]
; exit the forever loop after 5 seconds have passed
]
][
if entered-text <> "" [
write/append webserver join sent-message [newline]
]
; default case: as long as the entered message is not
; blank ([Enter]), write the message to the web server
; (append it to the current text)
]
]
; when the "forever" loop is exited, do the following:
cls print "Goodbye!"
write/append webserver join now [
": " name " has closed chat." newline]
wait 1
The bulk of this program runs within a "forever" loop, and uses a conditional "switch" statement to decide how to respond to user input. This is a classic structure that can be adjusted to match a variety of generalized situations in which the computer repeatedly waits for and responds to user interaction at the command prompt.
5.5 Image Effector
The next application creates a GUI interface, downloads and displays an image from the Internet, allows you to apply effects to it, and lets you save the effected image to the hard drive. In the mix, there are several routines which get data, and alert the user with text information.
REBOL [Title: ""]
; header is still required, even if a title isn't included
effect-types: ["Invert" "Grayscale" "Emboss" "Blur" "Sharpen"
"Flip 1x1" "Rotate 90" "Tint 83" "Contrast 66"
"Luma 150" "None"]
; this creates a short list of image effects that are built
; into Rebol, and assigns the variable word "effect-types"
; to the block
do %/c/play_sound.r
; The line above imports the simple "play-sound" function
; created earlier in the tutorial. For this program to work
; correctly as it is, the play_sound.r file should be saved
; to C:\
image-url: to-url request-text/title/default {
Enter the url of an image to use:} trim {
http://rebol.com/view/demos/palms.jpg}
; ask user for the location of a new image (with a default
; location), and assign it to the word "new-image"
gui: [
; The following code displays the program menu, using a
; "choice" button widget (a menu-select type of button
; built in to Rebol). The button is 160 pixels
; across, and is placed at the uppermost, leftmost
; pixel in the GUI (0x0) using the built-in word "at".
; The action block for the button contains various
; functions to be performed, based on the selected choice
; (using conditional "if" evaluations. This could have
; been done with less code, using a "switch" syntax.
; "If" was used, however, to demonstrate that there are
; always alternate ways to express yourself in code -
; just like in spoken language.).
across
; horizontally aligns all the following GUI widgets,
; so they appear next to each other in the layout
; (the default behavior in Rebol is to align elements
; vertically).
space -1
; changes the spacing of consecutive widgets so they're
; on top of each other
at 20x2 choice 160 tan trim {
Save Image} "View Saved Image" "Download New Image" trim {
-------------} "Exit" [
if value = "Save Image" [
filename: to-file request-file/title/file/save trim {
Save file as:} "Save" %/c/effectedimage.png
; request a filename to save the image as,
; defaults to "c:\effectedimage.png"
save/png filename to-image picture
; save the image to hard drive
]
if value = "View Saved Image" [
view-filename: to-file request-file/title/file trim {
View file:} "Save" filename
view/new center-face layout [image load view-filename]
; read the selected image from the hard drive
; and display it in a new GUI window
]
if value = "Download New Image" [
new-image: load to-url request-text/title/default trim {
Enter a new image url} trim {
http://www.rebol.com/view/bay.jpg}
; ask for the location of a new image,
; and assign it to the word "new-image"
picture/image: new-image
; replace the old image with the new one
show picture ; update the GUI display
]
if value = "-------------" [] ; don't do anything
if value = "Exit" [
play-sound %/c/windows/media/tada.wav
quit ; exit the program
]
]
choice tan "Info" "About"
[alert "Image Effector - Copyright 2005, Nick Antonaccio"]
; a simple "about" box
below
; vertically aligns successive GUI widgets -
; the opposite of "across"
space 5
; spread out the widgets some more
pad 2
; put 2 pixels of blank space before the next widget
box 550x1 white
; draws a line 550 pixels wide, 1 pixel tall
; (just a cosmetic separator)
pad 10
; put some more space between widgets
vh1 "Double click each effect in the list on the right:"
; a big text header for the GUI
return
; advances to the next row in the GUI
across
picture: image load image-url
; get the image entered at the beginning of the program,
; and give it a label
text-list data effect-types [
current-effect: to-string value
picture/effect: to-block form current-effect
show picture
]
; The code above creates a text-list gui widget
; and assigns a block of actions to it, to be run whenever the
; user clicks on the list. The block of actions is indented
; and each action is placed on separate line for readability.
; The first line assigns the word "current-effect" to the value
; which the user has selected from the list. The second line
; applies that effect to the image (the words "to-block" and "form"
; are required for the way effects are applied syntactically.
; The third line displays the newly effected image. The "show"
; word is _very_ important. It needs to be used whenever a GUI
; element is updated.
]
view/options center-face layout gui [no-title]
; display the gui block above
; "/options [no title]" displays the window without a title bar
; (so it can't be moved around),
; and "center-face" centers the window on the screen
5.6 Guitar Chord Diagram Maker
This program creates, saves, and prints collections of guitar chord fretboard diagrams. It demonstrates some common and useful file, data, and GUI manipulation techniques, including the drag-and-drop "feel" technique, used here to slide the pieces around the screen. It also demonstrates the very important technique of printing output to html, and then previewing in a browser (to be printed on paper, uploaded to a web site, etc.). This is a useful cross-platform technique that can be used to view and print formatted hard copies of Rebol data:
Rebol [Title: "Guitar Chord Diagram Maker"]
; load embedded images:
fretboard: load 64#{
iVBORw0KGgoAAAANSUhEUgAAAFUAAABkCAIAAAB4sesFAAAACXBIWXMAAAsTAAAL
EwEAmpwYAAAA2UlEQVR4nO3YQQqDQBAF0XTIwXtuNjfrLITs0rowGqbqbRWxEEL+
RFU9wJ53v8DN7Gezn81+NvvZXv3liLjmPX6n/4NL//72s9l/QGbWd5m53dbc8/kR
uv5RJ/QvzH42+9nsZ7OfzX62nfOPzZzzyNUxxh8+qhfVHo94/rM49y+b/Wz2s9nP
Zj+b/WzuX/cvmfuXzX42+9nsZ7OfzX4296/7l8z9y2Y/m/1s9rPZz2Y/m/vX/Uvm
/mWzn81+NvvZ7Gezn8396/4l2/n+y6N/f/vZ7Gezn81+tjenRWXD3TC8nAAAAABJ
RU5ErkJggg==
}
barimage: load 64#{
iVBORw0KGgoAAAANSUhEUgAAAEoAAAAFCAIAAABtvO2fAAAACXBIWXMAAAsTAAAL
EwEAmpwYAAAAHElEQVR4nGNsaGhgGL6AaaAdQFsw6r2hDIa59wCf/AGKgzU3RwAA
AABJRU5ErkJggg==
}
dot: load 64#{
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAACXBIWXMAAAsTAAAL
EwEAmpwYAAAAFElEQVR4nGNsaGhgwA2Y8MiNYGkA22EBlPG3fjQAAAAASUVORK5C
YII=
}
; Gui Design:
; The routine below was defined in the section about "feel":
movestyle: [
engage: func [f a e] [
if a = 'down [
initial-position: e/offset
remove find f/parent-face/pane f
append f/parent-face/pane f
]
if find [over away] a [
f/offset: f/offset + (e/offset - initial-position)
]
show f
]
]
; With that defined, adding "feel movestyle" to any widget
; makes it movable within the GUI. It's very useful for all
; sorts of graphic applications... If you want to pursue
; building graphic layouts that respond to user events, learning
; all about how "feel" works in Rebol is very important. See
; the URL above for more info.
gui: [
backdrop white
; makes the GUI background white
currentfretboard: image fretboard 255x300
; show the fretboard image, and resize it
; (the saved image is actually 85x100 pixels)
currentbar: image barimage 240x15 feel movestyle
; Show the bar image, resize it, and make it movable.
; Notice the "feel movestyle". Thats' what enables
; the dragging.
text "INSTRUCTIONS:" underline
text "Drag dots and other widgets onto the fretboard."
across
text "Resize the fretboard:"
tab
; "tab" aligns the next GUI element with a predefined
; column spacer
rotary "255x300" "170x200" "85x100" [
currentfretboard/size: to-pair value show currentfretboard
switch value [
"255x300" [currentbar/size: 240x15 show currentbar]
"170x200" [currentbar/size: 160x10 show currentbar]
"85x100" [currentbar/size: 80x5 show currentbar]
]
]
; The rotary button above lets you select a size for the
; fretboard. In the action block, the fretboard image is
; resized, and then the bar image is also resized,
; according to the value chosen. This keeps the bar size
; proportioned correctly to the fretboard image.
; After each resize, the GUI is updated to actually display
; the changed image. The built-in word "show" updates the
; display. This needs to be done whenever a widget is
; changed within a GUI. Be aware of this - not "show"ing
; a changed GUI element is an easily overlooked source of
; errors.
return
button "Save Diagram" [
filename: to-file request-file/save/file "1.png"
save/png filename to-image currentfretboard
]
; The action block of the above button requests a filename
; from the user, and then saves the current fretboard image
; to that filename.
tab
; The action block of the button below prints out a user-
; selected set of images to an html page, where they can be
; viewed together, uploaded the Internet, sent to a printer,
; etc.
button "Print" [
filelist:
sort request-file/title "Select image(s) to print:"
; Get a list of files to print.
html: copy "<html><body>"
; start creating a block that holds the html layout,
; and give it the label "html".
foreach file filelist [
append html rejoin [
{<img src="file:///} to-local-file file {">}
]
]
; The foreach loop builds an html layout that displays
; each of the selected images.
append html [</body></html>]
; finish up the html layout. Now the variable "html"
; contains a complete html document that will be
; written to the hard drive and opened in the default
; browser. The code below accomplishes that.
write %chords.html trim/auto html
browse %chords.html
]
]
; Each of the following loops puts 50 movable dots onto the GUI,
; all at the same locations. This creates three stacks of dots
; that the user can move around the screen and put onto the
; fretboard. There are three sizes to accommodate the resizing
; feature of the fretboard image. Notice the "feel movestyle"
; code at the end of each line. Again, that's what makes the
; dots draggable.
loop 50 [append gui [at 275x50 image dot 30x30 feel movestyle]]
loop 50 [append gui [at 275x100 image dot 20x20 feel movestyle]]
loop 50 [append gui [at 275x140 image dot 10x10 feel movestyle]]
; The following loops add some additional dragable widgets to
; the GUI.
loop 6 [append gui [at 273x165 text "X" bold feel movestyle]]
loop 6 [append gui [at 273x185 text "O" bold feel movestyle]]
view layout gui
5.7 Listview Database Front End
This example uses the listview module found at http://www.hmkdesign.dk/rebol/list-view/list-view.r. The listview module handles all the main work of displaying, sorting, filtering, altering, and manipulating data, with a familiar user interface that's easy to program. Documentation is available at http://www.hmkdesign.dk/rebol/list-view/list-view.html. This example downloads the list-view module from the Internet, and then imports it from the hard drive. To avoid that step, the module could simply be embedded in the code. Clicking on a column header in the example below sorts the data by the selected column, ascending or descending. Clicking the diamond in the upper right hand corner returns the data to its unsorted order. Selecting a row of data with the mouse allows each cell to be edited directly. Because inline editing is possible, no additional GUI widgets are required for data input/output. That's a powerful tool which is useful in a wide variety of situations.
Rebol [title: "Database"]
; The function below watches for the GUI close button, to keep
; the program from being shut down accidentally. The code was
; adjusted from an example at:
; http://www.rebolforces.com/view-faq.html
evt-close: func [face event] [
either event/type = 'close [
inform layout [
across
Button "Save Changes" [
; when the save button is clicked, a backup data
; file is automatically created:
backup-file: to-file rejoin ["backup_" now/date]
write backup-file read %database.db
save %database.db theview/data quit
]
Button "Lose Changes" [quit]
Button "CANCEL" [hide-popup]
] none ] [
event
]
]
insert-event-func :evt-close
; The code below is the list-view.r module in compressed format.
; It's decompressed, and then imported with the "do" command.
; Like any other module, you don't have to understand how it was
; programmed (the uncompressed code is all just native Rebol).
; You just have to include the compressed blob, and learn how
; to use it...
if not exists? %list-view.r [write %list-view.r read
http://www.hmkdesign.dk/rebol/list-view/list-view.r
]
do %list-view.r
; The following conditional evaluation checks to see if a
; database file exists. If not, it creates a file with
; some empty blocks:
if not exists? %database.db [write %database.db {[][]}]
; Now the stored data is read into a variable word:
database: load %database.db
; Here's the guts of the program. Be sure to read the
; list-view documentation to see how the widget works.
view center-face gui: layout [
h3 {To enter data, double-click any row, and type directly
into the listview. Click column headers to sort:}
theview: list-view 775x200 with [
data-columns: [Student Teacher Day Time Phone
Parent Age Payments Reschedule Notes]
data: copy database
tri-state-sort: false
editable?: true
]
across
button "add row" [theview/insert-row]
button "remove row" [
if (to-string request-list "Are you sure?"
[yes no]) = "yes" [
theview/remove-row
]
]
button "filter data" [
filter-text: request-text/title trim {
Filter Text (leave blank to refresh all data):}
if filter-text <> none [
theview/filter-string: filter-text
theview/update
]
]
button "save db" [
backup-file: to-file rejoin ["backup_" now/date]
write backup-file read %database.db
save %database.db theview/data
]
]
5.8 Peer-to-Peer Instant Messenger
|