Written for the Mar 22 LIAS Seminar on Llama Gui.
Slide 1: Llama Gui
Slide 2: Background
- Java is a cross-platform language
- Write once...
- GUI libraries/classes: Swing, AWT
- Both require lots of support code
- A button can be 8 lines or more for each button
- PLUS setup code for the layout manager
- Have to deal with layout managers, widgets, callbacks
Slide 3: Burrito
- December 2004...
- SOFIA AOR Editor + WASP ADP Gui = Frazzled Scott
- Use first AOR Editor version as prototype
- Distill both products down to requirements
- How are they similar
- Most of those "8 lines" for a button are nearly identical to eachother
- Most of the AOR Editor was: Label - field - units
Slide 4: Result
- Simple XML structure with most defaults implicit
- no need to have everywhere: grayed="false" etc.
- Use various layout managers "under the covers"
- Use XML Nesting to determine structure
- Those "8 lines" are all in the parser
- Runtime configurability
- App can be run on multiple platforms
- Encapsulate everything that is pure GUI code in one place
Slide 5: Supported GUI Elements
- Structure:
- Menus, Panels, Tabs, Scroll Panes, Borders, Columns
- Widgets:
- Button, Radio Button, Check Button
- Text box, Text area
- Slider, Image display, Combo Boxes
- Compound:
- File Chooser
- Accept/Cancel button Groupings
- units and range checking on applicable widgets
- default value/content for widgets
- conditionals - runtime-definable
- "LGJPanel" class that maintains it all
- retrieval of widgets, set/get contents, etc.
Slide 6: Comparison Example - layout manager
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
Insets i = new Insets( 4, 1, 1, 4 ); // s, w, e, n
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createEmptyBorder( 1,5,1,5 ));
//pane.setLayout(new GridLayout(0, 1, 5, 5));
pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS ));
JPanel summaryPane = new JPanel();
summaryPane.setBorder( BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder( "Summary" ),
BorderFactory.createEmptyBorder( 1,5,1,5 )));
// set up the layout
gridbag = new GridBagLayout();
c = new GridBagConstraints();
c.insets = i;
summaryPane.setLayout( gridbag );
This simply sets up a layout onto which widgets are placed.
There is no analog in
LlamaGui for this, since the layout is implicit.
Slide 7: Comparison Example - Swing - adding two widgets
// Title
// label (uses 'widgethelper' class, not shown)
summaryPane.add( wh.newLabel( gridbag, c, "Obs. Title", 0 ));
// text
title_text = new JTextField( 40 );
wh.setupWidgetPositionWide( gridbag, c, title_text, 0 );
gridbag.setConstraints( title_text, c );
title_text.setEditable( false );
title_text.setText( "the title" );
summaryPane.add( title_text );
this.widgetMap.put( "/AOR/SummaryInformation/ObservationTitle", title_text );
// Obj Name
// label (uses 'widgethelper' class, not shown)
summaryPane.add( wh.newLabel( gridbag, c, "Object Name", 2 ));
// text
objname_text = new JTextField( 40 );
objname_text.setEditable( false );
objname_text.setText( "an object" );
wh.setupWidgetPositionWide( gridbag, c, objname_text, 2 );
gridbag.setConstraints( objname_text, c );
summaryPane.add( objname_text );
this.widgetMap.put( "/AOR/SummaryInformation/ObjectName", objname_text );
- I stored the widget pointers in
widgetMap, for referencing later
- no need for a billion globals
- easier to maintain
- parser of the data file mapped to widget names one-for-one
Slide 8: Comparison Example - LlamaGui - adding two widgets
The equivalent of this in
LlamaGui (with the exception of wrapper XML...)
<Text label="Obs. Title"
ref="/AOR/SummaryInformation/ObservationTitle">the title</Text>
<Text label="Object Name"
ref="/AOR/SummaryInformation/ObjectName">an object</Text>
- Internally, a similar map is used, but you access them via support functions
Slide 9: Example - LlamaGui - support code
Setting up the GUI:
LGJParser theParser = new LGJParser();
if( theParser.initPanelFromFile( "gui.xml", "gloop", this ) == false ) {
theParser.printErrorDump( System.out );
System.exit( 0 );
}
LGJPanel thePanel = theParser.getLGJPanel();
// wrap it in a scroll pane for the heck of it
frame.getContentPane().add( new JScrollPane( thePanel ));
Handling button presses:
public void actionPerformed( java.awt.event.ActionEvent e )
{
// menu options and such...
if( e.getActionCommand().equals( "But1" ) ) {
System.out.println( "Button was pressed!" );
thePanel.setWidgetText( "field 1", "Pressed!" );
}
}
Here's an XML file that can be loaded in via the above code:
<LlamaGui>
<Panel id="gloop">
<Text ref="field 1" label="one">This is field 1</Text>
<Button ref="But1" label="Button!">Press Me Here</Button>
</Panel>
</LlamaGui>
Slide 10: Example - Menus and conditionals
- Lets the system decide segments of XML to use at runtime
<LlamaGui>
<MenuBar id="menus">
<Parameter istrue="not OS X">
<Menu ref="/menu1" label="File" mnem="F">
<MenuItem ref="app/reconnect" label="Reconnect"/>
<Separator/>
<MenuItem ref="quit" label="Quit"/>
</Menu>
</Parameter>
<Parameter istrue="OS X">
<Menu ref="/menu1" label="Connection" mnem="F">
<MenuItem ref="app/reconnect" label="Reconnect"/>
<Separator/>
<MenuItem ref="quit" label="Quit"/>
</Menu>
</Parameter>
<HorizontalGlue/>
<Menu ref="/help" label="Help">
<MenuItem ref="/help/help" alt="H" label="Help"/>
<MenuItem ref="/help/about" label="About..."/>
</Menu>
</MenuBar>
</LlamaGui>
Slide 11: Example - Menu support code
LGJParser theParser = new LGJParser();
boolean onOSX = true; /* set this elsewhere */
if( onOSX ) theParser.setParamString( "OS X", "true" );
if( !onOSX ) theParser.setParamString( "not OS X", "true" );
if( theParser.initPanelFromFile( "gui.xml", "gloop", this ) == false ) {
theParser.printErrorDump( System.out );
System.exit( 0 );
}
LGJMenuBar theMenubar = theParser.getLGJMenuBar();
frame.setJMenuBar( theMenuBar );
So, you can see that adding menus, and conditional sections is easy to do, although a little messy.
- can't really do "else" easily
- this is 'good enough' for now.
Slide 12: DEMO - ADP Gui
<LlamaGUI>
...
<Tabs>
<Tab label="Imgs" ref="/Images/tab">
<Columns>
<Col>
<ImageView bgcolor="LIGHTGRAY" type="single"
label="waiting for image data..."
w="270" h="160"
ref="/Images/Image1"/>
<Columns>
<Col>
<ComboBox ref="/Images/Image1 Sel"
label="" stretch="false">
<Option>- pause -</Option>
<Option>SWIR</Option>
<Option>MWIR</Option>
<Option selected="true">LWIR</Option>
<Option>Terrapix</Option>
<Option>GEO</Option>
<Option>Fire</Option>
</ComboBox>
</Col>
<Col>
<Text ref="/Images/Image1 Sequence" editable="false"
label="Seq" w="5">0</Text>
</Col>
<Col>
<Text ref="/Images/Image1 Temp" editable="false"
w="5">?</Text>
</Col>
</Columns>
</Col>
<Col>
<ImageView bgcolor="GRAY" type="single"
label="waiting for image data..."
w="270" h="160"
ref="/Images/Image2"/>
<Columns>
<Col>
<ComboBox ref="/Images/Image2 Sel"
label="" stretch="false">
<Option>- pause -</Option>
<Option>SWIR</Option>
<Option selected="true">MWIR</Option>
<Option>LWIR</Option>
<Option>Terrapix</Option>
<Option>GEO</Option>
<Option>Fire</Option>
</ComboBox>
</Col>
<Col>
<Text ref="/Images/Image2 Sequence" editable="false"
label="Seq" w="5">0</Text>
</Col>
<Col>
<Text ref="/Images/Image2 Temp" editable="false"
w="5">?</Text>
</Col>
</Columns>
</Col>
</Columns>
<Columns>
<Col>
<ImageView bgcolor="DARKGRAY" type="single"
label="waiting for image data..."
w="270" h="160"
ref="/Images/Image3"/>
<Columns>
<Col>
<ComboBox ref="/Images/Image3 Sel"
label="" stretch="false">
<Option>- pause -</Option>
<Option selected="true">SWIR</Option>
<Option>MWIR</Option>
<Option>LWIR</Option>
<Option>Terrapix</Option>
<Option>GEO</Option>
<Option>Fire</Option>
</ComboBox>
</Col>
<Col>
<Text ref="/Images/Image3 Sequence" editable="false"
label="Seq" w="5">0</Text>
</Col>
<Col>
<Text ref="/Images/Image3 Temp" editable="false"
w="5">?</Text>
</Col>
</Columns>
</Col>
<Col>
<ImageView bgcolor="CYAN" type="single"
label="waiting for image data..."
w="270" h="160"
ref="/Images/Image4"/>
<Columns>
<Col>
<ComboBox ref="/Images/Image4 Sel"
label="" stretch="false">
<Option>- pause -</Option>
<Option>SWIR</Option>
<Option>MWIR</Option>
<Option>LWIR</Option>
<Option selected="true">Terrapix</Option>
<Option>GEO</Option>
<Option>Fire</Option>
</ComboBox>
</Col>
<Col>
<Text ref="/Images/Image4 Sequence" editable="false"
label="Seq" w="5">0</Text>
</Col>
<Col>
<Text ref="/Images/Image4 Temp" editable="false"
w="5">?</Text>
</Col>
</Columns>
</Col>
</Columns>
</Tab>
<Tab label=" Big " ref="/Big/tab">
<ImageView type="single"
label="waiting for image data..."
w="500" h="340"
ref="/Big/Image"/>
<Columns>
<Col>
<ComboBox ref="/Big/Image Sel"
label="Band:" stretch="false">
<Option>- pause -</Option>
<Option>SWIR</Option>
<Option>MWIR</Option>
<Option>LWIR</Option>
<Option>Terrapix</Option>
<Option>GEO</Option>
<Option>Fire</Option>
</ComboBox>
</Col>
<Col>
<Text ref="/Big/Image Sequence" editable="false"
label="Sequence" w="5">0</Text>
</Col>
<Col>
<Text ref="/Big/Image Temp" editable="false"
w="5">?</Text>
</Col>
</Columns>
</Tab>
</Tabs>
...
</LlamaGUI>
Slide 13: Advantages
- VERY fast prototyping of GUI layouts
- The prototype is the usable final product
- The layout and structure of the GUI is easily changed
- The GUI layout is stored in an xml file, in the jar
- One 'engine' for your application
- Multiple front ends for it
- Llama Config class for loading/saving basic data, settings
Slide 14: Design issues
- conditionals are messy
- "Columns" are inconvenient - think HTML Tables...
<Columns>
<Col>
...
</Col><Col>
...
</Col>
</Columns>
- There's gotta be a better way than that...
- No macro system for even shorter simplifications
Overall though, I think it's advantageous to use it for making Java GUIs.
Slide 15: Integration with the WASP ADP Gui application
Additional materials used for this presentation are available at
http://www.cis.rit.edu/~sdlpci/seminars/