首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在JavaFX SceneGraph上工作

在JavaFX SceneGraph上工作
EN

Stack Overflow用户
提问于 2015-07-24 08:20:04
回答 1查看 244关注 0票数 2

我创建了一个要点,在GraphGymnastic中描述了我的问题。这个问题在很大程度上是用Thread.sleep()抽象出来的,但它起到了一定的作用。

问题描述

当一个事件被过滤(使用EventFilters)时,我想要遍历这个场景。当事件到达时,我想在图中的所有节点上计算一些内容。对于2个节点,这将在Platform.runLater()中很好地工作,但是稍后会有n个节点,计算可能需要一些时间。我不希望在计算过程中FX被阻塞。

所以我们考虑将计算委托给第二个线程。说完了。现在我们面临的问题是,我们在第二个线程上进行计算,但是这个线程保存了对图的引用,该图可以同时更改(我们同意,这在大多数情况下不会发生,而是需要考虑)。所以说,我们依靠的是“动态视图”。该怎么办?复制图表?然后,我们在开始时,阻塞UI线程复制图形。

这是一个纯粹的设计简约,我很感激听到有人已经做了这样的事情,如果有其他的想法或方法,如果有,如何解决这个优雅,而不是一些建筑工地。

谢谢任何能帮忙的人。

PS:在要点中,您必须取消注释,用我的评论注释这两行,以查看问题(按下按钮后,尝试移动窗口)。

编辑:精益求精,这是代码。

代码语言:javascript
复制
public class GraphGymnastic extends Application {
  final ExecutorService serv = Executors.newFixedThreadPool(2);
  public static void main(String argv[]) {
    launch(argv);
  }

  @Override public void start(Stage primaryStage) throws Exception {
    //Setup UI
    primaryStage.setTitle("Demo");
    final List<Node> nodesInGraph = new ArrayList<>();
    final FlowPane p = new FlowPane() {{
      setId("flowpane");
      getChildren().addAll(new Label("Label") {{
        setId("label");
      }}, new Button("Button") {{
        setId("button");
        // setOnMouseClicked(event -> handle(event, nodesInGraph)); //Uncomment and comment below to see effects!
        setOnMouseClicked(event -> handleAsync(event, nodesInGraph));
      }});
      setHgap(5);
    }};

    //Assume that this goes recursive and deep into a scene graph but still
    // returns a list
    //Here it takes the two childs for simplicity
    nodesInGraph.addAll(p.getChildrenUnmodifiable());

   //Show stage
    primaryStage.setScene(new Scene(p));
    primaryStage.show();
  }

  public void handle(MouseEvent ev, List<Node> nodesInGraph) {
    if (null != nodesInGraph)
      Platform.runLater(() -> nodesInGraph.forEach(node -> {
        //This will block the UI thread, so there is the need for a second
        //thread
        System.out.println(
            "Calculating heavy on node " + node.toString() + " with event from "
                + ev.getSource().toString());
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }));
  }

  public void handleAsync(MouseEvent ev, List<Node> nodesInGraph) {
    if (null != nodesInGraph)
      serv.submit(() -> nodesInGraph.forEach(node -> {
        //Now there is a second thread but it works on a LIVE view object
        // list, which is ugly
        //Option 1: Keep it like this, drawbacks? :S
        //Option 2: Copy the graph, costs performance... How deep should it
        // copy? :S
        System.out.println(
            "Calculating heavy on node " + node.toString() + " with event from "
                + ev.getSource().toString());
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }));
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-07-24 12:06:51

不是并发专家,所以这只是为了说明我的评论:提取fx线程上的某个状态,然后将该状态传递给另一个线程进行处理。

配料:

  • 节点的活动列表
  • 一个状态对象,它可以携带节点属性的快照(在询问时),然后对其执行一些冗长的处理。
  • 一个任务(截至fx并发):在后台线程中,它创建一个状态对象,切换到fx线程,复制相关状态,切换回背景,开始处理并在准备就绪时设置其值。
  • 侦听任务值的处理程序

守则:

代码语言:javascript
复制
// the worker
public static class SceneGraphWorker extends Task<NodeState> {

    private List<Node> nodes;
    private int current;

    public SceneGraphWorker(List<Node> nodes) {
        this.nodes = nodes;
    }

    @Override
    protected NodeState call() throws Exception {
        while (current >= 0) {
            NodeState state = new NodeState(current);
            CountDownLatch latch = new CountDownLatch(1);
            Platform.runLater(() -> {
                Node node = nodes.get(current);
                Bounds bounds = node.localToScene(node.getBoundsInLocal());
                state.setState(bounds.getMinX(), bounds.getMinY());
                state.setID(node.getId());
                current = current == nodes.size() - 1 ? -1 : current + 1;
                latch.countDown(); 
            });
            latch.await();
            state.process();
            updateValue(state);
        }
        return null;
    }

}

// the handler: listens to value
public void handleWithWorker(MouseEvent ev, List<Node> nodesInGraph) {
    Task worker = new SceneGraphWorker(nodesInGraph);
    worker.valueProperty().addListener((src, ov, nv) -> {
        progress.setText(nv != null ? nv.toString() : "empty");
    });
    new Thread(worker).start();
}

// the state object
public static class NodeState {
    double x, y;
    int index;
    private String name;
    public NodeState(int index) {
        this.index = index;
    }
    public void setState(double x, double y) {
        this.x = x;
        this.y = y;
    }
    public void setID(String name) {
        this.name = name;
    }
    public void process() throws InterruptedException {
        Thread.sleep(2000);
    }
    @Override
    public String toString() {
        return "[" + name + " x: " + x + " y: " + y + "]";
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31605531

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档