CSC212 Lab 15 2014
--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.
Contents
First Contact
- 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.
- 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 closest 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 to make something happen. 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. That's 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 will get the model to setup the graph, with its geometry.
- the controller gets the model to initialize the graph
- the geometry of the applet is setup so that it 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 does 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 );
}
- Note that we must verify that the mouse is over the button before we switch the state of the button.
- Try your new code, and verify that your button switches state whenever it is clicked (the cross bars alternate showing up or not).
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 the controller know when the button is clicked. This way the controller can decide what needs to be done in this case:
<source lang="java"> /** * 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() { if ( button1.containsMouse() ) { button1.switchState(); System.out.println( "Button: state = " + button1.isON() ); } }