Java tutorial
//----------------------------------------------------------------------------// // // // E v a l u a t i o n B o a r d // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright (C) Herve Bitteur 2000-2010. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.glyph.ui; import omr.constant.Constant; import omr.constant.ConstantSet; import omr.glyph.Evaluation; import omr.glyph.GlyphEvaluator; import omr.glyph.GlyphInspector; import omr.glyph.GlyphNetwork; import omr.glyph.Glyphs; import omr.glyph.Shape; import omr.glyph.facets.Glyph; import omr.log.Logger; import omr.selection.GlyphEvent; import omr.selection.MouseMovement; import omr.selection.SelectionHint; import omr.selection.UserEvent; import omr.sheet.Sheet; import omr.ui.Board; import omr.ui.util.Panel; import omr.util.Implement; import com.jgoodies.forms.builder.*; import com.jgoodies.forms.factories.FormFactory; import com.jgoodies.forms.layout.*; import org.bushe.swing.event.EventSubscriber; import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import javax.swing.*; /** * Class <code>EvaluationBoard</code> is a board dedicated to the display of * evaluation results performed by an evaluator. * * <p>By pressing one of the result buttons, the user can force the assignment * of the evaluated glyph. * * @author Herv Bitteur and Brenton Partridge */ class EvaluationBoard extends Board { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = Logger.getLogger(EvaluationBoard.class); /** Events this boards is interested in */ private static final Collection<Class<? extends UserEvent>> eventClasses; static { eventClasses = new ArrayList<Class<? extends UserEvent>>(); eventClasses.add(GlyphEvent.class); } /** Color for well recognized glyphs */ private static final Color EVAL_GOOD_COLOR = new Color(100, 150, 0); /** Color for hardly recognized glyphs */ private static final Color EVAL_SOSO_COLOR = new Color(255, 100, 150); //~ Instance fields -------------------------------------------------------- /** The evaluator this display is related to */ private final GlyphEvaluator evaluator = GlyphNetwork.getInstance(); /** Related glyphs controller */ private final GlyphsController glyphsController; /** Related sheet */ private final Sheet sheet; /** Lag view (if any) */ private final GlyphLagView view; /** Pane for detailed info display about the glyph evaluation */ private final Selector selector; /** Button for testing ratio of recognized glyphs */ private JButton testButton; /** Numeric result of whole sheet test */ private JLabel testPercent; /** Percentage result of whole sheet test */ private JLabel testResult; //~ Constructors ----------------------------------------------------------- //-----------------// // EvaluationBoard // //-----------------// /** * Create a simplified passive evaluation board with one neural network * evaluator * * @param glyphModel the related glyph model */ public EvaluationBoard(String name, GlyphsController glyphModel) { this(name, glyphModel, null, null); } //-----------------// // EvaluationBoard // //-----------------// /** * Create an evaluation board with one neural network evaluator, ability to * force glyph shape, and test buttons * * @param name a rather unique name for this board * @param glyphController the related glyph controller * @param sheet the related sheet, or null * @param view the related symbol glyph view */ public EvaluationBoard(String name, GlyphsController glyphController, Sheet sheet, GlyphLagView view) { super(name, "Neural", glyphController.getLag().getSelectionService(), eventClasses); this.glyphsController = glyphController; this.sheet = sheet; this.view = view; // Buttons // if (sheet != null) { // testButton = new JButton(new TestAction()); // testPercent = new JLabel("0%", SwingConstants.CENTER); // testResult = new JLabel("", SwingConstants.CENTER); // } selector = new Selector(); defineLayout(); } //~ Methods ---------------------------------------------------------------- //----------// // evaluate // //----------// /** * Evaluate the glyph at hand, and display the result from each evaluator in * its dedicated area * * @param glyph the glyph at hand */ public void evaluate(Glyph glyph) { if ((glyph == null) || (glyph.getShape() == Shape.COMBINING_STEM) || glyph.isBar()) { // Blank the output selector.setEvals(null, null); } else { selector.setEvals(evaluator.getAllEvaluations(glyph), glyph); } } //---------// // onEvent // //---------// /** * Call-back triggered when Glyph Selection has been modified * * @param event the (Glyph) Selection */ @Implement(EventSubscriber.class) public void onEvent(UserEvent event) { try { // Ignore RELEASING if (event.movement == MouseMovement.RELEASING) { return; } // Don't evaluate Added glyph, since this would hide Compound evaluation if (event.hint == SelectionHint.LOCATION_ADD) { return; } if (event instanceof GlyphEvent) { GlyphEvent glyphEvent = (GlyphEvent) event; Glyph glyph = glyphEvent.getData(); evaluate(glyph); } } catch (Exception ex) { logger.warning(getClass().getName() + " onEvent error", ex); } } //--------------// // defineLayout // //--------------// private void defineLayout() { final String buttonWidth = Panel.getButtonWidth(); final String fieldInterval = Panel.getFieldInterval(); final String fieldInterline = Panel.getFieldInterline(); FormLayout layout = new FormLayout(buttonWidth + "," + fieldInterval + "," + buttonWidth + "," + fieldInterval + "," + buttonWidth + "," + fieldInterval + "," + buttonWidth, ""); int visibleButtons = Math.min(constants.visibleButtons.getValue(), selector.buttons.size()); for (int i = 0; i < visibleButtons; i++) { layout.appendRow(FormFactory.PREF_ROWSPEC); } // Uncomment following line to have fixed sized rows, whether // they are filled or not ///layout.setRowGroups(new int[][]{{1, 3, 4, 5 }}); PanelBuilder builder = new PanelBuilder(layout, getBody()); builder.setDefaultDialogBorder(); CellConstraints cst = new CellConstraints(); // if (sheet != null) { // builder.add(testButton, cst.xy(3, r)); // builder.add(testPercent, cst.xy(5, r)); // builder.add(testResult, cst.xy(7, r)); // } for (int i = 0; i < visibleButtons; i++) { int r = i + 1; // -------------------------------- builder.add(selector.buttons.get(i).grade, cst.xy(1, r)); if (sheet != null) { builder.add(selector.buttons.get(i).button, cst.xyw(3, r, 5)); } else { builder.add(selector.buttons.get(i).field, cst.xyw(3, r, 5)); } } } //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- Evaluation.Doubt maxDoubt = new Evaluation.Doubt(100000.0, "Threshold on displayable doubt"); Constant.Integer visibleButtons = new Constant.Integer("buttons", 5, "Max number of buttons in the shape selector"); } //------------// // EvalButton // //------------// private class EvalButton implements ActionListener { //~ Instance fields ---------------------------------------------------- // Shape button or text field. Only one of them will be created and used final JButton button; final JLabel field; // The related doubt JLabel grade = new JLabel("", SwingConstants.RIGHT); //~ Constructors ------------------------------------------------------- public EvalButton() { grade.setToolTipText("Doubt of the evaluation"); if (sheet != null) { button = new JButton(); button.addActionListener(this); button.setToolTipText("Assignable shape"); button.setHorizontalAlignment(SwingConstants.LEFT); field = null; } else { field = new JLabel(); field.setHorizontalAlignment(JTextField.CENTER); field.setToolTipText("Evaluated shape"); field.setHorizontalAlignment(SwingConstants.LEFT); button = null; } } //~ Methods ------------------------------------------------------------ public void setEval(Evaluation eval, boolean barred, boolean enabled) { JComponent comp; if (sheet != null) { comp = button; } else { comp = field; } if (eval != null) { if (sheet != null) { button.setEnabled(enabled); if (barred) { button.setBackground(Color.PINK); } else { button.setBackground(null); } button.setText(eval.shape.toString()); button.setIcon(eval.shape.getDecoratedSymbol()); } else { if (barred) { field.setBackground(Color.PINK); } else { field.setBackground(null); } field.setText(eval.shape.toString()); field.setIcon(eval.shape.getDecoratedSymbol()); } comp.setVisible(true); if (eval.doubt <= GlyphInspector.getSymbolMaxDoubt()) { comp.setForeground(EVAL_GOOD_COLOR); } else { comp.setForeground(EVAL_SOSO_COLOR); } grade.setVisible(true); grade.setText(String.format("%.3f", eval.doubt)); } else { grade.setVisible(false); comp.setVisible(false); } } // Triggered by button @Implement(ActionListener.class) public void actionPerformed(ActionEvent e) { // Assign current glyph with selected shape if (glyphsController != null) { Glyph glyph = glyphsController.getLag().getSelectedGlyph(); if (glyph != null) { Shape shape = Shape.valueOf(button.getText()); // Actually assign the shape glyphsController.asyncAssignGlyphs(Glyphs.sortedSet(glyph), shape, false); } } } } //----------// // Selector // //----------// private class Selector { //~ Instance fields ---------------------------------------------------- // A collection of EvalButton's List<EvalButton> buttons; //~ Constructors ------------------------------------------------------- public Selector() { buttons = new ArrayList<EvalButton>(); for (int i = 0; i < constants.visibleButtons.getValue(); i++) { buttons.add(new EvalButton()); } setEvals(null, null); } //~ Methods ------------------------------------------------------------ //----------// // setEvals // //----------// /** * Display the evaluations with some text highlighting. Only relevant * evaluations are displayed (distance less than a given threshold) * * @param evals the ordered list of <b>all</b>evaluations from best to * worst * @param glyph evaluated glyph, to check forbidden shapes if any */ public void setEvals(Evaluation[] evals, Glyph glyph) { // Special case to empty the selector if (evals == null) { for (EvalButton evalButton : buttons) { evalButton.setEval(null, false, false); } return; } boolean enabled = !glyph.isVirtual(); double maxDist = constants.maxDoubt.getValue(); int iBound = Math.min(buttons.size(), evals.length); int i; for (i = 0; i < iBound; i++) { Evaluation eval = evals[i]; // Limitation on shape relevance if (eval.doubt > maxDist) { break; } // Barred on non-barred button buttons.get(i).setEval(eval, glyph.isShapeForbidden(eval.shape), enabled); } // Zero the remaining buttons for (; i < buttons.size(); i++) { buttons.get(i).setEval(null, false, false); } } } //------------// // TestAction // //------------// /** * Class <code>TestAction</code> uses the evaluator on all known glyphs * within the sheet, to check if they can be correctly recognized. This does * not modify the current glyph assignments. */ private class TestAction extends AbstractAction { //~ Constructors ------------------------------------------------------- public TestAction() { super("Global"); } //~ Methods ------------------------------------------------------------ @Implement(ActionListener.class) public void actionPerformed(ActionEvent e) { int ok = 0; int total = 0; final double maxDoubt = GlyphInspector.getSymbolMaxDoubt(); final int viewIndex = glyphsController.getLag().viewIndexOf(view); for (Glyph glyph : sheet.getActiveGlyphs()) { if (glyph.isKnown() && (glyph.getShape() != Shape.COMBINING_STEM)) { total++; Evaluation guess = evaluator.vote(glyph, maxDoubt); if ((guess != null) && (glyph.getShape() == guess.shape)) { ok++; view.colorizeGlyph(viewIndex, glyph, Shape.okColor); } else { view.colorizeGlyph(viewIndex, glyph, Shape.missedColor); } } } String pc = String.format(" %5.2f%%", ((double) ok * 100) / (double) total); testPercent.setText(pc); testResult.setText(ok + "/" + total); // Almost all glyphs may have been modified, so... view.repaint(); } } }