Pintar Funciones en JavaFX (LineChart)
En esta entrada vamos a ver como pintar funciones utilizando las interfaces gráficas de JavaFX. Si eres nuevo y no sabes de que va esto de JavaFX, te recomiendo que veas antes el siguiente tutorial: Introducción a Interfaces Gráficas con JavaFX y SceneBuilder (Video).
En este tutorial vamos a dibujar las funciones de f(x) = x, f(x)=x^2,…,f(x)=x^5. No es que sean unas funciones muy "chungas" de pintar, pero como ejemplo y para que quede claro, es mas que de sobra.
Tal y como se hizo en el tutorial anterior, nos creamos un nuevo proyecto "fxml". Una vez creado el proyecto, tendremos los 3 ficheros (2 ".java" y 1 ".fxml") que nos representará a cada uno de los ficheros que hay que tener en el patrón Modelo-Vista-Controlador. Lo primero que hacemos es ir a la Vista (es decir el ".fxml") y lo abrimos con el SceneBuilder. Una vez ahi, nos construimos la vista a nuestro antojo, incluyendo el objeto "LineChart" que sera sobre el que pintemos la función. En este caso hemos utilizado 2 "TextFiled" para acotar la función que vamos a pintar, es decir el 'x' máximo y el mínimo. Tambien utilizaremos un "ChoiceBox" para elegir la función que queremos pintar y por ultimo un boton para pintar la función. Con lo dicho hasta ahora, la vista quedaria de la siguiente forma:
Hay que tener mucho cuidado, con el LineChart y ver que es lo que pintamos en el. En este caso vamos a pintar puntos que estarán definidos por dos "double" por tanto habra que definirlos "a mano" en el fxml. No os preocupeis que al final de la entrada dejaremos el código para que lo veáis.
Una vez realizada la vista, tenemos que instanciar los elementos de la vista en el fichero del controlado y codificar lo necesario para pintar la función. Primero vamos a mostrar como definimos los elementos que hemos creado en la vista. Lo hacemos de la siguiente forma:
@FXML private TextField rangoMin; @FXML private TextField rangoMax; @FXML private ChoiceBox choiceFun; @FXML private Button button; // Declaramos el "LineChart" donde pintaremos la funcion @FXML private LineChart<Double, Double> graph; @FXML private NumberAxis x; @FXML private NumberAxis y;
Hay que darse cuenta que el nombre de los atributos que damos a los objetos de la vista los hemos definido en el SceneBuilder, en el campo llamado "fx:id". Sino hacemos esto, al ejecutar la aplicación no sabra que elemento es cada cosa. Por ejemplo al objeto "LineChart" que nos servirá para pintar la función lo hemos llamado "graph".
Ahora tenemos que crearnos un método para que nos pinte la función. Llegados a este punto hay que decir que el objeto "LineChart", no es mas que un objeto al que le pasas una serie de puntos a pintar en la gráfica y te los pinta y los une en el orden que se los pasas. Por tanto, nos tenemos que crear un ArrayList, que para el caso de JavaFX es un ObservableList, en el que guardaremos los puntos a pintar en la gráfica. Después, generaremos los puntos que queremos pintar, calculando la función correspondiente, (f(x)=x, f(x)=x^2,…), añadiendo cada uno de esos puntos al ObservableList, y por último le pasamos el ObservableList al LineChart para que pinte todos esos puntos. En resumen, el método encargado de hacer todo eso, es el siguiente método al que se le pasa como parámetros el máximo y el mínimo de la función, así como el grado de la función (grado = x^'n'):
/** * Método para pintar la gráfica * @param min * @param max * @param grado */ private void pintarGrafica (int min, int max, int grado){ // Creamos un ObservableList para guardar los puntos que pintaremos en la gráfica ObservableList<XYChart.Series<Double, Double>> lineChartData = FXCollections.observableArrayList(); // Instanciamos un punto a pintar LineChart.Series<Double, Double> series = new LineChart.Series<Double, Double>(); // Imprimimos la función que vamos a pintar series.setName("f(x^"+grado+")"); // obtenemos los puntos a pintar. Daros cuenta que los puntos a pintar estan definidos // por el valor de 'x' y el resultado de 'f(x)', siendo f(x)=Math.pow(x, grado) = x^grado for (double i = min; i<max; i=i+0.1){ series.getData().add(new XYChart.Data<Double, Double>(i, Math.pow(i, grado))); } // Guardamos todos los puntos de la función que hemos obtenido lineChartData.add(series); // Si No quereis que se pinten los puntos, poner a false graph.setCreateSymbols(true); // Ponemos los puntos en la gráfica graph.setData(lineChartData); graph.createSymbolsProperty(); }
Llamando a este método, se pintaria la función. Ya solo queda codificar la acción de pintar una vez que se le de al botón. Para hacer esto es muy importante recordaros que tenéis que escribir el nombre del método en el SceneBuilder para poder utilizarlo en el Controlador. Para ell0, en el SceneBuilder, seleccionais el boton, vais a la pentaña code > On Action, y ahi ponéis con el simbolo '#' previamente, el nombre del método a ejecutar cuando se pulse el boton, en este caso lo hemos llamado "#AccionPintar". Hecho esto, codificamos este método de la siguiente forma:
/** * Método que se ejecuta al pulsar el boton "Dibujar Funcion" * @param event */ @FXML private void AccionPintar(ActionEvent event) { int grado = getGradoFuncion(choiceFun.getValue().toString()); pintarGrafica(Integer.parseInt(rangoMin.getText()), Integer.parseInt(rangoMax.getText()), grado); }
Una vez hecho esto y hechos los métodos de apoyo, nos queda el siguiente programa:
Para f(x) = x
Para f(x) = x^2
Para f(x) = x^3
Para f(x) = x^4
Para f(x) = x^5
El proyecto para NetBeans lo podeis descargar de AQUI.
El proyecto para eclipse lo podeis descargar de AQUI. Recordar que para ejecutar este proyecto en eclipse es imprescindible importar el "jfxrt.jar" que encontrareis en la carpeta "lib".
Hola.
Soy nuevo en esto de tratar datos fuera de Excel. Estoy intentando hacer una gráfica simple donde, parecido a un bar plot, ir poniendo las frecuencias de número aleatorios para probar que siguen una distribución normal. Es decir como un histograma teniendo superpuesto un line chart. ¿Sería esto posible con JavaFX?
Hola, estoy intentando hacer una gráfica dinámica en la que se vaya mostrando en el tiempo un flujo de datos que me van llegando de un dispositivo externo. ¿Sabeis si existe algun método para para rellenar el vector de forma FIFO, o sea, que cuando entre un dato, el resto de los datos se desplace una posicion y el ultimo se elimine y que finalmente imprima?
oye amigo una pregunta, todas las librerias para javaFX son open source?
O sea, no requieren ninguna licencia?
Pues hasta donde yo se no se paga nada para trabajar con JavaFX. Igual hay por ahi alguna libreria o algo desarrollado por algún particular que si que será de pago, pero para hacer las cosas que mas o menos se han hecho siempre con GUIs, JavaFX es libre.