Partage
  • Partager sur Facebook
  • Partager sur Twitter

[JAVAFX]ObservableList recupére un item spécifique

Sujet résolu
24 février 2020 à 11:22:44

Bonjour à tous,

Je développe actuellement une application en java avec javafx. Je code l'interface des paramétres et pour cela je suis ammené a stocker les paramétres dans un fichier .properties comme suivant:

prop1=value1
prop2=value2
prop3=value2
....

J'ai donc créé une class configuration qui est une classe Model qui fait le lien entre mon fichier et mon application. Celle ci fait appelle a une classe que j'ai créé nommée: ApplicationPropertiesFileManager.

package application;

import Model.Parameter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import utils.PropertiesFileManager;
import java.util.Properties;

public class ApplicationPropertiesFileManager extends PropertiesFileManager {

    /**
     * Donnée membre contenant le chemin vers le fichier de configuration
     */
    private static String PATH_TO_APPLICATION_PROPERTIES_FILE = "C:\\Users\\Mrjer\\Desktop\\";

    /**
     * Donnée membre contenant le nom du fichier de configuration
     */
    private static String APPLICATION_PROPERTIES_FILE_NAME = "ApplicationProperties.properties";


    /**
     * Constructeur par défaut
     */
    public ApplicationPropertiesFileManager(){

        super(PATH_TO_APPLICATION_PROPERTIES_FILE,APPLICATION_PROPERTIES_FILE_NAME);

    }

    /**
     * Retourne toutes les paramétres sous forme d'une ObservableMap
     * @return
     */
    public ObservableList<Parameter> getAllProperties(){

        ObservableList<Parameter> values = FXCollections.observableArrayList();

        Properties ApplicationProperties = super.getPropertiesObject();

        for ( String key : ApplicationProperties.stringPropertyNames()){
            values.add(new Parameter(key,ApplicationProperties.getProperty(key)));
        }

        return values;

    }



}

ma classe configuration:

package application;

import java.sql.ResultSet;
import java.util.Locale;
import java.util.ResourceBundle;

import Model.Parameter;
import javafx.collections.*;
import javafx.concurrent.Service;
import javafx.concurrent.WorkerStateEvent;

public class Configuration {
	
	/**
	 * Donnée membre égale au chemin vers la base de donnée
	 */
	private static String PATH_TO_DATABASE;

	/**
	 * Donnée membre contenant les paramétres de l'application
	 */
	private ObservableList<Parameter> ApplicationSettings;


	/**
	 * Constructeur par défaut
	 */
	public Configuration() {

		//On récupére les paramétres de l'application
		this.initializeApplicationSettings();
		
	}

	/**
	 * Récupére les paramétres et leur valeurs et les stockent dans une liste Observable
	 */
	private void initializeApplicationSettings() {

		//on créé un manager pour manipuler notre fichier de settings
		ApplicationPropertiesFileManager propertiesFileManager = new ApplicationPropertiesFileManager();

		//on récupére tous nos paramétres
		this.ApplicationSettings = propertiesFileManager.getAllProperties();

		//Evénément qui se déclénche si on change un paramétre
		this.ApplicationSettings.addListener(new ListChangeListener<Parameter>() {
			@Override
			public void onChanged(Change<? extends Parameter> change) {

				while(change.next()){

					if(change.wasReplaced()){

						System.out.println("changer !!!");

					}

				}

			}
		});


	}
	

	/**
	 * Retourne la liste observable contenant les paramétres de l'application
	 * @return
	 */
	public ObservableList<Parameter> getApplicationSettings(){

		return this.ApplicationSettings;

	}

}

et mon Controller de la fenetre de Settings (raccourci):

/**
	 * Initialise la checkbox "Activer le risque par défaut" du paramétre "Risque par défaut"
	 */
	private void initializeDefaultRiskCheckBox(){

		//on récupére le status de l'option
		boolean defaultRiskChosen = Boolean.parseBoolean(Main.ApplicationProperties.getProperty("defaultRiskEnabled"));

		//on applique le status à la comboBox
		DefaultRiskCheckbox.setSelected(defaultRiskChosen);

		//Evénement déclenché si la valeur de notre checkbox change
		DefaultRiskCheckbox.selectedProperty().addListener(new ChangeListener<Boolean>() {
			@Override
			public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue) {

				if(newValue){ //la checkbox est coché
					DefaultRiskChosenComboBox.setDisable(false);

					System.out.println("ici");


                                        (1)
		

				}else{
				    ....
				}

			}
		});

	}


Mes paramétres sont stockés dans une ObservableList qui contient une liste d'objet de type Parameter.

J'ai plusieurs checkbox pour activer/désactiver plusieurs paramétres et le choix de l'utilisateur est stocké dans le fichier .properties. Si l'utilisateur décoche la checkbox alors le code change imédiatement la valeur a false et inversement.

J'aimerais au niveau de mon (1) dans le code pouvoir récupére un paramétre en particulier.

Je sais que je dois commencer par :

Main.configuration.getApplicationSettings(). ???

Mais je ne sais pas comment éviter de boucler sur les éléments de ma liste.

Merci beaucoup.





-
Edité par BowgartField 24 février 2020 à 11:33:23

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

24 février 2020 à 15:30:04

Dans une liste, tu ne peux retrouver un élément qu'en parcourant la liste. Si tu veux obtenir un paramètre à partir de son nom, il faut utiliser une Map.

Je suis moi-même en apprentissage sur JavaFX, mais pourquoi ne pas utiliser une Map dont les clés seraient les noms de tes propriétés, et les valeurs seraient les BooleanProperty des checkboxes ? Elles se mettraient automatiquement à jour et il te resterait seulement à les lire au début et les sauvegarder à la fin.

  • Partager sur Facebook
  • Partager sur Twitter
24 février 2020 à 20:16:19

Zachee54 a écrit:

Dans une liste, tu ne peux retrouver un élément qu'en parcourant la liste. Si tu veux obtenir un paramètre à partir de son nom, il faut utiliser une Map.

Je suis moi-même en apprentissage sur JavaFX, mais pourquoi ne pas utiliser une Map dont les clés seraient les noms de tes propriétés, et les valeurs seraient les BooleanProperty des checkboxes ? Elles se mettraient automatiquement à jour et il te resterait seulement à les lire au début et les sauvegarder à la fin.


Bonjour,

Merci pour votre réponse. J'ai effctivement réussi a résoudre mon probléme.

Mais effectivement au début j'avais effectivement utilisé une Map mais je ne trouvais pas ca optimiser objet donc j'ai changé. Cependant l'histoire de binding m'intéresse. Concrétement actuellement pour mes checkbox j'utilise 

checkbox.setDisabled(true);

Comment dois-je faire pour utiliser les différents StringProperty et les binder a mes checkbox ?

Merci beaucoup.

-
Edité par BowgartField 24 février 2020 à 20:18:13

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

25 février 2020 à 14:38:01

N'utilise pas de StringProperty, mais des BooleanProperty. Les checkboxes n'ont que des valeurs oui/non, donc elles utilisent des BooleanProperty.

Tu crées une Map dans laquelle tu insères tes propriétés avec le nom que tu souhaites, et tu les lies aux propriétés "selectedProperty" des checkboxes.

Map<String, Property<Boolean>> propertiesMap = new HashMap<>();

Property<Boolean> maPropriete = new SimpleBooleanProperty();

// Lier ta propriété avec la valeur de la checkbox
maPropriete.bindBidirectional(checkbox.selectedProperty());

// La mettre dans la Map sous un nom parlant
propertiesMap.put("MonOption", maPropriete);

// ...et recommencer pour toutes les propriétés qui t'intéressent
  • Partager sur Facebook
  • Partager sur Twitter
25 février 2020 à 14:57:35

Zachee54 a écrit:

N'utilise pas de StringProperty, mais des BooleanProperty. Les checkboxes n'ont que des valeurs oui/non, donc elles utilisent des BooleanProperty.

Tu crées une Map dans laquelle tu insères tes propriétés avec le nom que tu souhaites, et tu les lies aux propriétés "selectedProperty" des checkboxes.

Map<String, Property<Boolean>> propertiesMap = new HashMap<>();

Property<Boolean> maPropriete = new SimpleBooleanProperty();

// Lier ta propriété avec la valeur de la checkbox
maPropriete.bindBidirectional(checkbox.selectedProperty());

// La mettre dans la Map sous un nom parlant
propertiesMap.put("MonOption", maPropriete);

// ...et recommencer pour toutes les propriétés qui t'intéressent

Bien sûre pour les BooleanProperty. Mais en réalité mes valeur donc mon fichier .properties ne sont pas tout le temps true ou false c'est parfois un nombre aussi. Dans ce cas cela se complique car dans mon ObservableMap ne je sais pas si je récupére un String ou un boolean. Si vous avez une solution ou une piste.

Personnellement je pense qu'en faisant des classe enfants qui hérite de ma classe parent "Parameter" je devrais pouvoir réécire les données membre en string en boolea.

Effectivement je vais essayer de binder comme ca en attendant une solution a mon premier problème.

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

25 février 2020 à 15:15:24

Avant de te lancer dans le code, il y a un problème de conception : tu ne peux pas attendre de la machine qu'elle traduise automatiquement tes propriétés numériques en booléens, et tes booléens en propriétés numériques. C'est à toi de lui dire comment faire.

Donc la question n'est pas dans le binding. Elle est plutôt dans la façon dont tu initialises tes propriétés booléennes, puis dont tu les sauvegardes.

Pour ce que je commence à comprendre de ton code, tu utilises des objets de l'API JavaFX tels que ObservableList, ObservableMap, alors qu'ils ne sont pas du tout utilisés par JavaFX mais seulement par ton code "classique". Dans ce cas, utilise plutôt des collections classiques du package java.util.

Même remarque pour ta classe Parameter : je ne sais pas ce que c'est, mais à mon avis elle ne sert à rien parce que la fonctionnalité existe déjà dans l'API. Es-tu capable de dire ce qu'elle fait de plus que l'interface Property de JavaFX ?

  • Partager sur Facebook
  • Partager sur Twitter
25 février 2020 à 16:12:42

Pour la traduction ce n'est pas un problème. Il me suffit de convertir mon string en Boolean. La n'est pas le problème selon moi. Le problème est que dans le fichier de configuration .properties il peut y avoir cle1=true, cle2=4 par exemple. Il ne stocke pas uniquement l'état des checkbox mais également la valeur de Combobox.

Mes observableList sont utilise dans la classe "Configuration". Si une des valeurs des paramètres change alors je modifie le fichier de configuration. J'ai ajouté un Listener sur mon observableList. 

Mes autres ObservableList sont utilise pour les Combobox. 

Ma classe Parameter représente un paramétre stocker dans mon fichier de configuration:

package Model;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Parameter{

    private final StringProperty parameterName;
    private final StringProperty parameterValue;

    public Parameter(String name,String value){
        parameterName = new SimpleStringProperty(name);
        parameterValue = new SimpleStringProperty(value);
    }

    public String getParameterName(){
        return parameterName.get();
    }
    public String getParameterValue(){
        return parameterValue.get();
    }

    public void setParameterName(String name){
        this.parameterName.set(name);
    }
    public void setParameterValue(String value){
        this.parameterValue.set(value);
    }

    public StringProperty parameterName(){ return parameterName; }
    public StringProperty parameterValue(){ return parameterValue; }

}

Est-ce que celle-ci est vraiment utile?? c'est la question que je me pose.

-
Edité par BowgartField 25 février 2020 à 16:16:17

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

25 février 2020 à 17:20:16

BowgartField02 a écrit:

Pour la traduction ce n'est pas un problème. Il me suffit de convertir mon string en Boolean. La n'est pas le problème selon moi. Le problème est que dans le fichier de configuration .properties il peut y avoir cle1=true, cle2=4 par exemple. Il ne stocke pas uniquement l'état des checkbox mais également la valeur de Combobox.

En fait, tu voudrais un système unique qui gère des propriétés de type différents : booléens, entiers, texte, objets... ?

Java est un langage qui a un typage fort, donc il n'est pas fait pour traiter des objets dont on ne connaît pas le type. Il faut trouver une interface ou une super-classe commune à tous tes objets, et qui te suffise pour faire les traitements que tu souhaites.

Sans entrer dans le détail, les propriétés gérées par les checkboxes dans JavaFX n'ont pas grand-chose en commun avec les propriétés gérées par les combo boxes... Donc ce n'est pas possible. Ou bien ça t'obligerait à tester le type de chaque valeur, ce que je te déconseille fortement parce que c'est indigne d'un développeur Java, même pour un débutant.

Dans ce cas, voyons les choses autrement : pourquoi vouloir à tout prix accéder à tes propriétés par leur nom ?

Si c'est pour écrire un Listener sur chaque contrôle, ce n'est pas la peine. Il suffit que tu récupères les propriétés de chaque contrôle (selectedProperty() pour les checkboxes, getSelectionModel().selectedIndexProperty() pour les combo boxes, etc). Ces propriétés sont synchronisées en permanence.

Si c'est pour les charger et les sauvegarder, tu peux toujours utiliser une Map pour chaque type de contrôle (une Map pour les checkboxes, une map pour les combo boxes).

BowgartField02 a écrit:

Ma classe Parameter représente un paramétre stocker dans mon fichier de configuration:

...

Est-ce que celle-ci est vraiment utile?? c'est la question que je me pose.

Un paramètre, c'est un nom et une valeur.
Dans JavaFX, l'interface Property sert précisément à ça. Elle est donc parfaite pour stocker un paramètre.
Si tu as plusieurs propriétés et que tu veux les classer par leurs noms, alors une Map est exactement faite pour ça.

Quant à modifier le nom d'un Parameter avec setParameterName()... c'est très bien parce que ça respecte la norme des "beans", mais ça n'a probablement aucune utilité en effet.

Essaye de supprimer ta classe Parameter et de la remplacer par des objets de l'API, et tu verras bien s'il te manque une fonctionnalité.

  • Partager sur Facebook
  • Partager sur Twitter
25 février 2020 à 17:44:38

Zachee54 a écrit:

En fait, tu voudrais un système unique qui gère des propriétés de type différents : booléens, entiers, texte, objets... ?

C'est exactement cela que je souhaite faire. Mais ducoup je pense renoncer a l'idée parcequ'en effet j'ai directement penser a tester la valeur de ce que je recevais si c'était égale a "true ou false" alors j'aurais créé un simpleBooleanProperty etc ... mais je me suis instinctvement dit que quelque chose clochait dans ce système et vous venez de me le confirmer.

Zachee54 a écrit:

Dans ce cas, voyons les choses autrement : pourquoi vouloir à tout prix accéder à tes propriétés par leur nom ?

Si c'est pour écrire un Listener sur chaque contrôle, ce n'est pas la peine. Il suffit que tu récupères les propriétés de chaque contrôle (selectedProperty() pour les checkboxes, getSelectionModel().selectedIndexProperty() pour les combo boxes, etc). Ces propriétés sont synchronisées en permanence.

C'est ce que j'avais fais de base mais c'était du grand n'importe quoi ... J'avais des listener de partout qui s'actionnaient des qu'une checkbox était activer ou désactiver. J'avais du code de partout est j'ai rapidement abandonner l'idée de procéder de cette maniére. 

J'ai donc pensé que créé une ObservableList qui contiendrait X objets de type parameters qui représentent mes paramétres serait la réponse au problème d'organisation du code. Cependant apparement c'est pas la solution du tout.

Zachee54 a écrit:

Essaye de supprimer ta classe Parameter et de la remplacer par des objets de l'API, et tu verras bien s'il te manque une fonctionnalité.

Donc selon vous je dois supprimer ma classe Parameter qui est un doublon de l'interface Property et stocker directement mes Property dans une ObservableMap ?

Il y a t-il un problème de conception a stocker tous mes paramètres en String et de convertir quand j'en ai besoin ? car pour le coup mes checkbox ont toujours le même paramétre.

-
Edité par BowgartField 25 février 2020 à 18:43:29

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

26 février 2020 à 11:40:10

Tu as besoin d'une classe qui gère tes propriétés. Elle fait trois choses :

  • Elle utilise les propriétés des checkboxes (de type BooleanProperty) et combo boxes (ReadOnlyIntegerProperty), ou en tout cas des propriétés qui leur sont liées. Cela lui permet d'avoir toujours sous la main les valeurs à jour sans utiliser de Listener.
  • Elle doit être capable de les initialiser à partir du fichier, sachant que les propriétés sont stockées sous forme de paires de String (nom,valeur).
  • Elle est capable de les sauvegarder dans le fichier, sous forme de paires de String.

Dans l'initialisation, tu vas nécessairement passer par une étape qui va consister à associer chaque contrôle avec une des propriétés sauvegardées. Là, je ne vois pas comment automatiser...
Comme tu le fais manuellement, tu sais donc quel type de contrôle correspond à chaque propriété. Il suffit de définir deux méthodes, l'une qui parse des booléens et les associe à une checkbox, l'autre qui parse des entiers et les associe à une combo box. Pour chaque propriété une par une, tu appelles l'une ou l'autre méthode en lui précisant la checkbox ou la combo box associée.

Pour la sauvegarde, deux solutions :

  • soit tu ranges tes propriétés dans des Map différentes : une pour les BooleanProperty, l'autre pour les ReadOnlyIntegerProperty. Deux boucles suffisent pour les écrire toutes dans le fichier.

  • soit tu utilises une interface Parameter, qui aura deux implémentations concrètes : CheckboxParameter qui sera capable d'écrire des propriétés booléennes à partir d'une BooleanProperty, et ComboBoxParaleter qui sera capable d'écrire des nombres entiers à partir d'une ReadOnlyIntegerProperty.
    Ta Map sera de type Map<String,Parameter>.
    L'interface Parameter te permet ici de mettre en œuvre ce qu'on appelle le pattern Strategy, c'est-à-dire que ton application gère de manière transparente des objets qui doivent être traités de manière différente. L'avantage dans ce système, c'est que c'est toi qui définis ce qui doit être géré de telle façon ou telle autre, sans avoir besoin de tester le type des valeurs.

NB : au fait, est-ce que tu as pensé à utiliser java.util.prefs.Preferences plutôt que java.util.Properties ?
C'est une gestion plus moderne des propriétés qui permet de les sauvegarder dans les dossiers cachés de l'utilisateur. Elle a aussi le petit avantage de lire directement les booléens et les nombres sans t'obliger à les caster toi-même.
Ce sont de toutes petites améliorations, mais si ça convient à ce que tu veux...

-
Edité par Zachee54 26 février 2020 à 11:44:33

  • Partager sur Facebook
  • Partager sur Twitter
26 février 2020 à 12:51:28

Zachee54 a écrit:

Dans l'initialisation, tu vas nécessairement passer par une étape qui va consister à associer chaque contrôle avec une des propriétés sauvegardées. Là, je ne vois pas comment automatiser...

Ce qu'il faut savoir c'est que les clés stockés dans mon fichier correspondent exactement au nom de mes différents contrôles checkbox ou comboBox. Avec cette certitude il doit il avoir un moyen de récupérer un élément javafx d'apres son nom et ensuite je n'aurais plus qu'a binder sa Property avec la bonne qui est contenue dans mon ObservableMap.

Ce que je souhaiterais c'est que mes Checkbox et ComboBox n'est aucune connaissance de mon fichier properties. Elles s'en fichent de savoir d'ou sors leur état (true\false). Tous ce qu'elles font c'est changé d'état quand l'état du fichier de configuration change.

En gros cela donne ca:

(checkbox/combobox) -> ObservableMap() -> PropertiesFileManager() -> fichier .properties

Zachee54 a écrit:

Tu as besoin d'une classe qui gère tes propriétés. Elle fait trois choses :

  • Elle utilise les propriétés des checkboxes (de type BooleanProperty) et combo boxes (ReadOnlyIntegerProperty), ou en tout cas des propriétés qui leur sont liées. Cela lui permet d'avoir toujours sous la main les valeurs à jour sans utiliser de Listener.
  • Elle doit être capable de les initialiser à partir du fichier, sachant que les propriétés sont stockées sous forme de paires de String (nom,valeur).
  • Elle est capable de les sauvegarder dans le fichier, sous forme de paires de String.

Je créé une interface Parameter et une classe CheckboxParameter qui implémente mon interface Parameter et pareille pour ma classe ComboBoxParameter.

Ok mais ducoup une fois que ma classe ApplicationPropertiesFileManager a récupére toutes mes clé/valeur de mon fichier .properties. Elle doit :

1) créé une ObservableMap

2) boucler sur toutes mes clé/valeur:

             - créé un new checkBoxParameter si il la valeur est un boolean

             - créé un new comboBoxParameter si la valeur est un Integer

             - ajouter l'objet dans mon ObservableMap avec comme clé la clé de mon fichier .properties

Nous sommes d'accord sur le principe ?

Zachee54 a écrit:

Pour la sauvegarde, deux solutions :

  • soit tu ranges tes propriétés dans des Map différentes : une pour les BooleanProperty, l'autre pour les ReadOnlyIntegerProperty. Deux boucles suffisent pour les écrire toutes dans le fichier.

  • soit tu utilises une interface Parameter, qui aura deux implémentations concrètes : CheckboxParameter qui sera capable d'écrire des propriétés booléennes à partir d'une BooleanProperty, et ComboBoxParaleter qui sera capable d'écrire des nombres entiers à partir d'une ReadOnlyIntegerProperty.
    Ta Map sera de type Map<String,Parameter>.
    L'interface Parameter te permet ici de mettre en œuvre ce qu'on appelle le pattern Strategy, c'est-à-dire que ton application gère de manière transparente des objets qui doivent être traités de manière différente. L'avantage dans ce système, c'est que c'est toi qui définis ce qui doit être géré de telle façon ou telle autre, sans avoir besoin de tester le type des valeurs.

Oui j'ai entendu parler du Pattern Strategy, je suis entrain de me former sur cela, mais c'est encore très très trouble ...

Zachee54 a écrit:

NB : au fait, est-ce que tu as pensé à utiliser java.util.prefs.Preferences plutôt que java.util.Properties ?

C'est une gestion plus moderne des propriétés qui permet de les sauvegarder dans les dossiers cachés de l'utilisateur. Elle a aussi le petit avantage de lire directement les booléens et les nombres sans t'obliger à les caster toi-même.
Ce sont de toutes petites améliorations, mais si ça convient à ce que tu veux...

Oui j'avais entendu parler de ca. En réalité je pense que clea au un fichier properties m'est égale. A voir lequel est le plus simple d'implémentation et j'ai l'impression que Preferences l'est. Si j'ai bien compris j'ai pas a gérer le coté stockage c'est java qui le gére moi j'ai juste set() et get() a utiliser. 

EDIT:

J'ai taffer sur une architure comme celle-ci

Mon interface Configurable :

package Model;

import javafx.beans.property.Property;

public interface Configurable<T>{

    String getParameterName();
    T getParameterValue();

    Property<String> parameterName();
    Property<T> parameterValue();

}

Ma classe abstraire Parameter:

package Model;

public abstract class Parameter {}

Ma classe IntegerParameter (oui c'est plus logique):

package Model;

import javafx.beans.property.Property;

public class IntegerParameter extends Parameter implements Configurable<Integer> {

    @Override
    public String getParameterName() {
        return null;
    }

    @Override
    public Integer getParameterValue() {
        return null;
    }

    @Override
    public Property<String> parameterName() {
        return null;
    }

    @Override
    public Property<Integer> parameterValue() {
        return null;
    }
}

Et une classe StringParameter:

package Model;

import javafx.beans.property.Property;

public class StringParameter extends Parameter implements Configurable<String> {

    @Override
    public String getParameterName() {
        return null;
    }

    @Override
    public String getParameterValue() {
        return null;
    }

    @Override
    public Property<String> parameterName() {
        return null;
    }

    @Override
    public Property<String> parameterValue() {
        return null;
    }
}






-
Edité par BowgartField 27 février 2020 à 16:13:12

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

28 février 2020 à 19:03:51

UP !
  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

2 mars 2020 à 10:11:00

BowgartField02 a écrit:

Zachee54 a écrit:

Dans l'initialisation, tu vas nécessairement passer par une étape qui va consister à associer chaque contrôle avec une des propriétés sauvegardées. Là, je ne vois pas comment automatiser...

Ce qu'il faut savoir c'est que les clés stockés dans mon fichier correspondent exactement au nom de mes différents contrôles checkbox ou comboBox. Avec cette certitude il doit il avoir un moyen de récupérer un élément javafx d'apres son nom et ensuite je n'aurais plus qu'a binder sa Property avec la bonne qui est contenue dans mon ObservableMap.

Oui, sauf que c'est probablement une mauvaise idée de vouloir partir des valeurs (booléens ou numériques). Il vaut mieux partir des contrôles (checkbox ou combo box), parce que les contrôles sont beaucoup plus différents et beaucoup plus variés que les valeurs des propriétés.
Que feras-tu si tu ajoutes des TextFields ? Tu ajouteras une condition au cas la valeur ne soit ni booléenne, ni numérique, et tu en déduiras que c'est le texte d'un TextField. Sauf qu'un TextField peut contenir le texte "true" ou un nombre...
Que feras-tu quand tu ajouteras un autre contrôle qui utilise aussi des valeurs numériques ? Il faudra bien le différencier des combo boxes...
Que feras-tu si tu décides d'indexer les valeurs des combo boxes autrement que par leur index numérique ? {#emotions_dlg.langue}

NB : Je ne vois pas l'intérêt d'une ObservableMap. Une Map classique suffit.
Une ObservableMap permet d'être prévenu quand quelqu'un ajoute ou supprime une clé dans la Map. Tu n'en as rien à faire ici.

BowgartField02 a écrit:

J'ai taffer sur une architure comme celle-ci

Mon interface Configurable :

Tu n'as pas besoin d'une classe abstraite si tu n'as aucun code à écrire dedans.

Voilà ce que je te propose. Je nomme les classes en référence au pattern Strategy si ça t'aide à faire le lien avec ton problème :

interface ParameterStrategy {
    String getValue();
    void setValue(String value);
}

public class CheckBoxParameterStrategy implements ParameterStrategy {

    private final BooleanProperty booleanProperty;

    public CheckBoxParameterStrategy(CheckBox checkBox) {
        booleanProperty = checkBox.selectedProperty();
    }

    @Override
    public String getValue() {
        return booleanProperty.getValue().toString();
    }

    @Override
    public void setValue(String value) {
        boolean selected = Boolean.parseBoolean(value);
        booleanProperty.setValue(selected);
    }

}

public class ComboBoxParameterStrategy {

    private final ReadOnlyIntegerProperty integerProperty;

    public ComboBoxParameterStrategy(ComboBox comboBox) {
        SingleSelectionModel selectionModel = comboBox.getSelectionModel();
        integerProperty = selectionModel.selectedIndexProperty();
    }

    @Override
    public String getValue() {
        return integerProperty.getValue().toString();
    }

    @Override
    public void setValue(String value) {
        ... // A toi de bosser !
    }

}

// Dans le main
Map<String, ParameterStrategy> parameters = new HashMap<>();

parameters.put(checkbox1.getId(), new CheckBoxParameterStrategy(checkbox1));
parameters.put(combobox1.getId(), new ComboBoxParameterStrategy(combobox1));
...etc

Pour initialiser l'interface ou sauvegarder les propriétés, il suffit de parcourir la Map et d'appeler soit la méthode setValue() sur les objets ParameterStrategy, soit la méthode getValue().

  • Partager sur Facebook
  • Partager sur Twitter
2 mars 2020 à 18:08:21

Zachee54 a écrit:

BowgartField02 a écrit:

J'ai taffer sur une architure comme celle-ci

Mon interface Configurable :

Tu n'as pas besoin d'une classe abstraite si tu n'as aucun code à écrire dedans.

Voilà ce que je te propose. Je nomme les classes en référence au pattern Strategy si ça t'aide à faire le lien avec ton problème :

..

Pour initialiser l'interface ou sauvegarder les propriétés, il suffit de parcourir la Map et d'appeler soit la méthode setValue() sur les objets ParameterStrategy, soit la méthode getValue().

Merci beaucoup pour votre réponse. Cependant j'a une objection a faire. Avec la solution que vous m'avez donnée je suis toujous confontré au problème de savoir qu'elle classe ComboBoxParameter ou Checkbox utilisé . En effet ma Liste contenant les objets ParameterStrategy est initialisée au début de l'application et les élément rataché au items de cette List sont chargé qu'au niveau de la fenêtre de paramétres. C'est pour cela que j'avais créé des classes en fonction de leur type (boolean ou integer ou string) car je sais que si mon fichier contient pour une clé X et valeur Y de type string je devrais créé un object avec mes classes du même type. Ensuite c'est moi manuellement, lors de l'ouverture de la fenetre des paramétres, qui bindera les property de mes éléments aux property qui correspond dans ma liste.

J'ai fais cela car les paramétres de cette liste régissent le fonctionnement de l'application.

J'avais également utilisé une  ObservableList car je souhaite qu'a chaque modification de mes éléments type CheckBox et ComboBox mes préférences stocké dans un fichier de configuration s'actualisent automatiquement.

La technique que vous me fournissez m'oblige a parcourir une liste pour sauvegarder les paramétres alors qu'il me suffirait de mettre un listener sur mes Property().

EDIT:

Après m'être casser la tête a réflechir a une solution et en m'inspirant ce que vous m'avez montré. J'en ai peut-être trouvé une qui fonctionnerait. Malheuresement je n'arrive pas enlever l'étape qui regarde de quelle type sont les paramétres et créé un object en fonction de celui-ci.

package Model;

import javafx.beans.Observable;

public interface ParameterStrategy {

    String getParameterName();
    String getParameterValue();
    Observable getProperty();
}

public class BooleanParameterStrategy implements ParameterStrategy {

    private String ParameterName;
    private BooleanProperty Property;

    public BooleanParameterStrategy(String name, Boolean Boolean){

        //on initialise les données
        this.ParameterName = name;
        this.Property = new SimpleBooleanProperty(Boolean);

        //Evénement qui se déclenche lorsque la valeur change.
        // => inscrit la nouvelle valeur dans le fichier de préférence
        Property.addListener((observableValue, oldValue, newValue) -> {
            System.out.println("valeur changé");
        });

    }

    @Override
    public String getParameterName() {
        return ParameterName;
    }

    @Override
    public String getParameterValue() {
        return Property.toString();
    }

    @Override
    public Observable getProperty() {
        return Property;
    }
}

Donc, au démarrage de mon application, la classe de configuration récupére tous les paramétres stocké grace a Utils.Preferences (c'était plus facile). Ces paramétres sont stockés dans une Map<String,ParameterStrategy>: String me permet de récupére le ParameterStrategy a l'initialisation des mes controles.

Quand j'ouvre la fenêtre des paramétres, qui n'est pas la principale, le controleur de ma View fais ceci:

public void initialize(URL arg0, ResourceBundle arg1) {

		staticTradingFeesLockedCheckbox.selectedProperty().bindBidirectional(
				(BooleanProperty) Configuration.getApplicationPreferences().get("test").getProperty()
		);
}

On voit que je suis obligé de caster mon object récupérer mes malheureusement "Observable" est la seule (?) classe en commun avec tous les XProperty (StringProperty, IntegerProperty etc ...).

J'ai plus qu'a binder le Property de ma checkbox avec celui qui lui correspond dans ma Map.

Seul point noir, dans les préférences je stoke toutes mes valeurs en string. En effet je peux pas savoir le type de valeur donc je pouvais pas faire getString() ou getIntegert(). Ducoup, comme expliqué plus haut, une fois je dois forcément définir le type de valeur stocké dans mes préférences ...




-
Edité par BowgartField 2 mars 2020 à 19:44:19

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

3 mars 2020 à 11:22:10

Pour l'initialisation

En effet, tu devras toujours trouver quelque part les informations sur le type de chaque paramètre.

Jusqu'à présent je raisonnais avec des contrôles existants dans ta fenêtre JavaFX, et auxquels tu fais correspondre tes paramètres stockés. C'est la situation idéale car les contrôles sont connus à l'avance et le type du contrôle détermine le type du paramètre.

Là, tu m'expliques que tes contrôles sont créés dynamiquement à partir des paramètres, ce qui implique de :

  • parser la valeur du paramètre (ce qui présente un risque de confusion comme je l'ai déjà dit),
  • inférer le type de contrôle requis (alors que plusieurs contrôles peuvent utiliser le même type de paramètre...),
  • pour les combo box ou les listes, savoir quelles sont les valeurs possibles. Avoir l'index de la valeur sélectionnée ne sert à rien si tu n'as pas la liste des valeurs (dans le bon ordre !),
  • prévoir une valeur par défaut. Je ne vois pas comment tu peux faire ça si tu ne sais ni le type du contrôle, ni à quoi il sert.

Donc je trouve ton approche bizarre, mais c'est peut-être parce que je n'ai qu'une partie du problème.

Pour répondre à tes interrogations sur ce point : oui, il faut bien à un moment ou l'autre déterminer le type, que ce soit le type du paramètre, le type du contrôle ou la classe à utiliser pour le pattern Strategy.
C'est toi qui connais tes données de départ, donc c'est toi qui choisis ce qui est le plus adapté à ton cas. Personnellement, je te dis qu'il vaut mieux partir du type des contrôles dans la mesure où on les connaît.

Dans ton contrôleur

Je ne connais pas le contexte de ton code.

Je vois seulement que tu prends le problème par le mauvais bout. Si tu récupères la propriété avec getProperty(), tu vas de nouveau être embêté parce que tu auras des tas de propriétés de types différents et imprévisibles. Comme tu le dis, tu en es réduit à utiliser Observable comme plus petit dénominateur commun...

Profite pleinement du pattern Strategy : demande à la stratégie de construire elle-même le contrôle dont tu as besoin !

public interface ParameterStrategy {
    ...
    Control createControl();
}

public class CheckBoxParameterStategy {
    ...
    @Override
    public Control createControl() {
        CheckBox checkBox = new CheckBox();
        booleanProperty.bindBidirectional(checkBox.getSelectionModel().selectedIndexProperty());
        return checkBox;
    }
}

BowgartField02 a écrit:

La technique que vous me fournissez m'oblige a parcourir une liste pour sauvegarder les paramétres alors qu'il me suffirait de mettre un listener sur mes Property().

Pas du tout. Je te montre juste comment tu fais pour sauvegarder toutes tes propriétés.
Tu peux avoir par exemple un Listener commun, que tu places sur toutes les propriétés, et qui se charge de tout réenregistrer à chaque fois qu'une propriété est modifiée. (Tu pourrais même l'intégrer à ton interface ParamaterStrategy, en déclarant une méthode setListener() qui obligerait donc chaque stratégie enregistrer ce Listener sur la propriété qu'elles gèrent.)

public interface ParameterStrategy {
    String getValue();
    void setValue(String value);
    void addListener(ChangeListener<?> listener);
}

public class CheckBoxParameterStrategy {
    ...
    @Override
    public void addListener(final ChangeListener<Object> listener) {
        booleanProperty.addListener((observableValue, oldValue, newValue) -> listener.changed(observableValue, oldValue, newValue));
    }
}

Tu peux aussi tout-à-fait, comme tu le proposes, avoir un Listener sur chaque propriété qui se charge d'enregistrer cette propriété. Je te laisse réfléchir au code.
L'inconvénient c'est que si tu enregistres tes paramètres dans un fichier, tu ne peux pas réécrire un seul paramètre ; il faut que tu réécrives tout le fichier. Si tu utilises Preferences, tu n'as pas ce problème.

ObservableList

Les collections observables ne t'informent pas des modifications des objets qu'elles contiennent.

Elles t'informent des modifications survenues sur la collection elle-même, c'est-à-dire l'ajout, la suppression ou le remplacement d'un élément par un autre.

Si tu as une BooleanProperty dans une ObservableList, le fait que la BooleanProperty passe de "true" à "false" ne déclenchera pas les Listeners de l'ObservableList.

  • Partager sur Facebook
  • Partager sur Twitter
3 mars 2020 à 13:53:47

Pour les ObservableList effectivement j'ai remarqué mon erreur à la suite de votre objection à l'utilisation de celles-ci. Je n'utilise également plus de fichier .properties je suis passé a l'utilisation de Utils.Preferences qui me convenait bien plus.

Je vais essayer de détailler le fonctionnement de mon application au maximum pour que vous compreniez. Pour résoudre mon problème nous avons besoin de connaître uniquement ceci:

Elle se compose de 2 fenêtres:

  • Main
  • Settings

De deux controllers liés à celle-ci:

  • MainController
  • SettingsController

De plusieurs classe utilitaires:

  • Configurations
  • ApplicationsPreferences

Point de départ de mon application: Main.java. Charge la fenêtre Main et donc charge aussi le controlleur MainController.

Main.java:

@Override
	public void start(Stage primaryStage){

    	InitializeConfiguration();
		
	}

	private void InitializeConfiguration() {

		//on initialise la configuration
		configuration = new Configuration();

	}

Comme on le voit au démmarage on créé un nouvel objet "Configuration". Celui-ci contient la configuration de mon application tel que:

  • la langue choisie (language=fr)
  • si l'utilisateur est premium (premium=true/false)
  • le nombre de bonbon qui peut manger par jour (modifiable pour l'utilisateur dans la fenêtre Settings) (EatingDailyCandies=6)

Pourquoi avoir choisi de créé une configuration dès le début de l'application ? Parceque j'ai besoin du nombre de bonbon que l'utilisateur peut manger pour bloquer le bouton manger bonbon une fois le nombre atteint, par exemple.

La classe configuration:

public class Configuration {

	/**
	 * Donnée membre contenant les paramétres de l'application
	 */
	private static Map<String, ParameterStrategy> ApplicationPreference;


	/**
	 * Constructeur par défaut
	 */
	public Configuration() {

		//On récupére les paramétres de l'application
		this.initializeApplicationPreferences();
		

	}

	/**
	 * Récupére les paramétres et leur valeurs et les stockent dans une liste Observable
	 */
	private void initializeApplicationPreferences() {

		ApplicationPreference = new ApplicationPreferences().getAllPreferences();

	}
	
	/**
	 *
	 */
	public static Map<String, ParameterStrategy> getApplicationPreferences(){

		return ApplicationPreference;

	}


}

A la création d'un objet "Configuration" celui-ci récupére les paramétres stockés dans les préférences grace a ma classe ApplicationPreferences:

public class ApplicationPreferences {

    /**
     * Donnée membre contenant l'objet préférence
     */
    private Preferences preferences = null;

    /**
     * Constructeur par défaut
     */
    public ApplicationPreferences(){

        String APPLICATION_PROPERTIES_NODE_NAME = "fr/tradingjournal/preferences";

        try{

            //on regarde si les préférences ont déjà été initialiser
            if(Preferences.userRoot().nodeExists(APPLICATION_PROPERTIES_NODE_NAME)){

                System.out.println("existe");

                //on récupére les préférences
                preferences = Preferences.userRoot().node(APPLICATION_PROPERTIES_NODE_NAME);

            }else{

                System.out.println("test1");
                preferences = Preferences.userRoot().node(APPLICATION_PROPERTIES_NODE_NAME);

                initializePreferences();

            }

        }catch(Exception e){
            e.printStackTrace();
        }

    }

    /**
     * Initialise les préférence par défaut à partir d'un patron
     * -> le patron se situe dans INSEREZ ICI LE CHEMIN VERS UN FICHIER
     */
    private void initializePreferences() {

        preferences.put("language","fr_FR");
        preferences.put("test","true");
        preferences.remove("Test1");
        preferences.remove("Test");

    }

    /**
     * Permet de récupérer la valeur d'une préférence
     * @param preferenceName le nom de la préference dont ont veut récupérer la valeur
     * @return la valeur de la préférence
     */
    public String getPreferenceValue(String preferenceName){
        return preferences.get(preferenceName,null);
    }

    /**
     * Permet de changer la valeur d'une préférence
     * @param preferenceName le nom de la préference dont ont veut changer la valeur
     * @param preferenceValue La valeur de la préférence
     */
    public void setPreferenceValue(String preferenceName, String preferenceValue){

        preferences.put(preferenceName,preferenceValue);

    }

    /**
     * Permet de récupérer toutes les préférences
     * @return Toutes les préférences dans une ObservableMap<String,Parameter>
     */
    public Map<String, ParameterStrategy> getAllPreferences(){

        ObservableMap<String,ParameterStrategy> values = FXCollections.observableHashMap();

        /*
            Pour le debug -> m'affiche tous les paramétres ainsi que leur valeurs
         */
        try {

            for(String preference : preferences.keys()){
                System.out.println(preference + ":" + preferences.get(preference,null));
            }

        } catch (BackingStoreException e) {
            e.printStackTrace();
        }

        try{
            
            /*
                   (1)
             
            for(String preference : preferences.keys()){

                values.put( preference, new Parameter(preference));

            }*/

            values.put("test",new BooleanParameterStrategy("test",Boolean.parseBoolean(preferences.get("test","false"))));

        }catch(Exception e){
            e.printStackTrace();
        }

        return values;

    }

}

Ce qu'il y à a comprendre c'est que au niveau de (1) j'ai pas encore intriduit notre fameux test de type, vu que mes préférences stocke tout en string. J'ai commenté le code qui boucle sur toute les valeurs car cela nous importe peu et c'était plus facile pour les test.

Le plus important est ici, dans Configuration.java:

ApplicationPreference = new ApplicationPreferences().getAllPreferences();

On créé une instance de ApplicationPreference et on récupére directement toutes les préférences.

A la création d'une instance de "ApplicationPreferences": on verifie si l'abre de préférence existe si oui on charge un objet Preference qui va nous permettre de manipuler cette arbre, sinon on créé l'abre et on y ajoute les paramétres par défaut.

Ensuite  "getAllPreferences()" de la classe ApplicationPreference nous retourne une Map<String,ParameterStrategy> qui est donc notre qui stocke tous nos paramétres. Et qui stocke donc les fameux paramétres quei j'ai cité plus haut et qui sont indispensable au démarrage de mon application.

J'en viens a ma fenêtre "Settings", admettons que l'utilisateur veuille augmenter le nombre de bonbons par jour comment fait-il ? J'ai donc créé une fenêtre "Settings" qui affiche mes différents paramétres de facon a pouvoir les modifier. Par exemple le nombre de bonbon est modifiable au travers d'un spinner.

Je suis d'accord avec vous qu'un controlleur javaFX ne dispose pas que d'un type de valeur en particulier par exemple un comboBox peut avoir une liste de String ou d'integer. Cependant moi je sais exactement quelle type de valeur mettre dans mes controlleurs et surtout lequel.

Une fois cela dit, vous devriez comprendre que mon Map contenant mes paramétres a le cul entre deux chaises.

  • J'ai besoin de ces Property() pour les lié a mes controlleur qui permette de le modifier
  • Mais j'ai aussi besoin de leur valeur brut dans le reste de l'application

Vous proposez de lié directement les controlleur a leur valeur dans le Map. Mais ce n'est pas possible car mes CheckBox, Spinner, ComboBox n'existe que dans mon SettingsControlleur et ma Map est initialisé dés le debut de l'application (dans Main.java).

Maintenant que vous savez comment cela fonctionne je pense que vous comprenez pourquoi votre code me pose probléme.

-
Edité par BowgartField 3 mars 2020 à 14:07:34

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

3 mars 2020 à 14:49:34

BowgartField02 a écrit:

Une fois cela dit, vous devriez comprendre que mon Map contenant mes paramétres a le cul entre deux chaises.

  • J'ai besoin de ces Property() pour les lié a mes controlleur qui permette de le modifier
  • Mais j'ai aussi besoin de leur valeur brut dans le reste de l'application

Désolé, non, je ne comprends pas. :o

Dans une architecture MVC comme JavaFX, le contrôleur sert précisément à ça : garder la main sur les informations affichées par la vue.

Je vois un objet Configuration qui contient toutes les préférences et qui est défini dans la classe Main, donc facilement accessible à tous les objets que tu souhaiteras programmer autour.

Tu ne m'as pas dit non plus comment étaient créés les contrôles de ton interface graphique : dynamiquement à partir des préférences, ou bien hard-codées (dans la classe Main ou un fichier FXML) ?
Ca change tout parce qu'il faut savoir si ton application se basera :

  • sur les contrôles hard-codés, en initialisant leurs propriétés à partir des préférences utilisateur préenregistrées
  • ou sur les préférences utilisateur, en créant dynamiquement des contrôles correspondants.

Selon le cas, ton initialisation se présentera de façon totalement différente et tu n'auras pas besoin d'accéder aux données de la même façon.
Les ParameterStrategy doivent-ils savoir créer un contrôle ? Dois-tu pouvoir accéder à un contrôle par son nom, ou son type ? Dois-tu savoir binder des propriétés ? etc. Autant de questions auxquelles je ne peux pas répondre si tu ne sais pas comment tu veux t'y prendre ou si tu ne sais pas l'expliquer.

BowgartField02 a écrit:

Je suis d'accord avec vous qu'un controlleur javaFX ne dispose pas que d'un type de valeur en particulier par exemple un comboBox peut avoir une liste de String ou d'integer. Cependant moi je sais exactement quelle type de valeur mettre dans mes controlleurs et surtout lequel.

C'est là le paradoxe : tu sais exactement quels sont tes contrôles, mais tu ne veux pas t'en servir et tu veux à toute force essayer de deviner les choses à partir des préférences en essayant de deviner leur type. :waw:

BowgartField02 a écrit:

Vous proposez de lié directement les controlleur a leur valeur dans le Map. Mais ce n'est pas possible car mes CheckBox, Spinner, ComboBox n'existe que dans mon SettingsControlleur et ma Map est initialisé dés le debut de l'application (dans Main.java).

"Tout le monde savait que c'était impossible. Il est venu un type qui ne le savait pas et qui l'a fait." :lol:

Tu as évidemment accès tes contrôles depuis l'endroit que tu choisis. En tout cas ton contrôleur devrait y avoir accès, sinon je ne vois pas comment tu comptes faire.

Si tu crées les contrôles à partir de la Map, alors c'est normal et même nécessaire qu'elle soit créée dès le début de l'application.
Si tu crées la Map à partir des contrôles, qu'est-ce qui t'empêche de la créer plus tard, une fois que ton interface JavaFX est construite ?

Depuis le début, on dirait que tu rejettes toutes les solutions techniques que je te propose parce que tu ne sais pas comment les organiser dans ton application.
Il faut que tu puisses dire quelles sont les données source. Si tu rajoutes un contrôle, est-ce que la première exécution créera automatiquement une préférence correspondante (données source = les contrôles) ? Si tu ajoutes une préférence, est-ce que ça créera automatiquement un contrôle dans la fenêtre (données source = préférences) ?
Comment sais-tu quel contrôle correspond à quelle propriété : parce que tu les connais par coeur ou parce qu'il y a une règle qui permet de faire le lien ? Qui décide du nom des contrôles ? du nom des propriétés ?
Comment choisis-tu les valeurs possibles pour les combo box ? les bornes des spinners ?
etc etc.

C'est sûr que tant que tu n'auras pas décidé quelles sont les informations qui déterminent l'apparence et le fonctionnement de ton application, tu vas tourner en rond pour chercher la porte d'entrée et tu ne le trouveras jamais.

Je n'ai dit que deux choses essentielles :

  • Tu as besoin d'un pattern Strategy pour gérer des données de types totalement différents
  • Tu as tout intérêt à utiliser des contrôles hard-codés comme données de référence pour ton interface parce que ce sont les éléments les plus spécialisés, alors que les valeurs des paramètres ne sont pas typés, leur parsage peut être ambigu et ils peuvent correspondre à plusieurs types de contrôles.

-
Edité par Zachee54 3 mars 2020 à 14:52:47

  • Partager sur Facebook
  • Partager sur Twitter
3 mars 2020 à 16:10:28

Zachee54 a écrit:

Désolé, non, je ne comprends pas. :o

Dans une architecture MVC comme JavaFX, le contrôleur sert précisément à ça : garder la main sur les informations affichées par la vue.

Je vois un objet Configuration qui contient toutes les préférences et qui est défini dans la classe Main, donc facilement accessible à tous les objets que tu souhaiteras programmer autour.

J'ai besoin des paramétres pour 2 choses:

  • les utilisé pendant le cycle de mon application
  • les changer avec ma fenêtre "Settings"

Je dois donc pouvoir faire deux choses avec eux:

  • Binder leur Property() a la Property() du control qui va permettre de les modifier
  • Récupérer leur valeur, par exemple String ou Integer

Oui c'est justement pour cela que mon objet configuration est dans ma classe Main.java, TOUTES mes classes ont otentiellement besoins d'un valeur dedans. Pour reprendre mon exemple, si j'ai un bouton qui affiche le nombre de bonbon mangeable restant, je dois à un moment donné forcément récupérer la valeur du paramétre qui indique le nombre total qu'on peut manger et le nombre déja manger.

Zachee54 a écrit:

Tu ne m'as pas dit non plus comment étaient créés les contrôles de ton interface graphique : dynamiquement à partir des préférences, ou bien hard-codées (dans la classe Main ou un fichier FXML) ?

Ca change tout parce qu'il faut savoir si ton application se basera :

  • sur les contrôles hard-codés, en initialisant leurs propriétés à partir des préférences utilisateur préenregistrées
  • ou sur les préférences utilisateur, en créant dynamiquement des contrôles correspondants.

Selon le cas, ton initialisation se présentera de façon totalement différente et tu n'auras pas besoin d'accéder aux données de la même façon.
Les ParameterStrategy doivent-ils savoir créer un contrôle ? Dois-tu pouvoir accéder à un contrôle par son nom, ou son type ? Dois-tu savoir binder des propriétés ? etc. Autant de questions auxquelles je ne peux pas répondre si tu ne sais pas comment tu veux t'y prendre ou si tu ne sais pas l'expliquer.

C'est trés simple, c'est MOI créateur de l'application qui décide quels paramétres est modifiable. J'ai donc créé une interface javafx avec scenebuilder:

Voila donc à quoi ressemble mon interface.

Les différents controls que vous voyez, je connais leur id et il ne changera jamais. Je connais également le nom de la valeur qu'il permette de changer. Et cela ne changera pas non plus.

Zachee54 a écrit:

Si tu crées la Map à partir des contrôles, qu'est-ce qui t'empêche de la créer plus tard, une fois que ton interface JavaFX est construite ?

 Je serais curieux de savoir comment vous appliquez ceci (code que vous m'avez proposé):

// Dans le main
Map<String, ParameterStrategy> parameters = new HashMap<>();
 
parameters.put(checkbox1.getId(), new CheckBoxParameterStrategy(checkbox1));
parameters.put(combobox1.getId(), new ComboBoxParameterStrategy(combobox1));
...etc

A des controls qui n'existe même pas ... Reprenez moi si je me trompe mais pour accéder à un objet d'un interface JAVAFX il faut faire ceci

@FXML
CheckBox chk1;

et seulement ensuite je peux utiliser mon objet. En revanche si le control n'existe pas dans l'interface que gére mon controlleur je vais me faire jeter par la JVM car elle n'a aucun élément graphique existant a lié avec cette variable.

Zachee54 a écrit:

Selon le cas, ton initialisation se présentera de façon totalement différente et tu n'auras pas besoin d'accéder aux données de la même façon.

Les ParameterStrategy doivent-ils savoir créer un contrôle ? Dois-tu pouvoir accéder à un contrôle par son nom, ou son type ? Dois-tu savoir binder des propriétés ? etc. Autant de questions auxquelles je ne peux pas répondre si tu ne sais pas comment tu veux t'y prendre ou si tu ne sais pas l'expliquer.

Je pense que notre incompréhension vient du faite que je n'ai pas précisé que mon interface était statique, je ne créé rien dynamiquement. Mais surtout qu'elle provient d'un fichier fxml.

Ma classe ne doit pas savoir créé un control. Ils sont déja tous existant. Je dois pouvoir accéder a mon control par son nom. oui je dois binder leur propriété avec celle contenu dans ma Map. 

Zachee54 a écrit:

C'est là le paradoxe : tu sais exactement quels sont tes contrôles, mais tu ne veux pas t'en servir et tu veux à toute force essayer de deviner les choses à partir des préférences en essayant de deviner leur type. :waw:

Si je m'en sers dans mon SettingsController ... a cette endroit: 

public void initialize(URL arg0, ResourceBundle arg1) {
 
        staticTradingFeesLockedCheckbox.selectedProperty().bindBidirectional(
                (BooleanProperty) Configuration.getApplicationPreferences().get("test").getProperty()
        );
}

Je prend mon control, ici une CheckBox et je binde sa valeur bilatéralement a la valeur qui lui correspond dans ma Map.



-
Edité par BowgartField 3 mars 2020 à 16:11:36

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

3 mars 2020 à 17:29:23

Ah, voilà enfin ce que je voulais savoir ! Tu connais à l'avance tous tes contrôles et ils sont figés dans un fichier fxml. :ange:

BowgartField02 a écrit:

Je serais curieux de savoir comment vous appliquez ceci (code que vous m'avez proposé):

// Dans le main
Map<String, ParameterStrategy> parameters = new HashMap<>();
 
parameters.put(checkbox1.getId(), new CheckBoxParameterStrategy(checkbox1));
parameters.put(combobox1.getId(), new ComboBoxParameterStrategy(combobox1));
...etc

A des controls qui n'existe même pas ... Reprenez moi si je me trompe mais pour accéder à un objet d'un interface JAVAFX il faut faire ceci

@FXML
CheckBox chk1;

et seulement ensuite je peux utiliser mon objet. En revanche si le control n'existe pas dans l'interface que gére mon controlleur je vais me faire jeter par la JVM car elle n'a aucun élément graphique existant a lié avec cette variable.

Reste calme s'il-te-plaît. Je suis là pour t'aider parce que je le veux bien.

public class Settings extends Application {

    private final Configuration configuration = new Configuration();

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Paramètres");
        FXMLLoader loader = new FXMLLoader(getClass().getResource("settings.fxml"));
        Node root = loader.load();
        Scene scene = new Scene(root);

        // Les lignes importantes !
        SettingsController controller = loader.getController();
        controller.initConfiguration(configuration);
    }
}

public class SettingsController {

    @FXML
    CheckBox staticTradingFeesLockedCheckbox;

    @FXML
    ComboBox languageCombo;

    public void initConfiguration(Configuration configuration) {
        configuration.init("verr_courtage", staticTradingFeesLockedCheckbox);
        configuration.init("langue", languageCombo);
    }
}

public class Configuration {

    private final ApplicationPreferences applicationPreferences;

    private final Map<String, ParameterStrategy> parameters = new HashMap<>();

    public Configuration() {
        ... // Initialiser applicationPreferences en allant lire les préférences utilisateur
    }

    public void init(String key, CheckBox checkBox) {
        initImpl(key, new CheckBoxParameterStrategy(checkbox));
    }

    public void init(String key, ComboBox comboBox) {
        initImpl(key, new ComboBoxParameterStrategy(comboBox));
    }

    private void initImpl(String key, ParameterStrategy parameter) {
        parameter.setValue(applicationPreferences.getPreferenceValue(key));
        parameters.put(key, parameter);
    }
}

C'est une possiblité.

Tu peux aussi mettre tout dans le contrôleur :

public class SettingsController {

    private final ApplicationPreferences applicationPreferences;

    private Map<String, ParameterStrategy> parameters;

    @FXML
    private CheckBox staticTradingFeesLockedCheckbox;

    @FXML
    private ComboBox languageCombo;

    public SettingsController() {
        // Ici, tes contrôles ne sont pas encore créés

        // Récupère les préférences (peu importe comment)
        preferences = ApplicationPreferences.getApplicationPreferences();
    }

    @FXML
    public void initialize() {
        // Ici, tes contrôles ont déjà été créés
        addParameterFrom("verr_courtage", staticTradingFeesLockedCheckbox);
        addParameterFrom("language", languageCombo);
    }

    private void addParameterFrom(String key, Checkbox checkbox) {
        addParameter(key, new CheckBoxParameterStrategy(checkBox));
    }

    private void addParameterFrom(String key, ComboBox combo) {
        ...
    }

    private void addParameter(String key, ParameterStrategy parameter) {
        ... // Comme initImpl dans l'exemple d'avant
    }
}

Tu peux soit récuperer le contrôleur au niveau du FXMLLoader et le piloter "de l'extérieur", soit faire travailler le contrôleur lui-même dans sa méthode initialize().

Peu importe la façon dont tu récupères les Preferences dans ApplicationPreferences, ni où tu choisis de mettre ta Map (une fois je l'ai mise dans Configuration, une autre fois directement dans le contrôleur). C'est une autre question.

NB : tu peux reprendre mon premier code de CheckBoxParameterStrategy et ComboBoxParameterStrategy. Tu verras que tu n'as même pas besoin de faire un bind avec une autre propriété ; il suffit de stocker la propriété de la checkbox ou du modèle de la combo box.

-
Edité par Zachee54 3 mars 2020 à 17:40:25

  • Partager sur Facebook
  • Partager sur Twitter
3 mars 2020 à 18:31:58

Zachee54 a écrit:

Ah, voilà enfin ce que je voulais savoir ! Tu connais à l'avance tous tes contrôles et ils sont figés dans un fichier fxml. :ange:

Ahaha il fallait me demander ;) je pensais que nous nous étions compris.

Je suis d'accord avec votre code mais j'ai encore un point d'interrogation. Si j'ai bien compris comment vous résonnez et comment votre code fonctionne.

Certes je connais le type de Property il va me falloir pour stocker ma valeur mais je récupére mes préférences grâce a une bloucle ce qui va m'obliger a faire:

for(String preference : preferences.keys()){

			if( preference.equals("var2")){
				parameter.put(preference, new CheckBoxParameterStrategy(???));
			}elseif(preference.equals("var1")){
				parameter.put(preference,new ComboBoxParameterStrategy(????));
			}

		}

Ce qui n'est pas trés pratique car si je veux ajouter un nouveau paramétre je suis obligé d'ajouter une condition a mon if, et ensuite je mets quoi comme checkbox ???



-
Edité par BowgartField 3 mars 2020 à 21:24:59

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

4 mars 2020 à 23:20:35

Non, justement : tu lis chaque préférence pendant que tu parcours tes checkbox et combo box. C'est ce que fait la ligne :

parameter.setValue(applicationPreferences.getPreferenceValue(key));

L'intérêt de la méthode setValue déclarée dans l'interface ParameterStrategy est que tu peux lui passer la valeur de la préférence sous forme de texte sans te préoccuper de ce qu'elle en fera. C'est la stratégie qui s'occupe de faire le bon parsage.

Tu n'as plus besoin de parcourir tes préférences puisqu'elles sont traitées une par une, en fonction du contrôle auquel elles vont servir.

Ça rejoint ce que je te disais : ce sont les contrôles qui donnent le tempo, c'est eux la référence. Donc tout s'organise à partir d'eux dans les méthodes initConfiguration ou initialize dans les suggestions ci-dessus ; pas dans une boucle sur les préférences puisqu'on n'en a rien à cirer, elles sont seulement là au service des contrôles.

  • Partager sur Facebook
  • Partager sur Twitter
5 mars 2020 à 17:13:13

Le probléme avec votre approche est que cela implique de récupérer les préférences lors de l'ouverture de la fenêtre Settings. Mais j'en ai besoin dès le début de l'application .... Le probléme avec ce systéme c'est que mes paramétres sont socké dans un objet ParameterStrategy qui est initialiser a l'initialisation de mes controlls. Mais mes controls sont initilisé quand j'ai besoin de modifier la valeur qu'il permette de modifier.
  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

5 mars 2020 à 17:36:54

BowgartField02 a écrit:

Le probléme avec votre approche est que cela implique de récupérer les préférences lors de l'ouverture de la fenêtre Settings. Mais j'en ai besoin dès le début de l'application ....

Tu peux parfaitement charger tes préférences dès le début de l'application, et les conserver dans un objet java.util.Preferences ou ApplicationPreferences, suivant que tu veux rajouter ou non ajouter une couche d'abstraction entre les deux.
Ensuite tu passes cet objet à ton contrôleur, qui peut s'en servir une fois que les contrôles sont créés.

BowgartField02 a écrit:

Le probléme avec ce systéme c'est que mes paramétres sont socké dans un objet ParameterStrategy qui est initialiser a l'initialisation de mes controlls.

Les procédures que je t'ai proposées font deux choses :

  • elles créent des objets ParameterStrategy qui retiennent la propriété du contrôle. Cette propriété se met à jour toute seule, puisque c'est la propriété originale du contrôle.
    C'est encore mieux que si tu créais une propriété et que tu faisais un bind avec celle du contrôle, puisque tu as directement l'original au lieu d'avoir une copie ! J'ai l'impression que tu as du mal à saisir ça ?
  • elles initialisent le contrôle à partir de la préférence correspondante.

BowgartField02 a écrit:

Mais mes controls sont initilisé quand j'ai besoin de modifier la valeur qu'il permette de modifier.

Je ne vois pas bien ce que tu veux dire.

En tout cas, le système que je te propose t'affiche des contrôles avec les bonnes valeurs dès l'affichage de la fenêtre, et ils se mettent à jour automatiquement dans les ParameterStrategy (en fait dans les propriétés contenues dans les ParameterStrategy, donc tu n'as pas besoind e t'en occuper).
Il te reste seulement à écrire un Listener qui va se charger de sauvegarder ces paramètres dans tes préférences.

  • Partager sur Facebook
  • Partager sur Twitter
5 mars 2020 à 18:05:36

Zachee54 a écrit:

Tu peux parfaitement charger tes préférences dès le début de l'application, et les conserver dans un objet java.util.Preferences ou ApplicationPreferences, suivant que tu veux rajouter ou non ajouter une couche d'abstraction entre les deux.

Ensuite tu passes cet objet à ton contrôleur, qui peut s'en servir une fois que les contrôles sont créés.

 Effectivement je n'avais pas vu la chose de cette maniére ... Merci beaucoup :D.

Je voyais une systéme qui reste à jour en permanence d'où mon incomprehension. En effet j'aurais pu utiliser directement mes Property afin de garder une valeur toujous a jour de mes préférences. Mais utilisé ApplicationPreference.getPreference() comme moyen de récupérer mes préférence sans passer par mes Property() déroge a cette régle.

A part cela j'ai enfin compris tout le code :)

-
Edité par BowgartField 5 mars 2020 à 20:12:37

  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)

6 mars 2020 à 9:25:29

Oui, j'espère qu'on s'est bien compris.

Tes préférences sont toujours stockées dans l'objet Preferences, qui peut être encapsulé dans une classe ApplicationPreferences. Tu te sers de ça pour gérer ton application.

Les ParameterStrategy contiennent les données de la fenêtre Settings. C'est différent des Preferences dans le sens où l'utilisateur peut cocher/décocher des cases (modification des paramètres dans les ParameterStrategy), sans que ça ait nécessairement d'effet immédiat sur l'application. C'est-à-dire que les Preferences restent inchangées.
Ensuite, le Listener que tu écriras pourra te permettre de décider comment tu reportes les modifications des ParameterStrategy dans les Preferences : quand l'utilisateur clique sur "OK" dans la fenêtre Settings, ou bien immédiatement à chaque modification.

Finalement, on a construit une sorte de synchronisation entre les préférences utilisateurs et les données affichées/modifiées dans la fenêtre Settings ! Simplement tout ça doit être codé à la main ; il y a une phase d'initialisation (c'est ce dont on a le plus parlé) et une phase de flush (le Listener).

  • Partager sur Facebook
  • Partager sur Twitter
7 mars 2020 à 14:25:29

Merci beaucoup :)
  • Partager sur Facebook
  • Partager sur Twitter

Parfois il faut se demander si le problème n'est pas entre l’écran et la chaise ! :)