links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Morphic for Dummies
Last updated at 6:24 pm UTC on 14 December 2006
Doing a Simple UI Using Morphic

Note: This tutorial was salvaged from archive.org

This tutorial explains some workings of morphic and shows how I implemented the GUI for the Subway Simulation Project.

The first step in working with morphic is to create a morphic world. A morphic world is the "canvas" to which all the widgets and other components are added. A morphic world is quite robust and supports adding objects as needed and allows for changes in color, and such, as well.

You create the world by typing:

aWorld := WorldMorph new initialize.

(is there a zeroth step? When I do this, I get a dialog saying aWorld is undefined and do I want to create it as a temp. When I do that, I get a dialog saying "aWorld" is unused in this method, ok to remove it?" - whisper@accessone.com)

Try to execute the line. Nothing occurs. This is because you must tell the world to show itself, so in the next line type the following:

aWorld openWithTitle:'Hello World'.

And like magic up comes and empty window with the title 'Hello World'. You are now in the world of morphic. You can start adding components by right clicking anywhere and picking 'new morph'. This brings up a menu of all available morphic objects and also includes any morphic objects you created by subclassing from existing morphic objects.

Let's add a simple button to the world.

1) Right click in the world.

2) Select the 'new morph' item.

3) Select Morphic-Widgets|SimpleButtonMorph.

4) Click anywhere to place your button.

You now have a morphic world with a button. Now experiment a little and add some other morphs to your world.

Another great feature of the morphs is that they can all be modified after being placed. Right clicking on our button brings up a menu of available modifications. Let's start by moving the button around.

1) Right click on the button and select grab from the menu.

2) Now move the mouse around and you can place the button anywhere you wish.

You can also make some other modifications such as resizing and color changing. Now try out some of the other modifications until you feel comfortable with it.

Now that we discussed how to add the buttons for our GUI, let's add an area where we will show the text for our output.

We will use a TextMorph to show the output. Now add a TextMorph to the world.

1) Right click the world and select 'new morph'|Morph-Basic|TextMorph.

You now have text area to show the output.

All this may be nice and neat, but when you close the window, gone are your morphs. You can get around this problem by adding morphs from within your program.

You can add a button by typing the following:

aButton := SimpleButtonMorph new initialize. "initialize the button"
aButton bounds: (112@69 corner: 158@87) . "set up it coordiantes and size"
aButton label:'Button Label'. "change the button label"
aWorld addMorph: aButton. "tell the world to add the button"

You will now get a button added to your world. You may wonder where I got the value for my bounds. Like all objects, morphs can be inspected, and from there we can get some information about them.

Do the following to this:

1) Right click on your button and select 'inspect'.

You now have a new window showing all the variables of the morph. Now just place the button where you would like it and copy the bounds value after inspecting, and you have the coordinates to accurately place any object.

I will now show, step by step, how I created my user interface.

The first thing I did was make a new object and sublassed it off of SimpleButtonMorph. The reason for this: you must overwrite 'doButtonAction' method which defines what occurs when the button gets clicked.

In my doButtonAction the first thing I do is create a temprorary SubwaySimulation. This will let us access the output from the simulation. To ease the process, you should create methods in your program to return the output as a string.

Next I get the textArea I will be changing. I do this with the line:
textArea := owner findA:TextMorph.

The owner is the worldMorph and is analogous to calling super. findA returns the textmorph that was added to the world.

The rest of the method goes through various tests to determine which button was clicked, and sets the textArea content accordingly.

This is probably not the best way to this, but it is the simplest and fastest. A better way involves changing the doButtonAction method when you set your button up, so the doButtonAction would be different for each instance of the button and no tests would be needed.

Now let's add the buttons to the morph. This is done as outlined above. So simply add as many buttons as needed to satisfy your button needs. You can get their coordinates by first placing them then inspecting them.

Do the same for the text area and you are done. Now every time a button is clicked, the text area should change in accord with the string return from the associated method.

I include the code for my button and output methods so you can play around with it some more.

Nezir Teke: 
Winter 1998

'From Squeak 1.3 of Jan 16, 1998 on 14 March 1998 at 3:31:15 pm'!

SimpleButtonMorph subclass: #SubwayButton
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Subway'!

!SubwayButton methodsFor: 'events' stamp: 'nt 2/26/98 00:00'!

  "Perform the action of this button. Subclasses may override this method. The default behavior is to send the button's actionSelector to its target object with its arguments."
| textArea temp |
temp := SubwaySimulation new.
textArea := owner findA:TextMorph.

(self label = 'Train 1')
ifTrue: [ textArea contents: temp trainOutput1]
ifFalse: [ (self label = 'Train 2')
ifTrue: [textArea contents: temp trainOutput2]
ifFalse: [ (self label = 'Train 3')
ifTrue: [textArea contents: temp trainOutput3]
ifFalse: [ (self label = 'Train 4')
ifTrue: [textArea contents: temp trainOutput4]
ifFalse: [ (self label = 'Train 5')
                  ifTrue: [textArea contents: temp trainOutput5]
                  ifFalse: [textArea contents: temp systemOutput]]]]].

! !

'From Squeak 1.3 of Jan 16, 1998 on 14 March 1998 at 3:31:18 pm'!

Object subclass: #SubwayOutput

  instanceVariableNames: ''

  classVariableNames: ''

  poolDictionaries: ''

  category: 'Subway'!

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 23:56'!


aWorld TrainButton1 TrainButton2 TrainButton3 TrainButton4 TrainButton5

mytext outputText SystemOutputButton|

aWorld := WorldMorph new initialize.

mytext := 'I am the smartest man alive'.

"–text Area—"

outputText := TextMorph new initialize.

outputText bounds: (119@99 corner: 402@309).

outputText contents: mytext.

"–button 1—"

TrainButton1 := SubwayButton new initialize.

TrainButton1 bounds: (112@69 corner: 158@87) .

TrainButton1 label:'Train 1'.

"—button 2—-"

TrainButton2 := SubwayButton new initialize.

TrainButton2 bounds: (171@69 corner: 207@87) .

TrainButton2 label:'Train 2'.

"—button 3—-"

TrainButton3 := SubwayButton new initialize.

TrainButton3 bounds: (231@69 corner: 279@87) .

TrainButton3 label:'Train 3'.

"—button 4—-"

TrainButton4 := SubwayButton new initialize.

TrainButton4 bounds: (291@69 corner: 351@87) .

TrainButton4 label:'Train 4'.

"—button 5—-"

TrainButton5 := SubwayButton new initialize.

TrainButton5 bounds: (351@69 corner: 423@87) .

TrainButton5 label:'Train 5'.

"—button 6—-"

SystemOutputButton := SubwayButton new initialize.

SystemOutputButton bounds: (112@42 corner: 219@60) .

SystemOutputButton label:'System Histogram'.

"—–Adds to the world——"

aWorld addMorph: TrainButton1.

aWorld addMorph: TrainButton2.

aWorld addMorph: TrainButton3.

aWorld addMorph: TrainButton4.

aWorld addMorph: TrainButton5.

aWorld addMorph: SystemOutputButton.

aWorld addMorph: outputText.

aWorld openWithTitle:'SimTrains'.! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 23:44'!


"returns the output from the stats of the system"

^'Hey look at me Im the system stats'.!

]style[(12 40 10 5 34 2)f1b,f1,f1b,f1,f1b,f1! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:08'!


"returns the output from the stats of train 1"

^'I am Train 1'.! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!


"returns the output from the stats of train 1"

^'I am Train 2'.! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!


"returns the output from the stats of train 1"

^'I am Train 3'.! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:06'!


"returns the output from the stats of train 1"

^'I am Train 4'.! !

SubwayOutput methodsFor: 'output' stamp: 'nt 2/25/98 19:07'!


"returns the output from the stats of train 1"

^'I am Train 5'.! !