Graphical User
Interface - Stage 3
This third stage adds three new aspects to the GUI.
1) It introduces the GridLayout which organises the arrangement
of components in the frame.
2) It adds more Text Fields so that variables in the music composition
algorithms can be varied at each run.
These also require text labels to name the fields and some conversion
from the text field to variable integers.
3) We finally make the close box work. Such that it closes
the window and shuts down the program.
Click here to view source.
This is what the GUI will look similar to this.
Let's have a closer look.
import java.awt.*; import java.awt.event.*; import jm.JMC; import jm.music.data.*; import jm.util.*;
|
We import the usual suspects including the awt and awt.event
classes for the GUI.
public class SimpleGUI3 extends Frame implements ActionListener, WindowListener, JMC{ //-------------- TextField fileNameBox; TextField minPitch; TextField maxPitch; TextField numOfNotes; Button composeBtn; public static void main(String[] args) { new SimpleGUI3(); }
|
The class definition implements three things, the ActionListener
and WindowListener which monitor user actions, and the JMC for the jMusic
constants. This GUI has four TextFields which
are next declared.
The main() method is a simple call to the constructor.
public SimpleGUI3() { super("An almost real Application"); this.addWindowListener(this); this.setBackground(Color.green); this.setLayout(new GridLayout(5, 2, 5, 0)); Label minPL = new Label("Minimum MIDI Pitch", Label.RIGHT); this.add(minPL); minPitch = new TextField("50"); minPitch.setBackground(Color.lightGray); this.add(minPitch);
|
The first part of the constructor (which does most
of the work in this class) calls the Frame class (super class) with the
window title, the frame is registered with the windowListener and its
background colour set to green.
The Frame employs the GridLayout which arranges areas in the frame into
equal sized rectangles.
The arguments are number-of-rows, number-of-collumns, horizontal-gap-between-cells,
vertical-gap-between-cells.
The first label and text field are next added to the frame.
Label maxPL = new Label("Maximum MIDI Pitch", Label.RIGHT); this.add(maxPL); maxPitch = new TextField("70"); maxPitch.setBackground(Color.lightGray); this.add(maxPitch); Label numNL = new Label("Number of notes", Label.RIGHT); this.add(numNL); numOfNotes = new TextField("12"); numOfNotes.setBackground(Color.lightGray); this.add(numOfNotes); Label fNameLable = new Label("MIDI file name", Label.RIGHT); this.add(fNameLable); fileNameBox = new TextField("SimpleGUI_3.mid"); fileNameBox.setBackground(Color.lightGray); this.add(fileNameBox); Label dummy = new Label("", Label.RIGHT); this.add(dummy); Panel p = new Panel(); composeBtn = new Button("Compose"); composeBtn.addActionListener(this); composeBtn.setActionCommand("Create"); p.add(composeBtn); this.add(p); this.pack(); this.show(); }
|
The second part of the constructor continues to
add elements to the frame as we have seen in earlier GUI tutorials.
The button is this time put on to a panel. Doing this stops the button
occupying the entire space of the cell in the frame.
On a panel the button is more well behaved, its size is determined by
the button label and OS look and feel.
A panel called 'p' is created, the button is declared and added to the
panel, then the panel is added to the frame layout.
public void windowClosing(WindowEvent we) { System.exit(0); } public void windowActivated(WindowEvent we) {}; public void windowClosed(WindowEvent we) {}; public void windowDeactivated(WindowEvent we) {}; public void windowIconified(WindowEvent we) {}; public void windowDeiconified(WindowEvent we) {}; public void windowOpened(WindowEvent we) {};
|
Here the user events 'seen' by the WindowListener interface are dealt
with.
The only one we are concerned with is the WindowClosing event which is
generated when the window's close box is clicked.
When this happens the Java application is simply told to quit with the
command System.exit(0)
.
When implementing an interface all the methods of the interface must be
overridden, and that is why we have all the other window method stubs
- that is they consist only of {} and so do nothing. It would be easy
now to add code to deal with each of these events. it also gives you a
good idea of the kinds of window events that can be caught.
public void actionPerformed(ActionEvent ae) { if (ae.getSource() == composeBtn) { String fn = fileNameBox.getText(); if (fn != null) { makeMusic(fn, Integer.valueOf( minPitch.getText()).intValue(), Integer.valueOf( maxPitch.getText()).intValue(), Integer.valueOf( numOfNotes.getText()).intValue()); } else makeMusic("SimpleGUI_3.mid", Integer.valueOf( minPitch.getText()).intValue(), Integer.valueOf( maxPitch.getText()).intValue(), Integer.valueOf( numOfNotes.getText()).intValue()); } }
|
This method deals with the button click. It check to see if
there is a name of the MIDI file in the TextField, if so it gets all values
and passes them to the makeMusic() method. If not, it gets other values
and passes a default file name to the makeMusic() method.
Notice that to get the numerical values from the TextFields
they must be converted from strings to integers with code similar to:
Integer.valueOf(minPitch.getText()).intValue()
Where minPitch is the name of the TextField. Complicated I admit, but
. . . just copy it and use it.
public void makeMusic(String fileName, int minPitchVal, int maxPitchVal, int numOfNotesVal) { Score scale = new Score("JMDemo - SimpleGUI_3"); Part inst = new Part("Music", ATMOSPHERE, 0); Phrase phr = new Phrase(0.0); for(short i=0;i<numOfNotesVal;i++){ Note note = new Note((int)((Math.random()* Math.abs(maxPitchVal- minPitchVal))+ minPitchVal), SQ, (int)(Math.random() * 80 + 40)); phr.addNote(note); } inst.addPhrase(phr); scale.addPart(inst);
Write.midi(scale, fileName) } }
|
This is the code which generates the jMusic score. It simply
generates a stochastic melody with stochastic dynamics.
|