From ff5248437491f1829c0168b271e85cb358516577 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Mon, 9 Mar 2015 16:40:17 -0300 Subject: Moved GUI to its own repository --- src/jcgp/gui/GUI.java | 477 -------------------------------------------------- 1 file changed, 477 deletions(-) delete mode 100644 src/jcgp/gui/GUI.java (limited to 'src/jcgp/gui/GUI.java') diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java deleted file mode 100644 index 689f38c..0000000 --- a/src/jcgp/gui/GUI.java +++ /dev/null @@ -1,477 +0,0 @@ -package jcgp.gui; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.concurrent.Service; -import javafx.concurrent.Task; -import javafx.event.EventHandler; -import javafx.scene.Scene; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.Pane; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; -import jcgp.JCGP; -import jcgp.backend.modules.problem.TestCaseProblem.TestCase; -import jcgp.backend.resources.Resources; -import jcgp.gui.console.ConsolePane; -import jcgp.gui.dragresize.HorizontalDragResize; -import jcgp.gui.dragresize.VerticalDragResize; -import jcgp.gui.population.FunctionSelector; -import jcgp.gui.population.GUINode; -import jcgp.gui.population.PopulationPane; -import jcgp.gui.settings.SettingsPane; - -/** - * Main class for the graphical user interface (GUI). - *

- * This class declares the main method used when running the GUI. - * In addition, all main GUI panes are declared and instantiated here. - *

- * The user interface is divided into 3 main components: the node grid - * ({@link PopulationPane}), the control pane ({@link SettingsPane}) and - * the console ({@link ConsolePane}). Click on any of the links in - * brackets to see more information about each interface component. - *

- * This class also contains the instance of JCGP responsible for - * running the experiments in GUI mode. JCGP's execution must be delegated - * to a separate thread so that the GUI remains unblocked. This is done using - * a JavaFX {@code Service} which calls {@code nextGeneration()} in a loop - * until it is interrupted by the main JavaFX thread. - *
- * This service also handles flushing the console in a thread safe way. This - * is done by synchronizing the {@code nextGeneration()} and {@code flush()} - * method calls on a lock object. - * - * @author Eduardo Pedroni - * - */ -public class GUI extends Application { - - /* - * Actual GUI elements - */ - private Stage stage; - private PopulationPane populationPane; - private ConsolePane console; - private SettingsPane settingsPane; - private final FunctionSelector functionSelector; - - /* - * Flow control objects - */ - private boolean running = false; - private final Object printLock = new Object(); - private Service jcgpService; - private Runnable consoleFlush; - - /* - * The experiment itself - */ - private final JCGP jcgp; - - public static Resources resources; - - - /** - * Start JCGP with the user interface. - * - * @param args no arguments are used. - */ - public static void main(String[] args) { - // not much to do, simply launch the JavaFX application - launch(); - } - - /** - * Makes a new instance of GUI. This initialises the JCGP experiment and - * instantiates the function selector. It also creates the console flush task - * and the service responsible for running the JCGP experiment. - */ - public GUI() { - jcgp = new JCGP(); - resources = jcgp.getResources(); - functionSelector = new FunctionSelector(jcgp.getResources().getFunctionSet()); - - /* - * This task flushes the console in a thread-safe way. - * The problem is that this task is executed using Platform.runLater() - * to ensure that the flush itself happens on the JavaFX thread. However, - * runLater() is not guaranteed to run anytime soon. If the time taken for - * jcgp to perform a single generation is shorter than the time taken for - * this task to be executed by the platform, consoleFlush tasks will be - * scheduled faster than they can be executed and the console will eventually - * freeze. - * - * This is addressed by synchronizing the flushes with each nextGeneration() call. - */ - consoleFlush = new Runnable() { - @Override - public void run() { - /* - * Try to acquire printlock - wait here until jcgpService relinquishes it - * by calling wait(). This means that it is finished with the current generation - * and will wait for the console to be flushed to move on. - * It might be the case that the service has already released the lock by waiting - * on it; it makes no difference. In that case this will acquire the lock - * immediately and proceed to flush the console. - */ - synchronized(printLock) { - /* - * The lock is acquired, at this point we are certain that jcgpService - * cannot execute; it is currently waiting to be notified about the lock. - * No additional consoleFlush tasks can be scheduled with runLater() because - * the service is waiting. We can now take our time to flush the console. - */ - console.flush(); - /* - * Once the console finishes flushing, we notify jcgpService to perform the - * next generation. - */ - printLock.notifyAll(); - } - } - }; - - /* - * This service runs on a separate thread and performs - * the experiment, including console prints, in a thread-safe - * way. It is synchronized with consoleFlush. - */ - jcgpService = new Service () { - @Override - protected Task createTask() { - Task t = new Task() { - @Override - protected Void call() throws Exception { - /* - * Only execute if the experiment isn't finished - * and the service hasn't been cancelled. - */ - while (!isCancelled() && !jcgp.isFinished()) { - /* - * Attempt to acquire the printlock. - * Successfully doing so means no printing - * is currently taking place and we are free - * to schedule a print task. - * This lock acquisition should never block. It should - * not be possible to execute this statement without - * having been notified by consoleFlush. - */ - synchronized (printLock) { - /* - * Lock has been acquired, schedule a print - * task ahead of time. The actual print messages - * haven't been send to the console yet, that happens - * during nextGeneration(), but since we have the lock - * consoleFlush() will sit and wait for us to release it - * whenever we are finished queueing prints. - */ - Platform.runLater(consoleFlush); - /* - * Perform the actual generation. Here zero or more - * strings might be sent to the console buffer. - */ - jcgp.nextGeneration(); - /* - * The generation is complete, relinquish the lock. - * By this point chances are the platform is already trying - * to execute the consoleFlush task that we scheduled. If it - * hasn't already started though, it doesn't matter; we will - * wait for a notification on the lock, which will only come - * when printing is complete. - */ - printLock.wait(); - /* - * We have been notified. This means all buffered messages have - * been successfully flushed to the actual console control and - * we are now ready to perform another generation (or break out - * of the loop if the loop conditions are no longer met). - */ - } - /* - * We no longer own the lock, but neither does consoleFlush. - * The synchrony cycle has returned to its initial state, and we - * are free to acquire the lock again. - */ - } - /* - * Something happened to break the while loop - - * either the experiment finished or the user pressed - * pause. - */ - if (jcgp.isFinished()) { - // the experiment has finished, switch to pause mode - Platform.runLater(new Runnable() { - @Override - public void run() { - runningMode(false); - } - }); - } - return null; - } - }; - return t; - } - }; - } - - @Override - public void start(Stage primaryStage) throws Exception { - /* - * This method gets called when the application launches. Once it - * returns, the application falls into the main loop which handles - * events, so all elements must be constructed here. - */ - - // make the console and set it so it is used for JCGP prints - console = new ConsolePane(); - jcgp.setConsole(console); - - // store reference to the stage - stage = primaryStage; - - /* - * The experiment layer contains all of the experiment-related panes. - * The only element that sits higher than this is the function selector. - */ - BorderPane experimentLayer = new BorderPane(); - /* - * The left frame encapsulates the population pane and the console. - * It goes into the center position of the experiment layer, next to the settings pane. - */ - BorderPane leftFrame = new BorderPane(); - - /* - * The population pane is a TabPane containing a tab for each chromosome. - */ - populationPane = new PopulationPane(this); - - /* - * The settings pane is a big class containing the entire control pane - */ - settingsPane = new SettingsPane(this); - - // make control pane and console resizable - HorizontalDragResize.makeDragResizable(settingsPane); - VerticalDragResize.makeDragResizable(console); - // prevent resizables from growing larger than the experiment layer - settingsPane.maxWidthProperty().bind(experimentLayer.widthProperty()); - console.maxHeightProperty().bind(experimentLayer.heightProperty()); - - // put console and population pane in the main frame - leftFrame.setCenter(populationPane); - leftFrame.setBottom(console); - - // set the main frame and the control pane in the experiment layer - experimentLayer.setCenter(leftFrame); - experimentLayer.setRight(settingsPane); - - /* - * Now we deal with the stage. - */ - primaryStage.setTitle("JCGP"); - - // this pane holds the entire scene, that is its sole job. - Pane sceneParent = new Pane(); - // the experiment layer should fill the entire scene parent - experimentLayer.prefHeightProperty().bind(sceneParent.heightProperty()); - experimentLayer.prefWidthProperty().bind(sceneParent.widthProperty()); - // the function selector goes over the experiment layer so it doesn't get covered by other panes - sceneParent.getChildren().addAll(experimentLayer, functionSelector); - - // set the scene, minimum sizes, show - primaryStage.setScene(new Scene(sceneParent)); - primaryStage.setMinWidth(800); - primaryStage.setMinHeight(600); - primaryStage.show(); - - // when the main stage closes, close the test case table as well - primaryStage.setOnCloseRequest(new EventHandler() { - @Override - public void handle(WindowEvent event) { - if (settingsPane.getTestCaseTable() != null) { - settingsPane.getTestCaseTable().close(); - } - } - }); - } - - /** - * Run/pause method. - * Run the experiment if it is paused, or pause it if it is running. - *
- * This method is the callback used by the run/pause button. It - * controls the jcgp service. - */ - public void runPause() { - // do nothing if experiment is finished or parameters aren't valid - if (!jcgp.isFinished() && settingsPane.areParametersValid()) { - if (!running) { - runningMode(true); - jcgpService.restart(); - } else { - jcgpService.cancel(); - runningMode(false); - } - } - } - - /** - * Perform a single generation using {@code nextGeneration()}. - *
- * On top of that, this method performs all of the housekeeping - * that is normally done before and after running, such as - * refreshing the chromosome panes. - */ - public void step() { - // do nothing if experiment is finished, running or parameters aren't valid - if (!running && !jcgp.isFinished() && settingsPane.areParametersValid()) { - if (settingsPane.isResetRequired()) { - reset(); - } - jcgp.nextGeneration(); - console.flush(); - - populationPane.updateGenes(); - settingsPane.revalidateParameters(); - settingsPane.updateControls(false, jcgp.isFinished()); - } - } - - /** - * Reset button callback. If the parameters are valid, - * this resets the entire experiment by calling {@code reset()} - * on jcgp. - */ - public void reset() { - if (!running && settingsPane.areParametersValid()) { - setEvaluating(false); - jcgp.reset(); - settingsPane.applyParameters(); - reDraw(); - } - } - - /** - * Does a complete GUI refresh. - * This is potentially lengthy, so use with care. - */ - public void reDraw() { - populationPane.remakeTabs(); - settingsPane.revalidateParameters(); - settingsPane.updateControls(false, jcgp.isFinished()); - console.flush(); - } - - /** - * Toggles the entire GUI between run and pause - * mode. - *

- * A lot of the GUI must be enabled or disabled - * depending on what the experiment is doing. This - * method provides a one-line way to make - * all required adjustments. - * - * @param value true if experiment is running, false otherwise. - */ - private void runningMode(boolean value) { - if (value) { - if (settingsPane.isResetRequired()) { - reset(); - } - } else { - populationPane.updateGenes(); - settingsPane.revalidateParameters(); - } - populationPane.setDisable(value); - settingsPane.updateControls(value, jcgp.isFinished()); - - running = value; - } - - /** - * Refresh the function selector, used when functions are enabled or disabled. - */ - public void updateFunctionSelector() { - functionSelector.remakeFunctions(jcgp.getResources().getFunctionSet()); - } - - /** - * @return true if jcgp is evolving. - */ - public boolean isWorking() { - return running; - } - - /** - * Relocate the function selector to the right position - * relative to the specified node and set it visible. - * - * @param event the mouse event containing cursor coordinates. - * @param node the node whose function should be changed. - */ - public void bringFunctionSelector(MouseEvent event, GUINode node) { - functionSelector.relocateAndShow(event, node); - } - - /** - * @return a reference to the {@code JCGP} experiment. - */ - public JCGP getExperiment() { - return jcgp; - } - - /** - * Starts the evaluation process with the given test case. - * It does so by calling {@code evaluateTestCase()} on - * the population pane. - * - * @param testCase the test case to evaluate. - */ - public void evaluateTestCase(TestCase testCase) { - populationPane.evaluateTestCase(testCase); - } - - /** - * Hide all evaluated values. This should be called when - * evaluations are no longer being performed. - */ - public void hideGeneValues() { - populationPane.hideValues(); - } - - /** - * Set the system into evaluation mode. - * When in evaluation mode, the population pane - * refreshes the node values whenever connection - * changes happen. - * - * @param value true if evaluations are happening, false otherwise. - */ - public void setEvaluating(boolean value) { - populationPane.setEvaluating(value); - } - - /** - * @return a reference to the GUI stage. - */ - public Stage getStage() { - return stage; - } - - /** - * Writes all buffered content out to the GUI console. - */ - public void flushConsole() { - console.flush(); - } - - /** - * @return the index of the chromosome currently being looked at. - */ - public int getChromosomeIndex() { - return populationPane.getSelectionModel().getSelectedIndex(); - } -} -- cgit v1.2.3