2015-09-15 9 views
-1

Я пытаюсь создать проект в JavaFX, где - роботы, представленные точками, - перемещаются по холсту на основе некоторого коммуникационного протокола. К настоящему моменту я понял, что после MVC-подхода я должен хранить соответствующие данные (роботы) в объекте типа ObservableList, который контроллер затем может использовать для обновления холста.JavaFX - рисование объекта с изменением свойства (позиции) на холсте

Выполнение метода test() метода Controller.java Я получаю следующую ошибку, которую я не могу решить. Я прокомментировал соответствующую строку в Controller.java - ее удаление также устраняет сообщение об ошибке.

Я понимаю, что этот подход может быть далек от того, что является лучшей практикой. Я был бы очень благодарен за любые намеки на то, как я могу решить эту проблему или лучше использовать Observable, чтобы позволить контроллеру рисовать позицию роботов на холсте после его изменения.

Main.java

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Scene; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Stage; 

import java.io.IOException; 

public class Main extends Application { 

    public Stage primaryStage; 
    private BorderPane rootLayout; 
    private final Model model = new Model(); 

    @Override 
    public void start(Stage primaryStage) throws Exception{ 

     // this.model = new Model(); 
     this.primaryStage = primaryStage; 
     this.primaryStage.setTitle(""); 
     initRootLayout(); 

    } 

    public void initRootLayout(){ 
     try { 

      // Load root layout from fxml file. 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(Main.class.getResource("Overview.fxml")); 
      rootLayout = (BorderPane) loader.load(); 

      // Show the scene containing the root layout. 
      Scene scene = new Scene(rootLayout); 
      primaryStage.setScene(scene); 
      primaryStage.setHeight(500); 
      primaryStage.setWidth(500); 

      // Give the controller access to the main app. 
      Controller controller = loader.getController(); 
      controller.initModel(model); 



      primaryStage.show(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 


    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Model.java

import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 

public class Model { 

    private final ObservableList<Robot> robots = FXCollections.observableArrayList(); 

    public ObservableList<Robot> getRobots(){ return robots;} 

} 

Robot.java

import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.beans.property.SimpleIntegerProperty; 

import java.util.Random; 

public class Robot implements Runnable { 


    private IntegerProperty id = new SimpleIntegerProperty(); 
    public void setID(int id){this.id.set(id);} 
    public int getId(){ return this.id.get();}; 
    private Memory memory; 

    private DoubleProperty positionX = new SimpleDoubleProperty(); 
    public double getPositionX() {return positionX.get();}; 
    public DoubleProperty positionXProperty(){ return positionX;} 

    private DoubleProperty positionY = new SimpleDoubleProperty(); 
    public double getPositionY() {return positionY.get();}; 
    public DoubleProperty positionYProperty(){ return positionY;} 

    public void setPosition (double x, double y){ 

     this.positionX.set(x); 
     this.positionY.set(y); 
     this.memory.setPosition(x,y); 

    } 

    // Constructor 
    public Robot(int id, double x, double y){ 

     this.setID(id); 
     memory = new Memory(); 
     this.setPosition(x, y); 

    } 



    public double[] getPosition(){ 
     double [] ret = new double[2]; 
     ret[0] = this.memory.getPosition()[0]; 
     ret[1] = this.memory.getPosition()[1]; 
     return ret; 
    } 

    public void move (double targetx, double targety){ 
     System.out.println("Agent carrying " + this.getId + " has moved from " + this.getPositionX() + "/" + this.getPositionY() + " to " + targetx + "/" + targety + " ."); 
     this.setPosition(targetx, targety); 


    } 

    public void run(){ 

     while(true){ 
      // the robot gets assigned a new position based on a communication 
      // protocoll used by the Robots which I have removed for convenience 
      Random r = new Random(); 
      double nextX = 0 + (100) * r.nextDouble(); 
      double nextY = 0 + (100) * r.nextDouble(); 
      this.move(nextX, nextY); 

      try{ 
       Thread.sleep(2000); 
      } 
      catch(InterruptedException e){ 
       System.out.println(e); 
      } 
     } 
    } 
} 

Memory.java

public class Memory { 

    double[] position = new double[2]; 
    double[] plast = new double[2]; 

    // constructor 
    public Memory(){ 

     double[] position = new double[2]; 
     double[] plast = new double[2]; 

    } 

    public double[] getPosition(){ 
     return position; 
    } 


    public double[] getLastPosition(){ 
     return plast; 
    } 

    public void setLastPosition(double x, double y){ 
     plast[0] = x; 
     plast[1] = y; 
    } 
    public void setPosition(double x, double y){ 
     position[0] = x; 
     position[1] = y; 
    } 


} 

Controller.java

import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.fxml.FXML; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.paint.Color; 

public class Controller { 

    private Model model; 

    public void initModel(Model model) { 
     this.model = model ; 

     for (Robot robot : model.getRobots()){ 
      robot.positionXProperty().addListener(new ChangeListener<Number>() { 
       @Override 
       public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { 
        System.out.println("Observed change in value X of Robot carrying ID: " + robot.getId()); 
        drawPoint(robot.getPositionX(), robot.getPositionY()); 
        // if I comment out the above line I get no errors 
       } 
      }); 
      robot.positionYProperty().addListener(new ChangeListener<Number>() { 
       @Override 
       public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { 
        System.out.println("Observed change in value Y of Robot carrying ID: " + robot.getId()); 
        drawPoint(robot.getPositionX(), robot.getPositionY()); 
        // if I comment out the above line I get no errors 
       } 
      }); 
     } 
    } 

    @FXML 
    Canvas canvas; 


    public void test(){ 
     GraphicsContext gc =canvas.getGraphicsContext2D(); 
     gc.strokeRect(0,0,100,100); 

     Robot a = new Robot(0,25,25); 
     Robot b = new Robot(1,50,50); 

     model.getRobots().add(a); 
     model.getRobots().add(b); 

     Thread ta = new Thread(a); 
     Thread tb = new Thread(b); 

     initModel(model); 

     System.out.println("Starting threads."); 
     ta.start(); 
     tb.start(); 


    } 

    public void drawPoint(double x, double y){ 
     GraphicsContext gc = canvas.getGraphicsContext2D(); 
     gc.setFill(Color.rgb(255, 0, 0)); 
     gc.fillOval(x, y, 5, 5); 
    } 
} 

Overview.fxml

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.canvas.*?> 
<?import javafx.scene.control.*?> 
<?import java.lang.*?> 
<?import javafx.scene.layout.*?> 

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller"> 
    <top> 
     <Button fx:id="buttonTest" mnemonicParsing="false" onAction="#test" text="test()" BorderPane.alignment="CENTER" /> 
    </top> 
    <center> 
     <Canvas fx:id="canvas" height="200.0" width="200.0" BorderPane.alignment="CENTER" /> 
    </center> 
</BorderPane> 

Ошибка:

java.lang.InternalError: Unrecognized PGCanvas token: -96 
    at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:1146) 
    at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:595) 
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067) 
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959) 
    at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235) 
    at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576) 
    at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067) 
    at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959) 
    at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:474) 
    at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:327) 
    at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) 
    at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125) 
    at java.lang.Thread.run(Thread.java:745) 
+0

Если вы спрашиваете о коде, который генерирует ошибки во время выполнения, вы должны опубликовать пример, который фактически компилируется. В этом есть несколько ошибок компиляции. Чтобы создать [MCVE], вы должны фактически создать код, запустите ti, чтобы убедиться, что он демонстрирует проблемы, которые вы адресуете, а затем скопируйте код прямо здесь. Это не код, который вы выполнили. Я также рекомендую вам следовать [Шаблон свойств Java FX] (http://www.oracle.com/pls/topic/lookup?ctx=javase80&id=JFXBD107), так как вы используете свойства JavaFX. –

+0

Уважаемый James_D, благодарю вас за ответ. Я ценю ваш вклад и сожалею, что не дважды проверял код, который я представил. Я исправил все ошибки компиляции, поэтому теперь код должен быть запущен. Сообщение об ошибке идентично сообщению, которое я ранее подавал. Я попробовал следовать шаблону _Java FX Properties_ в классе Robot, как вы предложили. – Hips

ответ

0

Поскольку вы наблюдаете координаты роботов, и изменения пользовательского интерфейса в ответ, вы должны изменить координаты в потоке приложения FX, чтобы соблюдать правила однопоточности JavaFX. (Возможно, вы могли предположить, что слушатели планируют внести изменения в пользовательский интерфейс в потоке приложения FX, но тогда вам придется беспокоиться о синхронизации по координатам, поскольку они будут доступны из двух разных потоков.)

Итак, ваш метод run() должен выглядеть

общественного ничтожного пробега() {

while(true){ 
     // the robot gets assigned a new position based on a communication 
     // protocoll used by the Robots which I have removed for convenience 

     Platform.runLater(() -> { 

      Random r = new Random(); 
      double nextX = 0 + (100) * r.nextDouble(); 
      double nextY = 0 + (100) * r.nextDouble(); 
      this.move(nextX, nextY); 

     }); 

     try{ 
      Thread.sleep(2000); 
     } 
     catch(InterruptedException e){ 
      System.out.println(e); 
     } 
    } 
} 

Это устраняет непосредственную проблему; вы можете обнаружить, что есть другие.

Смежные вопросы