370 likes | 600 Views
By Kalman Hazins (kalman.hazins@jhuapl.edu). SWT TUTORIAL. Links. Tutorials: http://www.cs.umanitoba.ca/~eclipse/ Additional tutorials: http://www.developer.com/java/other/article.php/10936_3330861_1 Code examples:
E N D
By KalmanHazins (kalman.hazins@jhuapl.edu) SWT TUTORIAL
Links • Tutorials: • http://www.cs.umanitoba.ca/~eclipse/ • Additional tutorials: • http://www.developer.com/java/other/article.php/10936_3330861_1 • Code examples: • http://www.java2s.com/ExampleCode/SWT-JFace-Eclipse/CatalogSWT-JFace-Eclipse.htm • http://www.eclipse.org/swt/ • Eclipse - SWT Gui builders: • Jigloo - http://www.cloudgarden.com/jigloo • Eclipse ve (visual editor) • SwtGuiBuilder (trial) • Additional free IDEs with builders– Jdeveloper, NetBeans mattise
Agenda • Brief History of Java GUI • Installing SWT • Creating widgets • SWT Layouts • Event Handling • Sample Application
History of Java GUI • AWT – Abstract Windowing Toolkit • First released in 1995 as part of JDK 1.0 by Sun • Heavyweight GUI components – a Java component creates a peer component on the operating system and the peer draws itself. • Least Common Denominator problem – for simple GUI components creating a peer works, what about the components that are only available on some platforms and not others? If you want to write code that’s truly portable – you have to use components that are available on all platforms • The logic makes sense – but how do you develop a serious application using just the basic components (available on all platforms)? • You don’t … • Enter Swing
History of Java GUI (Cont.) • Swing (JFC – Java Foundation Classes) • Released in 1998 as part of JDK 1.1 • Lightweight GUI components – no need for operating system “peer”-based drawing. Let’s draw the components ourselves. Lightweight – there is no actual weight (operating system component) behind the Java component. • Nice toolkit with enhanced Model-View-Controller architecture and superior graphics 2D library. • Portable? You bet. Any problems? Now that you are not actually using the operating system components – you are just mimicking the behavior of the operating system. Hence, the look and feel as well as the response speed are not the same. • Swing has gotten much better with release 1.4.2 and 1.5 of SDK • Still, very few commercial applications are written in Swing.
History of Java GUI (Cont.) • SWT (Standard Widget Toolkit) • GUI toolkit released in November 2001 • Was initially designed to write the now extremely famous and popular Eclipse IDE • “Best of both worlds” approach – use native functionality when available, and Java implementation when unavailable • Takes on the appearance and behavior of the native platform • Because of its close bond with the operating system, SWT needs to be ported separately for every platform • Currently, implementations of SWT are already available for all major desktop operating systems and some handheld devices • The code YOU write will be portable for all the platforms that have SWT implementations for them (unless you decide to do some platform specific stuff like OLE on windows) • http://www.eclipse.org/swt/ - SWT home page
Installing SWT • To compile and run SWT applications you need • SWT jars (usually just swt.jar) • SWT native libraries • Detailed Instructions on obtaining and “installing” • Go to http://www.eclipse.org/downloads/index.php and follow one of the many mirror links • This will bring you to another page, where you want to follow the “Latest Release” link • This should bring you to a page which will say “Release Build: “ in bold letters on the very top • Scroll down to the bottom of the page till you see “SWT Binary and Source” section • Pick the platform you intend to develop on from the list (e.g. Windows XP) and click on “(http)” to start downloading the zip file • Extract the contents of the zip file into a directory and you are ready for the final step(s) (let’s say our directory path is C:\SWT)
Installing SWT (Cont.) • You need to include swt.jar (and possibly other jars) from the C:\SWT folder in your classpath (for an example on setting classpath see http://www.moreservlets.com/Using-Tomcat-4.html#Set-CLASSPATH) • You also need to include the native library files (e.g. *.dll) from C:\SWT in your java.library.path. • The easiest way to do this one is to execute the following command from a regular java program System.out.println(System.getProperty(“java.library.path”)); which will give you a list of the directories in your “library path”. You can then copy the native library files from C:\SWT to one of those directories. -OR- • Simply provide the path when running java java –Djava.library.path=C:\SWT classname
Creating Widgets • SWT Javadoc - http://www.jlab.net/eclipse/doc/3.1/index.html • Simplest SWT program (explained over the next few slides) • import org.eclipse.swt.widgets.*; • import org.eclipse.swt.widgets.Shell; • public class HelloWorld { • public static void main(String[] args) { • Display display = new Display(); • Shell shell = new Shell(display); • shell.setText("Hello World"); • shell.setSize(250, 100); • shell.open(); • while (!shell.isDisposed()) { • if (!display.readAndDispatch()) { • display.sleep(); • } • } • display.dispose(); • } • }
Creating Widgets (Cont.) • Think of Shell object (line 6) being almost the same as a JFrame in Swing, i.e. the highest-level container, which contains other widgets (e.g. buttons, labels etc.) inside of it • Unlike Swing • You need to create Display object (line 5), which represents the underlying windowing system. This in turn serves as a parent to the Shell. (In SWT, every widget you create will always have its parent passed into the widget’s constructor.) • You need to keep the Shell open to listen for events with a while loop (lines 12-16). Otherwise, the Shell will immediately close itself. • Since you are not just writing some Java code (having the luxury of garbage collector clean up after you), but are actually requesting some heavyweight resources from the underlying platform – you need to dispose of those resources when you are done with them(line 17). (Luckily, disposing of the parent disposes of its child, so 99% of time you will not be running around disposing of widgets right and left.) • If you are a Swing guy, and the above 3 things greatly bother you – you can write a simple SWTUtil class to help you deal with it …
Homegrown SWT Utility • SWTUtil.java import org.eclipse.swt.widgets.*; public class SWTUtil { private static Display display = new Display(); public static Shell getShell() { Shell shell = new Shell(display); return shell; } public static void openShell(Shell shell) { shell.open(); // This loop keeps the shell open constantly listening for events while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } }
SWT Utility in Action • “Simplest SWT program” rewritten using SWTUtil class import org.eclipse.swt.widgets.*; public class HelloWorld { public static void main(String[] args) { Shell shell = SWTUtil.getShell(); shell.setText(“Still Hello World"); shell.setSize(250, 100); SWTUtil.openShell(shell); } }
Widget creation explained • A widget in SWT is created by • Specifying parent • Specifying style • A parent is the container that the widget is created inside of (e.g. Shell). • Style is any constant from the SWT class (SWT.PUSH, SWT.BORDER, SWT.LEFT etc). • All styles applicable to the widget can be found in the Javadoc for that widget (as well as its superclasses if any). • If more than one style is desired – separate them out with a “|” bitwise operator (e.g. SWT.MULTI | SWT.V_SCROLL|SWT.H_SCROLL| SWT.BORDER creates a multiline textarea that has vertical and horizontal scrollbars and a border around it). If no specific style is desired – use SWT.NONE. The style you are requesting is only a hint, so if the underlying platform doesn’t support it – you are out of luck. • Creating Label • Label (Composite parent, int style) • Creating Button • Button(Composite parent, int style)
Label • Unselectable, uneditable text • SWT.VERTICAL or SWT.HORIZONTAL can be used together with SWT.SEPARATOR to create a label that is a horizontal/vertical separator • LabelWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class LabelWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Label World"); • shell.setLayout(new GridLayout()); // layouts are explained later • // Create labels • new Label(shell, SWT.NONE).setText("Regular label"); • new Label(shell, SWT.SEPARATOR); • new Label(shell, SWT.SEPARATOR|SWT.HORIZONTAL); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
Button • Once a button – always a button. All types of buttons are created using the Button class. Push buttons, radio buttons, check buttons, toggle buttons and even flat buttons. • ButtonWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class ButtonWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Button World"); • shell.setLayout(new GridLayout(2, true)); // layouts are explained later • new Button(shell, SWT.PUSH | SWT.FLAT).setText("Flat Push Button"); • new Button(shell, SWT.CHECK).setText("Check Button"); • new Button(shell, SWT.TOGGLE).setText("Toggle Button"); • new Button(shell, SWT.RADIO).setText("Radio Button"); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
Text • Different types of text widgets are created using Text class by providing the appropriate styles • TextWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class TextWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Text World"); • shell.setLayout(new GridLayout()); // layouts are explained later • new Text(shell, SWT.NONE).setText("Missing something ..."); • new Text(shell, SWT.BORDER); // regular textfield • new Text(shell, SWT.PASSWORD | SWT.BORDER).setText("password"); • new Text(shell, SWT.READ_ONLY | SWT.BORDER).setText("Can't type inside"); • new Text(shell, SWT.MULTI | SWT.V_SCROLL | SWT.WRAP • | SWT.BORDER).setText("\n\n\n"); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
List • Pick from a list – single or multiple selection available • ListWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class ListWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("List World"); • shell.setLayout(new GridLayout(2, true)); // layouts are explained later • String[] items = "One Two Three Four Five Six".split(" "); • List one = new List(shell, SWT.SINGLE | SWT.BORDER); • one.setItems(items); • one.select(2); • List two = new List(shell, SWT.MULTI | SWT.BORDER); • two.setItems(items); • two.setSelection(items); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
Combo • Benefits of both Text and List widgets in one! • ComboWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class ComboWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Combo World"); • shell.setLayout(new GridLayout(3, true)); // layouts are explained later • String[] items = "One Two Three Four Five Six".split(" "); • Combo one = new Combo(shell, SWT.DROP_DOWN); • one.setItems(items); • Combo two = new Combo(shell, SWT.DROP_DOWN | SWT.READ_ONLY); • two.setItems(items); • Combo three = new Combo(shell, SWT.SIMPLE); • three.setItems(items); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
Group • Used to group widgets together. (Very similar to JPanel in Swing) • GroupWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.GridLayout; • import org.eclipse.swt.widgets.*; • public class GroupWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Group World"); • shell.setLayout(new GridLayout()); // layouts are explained later • Group buttonGroup = new Group(shell, SWT.SHADOW_OUT); • buttonGroup.setText("Six buttons"); • buttonGroup.setLayout(new GridLayout(3, true)); • for(int i = 0; i < 6; i++) { • new Button(buttonGroup, SWT.RADIO).setText("Bottle " + (i + 1)); • } • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
Browser • One of the newer widgets of SWT for displaying web content. • Cool stuff!!! (See the sample application later) • BrowserWorld.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.browser.Browser; • import org.eclipse.swt.layout.FillLayout; • import org.eclipse.swt.widgets.Shell; • public class BrowserWorld { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("Browser World"); • shell.setLayout(new FillLayout()); • Browser browser = new Browser(shell, SWT.BORDER); • browser.setSize(900, 500); • browser.setUrl("http://www.eclipse.org/swt/"); • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
SWT Layouts • Layouts first introduced in AWT • Ease burden of laying out components • Promote cross-platform GUI design • SWT offers 5 layouts: • FillLayout • RowLayout • StackLayout • GridLayout • FormLayout • You set the composite’s layout by calling setLayout() • You don’t have to use a layout, but do you really want your widgets to disappear when the user resizes the window? • More on layouts http://www.eclipse.org/articles/Understanding%20Layouts/Understanding%20Layouts.htm
FillLayout • Places all widgets in either a single column or a single row depending on if SWT.VERTICAL or SWT.HORIZONTAL is used • Makes all widgets the same size • FillLayoutExample.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.FillLayout; • import org.eclipse.swt.widgets.*; • public class FillLayoutExample { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("FillLayoutExample"); • shell.setLayout(new FillLayout(SWT.HORIZONTAL)); • for(int i = 0; i < 3; i ++) { • new Button(shell, (i % 2 == 0) ? SWT.RADIO : SWT.PUSH).setText("Button " + i); • new Text(shell, SWT.BORDER).setText("same size"); • } • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
RowLayout • Similar to FillLayout -places all widgets in either a single column or a single row depending on if SWT.VERTICAL or SWT.HORIZONTAL is used • Doesn’t force all widgets to be the same size • Can wrap to a new row or column if it runs out of space • Can use RowData objects to determine initial heights/widths for controls • RowLayoutExample.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.RowData; • import org.eclipse.swt.layout.RowLayout; • import org.eclipse.swt.widgets.*; • public class RowLayoutExample { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("RowLayoutExample"); • shell.setLayout(new RowLayout(SWT.HORIZONTAL)); • for(int i = 0; i < 3; i ++) { • new Button(shell, (i % 2 == 0) ? SWT.RADIO : SWT.PUSH).setText("Button " + i); • new Text(shell, SWT.BORDER).setLayoutData(new RowData(5, 50)); • } • // pack and show • shell.pack(); • SWTUtil.openShell(shell); • } • }
StackLayout • Similar to CardLayout in Swing. The basic idea is that all widgets are stacked on top of each other and you can only see one at a time. • Need to set topControl attribute for the layout to show any widget on top • Need to call layout() on the Composite every time you reassign topControl • StackLayoutExample.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.custom.StackLayout; • import org.eclipse.swt.events.*; • import org.eclipse.swt.widgets.*; • public class StackLayoutExample { • public static void main(String[] args) { • final Shell shell = SWTUtil.getShell(); • shell.setText("RowLayoutExample"); • shell.setLayout(new StackLayout()); • final Button button1 = new Button(shell, SWT.PUSH); • button1.setText("Button One"); • final Button button2 = new Button(shell, SWT.PUSH); • button2.setText("Button Two"); • class StackLayoutAdapter extends SelectionAdapter { • public void widgetSelected(SelectionEvent event) { • ((StackLayout)shell.getLayout()).topControl = • (event.widget == button1) ? button2 : button1; • shell.layout(); • } • } • button1.addSelectionListener(new StackLayoutAdapter()); • button2.addSelectionListener(new StackLayoutAdapter()); • ((StackLayout)shell.getLayout()).topControl = button1; • shell.pack(); • SWTUtil.openShell(shell); • } • }
GridLayout • Lays out controls in a grid. • A lot of people love this powerful layout. • You can easily specify what you want to happen when the composite is resized • 6 attributes (defaults are preset): • int horizontalSpacing – horizontal space in pixels between adjacent cells • int verticalSpacing – vertical space in pixels between adjacent cells • boolean makeColumnsEqualWidth – forces all columns to be same width • int marginWidth – margin in pixels along right and left edges • int marginHeight – margin in pixels along top and bottom edges • int numColumns – number of columns for the layout • GridLayout(int numColumns, boolean makeColumnsEqualWidth) • Uses GridData objects to provide for better control • Too many options to list … • Warning for Swing programmers – DO NOT TRY TO REUSE GridData objects • Instead, you need to create a new GridData object for every widget that needs non-default behavior
GridLayoutExample.java • import org.eclipse.swt.SWT; • import org.eclipse.swt.layout.*; • import org.eclipse.swt.widgets.*; • public class GridLayoutExample { • public static void main(String[] args) { • Shell shell = SWTUtil.getShell(); • shell.setText("GridLayoutExample"); • shell.setLayout(new GridLayout(2, false)); // 2 columns, same width • // Username • new Label(shell, SWT.RIGHT).setText("Username:"); • Combo cmbUsername = new Combo(shell, SWT.DROP_DOWN); • cmbUsername.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); • cmbUsername.setItems(new String[]{"Howard", "Admin", "Kalman"}); • cmbUsername.setText("Admin"); • // Password • new Label(shell, SWT.RIGHT).setText("Password:"); • new Text(shell, SWT.BORDER | SWT.PASSWORD).GridData(GridData.FILL_HORIZONTAL)); • // Login Button • Button loginButton = new Button(shell, SWT.PUSH | SWT.FLAT); • loginButton.setText("Proceed to your account"); • GridData data = new GridData(GridData.FILL_HORIZONTAL); • data.horizontalSpan = 2; // span 2 columns • loginButton.setLayoutData(data); • shell.pack(); • SWTUtil.openShell(shell); • } • }
FormLayout • Considered the most complex layout of SWT • Based on y = ax + b (not that most people who use it care) • MAXIMUM flexibility • People who understand it – love it • The basic idea is that you layout the widgets relative to each other or to the enclosing composite • Needs a tutorial of its own and is therefore not covered here …
Event Handling • If you are familiar with Swing – this last part should be a joke. • SWT widgets can listen for events to happen. • If you would like to hear when and what they hear – you need to attach a listener to the widget • Listener is basically an interface that defines when certain behaviors happen • Sometimes, listener tries listening out for two many things, but you only care to listen for one particular event • In such a case – you need an adapter. Adapter is a class that implements the interface and from which you can just extend and override the method you are interested in. • Some of the popular listeners/adapters are listed below • FocusListener/FocusAdapter – listens for focus gained and focus lost events • KeyListener/KeyAdapter – listens for key releases and key presses • ModifyListener(only has 1 method) – listens for text modifications • VerifyListener – listens for (and potentially intercepts) text modifications • MouseListener/MouseAdapter – listens for mouse button presses • SelectionListener/SelectionAdapter – listens for selection events (similar to ActionListener in Swing)
EventHandling.java import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; public class EventHandling { private static boolean numbersOnly; public static void main(String[] args) { Shell shell = SWTUtil.getShell(); shell.setText("EventHandling"); shell.setLayout(new GridLayout(2, false)); // input Label lblInput = new Label(shell, SWT.RIGHT); lblInput.setText("Type in here:"); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END); lblInput.setLayoutData(data); Text input = new Text(shell, SWT.BORDER); input.addVerifyListener(new VerifyListener() { public void verifyText(VerifyEvent vEvent) { vEvent.doit = false; // don't allow anything but numbers if(!numbersOnly || vEvent.character == '\b') { vEvent.doit = true; } else if(Character.isDigit(vEvent.character) && numbersOnly) { vEvent.doit = true; } } });
EventHandling.java • Button btnAllow = new Button(shell, SWT.CHECK); • btnAllow.setText("Allow numbers only"); • data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER); • data.horizontalSpan = 2; • btnAllow.setLayoutData(data); • btnAllow.addSelectionListener(new SelectionAdapter() { • public void widgetSelected(SelectionEvent selectionEvent) { • numbersOnly = ((Button)(selectionEvent.widget)).getSelection(); • } • }); • shell.pack(); • SWTUtil.openShell(shell); • } • }
syncExec() / asyncExec() • You should only be updating user interfaces from the user interface thread. • For example, say you create a Label in your main thread. Then, you have a separate thread that does some sort of a long computation. Then, you want to update your label with the result of the second thread’s computation. You CANNOT just call label.setText(newValue) in your second thread. • Instead, you have to call either syncExec(Runnable) or asyncExec(Runnable) methods on the instance of Display // Say you run a thread that calculates new time approximately every second // and tries to update a label new Thread(new Runnable() { public void run() { Display display = shell.getDisplay(); while(!shell.isDisposed()) { try { display.asyncExec(new Runnable() { public void run() { clock.setText((new Date()).toString()); } }); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();
Sample Application • Goal: Write a cute browser in SWT (under 300 lines) that handles most HTML • CuteBrowser.java uses SWTUtil.java • Run the application on Windows XP computer that has java installed