Lab 7 - The Visualizer


What We're Gonna Do:

In this lab, you will build a visualizer to visualize a dictionary in GUI (Graphics User Interface). This visualizer will allow you to dynamically change bindings between data and presentation -- an object-oriented kind of visualizer.

OOA/OOD

This visualizer will visualize one collection of data in a dictionary using bar chart. The height of each bar corresponding a value in that set. So we will have two classes, Visualizer and Bar.

Visualizer

I am a Visualizer. I know which dictionary I am visualizing. I know all the bars I am using. I set their heights after I am specified which collection in that dictionary I will visualize. After I set up the window in which all bars will be displayed, I will tell those bars to display themselves.

Bar

I am a Bar. I know my height. I can set my height and tell the others my height if they want to know. I know the order of me in these bars. I also know my name, my color, etc., if possible. I can display myself in the window given to me.

OOP

Now file in Visualizer.st. In the Workspace type Visualizer demo and do it. You will see a window popped up with several bars! (In some window management system, you need to "do it" twice to see the result.)

Now read the code carefully before you begin to start your job.

Explanations of given code:

In the class method demo of Visualizer, at first a dataset as an array was formed. Then, you feed the dataset to the Visualizer then tell it what binding you want.

v forHeight: #avetemp
This should tell the visualizer that you want to visualize average temperatures as heightbars. Then, all the bars are shown by execution of show.

The data: method in class Visualizer is just to tell which dictionary is wanted, and the instance variable bars is initialized. In forHeight: method, the bars are generated and added to bars.

The method show is one of the most important method here.

        gc := (ExamplesBrowser prepareScratchWindowOfSize: 500 @ 200 ) 
              graphicsContext.
This line is to create a GraphicsContext (a kind of window) with size 500X200. The reason why numberOfBars is calculated is we want the visualizer to know how many entries there are, and assign an area for each bar. The maximum value of the data been visualized is needed in order to create the mapping from values to heights of bars in a give space.

In the showAt: withSize: highest method of Bar class, The size of the rectangle to be drawn is decided in this way: the "full size" rectangle will take a space in the given area with a quarter of the wide room in the left and right, and 1/5 space in top and bottom. The "full size" rectangle will be displayed if the height of this value is the highest among those of the bars. The size of rectangle is proportional to the height of this bar. The coordinations of the rectangle are added to aBarShape .

        gc displayPolygon: aBarShape at: index * x @ 0
This line is to display a polygon specified in aBarShape. The coordinations in aBarShape are relative, referred to the point after at:.

Your Job

It's your turn now. You are going to make it looks like the below.

You need to change the demo method as:

        v := Visualizer new data: cities.       
        v setName: #names.
        v forHeight: #avetemp.
        v show
Your job includes:
  • to display names of each bar under it.
  • to display the height of each bar above it.
  • to draw a "zero" line.

    Hints:

  • To draw a string in a GraphicsContext gc, you can use:
            gc displayString: aString at: aPoint.
    
    while aString can be something like 12 printString and aPoint can be something like 50@27. Notice that aPoint is an absolute coordination.

  • You need to add an instance variable to store the name of each bar.
  • The "zero" line can be drawn either by the bars a part by a part, or by the visualizer when it calls show method.

    After you finished, change your demo to see if you can use your visualizer to show the population of each city.

    Turning in the Code

    OK, that's all for today. Go to the system Browser and select the category holding the Visualizer classes. Choose the middle-button menu item File Out and type a file name (or the default category name) when prompted. Mail this file to cs2390@prism with the code of 'lab7'.

    Here's the visualizer code:

    Object subclass: #Visualizer
            instanceVariableNames: 'data bars '
            classVariableNames: ''
            poolDictionaries: ''
            category: 'Visualizer'!

    !Visualizer methodsFor: 'showing'!

    show | gc numberOfBars max| numberOfBars := bars size. gc := (ExamplesBrowser prepareScratchWindowOfSize: 500 @ 200 ) graphicsContext. max := bars first height. bars do: [:aBar | (max < (aBar height)) ifTrue: [max := aBar height]]. bars do: [:aBar | aBar showAt: gc withSize: (500/numberOfBars)@200 highest:max]! ! !Visualizer methodsFor: 'initialization'! data: aDictionary "connect this visualizer to a dictionary" data := aDictionary. bars := OrderedCollection new.! forHeight: anItem | collection aBar i| collection := data at: anItem. i := 0. collection do: [:aValue | aBar := Bar new. aBar height: aValue. aBar index: i. bars add: aBar. i := i+1].! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Visualizer class instanceVariableNames: ''! !Visualizer class methodsFor: 'demo'! demo | cities v | cities := Dictionary new. cities at: #names put: #(#Atlanta #Detroit #MexicoCity #Oz #NYC). cities at: #populations put: #(100000 1000000 10000000 10.78 3000000). cities at: #avetemp put: #(72 61 91 13 65). cities at: #meanness put: #(4 7 5 2 6). v := Visualizer new data: cities. "Opens up the Visualization window --just a Graphics pane." v forHeight: #avetemp. v show! ! Object subclass: #Bar instanceVariableNames: 'height index ' classVariableNames: '' poolDictionaries: '' category: 'Visualizer'! !Bar methodsFor: 'showing'! showAt: gc withSize: size highest: max | aBarShape x y | aBarShape := OrderedCollection new. x := size x. y := size y * 4 / 5. aBarShape add: x / 4 @ (y - (height * y * 3 / (max * 4))); add: x * 3 / 4 @ (y - (height * y * 3 / (max * 4))); add: x * 3 / 4 @ y; add: x / 4 @ y. gc displayPolygon: aBarShape at: index * x @ 0! ! !Bar methodsFor: 'accessing'! height ^height! height: aValue height := aValue! index: anIndex index := anIndex! !


    News Page | CS2390 Sum'97 Home Page | MMC-CaMILE | STABLE
    Questions/comments/concerns to guzdial@cc.gatech.edu
    Page last updated 8/11/97; 10:00:46 PM