CSC212 Lab 15 2014

From dftwiki3
Jump to: navigation, search

--D. Thiebaut (talk) 09:43, 27 November 2014 (EST)


This lab introduces the Model-View-Controller (MVC) system for implementing Graphical User Interfaces (GUI). Please review the slides from the MVC lecture notes if necessary.


First Contact

50x50Network.png


  • Create a new project, or add new files to an existing project. You will need to import core.jar from the Processing 2 app into your project, and then add it to the build path. This Tutorial on Processing.org is well detailed on how to do this.
  • You need to create 3 different classes, one for the controller, one for the viewer, and one for the model.
  • MVC1_controller.java
  • MVC1_viewer.java
  • MVC1_model.java
  • This tutorial contains the code for all three classes. Copy/paste the code into your three classes.


Run the Applet


  • The starting class is the viewer. It's the applet class.
  • Run it. Play with the applet to understand how it works.


Explore the Code


The Model


  • Read the code for the model. Notice that it contains the bare definition of a graph, its vertices, and its edges, and not much else. That's what a model should contain.
  • Look at the main public methods. They are few. They mostly allow one to
  • initialize a graph, if we want one already made.
  • add an edge
  • get the dimension of the grid
  • get the adjacency list of a vertex


Modification 1
Change the probability100 variable; it controls the probability of creating an edge between a vertex and its neighbors. Its value should range between 0 and 100. The lower the value, the less connected the graph is. The closer to 100, the more fully interconnected the vertices get. Run the applet again to see how connected or disconnected your grid becomes.


Modification 2
Change the maxNoVerticesRows and maxNoVerticesCols which define the number of rows and columns of the grid to something different. Say 40, 60. Or 60, 100. Notice that the visualization will match the new geometry.


The Controller


  • Read the code of the controller.
  • Notice that the most important action it performs is to set up relationships between the three components, controller, model, and viewer. This way the viewer can call methods of the controller or model, if needed, and similarly for the other two.
  • the setVertexUnderMouse() method is a call-back method called by the viewer whenever the mouse is over a vertex. It is a way for the controller to be made aware (by the viewer) that the mouse pointer is over a vertex, and that some action is required. In this case, the controller instructs the viewer to display the vertex number next to the mouse pointer, and to display its adjacency list at the bottom of the applet. The viewer should not really be the one deciding what to do. The controller is the one that should decide.


The Viewer


  • Read the code of the viewer. It is the most complex of the three.
  • It is the main entry point of the application. It is the class that starts it all.
  • The two important methods are:
  • setup(): it is called first, once. Its purpose is to
  • start the controller first.
  • the controller then gets the model to initialize the graph.
  • the controller makes the viewer initialize the geometry of the applet, which will match the grid structure of the graph.
  • draw(): it is called repeatedly, over and over, about 30 times a second. This is what inheriting from the PApplet class will do for us, automatically.
  • The other methods are used to relate vertices to locations on the canvas of the applet, or to figure out if the mouse is over a vertex.


Modification 3
Add a new method to the viewer:
        public void drawCircles( int vertexNo ) {
		int[] xy = getXY( vertexNo, model.getNoRows(), model.getNoCols() );
		int radius = frameCount % 15;
		ellipse( xy[0], xy[1], 2*radius, 2*radius );
	}
This method draws circles around the vertex passed as an argument. The radius of the circles changes with time.
The field frameCount is part of the PApplet class. It is incremented automatically every time the draw(0 method is called. So frameCount % 15 goes 0, 1, 2, 3,...14, 0, 1, 2, 3, 4... 14 in one seconds (since draw() is called 30 times a second). Drawing a circle whose radius goes from 0 to 14 twice in a second will make for some interesting animation.
Make the controller call this new method every time the mouse is over a vertex.
You should see some animation going on every time your mouse pointer is over a vertex.


GUI Elements


Capturing Mouse-Clicks


  • When the mouse is clicked anywhere on the applet, the PApplet class calls a method called mouseClicked(), if it is implemented. So if we want our program to change behavior when the mouse is clicked, all we have to do is create a mouseClicked() method in the viewer, and make it do something.
  • For right now, let's make it print something on the console. Add this method to the viewer:


	/**
	 * this is called automatically by the Applet if the user left-clicks the mouse
	 * on the canvas of the applet.  The location of the click can be found in mouseX
	 * and mouseY.
	 */
	public void mouseClicked() {
	        System.out.println( "Mouse clicked at "+mouseX + ", " + mouseY );
        }



Adding a Button


  • Let's add a very simple, "home-made" button to the viewer.
  • Add a new class called Button.java in your project.
import processing.core.PApplet;

/**
 * A simple class for implementing a square button box.  If the button
 * is clicked, a cross is drawn in the button, otherwise it is 
 * left empty.
 * The button has a status, depending on the last time it was clicked
 * by the mouse.  Every time it is clicked, it switches state.
 */
public class Button {
	private PApplet parent;	// link to applet, so the button can display itself
	private int x;			// x,y coordinates of top left corner
	private int y;
	private int w;			// width (default 20)
	private int h;			// height (default 20)
	boolean ONOFF;			// true = ON, false = OFF
	String caption;			// the text next to the button
	
	/**
	 * constructor
	 * @param p a reference to the PApplet, so that it can 
	 * @param xx the x coordinate of the top-left corner
	 * @param yy the y coordinate of the top-left corner
	 * @param s  the name to print next to the button
	 */
	Button( PApplet p, int xx, int yy, String s ) {
		parent  = p;
		x 		= xx;
		y 		= yy;
		w 		= 20;
		h 		= 20;
		ONOFF 	= false;
		caption = s;
	}
	
	public void setONOFF( boolean b ) 	{ ONOFF = b; 		}
	public boolean isON( ) 				{ return ONOFF; 	}
	public boolean isOFF( )				{ return !ONOFF; 	}
	public void switchState() 			{ ONOFF = ! ONOFF;  }
	
	/**
	 * draw the button on the applet.
	 */
	public void draw() {
		parent.fill( 255, 255, 255 );		// white background
		parent.stroke( 0, 0, 0 );			// black outline
		parent.strokeWeight( 2 );			// line width
		parent.rect( x, y, w, h );
		if ( ONOFF ) {
			parent.line( x,  y,  x+w,  y+h );
			parent.line( x+w, y, x, y+h );
		}
		parent.fill( 0, 0, 0 ); 			// black text
		parent.textAlign( parent.LEFT );
		parent.text( caption, x + w + 5, y + h/2 );
	}
	
	public boolean containsMouse() {
		return ( parent.mouseX >= x && parent.mouseX <= x+w 
				&& parent.mouseY >= y && parent.mouseY <= y+h );
	}
}
  • Add a new private member variable to the viewer, called button1, with type Button.
  • Initialize button1 in the setup() method:


		//--- create the button ---
		button1 = new Button( this, 10, HEIGHT-25, "Magic Button" );


  • Make the draw() method redraw the button, right after the call to background():
		// erase window, make background white
		background( 255, 255, 255 );
				
		// draw button
		button1.draw();


  • Finally, let's switch the state of button in the new mouseClicked() method:
	/**
	 * this is called automatically by the Applet if the user left-clicks the mouse
	 * on the canvas of the applet.  The location of the click can be found in mouseX
	 * and mouseY.
	 */
	public void mouseClicked() {
                System.out.println( "Mouse clicked at "+mouseX + ", " + mouseY );
		if ( button1.containsMouse() ) {
			button1.switchState();
		}
        }


  • Now you have a system in place for controlling the way your app work. All you need is to add if ( button1.isON() ) in front of some statement, and you can control whether it takes place, or not. For example, add this if-statement in draw() in front of the loop that draws the edges:


                   // get adjacency list for each vertex and display edges
                   if (button1.isON() )
			for ( int u:  model.adjList( v ) ) { 
				int[] xy2 = getXY( u, noRows, noCols );
				line( xy[0], xy[1], xy2[0], xy2[1] );
			}


Today's Challenge


Assignment


  • Make your program compute a DFS whenever the mouse is over a vertex.
  • The DFS will start with the vertex under the mouse.
  • The model will carry out the DFS (you'll need to add a visited array).
  • The viewer will display the vertices that have been visited by DFS in some color other than black, and the vertices that haven't been visited in black.
To set the color of a Processing shape, such as ellipse, you use fill( r, g, b ) to set the inside color and stroke( r, g, b ) to set the outline color. r, g, and b are numbers between 0 and 255 representing some amount of red, green, and blue.


Recommendations


  1. Implement DFS in the model. Make the visited array a member variable so that it can be accessed after the DFS has been computed, when the displaying of nodes takes place.
  2. Make the viewer alert the controller when the mouse is over a vertex.
  3. Make the controller tell the model to perform a DFS on the selected vertex.
  4. Create the needed methods in model so that the viewer can poll the model when it displays the vertices, so that it can pick the right color for each vertex.