Я хочу показать график графового потока на панели Javafx StackPane. Я знаю, что в сети и на этой странице есть похожие вопросы, но я получаю другой результат и ошибку.
Мой код находится в следующей функции:
void cargaGrafico() {
SingleGraph graph = aula.getSociogramas().get(currentSocio).getRelaPos().getGraph();
FxViewer v = new FxViewer(graph,FxViewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
v.enableAutoLayout();
FxViewPanel panel = (FxViewPanel)v.addDefaultView(false, new FxGraphRenderer());
spGraficos.getChildren().add(panel);
}
где spGraficos — это StackPane javafx. Когда я запускаю код, я получаю на панели очень маленький график, не интерактивный, и кажется, что график выполняется в другом потоке, потому что я получаю следующее исключение:
Exception in thread "Thread-2" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-2
at javafx.graphics@22-ea/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:294)
at javafx.graphics@22-ea/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:475)
at javafx.graphics@22-ea/javafx.animation.Animation.play(Animation.java:990)
at [email protected]/org.graphstream.ui.fx_viewer.FxViewer.lambda$init$1(FxViewer.java:220)
at java.base/java.lang.Thread.run(Thread.java:1570)e here
Я также искал список рассылки Graphstream, но, похоже, его уже нет в живых. Заранее спасибо!
Это минимальный воспроизводимый пример: В Intellij Idea я начинаю новый проект javafx, только с кнопкой и панелью привязки. Когда я нажимаю кнопку, граф графового потока заполняется, и его следует поместить в панель привязки. Результат тот же, маленький график и то же исключение. Код: ПриветПриложение.java
package com.probags.probags;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 820, 950);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
System.setProperty("org.graphstream.ui", "javafx");
launch();
}
}
Приветконтроллер.java
package com.probags.probags;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.ui.fx_viewer.FxViewPanel;
import org.graphstream.ui.fx_viewer.FxViewer;
import org.graphstream.ui.javafx.FxGraphRenderer;
public class HelloController {
@FXML
private AnchorPane apGraph;
@FXML
private Label welcomeText;
private Graph graph = new SingleGraph("Test");
@FXML
void onHelloButtonClick(ActionEvent event) {
populateGraph();
FxViewer v = new FxViewer(graph,FxViewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
v.enableAutoLayout();
FxViewPanel panel = (FxViewPanel)v.addDefaultView(false, new FxGraphRenderer());
apGraph.getChildren().add(panel);
}
void populateGraph() {
graph.addNode("A" );
graph.addNode("B" );
graph.addNode("C" );
graph.addEdge("AB", "A", "B");
graph.addEdge("BC", "B", "C");
graph.addEdge("CA", "C", "A");
}
}
модуль-info.java
module com.probags.probags {
requires javafx.controls;
requires javafx.fxml;
requires gs.core;
requires gs.ui.javafx;
opens com.probags.probags to javafx.fxml;
exports com.probags.probags;
}
Я использую gs.core и gs.ui.javafx 2.0, включенные в структуру проекта Intellij Idea от Maven.
Я не знаю граф-стрима, но у вас есть ThreadingModel.GRAPH_IN_GUI_THREAD
. И трассировка стека показывает, что вы не находитесь в потоке графического интерфейса. Чтобы запустить код в потоке графического интерфейса (JavaFX), используйте Platform.runLater. Поскольку графический поток, похоже, полагается на некоторый код Swing для некоторых операций, я не знаю, означает ли контекст этого, что поток графического интерфейса должен быть потоком JavaFX или потоком AWT. Если у вас по-прежнему возникают проблемы, предоставьте минимальный воспроизводимый пример.
Я попробовал использовать образец учебного приложения GraphStream JavaFX, и он у меня сработал нормально (JavaFX 22.0.2, Java 21, OS X 14.5 — Intel). Я использовал модель потоков по умолчанию из примера кода FxViewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD
.
Я попробовал создать проект по инструкции https://github.com/graphstream/gs-ui-javafx/tree/master.
Используемый значок был взят отсюда:
Код диаграммы был взят и изменен отсюда:
Изменения были незначительными: они касались загрузки ресурсов для значка, чтобы он работал в модульной среде, очистки для использования lamdas, удаления неиспользуемого кода и установки документированного системного свойства для маршрутизации графического потока через JavaFX.
Информацию об авторских правах смотрите в исходной ссылке.
Пример кода выводит 4 графика, здесь я показываю только один. Это довольно уродливый результат. Я не уверен, так ли это должно выглядеть, но именно это он и создал.
package org.example.graphstream;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.MultiGraph;
import org.graphstream.ui.fx_viewer.FxDefaultView;
import org.graphstream.ui.fx_viewer.FxViewer;
import org.graphstream.ui.view.Viewer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.stage.Stage;
import java.util.Objects;
public class TutorialDiagrams extends Application {
public static final String URL_IMAGE = Objects.requireNonNull(TutorialDiagrams.class.getResource("/org/graphstream/ui/viewer_fx/test/data/icon.png")).toString();
public static void main(String[] args) {
System.setProperty("org.graphstream.ui", "javafx");
launch(args);
}
public void start(Stage primaryStage) {
Scene scene1 = diagram1("diagram1", styleSheet1);
Scene scene2 = diagram1b("diagram1b", styleSheet1);
Scene scene3 = diagram2("diagram2", styleSheet1);
Scene scene4 = diagram3("diagram3", styleSheet2);
primaryStage.setScene(scene1);
primaryStage.setOnCloseRequest(t -> Platform.exit());
primaryStage.show();
Stage stage2 = new Stage();
stage2.setX(primaryStage.getX() + primaryStage.getWidth());
stage2.setY(primaryStage.getY());
stage2.setScene(scene2);
stage2.setOnCloseRequest(t -> Platform.exit());
stage2.show();
Stage stage3 = new Stage();
stage3.setX(primaryStage.getX());
stage3.setY(primaryStage.getY() + primaryStage.getY());
stage3.setScene(scene3);
stage3.setOnCloseRequest(t -> Platform.exit());
stage3.show();
Stage stage4 = new Stage();
stage4.setX(primaryStage.getX() + primaryStage.getWidth());
stage4.setY(primaryStage.getY() + primaryStage.getY());
stage4.setScene(scene4);
stage4.setOnCloseRequest(t -> Platform.exit());
stage4.show();
}
public Scene diagram(Graph graph, String style, String title, int width, int height) {
graph.setAttribute("ui.quality");
graph.setAttribute("ui.antialias");
graph.setAttribute("ui.stylesheet", style);
graph.setAttribute("ui.screenshot", title+".png");
Viewer viewer = new FxViewer( graph, FxViewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD );
FxDefaultView view = (FxDefaultView) viewer.addDefaultView(true);
view.resize(width, height);
return new Scene(view, width, height, true, SceneAntialiasing.DISABLED);
}
public Scene diagram1(String title, String styleSheet) {
MultiGraph graph = new MultiGraph(title);
Scene s = diagram(graph, styleSheet, title, 500, 250);
Node G = graph.addNode("Graph");
Node V = graph.addNode("Viewer");
Edge E = graph.addEdge("G->V", "Graph", "Viewer", true);
G.setAttribute("xyz", new double[] {0, 0, 0});
V.setAttribute("xyz", new double[] {1, 0, 0});
G.setAttribute("ui.label", "Graph");
V.setAttribute("ui.label", "Viewer");
return s ;
}
public Scene diagram1b(String title, String styleSheet) {
MultiGraph graph = new MultiGraph(title);
Scene s = diagram(graph, styleSheet, title, 500, 370);
Node G = graph.addNode("Graph");
Node V = graph.addNode("Viewer");
Node B1 = graph.addNode("bidon1");
Node B2 = graph.addNode("bidon2");
graph.addEdge("G->bidon1", "Graph", "bidon1", true);
graph.addEdge("bidon1->V", "bidon1", "Viewer", true);
graph.addEdge("V->bidon2", "Viewer", "bidon2", true);
graph.addEdge("bidon2->G", "bidon2", "Graph", true);
G.setAttribute("xyz", new double[]{0, 0, 0});
B1.setAttribute("xyz", new double[]{0, 0.5, 0});
V.setAttribute("xyz", new double[]{1, 0.5, 0});
B2.setAttribute("xyz", new double[]{1, 0, 0});
G.setAttribute("ui.label", "Graph");
V.setAttribute("ui.label", "Viewer");
B1.setAttribute("ui.class", "invisible");
B2.setAttribute("ui.class", "invisible");
return s;
}
public Scene diagram2(String title, String styleSheet) {
MultiGraph graph = new MultiGraph(title);
Scene s = diagram(graph, styleSheet, title, 500, 250);
Node G = graph.addNode("Graph");
Node P = graph.addNode("Pipe");
Node V = graph.addNode("Viewer");
graph.addEdge("G->P", "Graph", "Pipe", true);
graph.addEdge("P->V", "Pipe", "Viewer", true);
G.setAttribute("xyz", new double[] {0, 0, 0});
P.setAttribute("xyz", new double[] {1, 0, 0});
V.setAttribute("xyz", new double[] {2, 0, 0});
G.setAttribute("ui.label", "Graph");
P.setAttribute("ui.label", "Pipe");
V.setAttribute("ui.label", "Viewer");
return s;
}
public Scene diagram3(String title, String styleSheet) {
MultiGraph graph = new MultiGraph(title);
Scene s = diagram(graph, styleSheet, title, 800, 500);
Node G = graph.addNode("Graph");
Node V = graph.addNode("Viewer");
Node P1 = graph.addNode("GtoV");
Node P2 = graph.addNode("VtoG");
graph.addEdge("G->GtoV", "Graph", "GtoV", true);
graph.addEdge("GtoV->V", "GtoV", "Viewer", true);
graph.addEdge("VtoG<-V", "Viewer", "VtoG", true);
graph.addEdge("G<-VtoG", "VtoG", "Graph", true);
G.setAttribute("ui.label", "Graph");
P1.setAttribute("ui.label", "Pipe");
P2.setAttribute("ui.label", "ViewerPipe");
V.setAttribute("ui.label", "Viewer");
G.setAttribute("xyz", new double[] {-2, 0, 0});
P1.setAttribute("xyz", new double[] {-1, 1.4, 0});
P2.setAttribute("xyz", new double[] { 1, -1.4, 0});
V.setAttribute("xyz", new double[] { 2, 0, 0});
return s;
}
public String styleSheet1 =
"graph {"+
" padding: 90px;"+
"}"+
"node {"+
" size: 128px;"+
" shape: box;"+
" fill-mode: image-scaled;"+
" fill-image: url('"+URL_IMAGE+"');"+
" text-alignment: under;"+
" text-color: #DDD;"+
" text-background-mode: rounded-box;"+
" text-background-color: #333;"+
" text-padding: 4px;"+
"}"+
"node#Pipe {"+
" fill-image: url('"+URL_IMAGE+"');"+
"}"+
"node#Viewer {"+
" fill-image: url('"+URL_IMAGE+"');"+
"}"+
"node.invisible {"+
" fill-mode: plain;"+
" fill-color: #0000;"+
"}"+
"edge {"+
" size: 4px;"+
" fill-color: #979797;"+
" arrow-shape: none;"+
"}";
public String styleSheet2 =
"graph {"+
" padding: 90px;"+
"}"+
"node {"+
" size: 128px;"+
" shape: box;"+
" fill-mode: image-scaled;"+
" fill-image: url('"+URL_IMAGE+"');"+
" text-alignment: under;"+
" text-color: #DDD;"+
" text-background-mode: rounded-box;"+
" text-background-color: #333;"+
" text-padding: 4px;"+
"}"+
"node#Graph {"+
" fill-image: url('"+URL_IMAGE+"');"+
"}"+
"node#Viewer {"+
" fill-image: url('"+URL_IMAGE+"');"+
"}"+
"node#VtoG {"+
" fill-image: url('"+URL_IMAGE+"');"+
"}"+
"edge {"+
" size: 4px;"+
" fill-color: #979797;"+
" shape: L-square-line;"+
" arrow-size: 25px, 10px;"+
" arrow-shape: none;"+
"}";
}
модуль-info.java
module org.example.graphstream {
requires javafx.controls;
requires javafx.swing;
requires gs.core;
requires gs.ui.javafx;
exports org.example.graphstream;
}
pom.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>graph-stream</artifactId>
<version>1.0-SNAPSHOT</version>
<name>graph-stream</name>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>22.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>22.0.2</version>
</dependency>
<dependency>
<groupId>org.graphstream</groupId>
<artifactId>gs-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.graphstream</groupId>
<artifactId>gs-ui-javafx</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom.xml
То же, что и выше, просто добавьте следующую зависимость.
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>22.0.2</version>
</dependency>
привет-view.fxml
<?xml version = "1.0" encoding = "UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<VBox fx:id = "apGraph" xmlns = "http://javafx.com/javafx/19" xmlns:fx = "http://javafx.com/fxml/1"
fx:controller = "org.example.graphstream.HelloController">
<Button mnemonicParsing = "false" text = "Show Graph" onAction = "#onHelloButtonClick"/>
</VBox>
модуль.информация
module org.example.graphstream {
requires javafx.controls;
requires javafx.swing;
requires javafx.fxml;
requires gs.core;
requires gs.ui.javafx;
opens org.example.graphstream to javafx.fxml;
exports org.example.graphstream;
}
ПриветПриложение.java
package org.example.graphstream;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
stage.setScene(
new Scene(fxmlLoader.load(), 820, 950)
);
stage.show();
}
public static void main(String[] args) {
System.setProperty("org.graphstream.ui", "javafx");
launch();
}
}
Приветконтроллер.java
package org.example.graphstream;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.layout.VBox;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.ui.fx_viewer.FxViewPanel;
import org.graphstream.ui.fx_viewer.FxViewer;
import org.graphstream.ui.javafx.FxGraphRenderer;
public class HelloController {
@FXML
private VBox apGraph;
private Graph graph = new SingleGraph("Test");
@FXML
void onHelloButtonClick(ActionEvent event) {
populateGraph();
FxViewer v = new FxViewer(graph,FxViewer.ThreadingModel.GRAPH_IN_GUI_THREAD);
v.enableAutoLayout();
FxViewPanel panel = (FxViewPanel)v.addDefaultView(false, new FxGraphRenderer());
apGraph.getChildren().add(panel);
}
void populateGraph() {
graph.addNode("A" );
graph.addNode("B" );
graph.addNode("C" );
graph.addEdge("AB", "A", "B");
graph.addEdge("BC", "B", "C");
graph.addEdge("CA", "C", "A");
}
}
Что касается вашего кода, вы уверены, что он относится к последней версии графстрима? -> Используемая версия находится в pom.xml. Это версия, рекомендованная в настоящее время в файле сведений gs-ui-javafx , то есть версия 2.0 для gs-ui-javafx и gs-core. Код в ответе основан на коде учебника в репозитории Graphstream, большая его часть — это не мой код, а код из Graphstream. Вы можете запустить код, он работает и не имеет исключений потоков. Если в вашем коде по-прежнему есть исключения, отредактируйте вопрос, чтобы предоставить запрошенный минимальный воспроизводимый пример.
Спасибо за ответ!. Я отредактировал вопрос, добавив минимальный воспроизводимый пример.
@SerinusSerinus Я не могу повторить вашу проблему. Я добавил код, который пробовал, в раздел «Пример стиля FXML».
После долгих усилий над своим кодом мне удалось воспроизвести простую версию без исключений. Я понял, что проблема связана с библиотеками javafx, которые я использовал. Я использовал версию 22-ea+16 от Maven. Я вручную импортировал версию 22.0.2 из сети Oracle, и все работает нормально. Спасибо Jewelsea за то, что подсказали мне несколько интересных предложений.
ea
версии являются нестабильными версиями раннего доступа. Вероятно, вы столкнулись с ошибкой в версии раннего доступа, которая, к счастью, была исправлена в релизной версии.
Я могу запустить свой код, не думаю, что это проблема с библиотеками или конфигурацией. Единственный вопрос связан с потоком, в котором работает граф. Даже если я использую ThreadingModel.GRAPH_IN_ANOTHER_THREAD, я получаю то же исключение. Что касается вашего кода, вы уверены, что это последняя версия графстрима? В последней версии Viewer стал абстрактным классом. Я использую gs-core и gs-ui-javafx 2.0 (без предварительной альфа-версии). В любом случае, я не думаю, что это главное. Спасибо в любом случае!