Introduction to newLISP/Graphical interface

Graphical interfaces edit

Introduction edit

With newLISP you can easily build graphical interfaces for your applications. This introductory document is long enough already, so I won't describe the newLISP-GS feature set in any detail. But there's room for a short example to give you a taste of how it works.

The basic components of newLISP-GS are containers, widgets, events, and tags. Your application consists of containers, which hold widgets and other containers. By giving everything a tag (a symbol), you can control them easily. And when the user of the application clicks, presses, and slides things, events are sent back to newLISP-GS, and you write code to handle each event.

A simple application edit

To introduce the basic ideas, this chapter shows how easy it is to build a simple application, a colour mixer:

 

You can move the sliders around to change the colour of the central area of the window. The colour components (numbers between 0 and 1 for red, green, and blue), are shown in a text string at the bottom of the window.

In newLISP-GS the contents of a container are arranged depending on the type of layout manager you choose - at present you can have flow, grid, or border layouts.

The following diagram shows the structure of the application's interface. The primary container, which in this case is a frame called 'Mixer', is filled with other containers and widgets. The top area of the window, containing the sliders, consists of a panel called 'SliderPanel', which in turn is filled with three panels, one for each slider, called 'RedPanel', 'GreenPanel', and 'BluePanel'. Below, the middle area holds a canvas called 'Swatch' to show the colour, and at the bottom area there's a text label, called 'Value', displaying the RGB values as text. Each area is laid out using a different layout manager.

 

There's just a single handler required. This is assigned to the sliders, and it's triggered whenever a slider is moved.

The first step is to load the newLISP-GS module:

#!/usr/bin/env newlisp

(load (append (env "NEWLISPDIR") "/guiserver.lsp"))

This provides all the objects and functions required, in a context called gs.

The graphics system is initialized with a single function:

(gs:init)

The various parts of the interface can be added one by one. First I define the main window, and choose the border layout. Border layouts let you place each component into one of five zones, labelled "north", "west", "center", "east" and "south".

(gs:frame 'Mixer 200 200 400 300 "Mixer")
(gs:set-resizable 'Mixer nil)
(gs:set-border-layout 'Mixer)

The top panel to hold the sliders can now be added. I want the sliders to be stacked vertically, so I'll use a grid layout of 3 rows and 1 column:

(gs:panel 'SliderPanel)
(gs:set-grid-layout 'SliderPanel 3 1)

Each of the three colour panels, with its companion labels and slider, is defined. The sliders are assigned the slider-handler function. I can write that when I've finished defining the interface.

(gs:panel  'RedPanel)
(gs:panel  'GreenPanel)
(gs:panel  'BluePanel)

(gs:label  'Red   "Red"   "left" 50 10 )
(gs:label  'Green "Green" "left" 50 10 )
(gs:label  'Blue  "Blue"  "left" 50 10 )

(gs:slider 'RedSlider   'slider-handler "horizontal" 0 100 0)
(gs:slider 'GreenSlider 'slider-handler "horizontal" 0 100 0)
(gs:slider 'BlueSlider  'slider-handler "horizontal" 0 100 0)

(gs:label  'RedSliderStatus   "0"  "right" 50 10)
(gs:label  'GreenSliderStatus "0"  "right" 50 10)
(gs:label  'BlueSliderStatus  "0"  "right" 50 10)

The gs:add-to function adds components to a container, using the layout that's been assigned to it. If no layout has been assigned, the flow layout, a simple sequential layout, is used. Specify the target container first, then give the components to be added. So the objects tagged with 'Red, 'RedSlider, and 'RedSliderStatus are added one by one to the 'RedPanel container. When the three panels have been done, they can be added to the SliderPanel:

(gs:add-to 'RedPanel 'Red 'RedSlider 'RedSliderStatus) 
(gs:add-to 'GreenPanel 'Green 'GreenSlider 'GreenSliderStatus)
(gs:add-to 'BluePanel 'Blue 'BlueSlider 'BlueSliderStatus)

(gs:add-to 'SliderPanel 'RedPanel 'GreenPanel 'BluePanel)

You can draw all kinds of graphics on a canvas, although for this application I'm just going to use a canvas as a swatch, a single area of colour:

(gs:canvas 'Swatch)

(gs:label 'Value "")
(gs:set-font 'Value "Sans Serif" 16)

Now the three main components - the slider panel, the colour swatch, and the label for the value - can be added to the main frame. Because I assigned the border layout to the frame, I can place each component using directions:

(gs:add-to 'Mixer 'SliderPanel "north" 'Swatch "center" 'Value "south")

We haven't used the east and west areas.

By default, frames and windows start life as invisible, so now is a good time to make our main frame visible:

(gs:set-visible 'Mixer true)

That completes the application's structure. Now some initialization is required, so that something sensible appears when the application launches:

(set 'red 0 'green 0 'blue 0)
(gs:set-color 'Swatch (list red green blue))
(gs:set-text  'Value (string (list red green blue)))

Finally, I mustn't forget to write the handler code for the sliders. The handler is passed the id of the object which generated the event, and the slider's value. The code converts the value, an integer less than 100, to a number between 0 and 1. Then the set-color function can be used to set the colour of the canvas to show the new mixture.

(define (slider-handler id value)
  (cond 
     ((= id "MAIN:RedSlider") 
        (set 'red (div value 100))
        (gs:set-text 'RedSliderStatus (string red)))
     ((= id "MAIN:GreenSlider") 
       (set 'green (div value 100))
        (gs:set-text 'GreenSliderStatus (string green)))
     ((= id "MAIN:BlueSlider") 
       (set 'blue (div value 100))
       (gs:set-text 'BlueSliderStatus (string blue)))
     )
  (gs:set-color 'Swatch (list red green blue))
  (gs:set-text  'Value (string (list red green blue))))

Only one more line is necessary and we've finished. The gs:listen function listens for events and dispatches them to the handlers. It runs continuously, so you don't have to do anything else.

(gs:listen)

This tiny little application has barely scratched the surface of newLISP-GS, so take a look at the documentation and have a go!