//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //============================================================================= package org.uncommons.watchmaker.swing.evolutionmonitor; import java.awt.BorderLayout; import java.awt.Window; import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import org.jfree.chart.ChartFactory; import org.jfree.chart.StandardChartTheme; import org.uncommons.watchmaker.framework.PopulationData; import org.uncommons.watchmaker.framework.interactive.Renderer; import org.uncommons.watchmaker.framework.islands.IslandEvolutionObserver; import org.uncommons.watchmaker.swing.ObjectSwingRenderer; import org.uncommons.watchmaker.swing.SwingIslandEvolutionObserver; /** * The Evolution Monitor is a component that can be attached to an * {@link org.uncommons.watchmaker.framework.EvolutionEngine} to provide * real-time information (in a Swing GUI) about the current state of the * evolution. * @param <T> The type of the evolved entities monitored by this component. * @author Daniel Dyer */ public class EvolutionMonitor<T> implements IslandEvolutionObserver<T> { private final List<IslandEvolutionObserver<? super T>> views = new LinkedList<IslandEvolutionObserver<? super T>>(); private JComponent monitorComponent; private Window window = null; private final boolean islands; /** * <p>Creates an EvolutionMonitor with a single panel that graphs the fitness scores * of the population from generation to generation.</p> * <p>If you are using {@link org.uncommons.watchmaker.framework.islands.IslandEvolution}, * use the {@link #EvolutionMonitor(boolean)} constructor instead, to enable island support.</p> */ public EvolutionMonitor() { this(false); } /** * Creates an EvolutionMonitor with a single panel that graphs the fitness scores * of the population from generation to generation. * @param islands Whether the monitor should be configured for displaying data from * {@link org.uncommons.watchmaker.framework.islands.IslandEvolution}. Set this * parameter to false when using a standard {@link org.uncommons.watchmaker.framework.EvolutionEngine} * or if you don't want to display island-specific data for island evolution. */ public EvolutionMonitor(boolean islands) { this(new ObjectSwingRenderer(), islands); } /** * Creates an EvolutionMonitor with a second panel that displays a graphical * representation of the fittest candidate in the population. * @param renderer Renders a candidate solution as a JComponent. * @param islands Whether the monitor should be configured for displaying data from * {@link org.uncommons.watchmaker.framework.islands.IslandEvolution}. Set this * parameter to false when using a standard {@link org.uncommons.watchmaker.framework.EvolutionEngine} */ public EvolutionMonitor(final Renderer<? super T, JComponent> renderer, boolean islands) { this.islands = islands; if (SwingUtilities.isEventDispatchThread()) { init(renderer); } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { init(renderer); } }); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); throw new IllegalStateException(ex); } catch (InvocationTargetException ex) { throw new IllegalStateException(ex); } } } private void init(Renderer<? super T, JComponent> renderer) { // Make sure all JFreeChart charts are created with the legacy theme // (grey surround and white data area). ChartFactory.setChartTheme(StandardChartTheme.createLegacyTheme()); JTabbedPane tabs = new JTabbedPane(); monitorComponent = new JPanel(new BorderLayout()); monitorComponent.add(tabs, BorderLayout.CENTER); FittestCandidateView<T> candidateView = new FittestCandidateView<T>(renderer); tabs.add("Fittest Individual", candidateView); views.add(new SwingIslandEvolutionObserver<T>(candidateView, 300, TimeUnit.MILLISECONDS)); PopulationFitnessView fitnessView = new PopulationFitnessView(islands); tabs.add(islands ? "Global Population" : "Population Fitness", fitnessView); views.add(fitnessView); if (islands) { IslandsView islandsView = new IslandsView(); tabs.add("Island Populations", islandsView); views.add(new SwingIslandEvolutionObserver<Object>(islandsView, 300, TimeUnit.MILLISECONDS)); } JVMView jvmView = new JVMView(); tabs.add("JVM Memory", jvmView); StatusBar statusBar = new StatusBar(islands); monitorComponent.add(statusBar, BorderLayout.SOUTH); views.add(new SwingIslandEvolutionObserver<Object>(statusBar, 300, TimeUnit.MILLISECONDS)); } /** * {@inheritDoc} */ public void populationUpdate(PopulationData<? extends T> populationData) { for (IslandEvolutionObserver<? super T> view : views) { view.populationUpdate(populationData); } } /** * {@inheritDoc} */ public void islandPopulationUpdate(int islandIndex, PopulationData<? extends T> populationData) { for (IslandEvolutionObserver<? super T> view : views) { view.islandPopulationUpdate(islandIndex, populationData); } } public JComponent getGUIComponent() { return monitorComponent; } /** * Displays the evolution monitor component in a new {@link JFrame}. There is no * need to make sure this method is invoked from the Event Dispatch Thread, the * method itself ensures that the window is created and displayed from the EDT. * @param title The title for the new frame. * @param exitOnClose Whether the JVM should exit when the frame is closed. Useful * if this is the only application window. */ public void showInFrame(final String title, final boolean exitOnClose) { SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(exitOnClose ? JFrame.EXIT_ON_CLOSE : JFrame.DISPOSE_ON_CLOSE); showWindow(frame); } }); } /** * Displays the evolution monitor component in a new {@link JDialog}. There is no * need to make sure this method is invoked from the Event Dispatch Thread, the * method itself ensures that the window is created and displayed from the EDT. * @param owner The owning frame for the new dialog. * @param title The title for the new dialog. * @param modal Whether the */ public void showInDialog(final JFrame owner, final String title, final boolean modal) { SwingUtilities.invokeLater(new Runnable() { public void run() { JDialog dialog = new JDialog(owner, title, modal); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); showWindow(dialog); } }); } /** * Helper method for showing the evolution monitor in a frame or dialog. * @param newWindow The frame or dialog used to show the evolution monitor. */ private void showWindow(Window newWindow) { if (window != null) { window.remove(getGUIComponent()); window.setVisible(false); window.dispose(); window = null; } newWindow.add(getGUIComponent(), BorderLayout.CENTER); newWindow.pack(); newWindow.setVisible(true); this.window = newWindow; } }