How to simulate a queue and JavaFX stack with pause, threads (task, service)

I searched on YouTube, Stack-Overflow and fxexperience, even oracle documentation , but I still don't understand. There is no similar example :(

The problem is how to make a stack and queue simulator.

  • Generate 10 random numbers. Done.
  • Show numbers in the table. Done.
  • Use 10 random numbers to simulate a stack and queue. I do not know how to associate a service with TextField.
  • Pause the simulation. or Stop.

- The program needs a pause method. I do not know how to stop the pause. Perhaps with wait () and notify (). I dont know.

I used label.textProperty.bind (service.progressProperty ()). this works, but when I try to bind a variable instead, the updateProgress (i, n) method throws an exception.

Maybe I need to use 2 tasks.

Main class:

package simulation;

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

public class Main extends Application {

@Override
public void start(Stage primaryStage) throws Exception{
    Parent root = FXMLLoader.load(getClass().getResource("/simulation/simulation.fxml"));
    primaryStage.setTitle("JavaFX and concurrency, Stack and Queue");
    primaryStage.setScene(new Scene(root));
    primaryStage.show();
}


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

Controller Class:

package simulation;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;

import java.net.URL;
import java.util.ResourceBundle;

public class SimulationCt implements Initializable {

  @FXML private TableView table;
  @FXML private TableColumn i_tc;
  @FXML private TableColumn random_tc;

  @FXML private TextField stack_start;
  @FXML private TextField stack_1;
  @FXML private TextField stack_2;
  @FXML private TextField stack_3;
  @FXML private TextField stack_4;
  @FXML private TextField stack_5;
  @FXML private TextField stack_final;

  @FXML private TextField queue_start;
  @FXML private TextField queue_1;
  @FXML private TextField queue_2;
  @FXML private TextField queue_3;
  @FXML private TextField queue_4;
  @FXML private TextField queue_5;
  @FXML private TextField queue_final;

  @FXML private Button new_b;
  @FXML private Button play_pause_b;
  @FXML private Button stop_b;

  @FXML private ProgressBar progress_bar;

  private ObservableList<RandomNumber> numberList = FXCollections.observableArrayList();

  private CalculateService backProcess;

  @FXML
  private void createNew () {
    disableNew(true);
    generateRandom();
    backProcess = new CalculateService();
    progress_bar.progressProperty().bind(backProcess.progressProperty());
    Platform.runLater(() -> {
      backProcess.start();
    });
  }

  @FXML
  private void playPause () {
    if(backProcess.getState().equals(Worker.State.RUNNING)) {
      System.out.println("stoping...");
      backProcess.cancel();
    } else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
      System.out.println("restarting...");
      backProcess.restart();
    }
  }

  @FXML
  private void stop () {
    if(backProcess.getState().equals(Worker.State.RUNNING)) {
      System.out.println("stoping...");
      backProcess.cancel();
    } else if (backProcess.getState().equals(Worker.State.CANCELLED)) {
      System.out.println("already stoped...");
    }
    clearItems();
    disableNew(false);
  }

  // cleans the list and the progress bar.
  private void clearItems () {
    progress_bar.progressProperty().unbind();
    progress_bar.progressProperty().set(0.0);
    numberList.clear();
  }

  private void disableNew (boolean b) {
    new_b.setDisable(b);
    play_pause_b.setDisable(!b);
    stop_b.setDisable(!b);
  }

  // generates random numbers to fill the table, these numbers are the ones for the stack and the queue.
  private void generateRandom () {
    for (int i = 1; i < 11; i++) {
      int rnd = (int)(Math.random() * (200 - 0 + 1)) + 0;
      numberList.add(new RandomNumber(i, rnd ));
    }
  }

  private void startTable () {
    i_tc.setCellValueFactory( new PropertyValueFactory("i"));
    random_tc.setCellValueFactory( new PropertyValueFactory("number"));
    table.setItems(numberList);
  }

  @Override
  public void initialize(URL url, ResourceBundle rb) {
    disableNew(false);
    startTable();
  }
}

FXML:

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

  <?import javafx.scene.control.Button?>
  <?import javafx.scene.control.Label?>
  <?import javafx.scene.control.ProgressBar?>
  <?import javafx.scene.control.TableColumn?>
  <?import javafx.scene.control.TableView?>
  <?import javafx.scene.control.TextField?>
  <?import javafx.scene.layout.AnchorPane?>
  <?import javafx.scene.layout.ColumnConstraints?>
  <?import javafx.scene.layout.GridPane?>
  <?import javafx.scene.layout.HBox?>
  <?import javafx.scene.layout.RowConstraints?>
  <?import javafx.scene.layout.VBox?>

  <AnchorPane id="AnchorPane" prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="simulation.SimulationCt">
  <children>
        <TableView fx:id="table" layoutX="8.0" layoutY="10.0" prefHeight="282.0" prefWidth="162.0">
          <columns>
            <TableColumn fx:id="i_tc" prefWidth="28.0" text="i" />
            <TableColumn fx:id="random_tc" prefWidth="122.0" text="Random Number" />
          </columns>
        </TableView>
        <GridPane layoutX="303.0">
           <columnConstraints>
              <ColumnConstraints fillWidth="false" minWidth="10.0" prefWidth="50.0" />
              <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
              <ColumnConstraints fillWidth="false" halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="50.0" />
           </columnConstraints>
           <rowConstraints>
              <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
           </rowConstraints>
           <children>
              <Label text="Stack" />
              <VBox GridPane.rowIndex="1" GridPane.valignment="TOP">
                 <children>
                    <TextField fx:id="stack_start" prefHeight="25.0" prefWidth="25.0" />
                    <Label text="new" />
                 </children>
              </VBox>
              <VBox GridPane.columnIndex="1" GridPane.rowIndex="1">
                 <children>
                    <TextField fx:id="stack_1" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="stack_2" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="stack_3" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="stack_4" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="stack_5" prefHeight="25.0" prefWidth="25.0" />
                 </children>
              </VBox>
              <VBox GridPane.columnIndex="2" GridPane.rowIndex="1" GridPane.valignment="TOP">
                 <children>
                    <TextField fx:id="stack_final" prefHeight="25.0" prefWidth="25.0" />
                    <Label text="last" />
                 </children>
              </VBox>
           </children>
        </GridPane>
        <GridPane layoutX="193.0" layoutY="155.0">
           <columnConstraints>
              <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
              <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
              <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
           </columnConstraints>
           <rowConstraints>
              <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
           </rowConstraints>
           <children>
              <Label text="Queue" GridPane.columnIndex="1" />
              <VBox GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
                 <children>
                    <TextField fx:id="queue_start" prefHeight="25.0" prefWidth="25.0" />
                    <Label text="new" />
                 </children>
              </VBox>
              <HBox spacing="5.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
                 <children>
                    <TextField fx:id="queue_1" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="queue_2" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="queue_3" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="queue_4" prefHeight="25.0" prefWidth="25.0" />
                    <TextField fx:id="queue_5" prefHeight="25.0" prefWidth="25.0" />
                 </children>
              </HBox>
              <VBox GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
                 <children>
                    <TextField fx:id="queue_final" prefHeight="25.0" prefWidth="25.0" />
                    <Label text="last" />
                 </children>
              </VBox>
           </children>
        </GridPane>
        <Button fx:id="new_b" onAction="#createNew" layoutX="266.0" layoutY="243.0" mnemonicParsing="false" text="New" />
        <Button fx:id="play_pause_b" onAction="#playPause" layoutX="326.0" layoutY="243.0" mnemonicParsing="false" text="Play / Pause" />
        <Button fx:id="stop_b" onAction="#stop" layoutX="428.0" layoutY="243.0" mnemonicParsing="false" text="Stop" />
        <ProgressBar fx:id="progress_bar" layoutX="266.0" layoutY="277.0" prefWidth="200.0" progress="0.0" />
  </children>
  </AnchorPane>

DataHelper:

package simulation;

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

public class RandomNumber {

  private IntegerProperty i;
  private IntegerProperty number;

  public RandomNumber(int i, int number) {
    this.i = new SimpleIntegerProperty(i);
    this.number = new SimpleIntegerProperty(number);
  }

  public int getI() {
    return i.get();
  }

  public IntegerProperty iProperty() {
    return i;
  }

  public void setI(int i) {
    this.i.set(i);
  }

  public int getNumber() {
    return number.get();
  }

  public IntegerProperty numberProperty() {
    return number;
  }

  public void setNumber(int number) {
    this.number.set(number);
  }

}

Class of service:

package simulation;

import javafx.concurrent.Service;
import javafx.concurrent.Task;

public class CalculateService extends Service {
  int n = 20; // this does the trick to simulate the pause.
  int j = 0; // even if the task is canceled the last value is saved here.

  @Override
  protected Task createTask() {
    return new Task() {
      @Override protected Void call() throws Exception {
        int a;
        int b;
        int iterations;
        for (iterations = j; iterations <= n; iterations++) {
          j = iterations;
          if (isCancelled()) {
            updateMessage("Cancelled");
            break;
          }
          updateProgress(iterations, n);
          System.out.println("number: " + j);

          //Block the thread for a short time, but be sure
          //to check the InterruptedException for cancellation
          try {
            Thread.sleep(100);
          } catch (InterruptedException interrupted) {
            if (isCancelled()) {
              updateMessage("Cancelled");
              break;
            }
          }
        }
        return null;
      }
    };
  }
}

enter image description here

+4
source share
1 answer

There is a lot of code in this question, and I don’t think you need all this to solve the concepts that you are really asking about. Therefore, I will simply give a high level of response here. If you want to change your question to something much simpler with regard to the actual problem, then I can make it specific to this example.

I would probably try to do this without threads, but using the animation API. For example, you can use Timelinewith the following main circuit:

public class Controller {

    // @FXML-annotated UI elements...
    // Other state....

    private Timeline timeline ;

    @FXML
    public void initialize() {

        timeline = new Timeline(new KeyFrame(Duration.seconds(100)), e -> {
            if (moreStepsToDo()) {
                doNextStep();
            } else {
                stopSimulation();
            }
        });
        timeline.setCycleCount(Animation.INDEFINITE);
    }

    private boolean moreStepsToDo() {
        // return true if there are more steps in the simulation,
        // false otherwise
    }

    private void doNextStep() {
        // do next step in the simulation
    }

    @FXML
    private void stopSimulation() {
        timeline.stop();
    }

    @FXML
    private void pauseSimulation() {
        timeline.pause();
    }

    @FXML
    private void playSimulation() {
        timeline.play();
    }

    @FXML
    private void resetSimulation() {
        timeline.jumpTo(Duration.ZERO);
    }
}

, : FX, , . , . API- pause(), play() stop() , ; .

, ( , , vbox ).

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SimplePausableAnimation extends Application {

    private VBox left;
    private VBox right;
    private Timeline timeline;
    private Button pausePlay;

    @Override
    public void start(Stage primaryStage) {
        left = new VBox(10);
        left.setMinWidth(200);
        right = new VBox(10);
        right.setMinWidth(200);

        HBox hbox = new HBox(10, left, right);

        pausePlay = new Button();

        Button reset = new Button("Reset");
        reset.setOnAction(e -> reset());

        reset();

        BorderPane root = new BorderPane(hbox);

        HBox buttons = new HBox(5, pausePlay, reset);
        buttons.setAlignment(Pos.CENTER);

        root.setBottom(buttons);

        Scene scene = new Scene(root, 600, 600);

        primaryStage.setScene(scene);
        primaryStage.show();

    }

    private void reset() {

        if (timeline != null) {
            timeline.stop();
        }

        left.getChildren().clear();
        right.getChildren().clear();

        for (int i = 0; i < 5; i++) {
            left.getChildren().add(new Rectangle(100, 100, Color.CORNFLOWERBLUE));
        }

        timeline = new Timeline(new KeyFrame(Duration.seconds(1), e -> {
            if (moreStepsToDo()) {
                doNextStep();
            } else {
                timeline.stop();
            }
        }));
        timeline.setCycleCount(Animation.INDEFINITE);

        pausePlay.disableProperty().bind(Bindings.createBooleanBinding(() -> {
            if (left.getChildren().isEmpty()) {
                return true;
            }
            return false;
        }, left.getChildren()));

        pausePlay.textProperty().bind(Bindings.createStringBinding(() -> {
            if (timeline.getStatus() == Animation.Status.RUNNING) {
                return "Pause";
            }
            return "Play";
        }, timeline.statusProperty()));

        pausePlay.setOnAction(e -> {
            if (timeline.getStatus() == Animation.Status.RUNNING) {
                timeline.pause();
            } else {
                timeline.play();
            }
        });
    }

    private boolean moreStepsToDo() {
        return !left.getChildren().isEmpty();
    }

    private void doNextStep() {
        int n = left.getChildren().size();
        Node node = left.getChildren().remove(n - 1);
        right.getChildren().add(node);
    }

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

, - Semaphore , :

Semaphore pauser = new Semaphore(1);

Thread simulationThread = new Thread(() -> {
    try {
        while (! Thread.currentThread().isInterrupted()) {
            pauser.acquire();
            // do simulation step
            pauser.release();
            Thread.sleep(100);
        }
    } catch (InterruptedException exc) {
        // ignore and exit thread...
    }
});

(, Task, .)

pauser.acquire(); ( ), pauser.release(), , .

+1

Source: https://habr.com/ru/post/1695115/


All Articles