如何在线程内部使用矩形更新窗格?

huangapple go评论78阅读模式
英文:

How to update a Pane with rectangles on from within a thread?

问题

以下是你提供的代码部分的翻译:

package application;

import SortingTools.ShuttleSort;
import SortingTools.Simulation;
import SortingTools.Sorter;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;

public class Controller {
    
    Simulation sim;
    Sorter sorter;
    boolean ready;
    
    @FXML
    public void initialize() {
        ready = false;
    }
    
    @FXML
    Pane world;
    
    @FXML
    Button playButton;
    
    @FXML
    public void playButtonClicked() {
        if(ready) {
            System.out.println("Play button Clicked");
            this.sorter = new ShuttleSort(sim);
            sorter.sort(sim.getNumbers());
            /*sorter.start();
            try {
                sorter.join();
                System.out.println("Ended");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
            ready = false;
        } else {
            System.out.println("Click reset to load the data set before you click play!");
        }
        
    }
    
    @FXML
    Button pauseButton;
    
    @FXML
    public void pauseButtonClicked() {
        System.out.println("Pause button Clicked");
    }
    
    @FXML
    Button resetButton;
    
    @FXML
    public void resetButtonClicked() {
        System.out.println("Reset button Clicked");
        world.getChildren().clear();
        this.sim = new Simulation(world, 50);
        ready = true;
        
        //Rectangle r = new Rectangle(0, 0, 50, 350);
        //world.getChildren().add(r);
    }
    
    @FXML
    Button stepButton;
    
    @FXML
    public void stepButtonClicked() {
        System.out.println("Step button Clicked");
    }
}

package SortingTools;

import java.util.ArrayList;
import java.util.Random;

import javafx.scene.layout.Pane;

public class Simulation {
    private ArrayList<Number> numbers;
    public static final int MAX_NUM = 30;
    
    public Simulation(Pane world, int popSize) {
        numbers = new ArrayList<Number>();
        Random random = new Random();
        for(int i=0; i<popSize; i++) {
            numbers.add(new Number((random.nextInt(MAX_NUM-1)+1), i, popSize, world));
        }
    }

    public ArrayList<Number> getNumbers() {
        return numbers;
    }
    
    public void update() {
        for(Number num: numbers) {
            num.update();
        }
    }
}
package SortingTools;

import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

public class Number {
    private int size;
    private int position;
    public Pane world;
    private Rectangle r;
    private int rectWidth;
    
    public Number(int size, int position, int popSize, Pane world) {
        this.size = size;
        this.position = position;
        this.world = world;
        int rectWidth = (int)(world.getWidth()/popSize);
        this.rectWidth = rectWidth;
        int rectHeight = (int)((world.getHeight()/Simulation.MAX_NUM)*this.size);
        int rectY = (int) (world.getHeight()-rectHeight);
        int rectX = (int)(this.position*rectWidth);
        this.r = new Rectangle(rectX, rectY, rectWidth, rectHeight);
        r.setStroke(Color.BLACK);
        r.setFill(Color.WHITE);
        world.getChildren().add(r);
    }

    public int getSize() {
        return size;
    }

    public int getPosition() {
        return position;
    }
    
    public void setPosition(int newPosition) {
        this.position = newPosition;
    }
    
    public void update() {
        System.out.println(this.r.getX());
        this.r.setX(this.position*this.rectWidth);
        System.out.println(this.r.getX());
    }

    public Rectangle getR() {
        return r;
    }
}
package SortingTools;

import java.util.ArrayList;

import javafx.application.Platform;

public class Sorter {
    ArrayList<Number> numbers;
    Simulation sim;
    
    public Sorter(Simulation sim) {
        this.sim = sim;
        this.numbers = sim.getNumbers();
    }
    
    public abstract void sort(ArrayList<Number> numbers);
    
    public void swap(ArrayList<Number> numbers, int firstIndex, int secondIndex) {
        int firstNumber = numbers.get(firstIndex).getPosition();
        int secondNumber = numbers.get(secondIndex).getPosition();
        int holder = firstNumber;
        firstNumber = secondNumber;
        secondNumber = holder;
        numbers.get(firstIndex).setPosition(firstNumber);
        numbers.get(secondIndex).setPosition(secondNumber);
        Collections.swap(numbers, firstIndex, secondIndex);
    }
}
package SortingTools;

import java.util.ArrayList;

public class ShuttleSort extends Sorter {
    
    public ShuttleSort(Simulation sim) {
        super(sim);
    }

    public void sort(ArrayList<Number> numbers) {
        for(int i=0; i<numbers.size(); i++) {
            System.out.println("Pass " + i);
            for(int j=numbers.size()-1; j>i; j--) {
                System.out.println("j " + j);
                if(numbers.get(j).getSize() < numbers.get(j-1).getSize()) {
                    System.out.println(numbers.get(j).getSize());    
                    swap(numbers, j, j-1);
                    sim.update();
                }
            }
        }
        
        for(Number num: numbers) {
            System.out.println(num.getSize() + " " + num.getPosition());
        }
    }
}

请注意,这只是你提供的代码的翻译部分,不包括任何额外的内容或解释。如果你有其他问题或需要进一步帮助,请随时提问。

英文:

So I am making a sorting algorithm visualiser to practice coding and to get more comfortable with using a gui in my programs. I am using javafx for the gui as opposed to swing.

My program works like this. For the gui I have a Main class and a Controller class and an fxml stylesheet which I made in scene builder. The main is basically the template that comes with all javafx projects and the Controller ofcourse has the logic behind the gui such as what happens when you click the buttons. It is also where the simulation and sorting algorithm is initialised and launched from. The pane is where the algorithm is actually visualised using rectangles of varying heights to represent a numbers magnitude. So far I have only implemented a selection sort just so I could test that the sort would work. The sort works fine and behaves as expected but it does not visualise correctly.

When I start the program and generate the data to be sorted it shows up fine as rectangles in the correct position, width and height. What the GUI looks like after I generate the numbers. Also when the algorithm finishes and the numbers are in the correct place if I call sim.update() the rectangles update to show there sorted positions correctly on the pane. However it does not show the steps between these(even if I call sim.update() at the end of a pass or comparison). It's as if I press start, the algorithm runs, and then the gui updates to show them all in order. I want to see them swapping as the algorithm works.

I have looked around for the answer and the mention of Platform.runLater() looked like it may be helpful but I didn't really understand that as I had to pass a runnable into it and had no thread to pass it.

Below I will show some code and explain how it works and where I need help.


import SortingTools.ShuttleSort;
import SortingTools.Simulation;
import SortingTools.Sorter;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
public class Controller {
Simulation sim;
Sorter sorter;
boolean ready;
@FXML
public void initialize() {
ready = false;
}
@FXML
Pane world;
@FXML
Button playButton;
@FXML
public void playButtonClicked() {
if(ready) {
System.out.println(&quot;Play button Clicked&quot;);
this.sorter = new ShuttleSort(sim);
sorter.sort(sim.getNumbers());
/*sorter.start();
try {
sorter.join();
System.out.println(&quot;Ended&quot;);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
ready = false;
} else {
System.out.println(&quot;Click reset to load the data set before you click play!&quot;);
}
}
@FXML
Button pauseButton;
@FXML
public void pauseButtonClicked() {
System.out.println(&quot;Pause button Clicked&quot;);
}
@FXML
Button resetButton;
@FXML
public void resetButtonClicked() {
System.out.println(&quot;Reset button Clicked&quot;);
world.getChildren().clear();
this.sim = new Simulation(world, 50);
ready = true;
//Rectangle r = new Rectangle(0, 0, 50, 350);
//world.getChildren().add(r);
}
@FXML
Button stepButton;
@FXML
public void stepButtonClicked() {
System.out.println(&quot;Step button Clicked&quot;);
}
}

So I start up my program and there are the buttons and a blank pane. When I click reset I generate a new simulation object. When you click play a sorter is created and it sorts the ArrayList of numbers within the simulation. Ignore the commented code for now this was left out on purpose and will be explained later.


import java.util.ArrayList;
import java.util.Random;
import javafx.scene.layout.Pane;
public class Simulation {
private ArrayList&lt;Number&gt; numbers;
public static final int MAX_NUM = 30;
public Simulation(Pane world, int popSize) {
numbers = new ArrayList&lt;Number&gt;();
Random random = new Random();
for(int i=0; i&lt;popSize; i++) {
numbers.add(new Number((random.nextInt(MAX_NUM-1)+1), i, popSize, world));
}
}
public ArrayList&lt;Number&gt; getNumbers() {
return numbers;
}
//Loops threw wall the numbers and updates them due to there positions
public void update() {
for(Number num: numbers) {
num.update();
}
}
}

This is my simulation class. Here is where the ArrayList of Numbers are created. It is also where the update method is which is what is meant to update the x position of the rectangles due to their current position(this will be shown in the Number class).


import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Number {
private int size;
private int position;
public Pane world;
private Rectangle r;
private int rectWidth;
public Number(int size, int position, int popSize, Pane world) {
this.size = size;
this.position = position;
this.world = world;
int rectWidth = (int)(world.getWidth()/popSize);
this.rectWidth = rectWidth;
int rectHeight = (int)((world.getHeight()/Simulation.MAX_NUM)*this.size);
int rectY = (int) (world.getHeight()-rectHeight);
int rectX = (int)(this.position*rectWidth);
this.r = new Rectangle(rectX, rectY, rectWidth, rectHeight);
r.setStroke(Color.BLACK);
r.setFill(Color.WHITE);
world.getChildren().add(r);
}
public int getSize() {
return size;
}
public int getPosition() {
return position;
}
public void setPosition(int newPosition) {
this.position = newPosition;
}
//Relocates the rectangle due to its current position
public void update() {
System.out.println(this.r.getX());
this.r.setX(this.position*this.rectWidth);
System.out.println(this.r.getX());
}
public Rectangle getR() {
return r;
}
}

This is the Number class. Every number class has a rectangle. In the constructor you can see I add it to the world(which is the Pane) and this is when it gets drawn for the first time. You can also see an update method. When sim.update() is called it loops through all the numbers and calls their update methods which calculates a new x position for the rectangle due to their position.


import java.util.ArrayList;
import java.util.Collections;
public abstract class Sorter /*extends Thread*/{
ArrayList&lt;Number&gt; numbers;
Simulation sim;
public Sorter(Simulation sim) {
this.sim = sim;
this.numbers = sim.getNumbers();
}
/*public void run() {
System.out.println(&quot;Thread running&quot;);
sort(numbers);
}*/
//Sorts the ArrayList
public abstract void sort(ArrayList&lt;Number&gt; numbers);
//Swaps two numbers in the ArrayList and updates there position variables to reflect the swap
public void swap(ArrayList&lt;Number&gt; numbers, int firstIndex, int secondIndex) {
int firstNumber = numbers.get(firstIndex).getPosition();
int secondNumber = numbers.get(secondIndex).getPosition();
int holder = firstNumber;
firstNumber = secondNumber;
secondNumber = holder;
numbers.get(firstIndex).setPosition(firstNumber);
numbers.get(secondIndex).setPosition(secondNumber);
Collections.swap(numbers, firstIndex, secondIndex);
}
}

This is the sorter class. All the sorters I plan to make will extend this class. You will notice some commented out parts(as mentioned above). This was me trying to run the sorting class as a thread so I could do this.sleep() after I updated the rectangles position so people could actually see the algorithm working.


import java.util.ArrayList;
import javafx.application.Platform;
public class ShuttleSort extends Sorter{
public ShuttleSort(Simulation sim) {
super(sim);
}
public void sort(ArrayList&lt;Number&gt; numbers) {
for(int i=0; i&lt;numbers.size(); i++) {
System.out.println(&quot;Pass &quot; + i);
for(int j=numbers.size()-1; j&gt;i; j--) {
System.out.println(&quot;j &quot; + j);
if(numbers.get(j).getSize() &lt; numbers.get(j-1).getSize()) {
System.out.println(numbers.get(j).getSize());	
swap(numbers, j, j-1);
sim.update();
}
}
/*try {
this.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
for(Number num: numbers) {
System.out.println(num.getSize() + &quot; &quot; + num.getPosition());
}
}
}

Finally this is the Selection Sort I made. The actual logic of this works fine. You can see a sim.update() at the end of each comparison. The rectangles x is actually being updated so the sim.update() is working fine however the rectangles on the screen aren't actually changing position. However it seems as if the final sim.update() is being reflected on the screen. Another reason I also tried to run the sorter class as a thread is so that I could do thread.sleep() after each sim.update() as I thought maybe the algorithm was so quick that the gui didn't have time to update however the same thing happened. The algorithm ran(I could see this as the console was outputting all the println()) and the very last sim.update() from within the loop caused the finished product on the gui.

Any help and suggestions would be appreciated. I think running it in the thread is probably the way to go but for some reason no matter what I do sim.update() isn't being reflected on the screen until the very last one is called. Thank you.

答案1

得分: 1

你的问题的答案比问题本身要简单得多。你肯定是在阻塞JavaFX平台线程,前面提到的Platform.runLater()是一种可能的解决方案。你应该在单独的线程上运行排序,在每次迭代中通过Platform.runLater调用或其他更复杂的JavaFX并发编程技术来更新GUI。

英文:

The answer to your question is much more simple than the question itself. You are definitely blocking the JavaFX platform thread and the already mentioned Platform.runLater() is one possible solution. You should run your sorting on a separate thread and in each interation update the GUI within a Platform.runLater call or other more sophisticated concurrent programming techniques of JavaFX.

huangapple
  • 本文由 发表于 2020年7月26日 02:32:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/63092071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定