Bonjour,
Je rencontre le problème suivant :
J'utilise une classe DataSignal pour stocker notamment des séries de données.
La fenêtre graphique contient un tableau et un graphe.
Ce tableau contient une colonne affichant le nom de la série et une checkBox permettant d'activer / désactiver l'affichage des séries de données associées dans un graphe.
Un listener est utilisé pour mettre à jour le contenu de la liste affichage dans le graphe. Lorsque l'utilise active l'affichage dans le graphe, la liste de valeurs contenus dans l'instance de la classe 'DataSignal' est copié dans 'dataToPlotObservableList' utilisée pour l'affichage dans le graphe.
Classe DataSignal :
Classe Test :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package application; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.chart.LineChart; import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart.Series; public class DataSignal { // Nom de la série private SimpleStringProperty nomSerie ; // Propriété permettant de savoir si cette série est affichée ou non dans le graphe. private SimpleBooleanProperty showSerie ; // Liste permettant de stocker les séries de données associées à ce signal. private LineChart.Series<Double, Double> series = new LineChart.Series<Double, Double>(); public DataSignal(String nSerie) { this.nomSerie = new SimpleStringProperty(nSerie); this.showSerie = new SimpleBooleanProperty(false); series.setName(this.getNomSerie()); } // Routine permettant d'ajouter des valeurs à la série de données considérée public void addElementToSerie(Double time, Double AmplSignal) { series.getData().add(new XYChart.Data<Double, Double>(time, AmplSignal)); } // Getters and Setters public String getNomSerie() { return nomSerie.get(); } public void setNomSerie(String nSerie) { nomSerie.set(nSerie); series.setName(this.getNomSerie()); } public StringProperty nomSerieProperty() { return nomSerie; } public Boolean getShowSerie() { return showSerie.get(); } public void isShowSerie(Boolean sSerie) { showSerie.set(sSerie); } public BooleanProperty showSerieProperty() { return showSerie; } // Getters et Setters permettant l'accès aux différentes séries de données. public Series<Double, Double> getSeries() { return series; } public void setSeries(LineChart.Series<Double, Double> series) { this.series = series; } }
Le problème que je rencontre avec cette méthode vient du fait que lorsque l'utilisateur active puis désactive très rapidement la checkBox contenue dans le tableau, j'ai un message d'erreur qui s'affiche.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 package application; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart.Series; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javafx.util.Callback; public class Test extends Application { private BorderPane rootContentPane = new BorderPane(); private Scene scene = new Scene(rootContentPane); private TableView <DataSignal> dataTableView = new TableView<DataSignal>(); private TableColumn <DataSignal, String> nomSerie = new TableColumn<DataSignal, String>(); private TableColumn <DataSignal, Boolean> showSerie = new TableColumn<DataSignal, Boolean>(); // Liste contenant les différentes séries de données importées ou traitées (affichage dans dataTableView). private ObservableList <DataSignal> listeDataSeries = FXCollections.observableArrayList(); final NumberAxis xAxis = new NumberAxis(); final NumberAxis yAxis = new NumberAxis(); private LineChart graph = new LineChart(xAxis,yAxis); // Liste utilisée pour stocker les séries de données affichées dans le LineChart 'graph'. private ObservableList<Series<Double, Double>> dataToPlotObservableList = FXCollections.observableArrayList(); @Override public void start(Stage primaryStage) { // Création d'une instance de DataSignal contenant un signal de test. DataSignal newDataSignal = new DataSignal("test"); for (int i=0; i<1000; i++) { newDataSignal.addElementToSerie(Double.valueOf(i), Double.valueOf(i)); } // Mise en place d'un listener permettant de mettre à jour le contenu de 'dataToPlotObservableList' lorsque l'utilisateur selectionne ou déselectionne une série. InstallDataSerieListener(newDataSignal); listeDataSeries.add(newDataSignal); //les différents champs du tableau 'dataTableView' sont rendus éditables. dataTableView.setEditable(true); showSerie.setEditable(true); nomSerie.setEditable(true); // Le tri des colonnes du tableaux est désactivé. showSerie.setSortable(false); // Mise en forme du contenu des différentes colonnes du tableau. nomSerie.setCellValueFactory(new PropertyValueFactory<DataSignal, String>("nomSerie")); nomSerie.setCellFactory(TextFieldTableCell.<DataSignal>forTableColumn()); // Rend éditable le nom des séries de données. nomSerie.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<DataSignal, String>>() { @Override public void handle(TableColumn.CellEditEvent<DataSignal, String> event) { DataSignal rec = event.getTableView().getItems().get(event.getTablePosition().getRow()); String newValue = event.getNewValue(); rec.setNomSerie(newValue); } }); showSerie.setCellValueFactory(new PropertyValueFactory<DataSignal, Boolean>("showSerie")); showSerie.setCellFactory(new Callback<TableColumn<DataSignal, Boolean>, TableCell<DataSignal, Boolean>>() { public TableCell<DataSignal, Boolean> call(TableColumn<DataSignal, Boolean> p) { return new CheckBoxTableCell<DataSignal, Boolean>(); } }); dataTableView.getColumns().setAll(nomSerie, showSerie); dataTableView.setItems(listeDataSeries); graph.setData(dataToPlotObservableList); graph.setCreateSymbols(false); rootContentPane.setLeft(dataTableView); rootContentPane.setRight(graph); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } /** * Listener permettant de mettre à jour le contenu de 'dataToPlotObservableList' utilisé pour l'affichage des séries de données. * En fonction de la propriété de l'instance 'dataSignal', les données contenues dans 'dataSignal.getSeries()' sont ajoutées ou supprimées de 'dataToPlotObservableList'. * @param dataSignal */ private void InstallDataSerieListener(DataSignal dataSignal) { dataSignal.showSerieProperty().addListener((observable, oldValue, newValue) ->{ if (newValue) { dataToPlotObservableList.add(dataSignal.getSeries()); }else { dataToPlotObservableList.remove(dataSignal.getSeries()); } }); } }
Je pense que le problème vient du fait que le programme n'a pas eu le temps de supprimer les valeurs de 'dataToPlotObservableList' qu'il essaie de copier à nouveau du contenu dedans.
J'ai tenté de désactiver temporairement les checkBox le temps que les modifications soient faites, cela ne résout pas le problème, ou en tous cas, je ne m'y prends pas de la bonne façon.
Et du coup je ne suis pas certain d'avoir bien compris comment fonctionnent les listeners.
Lorsqu'une propriété change à plusieurs reprises, les instructions invoquées par le listener sont elles éxécutées en parallèle ou de manière séquentielle ? Etant donné le problème rencontré, j'aurais du coup tendance à dire que c'est de façon concurrente / parallèle... ?
Auriez vous des idées pour éviter ce problème?
Merci d'avance pour votre aide.
Voici le message d'erreur qui s'affiche :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Duplicate series added at javafx.scene.chart.XYChart.lambda$new$550(XYChart.java:133) at javafx.scene.chart.XYChart$$Lambda$97/1456497523.onChanged(Unknown Source) at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164) at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73) at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233) at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482) at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541) at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205) at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:155) at java.util.AbstractList.add(AbstractList.java:108) at application.test.lambda$0(test.java:125) at application.test$$Lambda$108/1667303582.changed(Unknown Source) at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:361) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) at javafx.beans.property.BooleanPropertyBase.fireValueChangedEvent(BooleanPropertyBase.java:103) at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144) at com.sun.javafx.binding.BidirectionalBinding$BidirectionalBooleanBinding.changed(BidirectionalBinding.java:264) at com.sun.javafx.binding.BidirectionalBinding$BidirectionalBooleanBinding.changed(BidirectionalBinding.java:227) at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) at javafx.beans.property.BooleanPropertyBase.fireValueChangedEvent(BooleanPropertyBase.java:103) at javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) at javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:144) at javafx.scene.control.CheckBox.setSelected(CheckBox.java:156) at javafx.scene.control.CheckBox.fire(CheckBox.java:236) at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182) at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96) at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89) at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) at javafx.event.Event.fireEvent(Event.java:198) at javafx.scene.Scene$MouseHandler.process(Scene.java:3758) at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3486) at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762) at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2495) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:350) at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(GlassViewEventHandler.java:385) at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$236/1058259401.get(Unknown Source) at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:404) at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:384) at com.sun.glass.ui.View.handleMouseEvent(View.java:555) at com.sun.glass.ui.View.notifyMouse(View.java:927) Exception in thread "JavaFX Application Thread" java.lang.NullPointerException at javafx.scene.chart.LineChart.layoutPlotChildren(LineChart.java:490) at javafx.scene.chart.XYChart.layoutChartChildren(XYChart.java:731) at javafx.scene.chart.Chart$1.layoutChildren(Chart.java:94) at javafx.scene.Parent.layout(Parent.java:1076) at javafx.scene.Parent.layout(Parent.java:1082) at javafx.scene.Parent.layout(Parent.java:1082) at javafx.scene.Scene.doLayoutPass(Scene.java:552) at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397) at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:314) at com.sun.javafx.tk.Toolkit$$Lambda$224/529040000.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:313) at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:340) at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:525) at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:505) at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$400(QuantumToolkit.java:334) at com.sun.javafx.tk.quantum.QuantumToolkit$$Lambda$41/986843838.run(Unknown Source) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Partager