diff options
Diffstat (limited to 'src/jcgp/gui')
25 files changed, 0 insertions, 3579 deletions
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). - * <br><br> - * This class declares the main method used when running the GUI. - * In addition, all main GUI panes are declared and instantiated here. - * <br><br> - * 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. - * <br><br> - * 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. - * <br> - * 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<Void> 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<Void> () { - @Override - protected Task<Void> createTask() { - Task<Void> t = new Task<Void>() { - @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<WindowEvent>() { - @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. - * <br> - * 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()}. - * <br> - * 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. - * <br><br> - * 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<Object> 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(); - } -} diff --git a/src/jcgp/gui/console/ConsolePane.java b/src/jcgp/gui/console/ConsolePane.java deleted file mode 100644 index de193a5..0000000 --- a/src/jcgp/gui/console/ConsolePane.java +++ /dev/null @@ -1,122 +0,0 @@ -package jcgp.gui.console; - -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.event.EventDispatchChain; -import javafx.event.EventDispatcher; -import javafx.event.EventHandler; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.MenuItem; -import javafx.scene.control.SeparatorMenuItem; -import javafx.scene.control.TextArea; -import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.AnchorPane; -import jcgp.backend.resources.Console; -import jcgp.gui.constants.Constants; - -/** - * Console pane used by the GUI to display CGP output messages. - * This class realises {@code Console}. It consists of a JavaFX - * {@code TextArea} and a {@code StringBuffer}. The buffer is filled - * as print messages are queued. Calling {@code flush()} writes the - * contents of the buffer to the {@code TextArea} and empties the buffer. - * - * @see Console - * @author Eduardo Pedroni - * - */ -public class ConsolePane extends AnchorPane implements Console { - - private TextArea textArea = new TextArea("Welcome to JCGP!\n"); - private StringBuffer printBuffer = new StringBuffer(); - - /** - * Creates a new instance of this class. - */ - public ConsolePane() { - super(); - textArea.setEditable(false); - /* - * This nasty hack is needed because the default TextArea ContextMenu is not - * in the public API, making it impossible to override it with a custom one. - * This has not been fixed as of 8/4/2014. - * - * The following code modifies the EventDispatcher to consume the right mouse - * button click, preventing the default menu from appearing. It propagates the mouse - * click further so other elements will respond appropriately. - * - * TODO this should be refactored once the API is updated. - */ - final EventDispatcher initial = textArea.getEventDispatcher(); - textArea.setEventDispatcher(new EventDispatcher() { - @Override - public Event dispatchEvent(Event event, EventDispatchChain tail) { - if (event instanceof MouseEvent) { - MouseEvent mouseEvent = (MouseEvent)event; - if (mouseEvent.getButton() == MouseButton.SECONDARY || - (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isControlDown())) { - event.consume(); - } - } - return initial.dispatchEvent(event, tail); - } - }); - - // make the new context menu including the clear option - MenuItem copySelected = new MenuItem("Copy"); - copySelected.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - textArea.copy(); - } - }); - MenuItem selectAll = new MenuItem("Select all"); - selectAll.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - textArea.selectAll(); - } - }); - MenuItem clearConsole = new MenuItem("Clear"); - clearConsole.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - textArea.setText(""); - } - }); - - textArea.setContextMenu(new ContextMenu(copySelected, - selectAll, - new SeparatorMenuItem(), - clearConsole)); - - // anchor the text area so it resizes automatically - AnchorPane.setTopAnchor(textArea, Constants.RESIZE_MARGIN); - AnchorPane.setBottomAnchor(textArea, 0.0); - AnchorPane.setRightAnchor(textArea, 0.0); - AnchorPane.setLeftAnchor(textArea, 0.0); - - setMinHeight(Constants.CONSOLE_MIN_HEIGHT); - setPrefHeight(Constants.CONSOLE_MIN_HEIGHT); - - getChildren().add(textArea); - } - - @Override - public void println(String s) { - printBuffer.append(s + "\n"); - } - - @Override - public void print(String s) { - printBuffer.append(s); - } - - @Override - public void flush() { - textArea.appendText(printBuffer.toString()); - printBuffer = new StringBuffer(); - } - -} diff --git a/src/jcgp/gui/constants/Constants.java b/src/jcgp/gui/constants/Constants.java deleted file mode 100644 index 509d982..0000000 --- a/src/jcgp/gui/constants/Constants.java +++ /dev/null @@ -1,170 +0,0 @@ -package jcgp.gui.constants; - -import javafx.scene.paint.Paint; - -/** - * Holds the constants used in the GUI. - * - * @author Eduardo Pedroni - * - */ -public final class Constants { - - /** - * Private constructor to prevent instantiation. - */ - private Constants(){} - - /*--------------------------------------------------------------------------------------------------- - * Colour Strings - *-------------------------------------------------------------------------------------------------*/ - /** - * A {@code String} containing the colour used for representing neutrality. - */ - public static final String NEUTRAL_COLOUR = "#FFFFFF"; - /** - * A {@code String} containing the hexadecimal colour used for representing a hard highlight. - * A "hard" select, for instance, happens when an output path is locked on the chromosome pane. - */ - public static final String HARD_HIGHLIGHT_COLOUR = "#5496FF"; - /** - * A {@code String} containing the hexadecimal colour used for a medium highlight. - * One example of such a selection is the colour applied to a node when it is hovered over. - */ - public static final String MEDIUM_HIGHLIGHT_COLOUR = "#75BAFF"; - /** - * A {@code String} containing the hexadecimal colour used for a soft highlight. - * When hovering over a node, its connections are soft-selected. - */ - public static final String SOFT_HIGHLIGHT_COLOUR = "#C7DFFF"; - /** - * A {@code String} containing the hexadecimal colour used for representing a good selection. - * Ideally a shade of green, used for instance when a manual connection is valid. - */ - public static final String GOOD_SELECTION_COLOUR = "#38C25B"; - /** - * A {@code String} containing the hexadecimal colour used for representing a neutral selection. - * Ideally a shade of yellow, used for instance when a manual connection is already the current connection. - */ - public static final String NEUTRAL_SELECTION_COLOUR = "#FFEF73"; - /** - * A {@code String} containing the hexadecimal colour used for representing a bad selection. - * Ideally a shade of red, use for instance when a manual connection is not valid. - */ - public static final String BAD_SELECTION_COLOUR = "#FF5C5C"; - /** - * A {@code String} containing the hexadecimal colour used for the gene sockets. - */ - public static final String SOCKET_COLOUR = "#FFFFFF"; - - /*--------------------------------------------------------------------------------------------------- - * Colour Paints - *-------------------------------------------------------------------------------------------------*/ - /** - * A {@code Paint} containing the colour used for representing neutrality. - */ - public static final Paint NEUTRAL_PAINT = Paint.valueOf(NEUTRAL_COLOUR); - /** - * A {@code Paint} containing the colour used for representing a hard highlight. - * A "hard" select, for instance, happens when an output path is locked on the chromosome pane. - */ - public static final Paint HARD_HIGHLIGHT_PAINT = Paint.valueOf(HARD_HIGHLIGHT_COLOUR); - /** - * A {@code Paint} containing the colour used for a medium highlight. - * One example of such a selection is the colour applied to a node when it is hovered over. - */ - public static final Paint MEDIUM_HIGHLIGHT_PAINT = Paint.valueOf(MEDIUM_HIGHLIGHT_COLOUR); - /** - * A {@code Paint} containing the colour used for a soft highlight. - * When hovering over a node, its connections are soft-selected. - */ - public static final Paint SOFT_HIGHLIGHT_PAINT = Paint.valueOf(SOFT_HIGHLIGHT_COLOUR); - /** - * A {@code Paint} containing the colour used for representing a good selection. - * Ideally a shade of green, used for instance when a manual connection is valid. - */ - public static final Paint GOOD_SELECTION_PAINT = Paint.valueOf(GOOD_SELECTION_COLOUR); - /** - * A {@code Paint} containing the colour used for representing a neutral selection. - * Ideally a shade of yellow, used for instance when a manual connection is already the current connection. - */ - public static final Paint NEUTRAL_SELECTION_PAINT = Paint.valueOf(NEUTRAL_SELECTION_COLOUR); - /** - * A {@code Paint} containing the colour used for representing a bad selection. - * Ideally a shade of red, use for instance when a manual connection is not valid. - */ - public static final Paint BAD_SELECTION_PAINT = Paint.valueOf(BAD_SELECTION_COLOUR); - /** - * A {@code Paint} containing the colour used for the gene sockets. - */ - public static final Paint SOCKET_PAINT = Paint.valueOf(SOCKET_COLOUR); - - /*--------------------------------------------------------------------------------------------------- - * Sizes and distances - *-------------------------------------------------------------------------------------------------*/ - /** - * The width or height of the area that can be clicked on - * to drag-resize a pane. - */ - public static final double RESIZE_MARGIN = 5.0; - /** - * The minimum width of the settings pane, to prevent it - * from being resized beyond visibility. - */ - public static final double SETTINGS_MIN_WIDTH = 200; - /** - * The minimum width of the console pane, to prevent it - * from being resized beyond visibility. - */ - public static final double CONSOLE_MIN_HEIGHT = 100; - /** - * Radius used for the representation of nodes in the grid. - */ - public static final double NODE_RADIUS = 35; - /** - * Spacing between each node. - */ - public static final double SPACING = 15; - /** - * The margin between the genes and the edge of the chromosome pane. - */ - public static final double CHROMOSOME_PANE_MARGIN = 10; - /** - * The angle across which the node's sockets are evenly distributed. - */ - public static final double THETA = Math.PI / 1.4; - /** - * The radius of the connection sockets, calculated as a function of NODE_RADIUS. - */ - public static final double SOCKET_RADIUS = Math.sqrt(NODE_RADIUS) / 1.8; - /** - * Size of the text in each node. - */ - public static final double NODE_TEXT = NODE_RADIUS / 2.5; - - /*--------------------------------------------------------------------------------------------------- - * CSS Styles - * TODO extract to stylesheet? - *-------------------------------------------------------------------------------------------------*/ - /** - * The basic style of text boxes used in parameters. - */ - public static final String BASE_TEXT_STYLE = "-fx-border-color: #C9C9C9; -fx-border-radius: 2; -fx-padding: 0; "; - /** - * The basic style of check boxes used in parameters. - */ - public static final String BASE_CHECKBOX_STYLE = "-fx-padding: 0; "; - /** - * The style applied to invalid parameters, using BAD_SELECTION_COLOUR. - */ - public static final String INVALID_PARAMETER_STYLE = "-fx-background-color: " + BAD_SELECTION_COLOUR; - /** - * The style applied to neutral parameters, using NEUTRAL_SELECTION_COLOUR. - */ - public static final String WARNING_PARAMETER_STYLE = "-fx-background-color: " + NEUTRAL_SELECTION_COLOUR; - /** - * The style applied to valid parameters, using NEUTRAL_COLOUR. - */ - public static final String VALID_PARAMETER_STYLE = "-fx-background-color: " + NEUTRAL_COLOUR; - -} diff --git a/src/jcgp/gui/constants/Position.java b/src/jcgp/gui/constants/Position.java deleted file mode 100644 index 6d4e02b..0000000 --- a/src/jcgp/gui/constants/Position.java +++ /dev/null @@ -1,103 +0,0 @@ -package jcgp.gui.constants; - -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import jcgp.gui.GUI; -import jcgp.gui.population.GUIGene; -import jcgp.gui.population.GUIInput; -import jcgp.gui.population.GUINode; -import jcgp.gui.population.GUIOutput; - -/** - * Abstracts the task of positioning GUI components. - * <br> - * Do not instantiate this class; instead, use the {@code public static} methods provided. - * - * @author Eduardo Pedroni - * - */ -public final class Position { - - /** - * Private constructor to prevent instantiation. - */ - private Position() {} - - /** - * Sets the X and Y layouts of the specified input to the appropriate values, according to its index. - * - * @param input the {@code GUIInput} instance to relocate. - */ - public static void place(GUIInput input) { - // inputs are the first column, so we only worry about the margin and their index - input.relocate(Constants.CHROMOSOME_PANE_MARGIN, - input.getInput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN); - } - - /** - * Sets the X and Y layouts of the specified node to the appropriate values, according to its row and column values. - * This also connects the start of every line with its respective socket. Therefore, this method should be called at least - * once when the {@code GUINode} is instantiated. - * - * @param node the {@code GUINode} instance to relocate. - */ - public static void place(GUINode node) { - // calculate x and y offsets, in relation to the layout origin - double xOffset = (node.getNode().getColumn() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN; - double yOffset = node.getNode().getRow() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN; - - // move node - node.relocate(xOffset, yOffset); - - // use the offset and the socket positions to connect the lines - for (int i = 0; i < GUI.resources.arity(); i++) { - node.getLines()[i].setStartX(node.getSocket(i).getCenterX() + xOffset + Constants.NODE_RADIUS + Constants.SOCKET_RADIUS); - node.getLines()[i].setStartY(node.getSocket(i).getCenterY() + yOffset + Constants.NODE_RADIUS); - } - } - - /** - * Sets the X and Y layouts of the specified output to the appropriate values, according to its index. - * This also connects the start of the output's single line to its single input socket.Therefore, - * this method should be called at least once when the {@code GUIOutput} is instantiated. - * - * @param output the {@code GUIOutput} instance to relocate. - */ - public static void place(GUIOutput output) { - // the output's position is a function of the number of columns and its own index - output.relocate(((GUI.resources.columns() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.CHROMOSOME_PANE_MARGIN, - output.getOutput().getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.CHROMOSOME_PANE_MARGIN); - output.getLines()[0].setStartX(output.getLayoutX() - Constants.NODE_RADIUS); - output.getLines()[0].setStartY(output.getLayoutY()); - } - - /** - * Connects the end of a specified line to the specified gene. - * - * @param line the line to connect. - * @param target the target gene to connect to. - */ - public static void connect(Line line, GUIGene target) { - // set line ends based on the layout position of the target - line.setEndX(target.getLayoutX() + Constants.NODE_RADIUS); - line.setEndY(target.getLayoutY()); - } - - /** - * Relocates the given socket to the appropriate position given the - * socket's index. - * - * @param index the socket index. - * @param socket the {@code Circle} instance to relocate. - */ - public static void placeSocket(int index, Circle socket) { - // calculate the angle with respect to the x-axis - double angle = (((index + 1) / ((double) (GUI.resources.arity() + 1))) * Constants.THETA) - (Constants.THETA / 2); - // convert to cartesian form - double xPos = -Math.cos(angle) * Constants.NODE_RADIUS; - double yPos = Math.sin(angle) * Constants.NODE_RADIUS; - // set centre - socket.setCenterX(xPos); - socket.setCenterY(yPos); - } -} diff --git a/src/jcgp/gui/dragresize/HorizontalDragResize.java b/src/jcgp/gui/dragresize/HorizontalDragResize.java deleted file mode 100644 index e88eafd..0000000 --- a/src/jcgp/gui/dragresize/HorizontalDragResize.java +++ /dev/null @@ -1,131 +0,0 @@ -package jcgp.gui.dragresize; - -import javafx.event.EventHandler; -import javafx.scene.Cursor; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Region; -import jcgp.gui.constants.Constants; - -/** - * This class adds horizontal drag resize functionality to any - * arbitrary region provided. This is done by using the static - * method {@code makeDragResizable()}. - * <br><br> - * This is based on a class by Andrew Till found on: - * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html - * - */ -public class HorizontalDragResize { - - private boolean dragging = false; - private final Region region; - - /** - * For internal use only, creates an instance of the actual - * resizer used. - * - * @param region the region to make resizable. - */ - private HorizontalDragResize(Region region) { - this.region = region; - } - - /** - * Makes the specified region drag resizable. - * This particular implementation only creates a resize - * click-and-drag area on the left side of the region. - * The resize area is defined by {@code GUI.RESIZE_MARGIN}. - * - * @param region the region to make resizable. - */ - public static void makeDragResizable(final Region region) { - // make the instance, this actually performs the resizing - final HorizontalDragResize dr = new HorizontalDragResize(region); - - // set mouse listeners - region.setOnMousePressed(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mousePressed(event); - } - }); - region.setOnMouseDragged(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseDragged(event); - } - }); - region.setOnMouseMoved(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseMoved(event); - } - }); - region.setOnMouseReleased(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseReleased(); - } - }); - - } - - /** - * If the press happened in the resize area, raise the drag flag. - * - * @param event the associated mouse event. - */ - private void mousePressed(MouseEvent event) { - if(isInDraggableZone(event)) { - dragging = true; - } - } - - /** - * If drag flag is high, resize the region to match the mouse position. - * - * @param event the associated mouse event. - */ - private void mouseDragged(MouseEvent event) { - if(dragging) { - double newWidth = region.getWidth() - event.getX(); - if (newWidth >= region.getMinWidth()) { - region.setPrefWidth(newWidth); - } else { - region.setPrefWidth(region.getMinWidth()); - } - } - } - - /** - * Change the cursor if the mouse position overlaps with the resize area. - * - * @param event the associated mouse event. - */ - private void mouseMoved(MouseEvent event) { - if(isInDraggableZone(event) || dragging) { - region.setCursor(Cursor.H_RESIZE); - } else { - region.setCursor(Cursor.DEFAULT); - } - } - - /** - * Finish resizing. - */ - private void mouseReleased() { - dragging = false; - region.setCursor(Cursor.DEFAULT); - } - - /** - * Assert whether the mouse cursor is in the draggable area defined by {@code GUI.RESIZE_MARGIN}. - * - * @param event the associated mouse event. - * @return true if the mouse position is in the draggable area. - */ - private boolean isInDraggableZone(MouseEvent event) { - return event.getX() < (Constants.RESIZE_MARGIN); - } - -} diff --git a/src/jcgp/gui/dragresize/VerticalDragResize.java b/src/jcgp/gui/dragresize/VerticalDragResize.java deleted file mode 100644 index 4f784e5..0000000 --- a/src/jcgp/gui/dragresize/VerticalDragResize.java +++ /dev/null @@ -1,132 +0,0 @@ -package jcgp.gui.dragresize; - -import javafx.event.EventHandler; -import javafx.scene.Cursor; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Region; -import jcgp.gui.constants.Constants; - -/** - * This class adds vertical drag resize functionality to any - * arbitrary region provided. This is done by using the static - * method {@code makeDragResizable()}. - * <br><br> - * This is based on a class by Andrew Till found on: - * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html - * - */ -public class VerticalDragResize { - - private boolean dragging = false; - private final Region region; - - /** - * For internal use only, creates an instance of the actual - * resizer used. - * - * @param region the region to make resizable. - */ - private VerticalDragResize(Region region) { - this.region = region; - } - - /** - * Makes the specified region drag resizable. - * This particular implementation only creates a resize - * click-and-drag area on the top side of the region. - * The resize area is defined by {@code GUI.RESIZE_MARGIN}. - * - * @param region the region to make resizable. - */ - public static void makeDragResizable(final Region region) { - // make the instance, this actually performs the resizing - final VerticalDragResize dr = new VerticalDragResize(region); - - // set mouse listeners - region.setOnMousePressed(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mousePressed(event); - } - }); - region.setOnMouseDragged(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseDragged(event); - } - }); - region.setOnMouseMoved(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseMoved(event); - } - }); - region.setOnMouseReleased(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dr.mouseReleased(); - } - }); - - } - - /** - * If the press happened in the resize area, raise the drag flag. - * - * @param event the associated mouse event. - */ - private void mousePressed(MouseEvent event) { - if(isInDraggableZone(event)) { - dragging = true; - } - } - - /** - * If drag flag is high, resize the region to match the mouse position. - * - * @param event the associated mouse event. - */ - private void mouseDragged(MouseEvent event) { - if(dragging) { - double newHeight = region.getHeight() - event.getY(); - if (newHeight >= region.getMinHeight()) { - region.setPrefHeight(newHeight); - } else { - region.setPrefHeight(region.getMinHeight()); - } - } - } - - /** - * Change the cursor if the mouse position overlaps with the resize area. - * - * @param event the associated mouse event. - */ - private void mouseMoved(MouseEvent event) { - if(isInDraggableZone(event) || dragging) { - region.setCursor(Cursor.V_RESIZE); - } - else { - region.setCursor(Cursor.DEFAULT); - } - } - - /** - * Finish resizing. - */ - private void mouseReleased() { - dragging = false; - region.setCursor(Cursor.DEFAULT); - } - - /** - * Assert whether the mouse cursor is in the draggable area defined by {@code GUI.RESIZE_MARGIN}. - * - * @param event the associated mouse event. - * @return true if the mouse position is in the draggable area. - */ - private boolean isInDraggableZone(MouseEvent event) { - return event.getY() < (Constants.RESIZE_MARGIN); - } - -} diff --git a/src/jcgp/gui/handlers/InputHandlers.java b/src/jcgp/gui/handlers/InputHandlers.java deleted file mode 100644 index cc677eb..0000000 --- a/src/jcgp/gui/handlers/InputHandlers.java +++ /dev/null @@ -1,56 +0,0 @@ -package jcgp.gui.handlers; - -import javafx.event.EventHandler; -import javafx.scene.input.MouseEvent; -import jcgp.gui.population.GUIGene.GUIGeneState; -import jcgp.gui.population.GUIInput; - -/** - * Holds the handlers that define the behaviour of {@code GUIInput}. - * <br><br> - * The handlers are instantiated here statically and added to {@code GUIInput} - * instances using {@code InputHandlers.addHandlers(...)}. This guarantees that - * all inputs behave the same way without instantiating a new set of handlers for - * each input instance. - * - * @author Eduardo Pedroni - * - */ -public final class InputHandlers { - - /** - * Private constructor to prevent instantiation. - */ - private InputHandlers() {} - - /** - * Inputs don't do much; set state to hover when mouse enters. - */ - private static EventHandler<MouseEvent> mouseEnteredHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - ((GUIInput) event.getSource()).setState(GUIGeneState.HOVER); - } - }; - - /** - * Inputs don't do much; set state to neutral when mouse exits. - */ - private static EventHandler<MouseEvent> mouseExitedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - ((GUIInput) event.getSource()).setState(GUIGeneState.NEUTRAL); - } - }; - - /** - * Adds all handlers to the specified input. - * - * @param input the {@code GUIInput} to which the handlers will be added. - */ - public static void addHandlers(GUIInput input) { - input.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEnteredHandler); - input.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler); - } - -} diff --git a/src/jcgp/gui/handlers/NodeHandlers.java b/src/jcgp/gui/handlers/NodeHandlers.java deleted file mode 100644 index b413a62..0000000 --- a/src/jcgp/gui/handlers/NodeHandlers.java +++ /dev/null @@ -1,164 +0,0 @@ -package jcgp.gui.handlers; - -import javafx.event.EventHandler; -import javafx.scene.input.MouseDragEvent; -import javafx.scene.input.MouseEvent; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import jcgp.backend.population.Gene; -import jcgp.gui.GUI; -import jcgp.gui.constants.Position; -import jcgp.gui.population.ChromosomePane; -import jcgp.gui.population.GUIConnection; -import jcgp.gui.population.GUIGene; -import jcgp.gui.population.GUIGene.GUIGeneState; -import jcgp.gui.population.GUINode; - -/** - * Holds the handlers that define the behaviour of {@code GUINode}. - * <br><br> - * The handlers are instantiated here statically and added to {@code GUINode} - * instances using {@code NodeHandlers.addHandlers(...)}. This guarantees that - * all nodes behave the same way without instantiating a new set of handlers for - * each node instance. - * - * @author Eduardo Pedroni - * - */ -public final class NodeHandlers { - - /** - * Private constructor to prevent instantiation. - */ - private NodeHandlers() {} - - /** - * Set the node to {@code GUIGeneState.HOVER} state, and set its immediate connections to {@code GUIGeneState.EXTENDED_HOVER}. - */ - private static EventHandler<MouseEvent> mouseEnteredHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // acquire the source, we can safely cast it to GUINode - GUINode source = (GUINode) event.getSource(); - - source.setState(GUIGeneState.HOVER); - for (int i = 0; i < GUI.resources.arity(); i++) { - ((GUIGene) ((Gene) source.getNode().getConnection(i)).getGUIObject()).setState(GUIGeneState.EXTENDED_HOVER); - } - } - }; - - /** - * Set the node and its immediate connections to {@code GUIGeneState.NEUTRAL} state. - */ - private static EventHandler<MouseEvent> mouseExitedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // acquire the source, we can safely cast it to GUINode - GUINode source = (GUINode) event.getSource(); - - if (Target.getSourceMutable() != source) { - source.setState(GUIGeneState.NEUTRAL); - for (int i = 0; i < GUI.resources.arity(); i++) { - ((GUIGene) ((Gene) source.getNode().getConnection(i)).getGUIObject()).setState(GUIGeneState.NEUTRAL); - } - } - } - }; - - private static EventHandler<MouseEvent> socketDragDetected = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // it's safe to assume that the source is the socket - ((GUINode) ((Circle) event.getSource()).getParent()).startFullDrag(); - } - }; - - private static EventHandler<MouseEvent> socketMousePressedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // it's safe to assume that the source is the socket - Target.start((Circle) event.getSource()); - } - }; - - private static EventHandler<MouseEvent> socketMouseDraggedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // this can only happen after a press, so we know Target is up-to-date - if (!Target.isProspecting()) { - GUINode node = (GUINode) Target.getSourceMutable(); - Line line = Target.getConnectionLine(); - line.setEndX(event.getX() + node.getLayoutX()); - line.setEndY(event.getY() + node.getLayoutY()); - } - - } - }; - - private static EventHandler<MouseEvent> socketMouseReleasedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - - GUINode node = (GUINode) ((Circle) event.getSource()).getParent(); - int connectionId = Integer.valueOf(((Circle) event.getSource()).getId()); - - Position.connect(node.getLines()[connectionId], (GUIGene) ((Gene) node.getNode().getConnection(connectionId)).getGUIObject()); - } - }; - - private static EventHandler<MouseDragEvent> dragEnteredHandler = new EventHandler<MouseDragEvent>() { - @Override - public void handle(MouseDragEvent event) { - // acquire the source, we can safely cast it to GUINode - GUINode source = (GUINode) event.getSource(); - if (Target.getCurrentConnection() == source) { - source.setState(GUIGeneState.NEUTRAL_TARGET); - // we are now prospecting - Target.setProspecting(true); - Position.connect(Target.getConnectionLine(), source); - } else if (ChromosomePane.isAllowed(Target.getSourceMutable(), (GUIConnection) source)) { - source.setState(GUIGeneState.GOOD_TARGET); - // we are now prospecting - Target.setProspecting(true); - Position.connect(Target.getConnectionLine(), source); - } else { - source.setState(GUIGeneState.BAD_TARGET); - } - } - }; - - private static EventHandler<MouseDragEvent> dragExitedHandler = new EventHandler<MouseDragEvent>() { - @Override - public void handle(MouseDragEvent event) { - // acquire the source, we can safely cast it to GUINode - GUINode source = (GUINode) event.getSource(); - source.setState(GUIGeneState.NEUTRAL); - - // no longer prospecting - Target.setProspecting(false); - } - }; - - /** - * Adds all handlers to the specified node. - * - * @param node the {@code GUINode} to which the handlers will be added. - */ - public static void addHandlers(GUINode node) { - node.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEnteredHandler); - node.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler); - - node.addEventHandler(MouseDragEvent.MOUSE_DRAG_ENTERED, dragEnteredHandler); - node.addEventHandler(MouseDragEvent.MOUSE_DRAG_EXITED, dragExitedHandler); - - Circle[] sockets = node.getSockets(); - for (int s = 0; s < sockets.length; s++) { - - sockets[s].addEventFilter(MouseEvent.DRAG_DETECTED, socketDragDetected); - sockets[s].addEventHandler(MouseEvent.MOUSE_PRESSED, socketMousePressedHandler); - sockets[s].addEventHandler(MouseEvent.MOUSE_DRAGGED, socketMouseDraggedHandler); - sockets[s].addEventHandler(MouseEvent.MOUSE_RELEASED, socketMouseReleasedHandler); - } - } -} diff --git a/src/jcgp/gui/handlers/OutputHandlers.java b/src/jcgp/gui/handlers/OutputHandlers.java deleted file mode 100644 index b89d746..0000000 --- a/src/jcgp/gui/handlers/OutputHandlers.java +++ /dev/null @@ -1,84 +0,0 @@ -package jcgp.gui.handlers; - -import javafx.event.EventHandler; -import javafx.scene.input.MouseEvent; -import jcgp.backend.population.Gene; -import jcgp.gui.population.GUIConnection; -import jcgp.gui.population.GUIGene.GUIGeneState; -import jcgp.gui.population.GUIOutput; - -/** - * Holds the handlers that define the behaviour of {@code GUIOutput}. - * <br><br> - * The handlers are instantiated here statically and added to {@code GUIOutput} - * instances using {@code OutputHandlers.addHandlers(...)}. This guarantees that - * all outputs behave the same way without instantiating a new set of handlers for - * each output instance. - * - * @author Eduardo Pedroni - * - */ -public final class OutputHandlers { - - /** - * Private constructor to prevent instantiation. - */ - private OutputHandlers() {} - - /** - * Set the output to {@code GUIGeneState.HOVER} state, and recursively set its active genes - * to {@code GUIGeneState.ACTIVE_HOVER}. - */ - private static EventHandler<MouseEvent> mouseEnteredHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // acquire the source, we can safely cast it to GUIOutput - GUIOutput source = (GUIOutput) event.getSource(); - - source.setState(GUIGeneState.HOVER); - ((GUIConnection) ((Gene) source.getOutput().getSource()).getGUIObject()).setStateRecursively(GUIGeneState.ACTIVE_HOVER); - } - }; - - /** - * Set the output and all of its active genes to {@code GUIGeneState.NEUTRAL} state. - */ - private static EventHandler<MouseEvent> mouseExitedHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // acquire the source, we can safely cast it to GUIOutput - GUIOutput source = (GUIOutput) event.getSource(); - - source.setState(GUIGeneState.NEUTRAL); - ((GUIConnection) ((Gene) source.getOutput().getSource()).getGUIObject()).setStateRecursively(GUIGeneState.NEUTRAL); - } - }; - - /** - * If the output is locked, unlock it and all of its associated genes recursively. - * If it is unlocked, lock it and its active genes. - */ - private static EventHandler<MouseEvent> mouseClickHandler = new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // acquire the source, we can safely cast it to GUIOutput - GUIOutput source = (GUIOutput) event.getSource(); - - boolean lock = !source.isLocked(); - source.setLock(lock); - ((GUIConnection) ((Gene) source.getOutput().getSource()).getGUIObject()).setLockRecursively(lock); - } - }; - - /** - * Adds all handlers to the specified output. - * - * @param output the {@code GUIOutput} to which the handlers will be added. - */ - public static void addHandlers(GUIOutput output) { - output.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEnteredHandler); - output.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler); - output.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickHandler); - } - -} diff --git a/src/jcgp/gui/handlers/Target.java b/src/jcgp/gui/handlers/Target.java deleted file mode 100644 index b050663..0000000 --- a/src/jcgp/gui/handlers/Target.java +++ /dev/null @@ -1,70 +0,0 @@ -package jcgp.gui.handlers; - -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import jcgp.gui.population.GUIConnection; -import jcgp.gui.population.GUIMutable; - -/** - * @author Eduardo Pedroni - * - */ -public final class Target { - - /** - * Private constructor to prevent instantiation. - */ - private Target() {} - - private static GUIConnection targetConnection; - private static GUIMutable sourceMutable; - private static int connectionIndex; - private static Line connectionLine; - private static Circle sourceSocket; - private static boolean prospecting = false; - - public static void start(Circle newSocket) { - // store new socket - sourceSocket = newSocket; - // derive the rest of the information from it - connectionIndex = Integer.valueOf(newSocket.getId()); - sourceMutable = (GUIMutable) newSocket.getParent(); - connectionLine = sourceMutable.getLines()[connectionIndex]; - } - - public static GUIMutable getSourceMutable() { - return sourceMutable; - } - - public static int getConnectionIndex() { - return connectionIndex; - } - - public static Line getConnectionLine() { - return connectionLine; - } - - public static Circle getSourceSocket() { - return sourceSocket; - } - - public static GUIConnection getTarget() { - return targetConnection; - } - - public static GUIConnection getCurrentConnection() { - return sourceMutable.getConnections()[connectionIndex]; - } - - public static void setProspecting(boolean value) { - prospecting = value; - } - - public static boolean isProspecting() { - return prospecting; - } - - public static void setTarget(GUIConnection newTarget) { - targetConnection = newTarget; - } -} diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java deleted file mode 100644 index a87a054..0000000 --- a/src/jcgp/gui/population/ChromosomePane.java +++ /dev/null @@ -1,140 +0,0 @@ -package jcgp.gui.population; - -import java.util.ArrayList; - -import javafx.scene.control.ScrollPane; -import javafx.scene.layout.Pane; -import javafx.scene.shape.Line; -import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Node; -import jcgp.gui.GUI; - -/** - * This extension of {@code ScrollPane} contains a series of - * nodes, inputs and outputs spread across a grid. It also contains - * all of the connection lines laid over the nodes, inputs and outputs. - * - * - * @author Eduardo Pedroni - * - */ -public class ChromosomePane extends ScrollPane { - - private GUIInput[] guiInputs; - private GUINode[][] guiNodes; - private GUIOutput[] guiOutputs; - - private Pane content; - - private boolean target = false; - - public ChromosomePane(Chromosome chromosome) { - super(); - - ArrayList<Line> connectionLines = new ArrayList<Line>(); - - int rows = GUI.resources.rows(); - int columns = GUI.resources.columns(); - - content = new Pane(); - content.setId("content pane for genes"); - - /* - * inputs - */ - guiInputs = new GUIInput[GUI.resources.inputs()]; - for (int i = 0; i < guiInputs.length; i++) { - guiInputs[i] = new GUIInput(chromosome.getInput(i)); - } - // add inputs to content pane - content.getChildren().addAll(guiInputs); - - /* - * nodes - */ - guiNodes = new GUINode[rows][columns]; - for (int c = 0; c < columns; c++) { - for (int r = 0; r < rows; r++) { - // make the connection lines - Line lines[] = new Line[GUI.resources.arity()]; - for (int l = 0; l < lines.length; l++) { - lines[l] = new Line(); - lines[l].setMouseTransparent(true); - lines[l].setVisible(false); - connectionLines.add(lines[l]); - } - // make the GUI element - guiNodes[r][c] = new GUINode(chromosome.getNode(r, c), lines); - // add node to content pane - content.getChildren().add(guiNodes[r][c]); - } - } - - /* - * outputs - */ - guiOutputs = new GUIOutput[GUI.resources.outputs()]; - for (int i = 0; i < guiOutputs.length; i++) { - // make the connection line - Line line = new Line(); - line.setVisible(false); - line.setMouseTransparent(true); - connectionLines.add(line); - // make the GUI element - guiOutputs[i] = new GUIOutput(chromosome.getOutput(i), line); - } - // add outputs to content pane - content.getChildren().addAll(guiOutputs); - - // add lines to the pane on top of genes - content.getChildren().addAll(connectionLines); - - setPrefWidth(620); - setContent(content); - } - - protected boolean isTarget() { - return target; - } - - protected void setTarget(boolean newValue) { - target = newValue; - } - - public void updateGenes(Chromosome chr) { - for (int r = 0; r < GUI.resources.rows(); r++) { - for (int c = 0; c < GUI.resources.columns(); c++) { - guiNodes[r][c].setNode(chr.getNode(r, c)); - } - } - for (int i = 0; i < guiOutputs.length; i++) { - guiOutputs[i].setOutput(chr.getOutput(i)); - } - } - - public static boolean isAllowed(GUIMutable source, GUIConnection target) { - if (source instanceof GUINode) { - // if the source is a node, all inputs and some nodes are valid - if (target instanceof GUIInput) { - return true; - } else if (target instanceof GUINode) { - // target and source are nodes, let's look at levels back - Node t = ((GUINode) target).getNode(), s = ((GUINode) source).getNode(); - if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= GUI.resources.levelsBack()) { - return true; - } - } - return false; - } else if (source instanceof GUIOutput) { - // if the source is an output, any node or input is valid - if (target instanceof GUINode || target instanceof GUIInput) { - return true; - } else { - // this should never happen... - return false; - } - } - // if the source was neither node nor output, something bad is happening - throw new ClassCastException("Source was neither GUINode nor GUIOutput."); - } -} diff --git a/src/jcgp/gui/population/FunctionSelector.java b/src/jcgp/gui/population/FunctionSelector.java deleted file mode 100644 index 14614e5..0000000 --- a/src/jcgp/gui/population/FunctionSelector.java +++ /dev/null @@ -1,80 +0,0 @@ -package jcgp.gui.population; - -import javafx.event.EventHandler; -import javafx.scene.control.Label; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.VBox; -import jcgp.backend.function.FunctionSet; -import jcgp.gui.constants.Constants; - -/** - * A menu class, exposes all of the allowed functions - * when called by a node, so that the node function can be changed. - * - * - * @author Eduardo Pedroni - * - */ -public class FunctionSelector extends VBox { - - private GUINode target; - - public FunctionSelector(FunctionSet functionSet) { - setFillWidth(true); - setVisible(false); - setStyle("-fx-border-color: #A0A0A0; -fx-border-width: 1 1 0 1"); - - remakeFunctions(functionSet); - - addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dismiss(); - } - }); - } - - public void remakeFunctions(final FunctionSet fs) { - getChildren().clear(); - - for (int i = 0; i < fs.getAllowedFunctionCount(); i++) { - final int index = i; - Label l = new Label(fs.getAllowedFunction(i).toString()); - l.setMaxWidth(Double.MAX_VALUE); - l.setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); - - l.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - ((Label) event.getSource()).setStyle("-fx-background-color: " + Constants.SOFT_HIGHLIGHT_PAINT + "; -fx-border-color: #B0B0B0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); - } - }); - l.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - ((Label) event.getSource()).setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); - } - }); - l.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - //target.setFunction(fs.getAllowedFunction(index)); - dismiss(); - } - }); - - getChildren().add(l); - } - } - - public void relocateAndShow(MouseEvent event, GUINode node) { - relocate(event.getSceneX() - 5, event.getSceneY() - 5); - target = node; - setVisible(true); - } - - private void dismiss() { - setVisible(false); - } - -} diff --git a/src/jcgp/gui/population/GUIConnection.java b/src/jcgp/gui/population/GUIConnection.java deleted file mode 100644 index dc7fcc8..0000000 --- a/src/jcgp/gui/population/GUIConnection.java +++ /dev/null @@ -1,32 +0,0 @@ -package jcgp.gui.population; - -import jcgp.gui.population.GUIGene.GUIGeneState; - -/** - * A loose equivalent to {@link jcgp.backend.population.Connection}. - * <br> - * This defines behaviour that all GUI representations of connections - * should be capable of. - * - * @author Eduardo Pedroni - * - */ -public interface GUIConnection { - - /** - * Set the connection's state, but also recursively propagate that state - * all the way back to the inputs. - * - * @param state the state to set. - */ - public void setStateRecursively(GUIGeneState state); - - /** - * Add or remove a lock, but also recursively propagate that change - * all the way back to the inputs. - * - * @param value true to lock, false to unlock. - */ - public void setLockRecursively(boolean value); - -} diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java deleted file mode 100644 index 5e6107f..0000000 --- a/src/jcgp/gui/population/GUIGene.java +++ /dev/null @@ -1,187 +0,0 @@ -package jcgp.gui.population; - -import javafx.geometry.VPos; -import javafx.scene.Group; -import javafx.scene.paint.Paint; -import javafx.scene.shape.Circle; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.scene.text.TextAlignment; -import jcgp.gui.constants.Constants; - -/** - * Defines the general behaviour of the visual representation of each chromosome gene. - * <br><br> - * In practice, this is subclass of {@code javafx.scene.Group} containing a {@code Circle} - * object and a {@code Text} object. Subclasses may add further elements to the group, for - * instance to display connection input and output sockets. - * <br><br> - * Genes also contain a locked property. When locked, some gene states behave slightly - * differently. This is used so genes remain highlighted even in the neutral state. The - * gene lock is in fact recursive; a gene can be locked multiple times and only unlocking - * it as many times will actually revert it back to its unlocked state. This allows multiple - * pathways to lock the same gene independently without affecting each other; the gene remains - * locked until no pathways are locking it. - * - * @author Eduardo Pedroni - * - */ -public abstract class GUIGene extends Group { - - /** - * This {@code enum} type defines a finite list of all states - * a gene can take. Each state represents a particular steady - * situation, and has its own GUI appearance associated with it: - * a combination of connection line visibility, gene background colour - * and other visual characteristics. - * - * @author Eduardo Pedroni - * - */ - public enum GUIGeneState { - /** - * No user interaction at all. - */ - NEUTRAL, - /** - * User is simply hovering over the node. - */ - HOVER, - /** - * User is hovering over a node connected to this one. - */ - EXTENDED_HOVER, - /** - * User is hovering over an output connected to this gene. - */ - ACTIVE_HOVER, - - GOOD_TARGET, - - NEUTRAL_TARGET, - - BAD_TARGET - } - - private GUIGeneState currentState = GUIGeneState.NEUTRAL; - - private Text text; - private Circle mainCircle; - - /** - * Recursive lock; lock == 0 means unlocked, lock > 0 means locked. - * Accessing using {@code setLock(...)}. - */ - private int lock = 0; - - /** - * Initialises the {@code Text} and {@code Circle} objects so that all genes are standardised. - */ - protected GUIGene() { - text = new Text(); - text.setFont(Font.font("Arial", 12)); - text.setTextOrigin(VPos.CENTER); - text.setTextAlignment(TextAlignment.CENTER); - text.setWrappingWidth(Constants.NODE_RADIUS * 2); - text.setX(-Constants.NODE_RADIUS); - - mainCircle = new Circle(Constants.NODE_RADIUS, Constants.NEUTRAL_PAINT); - mainCircle.setStroke(Paint.valueOf("black")); - - getChildren().addAll(mainCircle, text); - } - - /** - * Sets the gene's text field. - * - * @param newText the text string to be displayed. - */ - public void setText(String newText) { - text.setText(newText); - } - - /** - * @return the gene's current state. - */ - public GUIGeneState getState() { - return currentState; - } - - /** - * Gene states are standardised: all gene subclasses behave the same way in each state. - * <br> - * This design choice was made for the sake of consistency. Rather than controlling the - * appearance of the genes with logic in the state transition method AND the mouse handlers, - * the states are now consistent across all types of gene. The mouse handlers implement - * whatever logic is necessary to determine the gene's new state given a certain user input, - * but the states themselves are the same for all genes. - * <br> - * The transition logic for each type of gene is defined in its respective handler class: - * {@code InputHandlers}, {@code NodeHandlers} and {@code OutputHandlers}. - * - * @param newState the gene's new state. - */ - public final void setState(GUIGeneState newState) { - switch (newState) { - case NEUTRAL: - mainCircle.setFill(isLocked() ? Constants.HARD_HIGHLIGHT_PAINT : Constants.NEUTRAL_PAINT); - setLinesVisible(isLocked()); - break; - case HOVER: - mainCircle.setFill(Constants.MEDIUM_HIGHLIGHT_PAINT); - setLinesVisible(true); - break; - case EXTENDED_HOVER: - mainCircle.setFill(Constants.SOFT_HIGHLIGHT_PAINT); - setLinesVisible(isLocked()); - break; - case ACTIVE_HOVER: - mainCircle.setFill(Constants.SOFT_HIGHLIGHT_PAINT); - setLinesVisible(true); - break; - case GOOD_TARGET: - mainCircle.setFill(Constants.GOOD_SELECTION_PAINT); - break; - case NEUTRAL_TARGET: - mainCircle.setFill(Constants.NEUTRAL_SELECTION_PAINT); - break; - case BAD_TARGET: - mainCircle.setFill(Constants.BAD_SELECTION_PAINT); - break; - } - currentState = newState; - } - - /** - * For the sake of practicality, all {@code GUIGene} instances must implement this - * method. It sets the visibility of all of the gene's lines, if it has any. - * - * @param value the visibility value. - */ - protected abstract void setLinesVisible(boolean value); - - /** - * @return true if the gene is locked, false otherwise. - */ - public boolean isLocked() { - return lock > 0; - } - - /** - * Locks or unlocks the gene once. Locked genes - * behave slightly differently in some states. - * <br> - * Unlocking an already unlocked gene does nothing. - * - * @param value true to lock, false to unlock; - */ - public void setLock(boolean value) { - if (value) { - lock++; - } else if (lock > 0) { - lock--; - } else { - lock = 0; - } - } -} diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java deleted file mode 100644 index 3db7416..0000000 --- a/src/jcgp/gui/population/GUIInput.java +++ /dev/null @@ -1,70 +0,0 @@ -package jcgp.gui.population; - -import javafx.scene.paint.Paint; -import javafx.scene.shape.Circle; -import jcgp.backend.population.Input; -import jcgp.gui.constants.Constants; -import jcgp.gui.constants.Position; -import jcgp.gui.handlers.InputHandlers; - -/** - * The GUI counterpart of {@link jcgp.backend.population.Input}. This is a - * subclass of {@code GUIGene} which represents a chromosome input. - * - * @author Eduardo Pedroni - */ -public class GUIInput extends GUIGene implements GUIConnection { - - private Input input; - - /** - * Instantiate {@code GUIInput} given an {@code Input}. - * - * @param input the associated backend input. - */ - public GUIInput(final Input input) { - super(); - // store the input, associate itself with it - this.input = input; - input.setGUIObject(this); - - // inputs only have a single output socket - Circle outputSocket = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); - outputSocket.setStroke(Paint.valueOf("black")); - outputSocket.setId(String.valueOf(0)); - getChildren().add(outputSocket); - - // relocate to the right position, add mouse handlers - Position.place(this); - InputHandlers.addHandlers(this); - } - - /** - * @return the {@code Input} instance associated with this object. - */ - public Input getInput() { - return input; - } - - /** - * Associates this instance with a new input. - * - * @param input the new input. - */ - void setInput(Input input) { - this.input = input; - } - - @Override - public void setStateRecursively(GUIGeneState state) { - setState(state); - } - - @Override - protected void setLinesVisible(boolean value) {} - - @Override - public void setLockRecursively(boolean value) { - setLock(value); - } -} diff --git a/src/jcgp/gui/population/GUIMutable.java b/src/jcgp/gui/population/GUIMutable.java deleted file mode 100644 index fa996e2..0000000 --- a/src/jcgp/gui/population/GUIMutable.java +++ /dev/null @@ -1,20 +0,0 @@ -package jcgp.gui.population; - -import javafx.scene.shape.Line; - -/** - * A loose equivalent to {@link jcgp.backend.population.Mutable}. - * <br> - * This defines behaviour that all GUI representations of mutables - * should be capable of. - * - * @author Eduardo Pedroni - * - */ -public interface GUIMutable { - - public Line[] getLines(); - - public GUIConnection[] getConnections(); - -} diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java deleted file mode 100644 index 1a32426..0000000 --- a/src/jcgp/gui/population/GUINode.java +++ /dev/null @@ -1,134 +0,0 @@ -package jcgp.gui.population; - -import javafx.scene.paint.Paint; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import jcgp.backend.population.Gene; -import jcgp.backend.population.Node; -import jcgp.gui.GUI; -import jcgp.gui.constants.Constants; -import jcgp.gui.constants.Position; -import jcgp.gui.handlers.NodeHandlers; - -/** - * The GUI counterpart of {@link jcgp.backend.population.Node}. This is a - * subclass of {@code GUIGene} which represents a chromosome node. - * - * @author Eduardo Pedroni - */ -public class GUINode extends GUIGene implements GUIMutable, GUIConnection { - - private Node node; - private Line[] lines; - private Circle[] sockets; - - /** - * Instantiate {@code GUINode} given a {@code Node} and the lines needed - * to show its connections. - * - * @param node the associated backend node. - * @param lines the lines used to display connections. - */ - public GUINode(Node node, Line[] lines) { - super(); - // store references, associate with node - this.node = node; - this.lines = lines; - node.setGUIObject(this); - - // create the output socket - Circle output = new Circle(Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT); - output.setStroke(Paint.valueOf("black")); - - // create input sockets - sockets = new Circle[GUI.resources.arity()]; - for (int l = 0; l < sockets.length; l++) { - sockets[l] = new Circle(Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT); - sockets[l].setStroke(Paint.valueOf("black")); - sockets[l].setId(String.valueOf(l)); - // relocate them - Position.placeSocket(l, sockets[l]); - Position.connect(lines[l], (GUIGene) ((Gene) node.getConnection(l)).getGUIObject()); - } - - // add elements - getChildren().addAll(sockets); - getChildren().add(output); - - // relocate node, add handlers - Position.place(this); - NodeHandlers.addHandlers(this); - } - - /** - * @return the {@code Node} instance associated with this object. - */ - public Node getNode() { - return node; - } - - /** - * Associates this instance with a new node. - * - * @param node the new node. - */ - void setNode(Node node) { - this.node = node; - } - - @Override - public Line[] getLines() { - return lines; - } - - /** - * Returns one of this object's connection sockets. They are - * indexed in the same order as lines and the connections - * they represent. - * - * @param index the socket to return. - * @return the indexed socket object. - */ - public Circle getSocket(int index) { - return sockets[index]; - } - - /** - * @return the entire {@code Socket} array. - */ - public Circle[] getSockets() { - return sockets; - } - - @Override - public void setStateRecursively(GUIGeneState state) { - setState(state); - for (int i = 0; i < GUI.resources.arity(); i++) { - ((GUIConnection) ((Gene) node.getConnection(i)).getGUIObject()).setStateRecursively(state); - } - } - - @Override - protected void setLinesVisible(boolean value) { - for (int i = 0; i < lines.length; i++) { - lines[i].setVisible(value); - } - } - - @Override - public void setLockRecursively(boolean value) { - setLock(value); - for (int i = 0; i < GUI.resources.arity(); i++) { - ((GUIConnection) ((Gene) node.getConnection(i)).getGUIObject()).setLockRecursively(value); - } - } - - @Override - public GUIConnection[] getConnections() { - GUIConnection[] connections = new GUIConnection[GUI.resources.arity()]; - for (int c = 0; c < connections.length; c++) { - connections[c] = (GUIConnection) ((Gene) node.getConnection(c)).getGUIObject(); - } - return connections; - } -} diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java deleted file mode 100644 index f023d00..0000000 --- a/src/jcgp/gui/population/GUIOutput.java +++ /dev/null @@ -1,79 +0,0 @@ -package jcgp.gui.population; - -import javafx.scene.paint.Paint; -import javafx.scene.shape.Circle; -import javafx.scene.shape.Line; -import jcgp.backend.population.Gene; -import jcgp.backend.population.Output; -import jcgp.gui.constants.Constants; -import jcgp.gui.constants.Position; -import jcgp.gui.handlers.OutputHandlers; - -/** - * The GUI counterpart of {@link jcgp.backend.population.Output}. This is a - * subclass of {@code GUIGene} which represents a chromosome output. - * - * @author Eduardo Pedroni - */ -public class GUIOutput extends GUIGene implements GUIMutable { - - private Output output; - private Line line; - - /** - * Instantiate {@code GUIOutput} given an {@code Output} and the line needed - * to show its connection. - * - * @param output the associated backend output. - * @param line the line used to display connection. - */ - public GUIOutput(final Output output, Line line) { - super(); - // store references, associate with backend object - this.output = output; - this.line = line; - output.setGUIObject(this); - - // create input socket - Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT); - socket.setStroke(Paint.valueOf("black")); - socket.setId(String.valueOf(0)); - Position.connect(line, (GUIGene) ((Gene) output.getSource()).getGUIObject()); - getChildren().add(socket); - - // relocate output, add handlers - Position.place(this); - OutputHandlers.addHandlers(this); - } - - /** - * @return the {@code Output} instance associated with this object. - */ - public Output getOutput() { - return output; - } - - /** - * Associates this instance with a new output. - * - * @param output the new output. - */ - void setOutput(Output output) { - this.output = output; - } - - @Override - public Line[] getLines() { - return new Line[] {line}; - } - - @Override - protected void setLinesVisible(boolean value) { - line.setVisible(value); - } - - @Override - public GUIConnection[] getConnections() { - return new GUIConnection[] {(GUIConnection) output.getGUIObject()}; - } -} diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java deleted file mode 100644 index 51b5ba4..0000000 --- a/src/jcgp/gui/population/PopulationPane.java +++ /dev/null @@ -1,75 +0,0 @@ -package jcgp.gui.population; - -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import jcgp.JCGP; -import jcgp.backend.modules.problem.TestCaseProblem; -import jcgp.backend.modules.problem.TestCaseProblem.TestCase; -import jcgp.gui.GUI; - -public class PopulationPane extends TabPane { - - private GUI gui; - private TestCase<Object> currentTestCase; - private boolean evaluating = false; - - public PopulationPane(GUI gui) { - super(); - this.gui = gui; - setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - remakeTabs(); - } - - public void remakeTabs() { - getTabs().clear(); - JCGP jcgp = gui.getExperiment(); - - Tab tab; - ChromosomePane cp; - for (int i = 0; i < jcgp.getResources().populationSize(); i++) { - cp = new ChromosomePane(jcgp.getPopulation().get(i)); - tab = new Tab("Chr " + i); - tab.setContent(cp); - getTabs().add(tab); - } - } - - public void updateGenes() { - if (evaluating) { - evaluateTestCase(currentTestCase); - } - for (int i = 0; i < getTabs().size(); i++) { - ((ChromosomePane) getTabs().get(i).getContent()).updateGenes(gui.getExperiment().getPopulation().get(i)); - } - } - - public void evaluateTestCase(TestCase<Object> testCase) { - if (gui.getExperiment().getProblem() instanceof TestCaseProblem && testCase != null) { - currentTestCase = testCase; - if (testCase.getInputs().length == gui.getExperiment().getResources().inputs()) { - evaluating = true; - for (int i = 0; i < getTabs().size(); i++) { - //((ChromosomePane) getTabs().get(i).getContent()).setInputs(testCase.getInputs()); - } - } else { - throw new IllegalArgumentException("Test case has " + testCase.getInputs().length - + " inputs and chromosome has " + gui.getExperiment().getResources().inputs()); - } - } - } - - public void hideValues() { - evaluating = false; - for (int i = 0; i < getTabs().size(); i++) { - //((ChromosomePane) getTabs().get(i).getContent()).updateValues(); - } - } - - public boolean isEvaluating() { - return evaluating; - } - - public void setEvaluating(boolean value) { - evaluating = value; - } -} diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java deleted file mode 100644 index bad42cd..0000000 --- a/src/jcgp/gui/settings/SettingsPane.java +++ /dev/null @@ -1,595 +0,0 @@ -package jcgp.gui.settings; - -import java.io.File; -import java.util.ArrayList; - -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.geometry.Insets; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.ScrollPane; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.stage.FileChooser; -import javafx.stage.FileChooser.ExtensionFilter; -import jcgp.JCGP; -import jcgp.backend.function.FunctionSet; -import jcgp.backend.modules.es.EvolutionaryStrategy; -import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.modules.problem.Problem; -import jcgp.backend.modules.problem.TestCaseProblem; -import jcgp.backend.parameters.Parameter; -import jcgp.gui.GUI; -import jcgp.gui.constants.Constants; -import jcgp.gui.settings.parameters.GUIParameter; -import jcgp.gui.settings.testcase.TestCaseTable; - -/** - * This is a fairly hefty class which encapsulates the entire right-hand - * control pane. It contains base parameters, module selectors and their - * associated parameters, flow controls and file loading/saving buttons. - * <br><br> - * A single instance of this class is used in {@code GUI}. - * - * - * @author Eduardo Pedroni - * - */ -public class SettingsPane extends AnchorPane { - - /* - * The primary containers, these make up each section of the settings pane. - */ - private VBox mainContainer; - private VBox baseParameterPane, eaPane, mutatorPane, problemPane; - private VBox nodeFunctions; - - // all buttons - private Button runPause = new Button("Run"), step = new Button("Step"), reset = new Button("Reset"); - private Button loadParameters = new Button("Load parameters"), loadChromosome = new Button("Load chromosome"), saveChromosome = new Button("Save chromosome"); - - // this is a list of parameters used for parameter validity checks - private ArrayList<GUIParameter<?>> parameters = new ArrayList<GUIParameter<?>>(); - - // the test case table stage - private TestCaseTable testCaseTable; - - // a reference to the parent GUI - private GUI gui; - - private int currentArity; - - /** - * Create a new instance of {@code SettingsPane} associated - * with the specified {@code GUI} object. - * - * @param gui a reference to this object's parent. - */ - public SettingsPane(GUI gui) { - super(); - this.gui = gui; - - // acquire a reference to jcgp, for convenience - final JCGP jcgp = gui.getExperiment(); - - // make the overarching container - mainContainer = new VBox(8); - mainContainer.setPadding(new Insets(5, Constants.RESIZE_MARGIN, 0, 2)); - - setMinWidth(Constants.SETTINGS_MIN_WIDTH); - setPrefWidth(Constants.SETTINGS_MIN_WIDTH); - - // initialise all sub-divisions - initialiseBaseParameters(jcgp); - - initialiseEAParameters(jcgp); - - initialiseMutatorParameters(jcgp); - - initialiseProblemTypeParameters(jcgp, gui); - - createControls(gui); - - // prepare the scroll pane - ScrollPane scroll = new ScrollPane(); - scroll.setFitToWidth(true); - scroll.setContent(mainContainer); - scroll.setStyle("-fx-background-color: #FFFFFF"); - - // anchor the scroll pane to itself, bearing in mind the resize margin - AnchorPane.setTopAnchor(scroll, 0.0); - AnchorPane.setBottomAnchor(scroll, 0.0); - AnchorPane.setRightAnchor(scroll, 0.0); - AnchorPane.setLeftAnchor(scroll, Constants.RESIZE_MARGIN); - - // add the scroll pane, all done! - getChildren().add(scroll); - } - - /** - * Creates the base parameters pane - * - * @param jcgp - */ - private void initialiseBaseParameters(JCGP jcgp) { - baseParameterPane = new VBox(2); - - Text header = new Text("Base Parameters"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - baseParameterPane.getChildren().add(header); - - parameters.add(GUIParameter.create(jcgp.getResources().getRowsParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getColumnsParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getInputsParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getOutputsParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getLevelsBackParameter(), this)); - - GUIParameter<?> gp = GUIParameter.create(jcgp.getResources().getPopulationSizeParameter(), this); - gp.setPadding(new Insets(0, 0, 10, 0)); - parameters.add(gp); - - parameters.add(GUIParameter.create(jcgp.getResources().getCurrentGenerationParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getGenerationsParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getCurrentRunParameter(), this)); - - gp = GUIParameter.create(jcgp.getResources().getRunsParameter(), this); - gp.setPadding(new Insets(0, 0, 10, 0)); - parameters.add(gp); - - parameters.add(GUIParameter.create(jcgp.getResources().getSeedParameter(), this)); - parameters.add(GUIParameter.create(jcgp.getResources().getReportIntervalParameter(), this)); - - baseParameterPane.getChildren().addAll(parameters); - mainContainer.getChildren().add(baseParameterPane); - } - - private void initialiseEAParameters(final JCGP jcgp) { - eaPane = new VBox(2); - - Text header = new Text("Evolutionary Strategy"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - final ComboBox<EvolutionaryStrategy> esCBox = new ComboBox<EvolutionaryStrategy>(); - esCBox.getItems().addAll(jcgp.getEvolutionaryStrategies()); - esCBox.getSelectionModel().select(jcgp.getEvolutionaryStrategy()); - esCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - - final VBox eaParameters = new VBox(2); - - refreshParameters(jcgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); - - esCBox.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - jcgp.setEvolutionaryStrategy(esCBox.getSelectionModel().getSelectedIndex()); - refreshParameters(esCBox.getSelectionModel().getSelectedItem().getLocalParameters(), eaParameters); - gui.flushConsole(); - } - }); - - eaPane.getChildren().addAll(header, esCBox, eaParameters); - mainContainer.getChildren().add(eaPane); - } - - private void initialiseMutatorParameters(final JCGP jcgp) { - mutatorPane = new VBox(2); - - Text header = new Text("Mutator"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - final ComboBox<Mutator> mutatorCBox = new ComboBox<Mutator>(); - mutatorCBox.getItems().addAll(jcgp.getMutators()); - mutatorCBox.getSelectionModel().select(jcgp.getMutator()); - mutatorCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - - final VBox mutatorParameters = new VBox(2); - refreshParameters(jcgp.getMutator().getLocalParameters(), mutatorParameters); - - mutatorCBox.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - jcgp.setMutator(mutatorCBox.getSelectionModel().getSelectedIndex()); - refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); - gui.flushConsole(); - } - }); - - mutatorPane.getChildren().addAll(header, mutatorCBox, mutatorParameters); - mainContainer.getChildren().add(mutatorPane); - } - - private void initialiseProblemTypeParameters(final JCGP jcgp, final GUI gui) { - updateArity(); - - problemPane= new VBox(2); - - Text header = new Text("Problem Type"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - final ComboBox<Problem> problemCBox = new ComboBox<Problem>(); - problemCBox.getItems().addAll(jcgp.getProblems()); - problemCBox.getSelectionModel().select(jcgp.getProblem()); - problemCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - - final VBox problemParameters = new VBox(2); - problemParameters.setPadding(new Insets(0, 0, 4, 0)); - refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); - - final HBox testCaseControlContainer = new HBox(2); - - final Button showTestCaseButton = makeTestCaseButton(); - final Button loadProblemDataButton = makeLoadTestCaseButton(); - HBox.setHgrow(showTestCaseButton, Priority.ALWAYS); - showTestCaseButton.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(loadProblemDataButton, Priority.ALWAYS); - loadProblemDataButton.setMaxWidth(Double.MAX_VALUE); - - if (jcgp.getProblem() instanceof TestCaseProblem<?>) { - testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadProblemDataButton); - remakeTestCaseTable(); - } else { - testCaseControlContainer.getChildren().add(loadProblemDataButton); - } - - nodeFunctions = new VBox(2); - nodeFunctions.setPadding(new Insets(0, 0, 4, 0)); - refreshFunctions(); - - problemCBox.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - jcgp.setProblem(problemCBox.getSelectionModel().getSelectedIndex()); - updateArity(); - refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); - if (testCaseTable != null) { - testCaseTable.close(); - } - gui.setEvaluating(false); - refreshFunctions(); - testCaseControlContainer.getChildren().clear(); - if (jcgp.getProblem() instanceof TestCaseProblem) { - testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadProblemDataButton); - remakeTestCaseTable(); - } else { - testCaseControlContainer.getChildren().add(loadProblemDataButton); - } - gui.reset(); - } - }); - - problemPane.getChildren().addAll(header, problemCBox, problemParameters, nodeFunctions, testCaseControlContainer); - mainContainer.getChildren().add(problemPane); - - } - - private Button makeLoadTestCaseButton() { - Button b = new Button("Load data"); - b.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - FileChooser fc = new FileChooser(); - fc.setTitle("Open problem file..."); - fc.getExtensionFilters().add(new ExtensionFilter("CGP " + gui.getExperiment().getProblem() + " files", "*" + ((TestCaseProblem<?>) gui.getExperiment().getProblem()).getFileExtension())); - fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*")); - File chrFile = fc.showOpenDialog(gui.getStage()); - if (chrFile != null) { - gui.getExperiment().loadProblemData(chrFile); - remakeTestCaseTable(); - gui.reDraw(); - } - } - }); - return b; - } - - private Button makeTestCaseButton() { - Button b = new Button("Show data"); - b.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - testCaseTable.show(); - } - }); - return b; - } - - private void createControls(final GUI gui) { - Text header = new Text("Experiment controls"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - final VBox controls = new VBox(2); - controls.setFillWidth(true); - - final HBox flowButtons = new HBox(2); - runPause.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - gui.runPause(); - } - }); - - step.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - gui.step(); - } - }); - - reset.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - gui.reset(); - } - }); - - HBox.setHgrow(runPause, Priority.ALWAYS); - runPause.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(step, Priority.ALWAYS); - step.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(reset, Priority.ALWAYS); - reset.setMaxWidth(Double.MAX_VALUE); - - flowButtons.getChildren().addAll(runPause, step, reset); - flowButtons.setPadding(new Insets(0, 0, 10, 0)); - - loadParameters.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - FileChooser fc = new FileChooser(); - fc.setTitle("Open .par file..."); - fc.getExtensionFilters().add(new ExtensionFilter("CGP parameter files", "*.par")); - fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*")); - File parFile = fc.showOpenDialog(gui.getStage()); - if (parFile != null) { - gui.getExperiment().loadParameters(parFile); - gui.reDraw(); - refreshFunctions(); - } - gui.flushConsole(); - } - }); - - loadChromosome.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - FileChooser fc = new FileChooser(); - fc.setTitle("Load .chr file..."); - fc.getExtensionFilters().add(new ExtensionFilter("CGP chromosome files", "*.chr")); - fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*")); - File chrFile = fc.showOpenDialog(gui.getStage()); - if (chrFile != null) { - gui.getExperiment().loadChromosome(chrFile, gui.getChromosomeIndex()); - gui.reDraw(); - } - gui.flushConsole(); - } - }); - saveChromosome.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - FileChooser fc = new FileChooser(); - fc.setTitle("Save .chr file..."); - fc.getExtensionFilters().add(new ExtensionFilter("CGP chromosome files", "*.chr")); - fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*")); - File chrFile = fc.showSaveDialog(gui.getStage()); - if (chrFile != null) { - gui.getExperiment().saveChromosome(chrFile, gui.getChromosomeIndex()); - } - gui.flushConsole(); - } - }); - - HBox.setHgrow(loadParameters, Priority.ALWAYS); - loadParameters.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(loadChromosome, Priority.ALWAYS); - loadChromosome.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(saveChromosome, Priority.ALWAYS); - saveChromosome.setMaxWidth(Double.MAX_VALUE); - - controls.getChildren().addAll(header, flowButtons, loadParameters, loadChromosome, saveChromosome); - - mainContainer.getChildren().add(controls); - } - - /** - * Builds {@code GUIParameter}s and adds them to the provided {@code VBox}. - * The parameters built are taken from the specified list. - * - * @param newParameters the list of parameters to add. - * @param container the container to add the parameters to. - */ - private void refreshParameters(ArrayList<Parameter<?>> newParameters, VBox container) { - // remove what is currently in the container from the parameter list - parameters.removeAll(container.getChildren()); - // remove everything in the container - container.getChildren().clear(); - // if there are parameters to add, add them all - if (newParameters != null) { - for (int i = 0; i < newParameters.size(); i++) { - // factory method returns the right subtype of GUIParameter - GUIParameter<?> guiParameter = GUIParameter.create(newParameters.get(i), this); - // make sure to add it to the parameter list as well - parameters.add(guiParameter); - container.getChildren().add(guiParameter); - } - } - // do a quick refresh just in case something is invalid - revalidateParameters(); - } - - /** - * This method handles a problem type change by updating the list of allowed - * node functions. - * <br><br> - * It does so by creating new checkboxes for each function in the function set. - */ - private void refreshFunctions() { - // remove all current functions - nodeFunctions.getChildren().clear(); - CheckBox checkBox; - // get a reference to the function set - final FunctionSet functionSet = gui.getExperiment().getResources().getFunctionSet(); - for (int i = 0; i < functionSet.getTotalFunctionCount(); i++) { - // add a checkbox for each function - checkBox = new CheckBox(functionSet.getFunction(i).toString()); - checkBox.setId(String.valueOf(i)); - // make sure the selection matches the function set - checkBox.setSelected(functionSet.isEnabled(functionSet.getFunction(i))); - final int index = i; - // set listener so function set gets updated if the checkboxes change - checkBox.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - if (((CheckBox) event.getSource()).isSelected()) { - functionSet.enableFunction(index); - } else { - functionSet.disableFunction(index); - } - gui.updateFunctionSelector(); - revalidateParameters(); - } - }); - // add the new checkbox - nodeFunctions.getChildren().add(checkBox); - } - // make sure function selector has all functions - gui.updateFunctionSelector(); - } - - /** - * @return true if the experiment is currently evolving something, false otherwise. - */ - public boolean isExperimentRunning() { - return gui.isWorking(); - } - - /** - * - * @return true if the experiment needs to be reset, false if otherwise. - */ - public boolean isResetRequired() { - for (GUIParameter<?> parameter : parameters) { - if (parameter.requiresReset()) { - return true; - } - } - if (arityChanged()) { - return true; - } - return false; - } - - /** - * @return true if no parameters have their status set to ParameterStatus.INVALID. - */ - public boolean areParametersValid() { - for (GUIParameter<?> parameter : parameters) { - if (!parameter.isValid()) { - return false; - } - } - return true; - } - - /** - * Calls validate() on every parameter. This is called whenever a parameter changes, - * so that other parameters update their status in case they were dependent on the - * changed parameter. - * <br><br> - * This also disables the controls if a reset is necessary, preventing the experiment - * from running until it has happened. - */ - public void revalidateParameters() { - boolean disableControls = false; - for (GUIParameter<?> parameter : parameters) { - parameter.validate(); - if (parameter.requiresReset()) { - disableControls = true; - } - } - if (arityChanged()) { - disableControls = true; - } - - runPause.setDisable(disableControls); - step.setDisable(disableControls); - } - - /** - * Calls applyValue() on every parameter. This is called when a reset occurs, so that - * the new value will be used as a reference instead of the old reference value. - * <br><br> - * It also closes the test case table, just in case. - */ - public void applyParameters() { - for (GUIParameter<?> parameter : parameters) { - parameter.applyValue(); - } - updateArity(); - if (testCaseTable != null) { - testCaseTable.close(); - } - } - - /** - * Updates all of the controls to their appropriate state based on the status of the - * experiment, in order to prevent inappropriate operations if the experiment is - * running or finished. - * - * @param running true if the experiment is running. - * @param finished true if the experiment is finished. - */ - public void updateControls(boolean running, boolean finished) { - baseParameterPane.setDisable(running); - eaPane.setDisable(running); - mutatorPane.setDisable(running); - problemPane.setDisable(running); - - runPause.setText(running ? "Pause" : "Run"); - runPause.setDisable(finished); - step.setDisable(running || finished); - reset.setDisable(running); - - loadParameters.setDisable(running); - loadChromosome.setDisable(running); - saveChromosome.setDisable(running); - - testCaseTable.getTable().setDisable(running); - } - - private void remakeTestCaseTable() { - boolean wasShowing = false; - if (testCaseTable != null) { - wasShowing = testCaseTable.isShowing(); - testCaseTable.close(); - } - testCaseTable = new TestCaseTable((TestCaseProblem<Object>) gui.getExperiment().getProblem(), gui); - if (wasShowing) { - testCaseTable.show(); - } - } - - public TestCaseTable getTestCaseTable() { - return testCaseTable; - } - - private void updateArity() { - currentArity = gui.getExperiment().getProblem().getFunctionSet().getMaxArity(); - } - - private boolean arityChanged() { - return currentArity != gui.getExperiment().getProblem().getFunctionSet().getMaxArity(); - } -} diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java deleted file mode 100644 index a1f03fe..0000000 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ /dev/null @@ -1,82 +0,0 @@ -package jcgp.gui.settings.parameters; - -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Control; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.constants.Constants; -import jcgp.gui.settings.SettingsPane; - -/** - * This extension of @code{GUIParameter} uses a @code{CheckBox} to display - * the value of a @code{BooleanParameter}. It cannot be constructed - * directly - instead, use @code{GUIParameter.create()}. - * <br><br> - * See {@link GUIParameter} for more information. - * - * @author Eduardo Pedroni - */ -public class GUIBooleanParameter extends GUIParameter<Boolean> { - - private CheckBox checkBox; - - /** - * This protected constructor is intended for use - * by the factory method only. - * - */ - protected GUIBooleanParameter(BooleanParameter parameter, SettingsPane sp) { - super(parameter, sp); - } - - @Override - protected Control makeControl() { - checkBox = new CheckBox(); - checkBox.setSelected(parameter.get()); - - return checkBox; - } - - @Override - protected void setControlListeners() { - /* pass the CheckBox value back to the parameter whenever it gets - * modified, provided the experiment isn't running */ - checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() { - @Override - public void changed( - ObservableValue<? extends Boolean> observable, - Boolean oldValue, Boolean newValue) { - if (!settingsPane.isExperimentRunning()) { - parameter.set(newValue); - settingsPane.revalidateParameters(); - } - } - }); - } - - @Override - protected void setValidityStyle() { - // update the Control's style and tooltip based on the status of the parameter - if (parameter.getStatus() == ParameterStatus.INVALID) { - checkBox.setStyle(Constants.BASE_CHECKBOX_STYLE + Constants.INVALID_PARAMETER_STYLE); - checkBox.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else if (parameter.getStatus() == ParameterStatus.WARNING - || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - checkBox.setStyle(Constants.BASE_CHECKBOX_STYLE + Constants.WARNING_PARAMETER_STYLE); - checkBox.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else { - checkBox.setStyle(Constants.BASE_CHECKBOX_STYLE + Constants.VALID_PARAMETER_STYLE); - checkBox.setTooltip(null); - } - } - - @Override - public void refreshValue() { - checkBox.setSelected(parameter.get()); - } - -} diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java deleted file mode 100644 index feee34c..0000000 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ /dev/null @@ -1,110 +0,0 @@ -package jcgp.gui.settings.parameters; - -import java.text.DecimalFormat; - -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.geometry.Pos; -import javafx.scene.control.Control; -import javafx.scene.control.TextField; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.constants.Constants; -import jcgp.gui.settings.SettingsPane; - -/** - * This extension of @code{GUIParameter} uses a @code{TextField} to display - * the value of a @code{DoubleParameter}. It cannot be constructed - * directly - instead, use @code{GUIParameter.create()}. - * <br><br> - * See {@link GUIParameter} for more information. - * - * @author Eduardo Pedroni - */ -public class GUIDoubleParameter extends GUIParameter<Number> { - - private TextField textField; - private DecimalFormat decimalFormat; - - /** - * This protected constructor is intended for use - * by the factory method only. - * - */ - protected GUIDoubleParameter(DoubleParameter parameter, SettingsPane sp) { - super(parameter, sp); - } - - @Override - protected Control makeControl() { - // we use a text field, and a formatting class to enforce decimals - decimalFormat = new DecimalFormat(); - decimalFormat.setMaximumFractionDigits(10); - textField = new TextField(decimalFormat.format(parameter.get().doubleValue())); - textField.setStyle(Constants.VALID_PARAMETER_STYLE); - textField.setAlignment(Pos.CENTER_RIGHT); - textField.prefWidthProperty().bind(widthProperty().divide(2)); - return textField; - } - - @Override - protected void setControlListeners() { - /* pass the TextField value back to the parameter whenever it gets - * modified, provided it is not empty, the experiment isn't running - * and it matches the double-precision regex filter */ - textField.textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed( - ObservableValue<? extends String> observable, - String oldValue, String newValue) { - if (!settingsPane.isExperimentRunning()) { - if (newValue.matches("^[-+]?[0-9]*\\.?[0-9]+$")) { - if (!newValue.isEmpty()) { - double value = Double.parseDouble(newValue); - parameter.set(value); - settingsPane.revalidateParameters(); - } - } else { - refreshValue(); - } - } - } - }); - /* if the TextField loses focus and is empty, set it to the current - * value of the parameter */ - textField.focusedProperty().addListener(new ChangeListener<Boolean>() { - @Override - public void changed( - ObservableValue<? extends Boolean> observable, - Boolean oldValue, Boolean newValue) { - if (!newValue) { - refreshValue(); - } - } - }); - } - - @Override - protected void setValidityStyle() { - // update the Control's style and tooltip based on the status of the parameter - if (parameter.getStatus() == ParameterStatus.INVALID) { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.INVALID_PARAMETER_STYLE); - textField.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.WARNING_PARAMETER_STYLE); - textField.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.VALID_PARAMETER_STYLE); - textField.setTooltip(null); - } - } - - @Override - public void refreshValue() { - if (!textField.isFocused()) { - textField.setText(decimalFormat.format(parameter.get().doubleValue())); - } - } -} diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java deleted file mode 100644 index bcfbe50..0000000 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ /dev/null @@ -1,107 +0,0 @@ -package jcgp.gui.settings.parameters; - -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.geometry.Pos; -import javafx.scene.control.Control; -import javafx.scene.control.TextField; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.constants.Constants; -import jcgp.gui.settings.SettingsPane; - -/** - * This extension of @code{GUIParameter} uses a @code{TextField} to display - * the value of a @code{IntegerParameter}. It cannot be constructed - * directly - instead, use @code{GUIParameter.create()}. - * <br><br> - * See {@link GUIParameter} for more information. - * - * @author Eduardo Pedroni - */ -public class GUIIntegerParameter extends GUIParameter<Number> { - - private TextField textField; - - /** - * This protected constructor is intended for use - * by the factory method only. - * - */ - protected GUIIntegerParameter(IntegerParameter parameter, SettingsPane sp) { - super(parameter, sp); - } - - @Override - protected Control makeControl() { - // this uses a text field - textField = new TextField(String.valueOf(parameter.get())); - textField.setStyle(Constants.VALID_PARAMETER_STYLE); - textField.setAlignment(Pos.CENTER_RIGHT); - textField.prefWidthProperty().bind(widthProperty().divide(2)); - - return textField; - } - - @Override - protected void setControlListeners() { - /* pass the TextField value back to the parameter whenever it gets - * modified, provided it is not empty, the experiment isn't running - * and it matches the integer regex pattern */ - textField.textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed( - ObservableValue<? extends String> observable, - String oldValue, String newValue) { - if (!settingsPane.isExperimentRunning()) { - if (newValue.matches("[0-9]*")) { - if (!newValue.isEmpty()) { - int value = Integer.parseInt(newValue); - parameter.set(value); - settingsPane.revalidateParameters(); - } - } else { - refreshValue(); - } - } - } - }); - /* if the TextField loses focus and is empty, set it to the current - * value of the parameter */ - textField.focusedProperty().addListener(new ChangeListener<Boolean>() { - @Override - public void changed( - ObservableValue<? extends Boolean> observable, - Boolean oldValue, Boolean newValue) { - if (!newValue) { - refreshValue(); - } - } - }); - } - - @Override - protected void setValidityStyle() { - // update the Control's style and tooltip based on the status of the parameter - if (parameter.getStatus() == ParameterStatus.INVALID) { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.INVALID_PARAMETER_STYLE); - textField.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else if (parameter.getStatus() == ParameterStatus.WARNING - || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.WARNING_PARAMETER_STYLE); - textField.setTooltip(tooltip); - tooltip.setText(parameter.getStatus().getDetails()); - } else { - textField.setStyle(Constants.BASE_TEXT_STYLE + Constants.VALID_PARAMETER_STYLE); - textField.setTooltip(null); - } - } - - @Override - public void refreshValue() { - if (!textField.isFocused()) { - textField.setText(parameter.get().toString()); - } - } -} diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java deleted file mode 100644 index 59aecf6..0000000 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ /dev/null @@ -1,235 +0,0 @@ -package jcgp.gui.settings.parameters; - -import java.util.concurrent.atomic.AtomicBoolean; - -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.geometry.Pos; -import javafx.scene.control.Control; -import javafx.scene.control.Label; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.settings.SettingsPane; - -/** - * - * This is the base class for all @code{GUIParameter}s. Using the factory method @code{GUIParameter.create()} - * generates an appropriate instance of this class for the specified parameter. - * <br><br> - * A @code{GUIParameter} is an @code{HBox} containing a @code{Text} for the parameter name - * and a @code{Control} for interaction. - * It stores an instance of its associated @code{Parameter} object and also contains a @code{Tooltip} for - * displaying status information. - * <br><br> - * Monitor parameters are updated automatically and have their @code{Control} disabled so - * that no changes can be made via the GUI. - * Non-monitor parameters are updated automatically as well, but may be changed by the user - * if the program is not evolving. - * - * @see Parameter - * @author Eduardo Pedroni - * @param <T> the parameter data type - */ -public abstract class GUIParameter<T> extends HBox { - - private Label name; - private Control valueControl; - - protected SettingsPane settingsPane; - protected Tooltip tooltip; - protected Parameter<T> parameter; - - /** This is the lock used to prevent more than one update task to be scheduled - * at the same time on the same GUIParameter. */ - private AtomicBoolean updateLock = new AtomicBoolean(false); - - /** - * This value is used to assert whether the control has changed values since - * the program last ran. Therefore, it is updated whenever a generation occurs - * or the experiment is reset. - */ - private T referenceValue; - - /** - * This protected template constructor contains the common elements to all - * @code{GUIParameter}s and should be invoked by any subclasses using @code{super()}. It - * defers the creation of the parameter {@code Control} object to the subclass - * currently being built (which in turn is defined by the factory method). - * - * @param parameter a @code{Parameter} for which to generate a @code{GUIParameter}. - * @param sp a reference to the @code{SettingsPane}. - */ - protected GUIParameter(Parameter<T> parameter, final SettingsPane settingsPane) { - this.parameter = parameter; - this.referenceValue = parameter.get(); - - this.settingsPane = settingsPane; - - setAlignment(Pos.CENTER_LEFT); - setSpacing(5); - - name = new Label(parameter.toString()); - // set text width to half of the total width of the GUIParameter - name.prefWidthProperty().bind(widthProperty().divide(2)); - - // the tooltip is the hover-over label containing status information, when appropriate - tooltip = new Tooltip(); - tooltip.setSkin(null); - - valueControl = makeControl(); - - // if the parameter is a monitor, it should be permanently disabled - valueControl.setDisable(parameter.isMonitor()); - - // bind to parameter value property in a thread-safe way - makeThreadSafeBinding(); - - // if parameter is not a monitor, make sure the control is constrained appropriately - if (!parameter.isMonitor()) { - setControlListeners(); - } - - getChildren().addAll(name, valueControl); - } - - /** - * Factory method to create @code{GUIParameter}s from @code{Parameter}s. - * Use this to create an appropriate @code{GUIParameter} from any instance of @code{Parameter}, - * rather than manually downcasting the @code{Parameter} object every time. - * - * @param parameter a parameter for which to generate a @code{GUIParameter}. - * @param sp a reference to the @code{SettingsPane}. - * @return an appropriate instance of @code{GUIParameter}. - */ - public static GUIParameter<?> create(Parameter<?> parameter, SettingsPane sp) { - if (parameter instanceof IntegerParameter) { - return new GUIIntegerParameter((IntegerParameter) parameter, sp); - } else if (parameter instanceof DoubleParameter) { - return new GUIDoubleParameter((DoubleParameter) parameter, sp); - } else if (parameter instanceof BooleanParameter) { - return new GUIBooleanParameter((BooleanParameter) parameter, sp); - } else { - throw new ClassCastException("No GUIParameter subclass exists for argument of type " + parameter.getClass()); - } - } - - /** - * Parameters are intended to communicate information from the experiment - * to the GUI. Since the experiment runs on a separate threads and it is illegal - * to modify JavaFX objects from outside the JavaFX Application thread, this - * special ChangeListener updates the GUIParameter in a safe way. - * <br><br> - * Note that this is applied to all parameters regardless of whether they are - * monitors or not; the only difference between monitor and non-monitor parameters - * is that monitor parameters cannot be modified from the GUI. - */ - private void makeThreadSafeBinding() { - parameter.valueProperty().addListener(new ChangeListener<Object>() { - @Override - public void changed( - ObservableValue<? extends Object> observable, - Object oldValue, Object newValue) { - // only do this if the experiment is running - if (settingsPane.isExperimentRunning() || !isFocused()) { - /* here's the catch - atomically get the lock state and set it to true - * the lock will only be false again when the runnable is finished executing, - * preventing multiple runnables to concurrently update the same GUIParameter - */ - if (!updateLock.getAndSet(true)) { - Platform.runLater(new Runnable() { - @Override - public void run() { - refreshValue(); - updateLock.set(false); - } - }); - } - } - } - }); - } - - /** - * @return true if the current value of the parameter does not prevent the - * experiment from running. - */ - public boolean isValid() { - return parameter.getStatus() != ParameterStatus.INVALID; - } - - /** - * Force the parameter to validate its current value, and apply the associated - * style to the @code{GUIParameter}. - */ - public void validate() { - parameter.validate(parameter.get()); - setValidityStyle(); - } - - /** - * Certain parameter changes might require the experiment to be reset, either - * because the parameter is critical or because its status requires a reset. - * - * @return true if an experiment reset is required due to this parameter changing. - */ - public boolean requiresReset() { - return (parameter.isCritical() && !parameter.get().equals(referenceValue)) - || parameter.getStatus() == ParameterStatus.WARNING_RESET; - } - - /** - * Set the current parameter value as the reference value of the @code{GUIParameter}. - * The new reference value will be used to determine the validity of the parameter, - * should its value change. - */ - public void applyValue() { - referenceValue = parameter.get(); - } - - /* - * The following prototypes are instance-dependent and are called from - * GUIParameter() as necessary. - */ - /** - * This method returns the @code{Control} object used to control the parameter. - * <br><br> - * Implementations of @code{GUIParameter} must override this method and return - * a @code{Control} appropriate to the type of parameter. This will typically be - * done by referencing the protected field @code{GUIParameter.parameter}. - * - * @return the Control object to be added to the GUIParameter. - */ - protected abstract Control makeControl(); - - /** - * Adds the necessary handlers to the @code{Control} object in order to modify - * the underlying parameter. This will typically consist of filtering key - * presses to ensure no invalid characters are inserted, applying the new - * value to the underlying parameter and revalidating the parameters to - * reflect the changes made. - */ - protected abstract void setControlListeners(); - - /** - * This method is called to style the @code{GUIParameter} according to the status of - * the parameter, which can be obtained with @code{parameter.getStatus()}. While the - * subclass is free to style itself in any way, the CSS strings defined here - * (INVALID_PARAMETER_STYLE, WARNING_PARAMETER_STYLE, VALID_PARAMETER_STYLE) - * provide a way to keep the GUI consistent. - * - * @see ParameterStatus - */ - protected abstract void setValidityStyle(); - - /** - * Update the control so it shows the correct value of the parameter. This method - * is used exclusively by the thread-safe binding created if the module is a monitor. - */ - protected abstract void refreshValue(); -} diff --git a/src/jcgp/gui/settings/testcase/TestCaseTable.java b/src/jcgp/gui/settings/testcase/TestCaseTable.java deleted file mode 100644 index 605b75e..0000000 --- a/src/jcgp/gui/settings/testcase/TestCaseTable.java +++ /dev/null @@ -1,124 +0,0 @@ -package jcgp.gui.settings.testcase; - -import java.util.ArrayList; - -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.collections.ObservableList; -import javafx.event.EventHandler; -import javafx.scene.Scene; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableColumn.CellDataFeatures; -import javafx.scene.control.TableView; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; -import javafx.util.Callback; -import jcgp.backend.modules.problem.TestCaseProblem; -import jcgp.backend.modules.problem.TestCaseProblem.TestCase; -import jcgp.backend.resources.Resources; -import jcgp.gui.GUI; - -/** - * This is a test case table. For problems that have test cases, - * this table shows the test case inputs and outputs. Clicking on - * a test case (one is shown per row) applies the values to all - * chromosome inputs shows the calculated values throughout the chromosome. - * - * @author Eduardo Pedroni - * - */ -public class TestCaseTable extends Stage { - - private TableView<TestCase<Object>> table; - - /** - * Make a new instance of {@code TestCaseTable}. - * - * @param testCaseProblem the {@code TestCaseProblem} whose data must be displayed. - * @param gui a reference to the GUI. - */ - public TestCaseTable(final TestCaseProblem<Object> testCaseProblem, final GUI gui) { - super(); - - Resources resources = gui.getExperiment().getResources(); - - // create the actual table view - table = new TableView<TestCase<Object>>(); - // get test cases from problem - ObservableList<TestCase<Object>> testCaseList = testCaseProblem.getTestCases(); - - // prepare input and output columns - ArrayList<TableColumn<TestCase<Object>, String>> inputs = new ArrayList<TableColumn<TestCase<Object>, String>>(resources.inputs()); - ArrayList<TableColumn<TestCase<Object>, String>> outputs = new ArrayList<TableColumn<TestCase<Object>, String>>(resources.outputs()); - - // create input columns - TableColumn<TestCase<Object>, String> tc; - for (int i = 0; i < resources.inputs(); i++) { - tc = new TableColumn<TestCase<Object>, String>("I: " + i); - inputs.add(tc); - final int index = i; - tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<TestCase<Object>,String>, ObservableValue<String>>() { - @Override - public ObservableValue<String> call(CellDataFeatures<TestCase<Object>, String> param) { - // create a new string property and give it the test case value, no need for dynamic binding - this wont change often - return new SimpleStringProperty(param.getValue().getInputs()[index].toString()); - } - }); - tc.setSortable(false); - // set column width so all columns are distributed across the width of the stage - tc.prefWidthProperty().bind(table.widthProperty().divide(resources.inputs() + resources.outputs())); - } - - // create output columns - for (int o = 0; o < resources.outputs(); o++) { - tc = new TableColumn<TestCase<Object>, String>("O: " + o); - outputs.add(tc); - final int index = o; - tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<TestCase<Object>,String>, ObservableValue<String>>() { - @Override - public ObservableValue<String> call(CellDataFeatures<TestCase<Object>, String> param) { - // create a new string property and give it the test case value, no need for dynamic binding - this wont change often - return new SimpleStringProperty(param.getValue().getOutputs()[index].toString()); - } - }); - tc.setSortable(false); - // set column width so all columns are distributed across the width of the stage - tc.prefWidthProperty().bind(table.widthProperty().divide(resources.inputs() + resources.outputs())); - } - - // add created columns - table.getColumns().addAll(inputs); - table.getColumns().addAll(outputs); - - // populate table with actual data - table.setItems(testCaseList); - - // apply test case values when a new test case is selected - table.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TestCase<Object>>() { - @Override - public void changed(ObservableValue<? extends TestCase<Object>> observable, TestCase<Object> oldValue, TestCase<Object> newValue) { - gui.evaluateTestCase(newValue); - } - }); - - // when the stage is closed, clear the selection - // this doesn't work if the stage is closed by the program for some reason... - setOnCloseRequest(new EventHandler<WindowEvent>() { - @Override - public void handle(WindowEvent event) { - gui.hideGeneValues(); - table.getSelectionModel().clearSelection(); - } - }); - - setScene(new Scene(table)); - } - - /** - * @return a reference to the actual table of test cases. - */ - public TableView<TestCase<Object>> getTable() { - return table; - } -} |