質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

textAreaで1行ずつテキストを表示させたいです。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,690

score 21

前提・実現したいこと

練習としてjavafxを使用してコマンドプロンプトの模擬アプリを作ろうと思っています。
その中で本来のコマンドプロンプトでpingコマンドを使った時同様、
10.55.55.33 からの応答: バイト数 =32 時間 <1ms TTL=128
10.55.55.33 からの応答: バイト数 =32 時間 <1ms TTL=128
10.55.55.33 からの応答: バイト数 =32 時間 <1ms TTL=128
10.55.55.33 からの応答: バイト数 =32 時間 <1ms TTL=128
の部分のところを例えば1行1秒ずつでtextAreaに表示させたいのです。

ソースを載せるのでどこを直した方がいいか教えてください。

ド初心者なので多少のことは目をつぶって頂けるとありがたいです。

発生している問題・エラーメッセージ

コンパイルしてもエラーは出てないのですが、
実際に実行すると、4秒経った後に全部の本来表示させたい文が出てくるのです。

該当のソースコード

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import java.awt.Dimension;
import javafx.scene.control.TextArea;
import java.util.*;

public class sample extends Application {
Label l1, l2; // ラベル
TextField tx1, tx2; // テキストフィールド
Button start, buttonSub, buttonMul, buttonDiv, buttonMod, bC; // ボタン
TextArea textArea,ta2; // テキストエリア
ChoiceBox<String> cb;//チョイスボックス
RadioButton rb1,rb2,rb3;//ラジオボタン
ToggleGroup ping;
int num1, num2;

public void start(Stage stage) {
stage.setWidth(1200);
stage.setHeight(700);


l1 = new Label("pingコマンドデモ");
l1.setFont(new Font(40));

l2 = new Label("コマンド選択してください");

rb1=new RadioButton("ping 10.55.55.33");
ping=new ToggleGroup();
rb1.setToggleGroup(ping);
rb1.setSelected(true);


bC = new Button("Clear");
bC.setOnAction(event -> buttonClearPressed());
start = new Button("実行");
start.setOnAction(event -> buttonStartPressed());

textArea = new TextArea("実行結果が表示されます。\nC:¥Users¥f17K6○○○>");
textArea.setStyle("-fx-text-fill: white;-fx-font-size: 16;");

HBox hbox = new HBox(5);
hbox.getChildren().addAll(start,bC);

VBox vbox = new VBox(5);
vbox.setPadding(new Insets(20, 25, 25, 25));
vbox.getChildren().addAll(l1,l2,rb1,hbox);

BorderPane root = new BorderPane();
root.setLeft(vbox);
root.setCenter(textArea);
stage.setScene(new Scene(root));
stage.show();
textArea.lookup(".content").setStyle("-fx-background-color: black;");//色の変更
}

void buttonClearPressed() {
textArea.clear();
textArea.setText("実行結果が表示されます。\nC:¥Users¥f17K6○○○>");
}

void buttonStartPressed(){
  int ransu,a=0,b=0,c=0;
  if ("ping 10.55.55.33"==((RadioButton)ping.getSelectedToggle()).getText()){
    textArea.appendText("ping 10.40.83.33\n10.40.83.33 に ping を送信しています 32 バイトのデータ:");

    for(int i=0;i<4;i++){
      try{
        Thread.sleep(1000);
      }catch(InterruptedException e){
        System.out.println(e);
      }
      textArea.appendText("\n10.55.55.33 からの応答: バイト数 =32 時間 <1ms TTL=128");
  }

    textArea.appendText("\n10.55.55.33 の ping 統計:\n  パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)\n  ラウンド トリップの概算時間 (ミリ秒):\n  最小 = 0ms、最大 = 0ms、平均 = 0ms");
  textArea.appendText("\n\nC:¥Users¥f17K6○○○>");

}
}

public static void main(String[] args) {
launch();
}
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

4秒後に一度に変更内容が反映された原因

「単一のイベントハンドラーの処理の中でsleepしながらノードの状態を更新しようとしているから」といえます。

画面を構成する要素(Node)インスタンスの状態(プロパティー)を変更するとそれがディスプレイ上に反映されるのですが、その反映はプロパティーを変更すると同時に行われるわけではありません。ではいつ反映されるかというと、イベントハンドラーからイベントループへ戻った時点と考えれば概ねよいと思います。

要するにご質問のコードの動きは以下のようになったと考えられます。

(A1) JavaFXランタイム内: マウスイベント検出
(B2) JavaFXランタイム内: マウスイベントに対するハンドラー(buttonStartPressed)起動
(C3) ハンドラー内:
        TextAreaへテキストを追記
        1秒sleep
        TextAreaへテキストを追記
        1秒sleep
        ...
        TextAreaへテキストを追記
        <=イベントループへ復帰(return)
(A2) Nodeの状態が変化していたとき、変更内容をレンダリングスレッドへ転送する。
     つまりこの時点でようやく(C3)での変更の内容が画面上へ描画される

対処

イベントハンドラーは「ごく短い時間だけ必要な処理を行い、直ちにイベントループへ復帰」するのが原則です。ご質問のコードのようにハンドラーの中でsleepしてしまうと「このハンドラーが終了するまでイベントループが停止し、アプリケーションがイベントに適切に反応しなくなります。それは本質問の主題以上に悪影響を及ぼします。

対処の方法は以下の2通りが考えられます。

(A) 一定時間が経過したら別のイベントを発生させそのハンドラーの中で必要な動作をする
具体的にはTimelineクラスなどが使いやすいです。
(B) 別のスレッドでsleepなどによる処理シーケンスを実装し、そのなかで画面の更新をPlatform.runLaterで要求する

アニメーションのようにあらかじめ決まった時間で画面を更新したいなら(A)の方式が適切と思います。一方「イベントループでは検出できないタイミング(例えば別プロセスで実行中のプログラムが標準出力へ何かを出力した契機)で画面を更新したい場合、(B)の方法がやりやすいと思います。

以下に(B)を用いた簡単な例を挙げます。

void buttonStartPressed() {
  if (rb1.isSelected()) {
    Thread thread = new Thread() {
      @Override
      public void run() {
        Platform.runLater(() -> textArea.appendText("first\n"));
        // for文をforEachに変更しています。Platform.runLaterで用いるlambda式から
        // ループの制御変数iを参照するにはiがfinalでなくてはならず、forループが使いにくいからです
        IntStream.range(0, 4).forEach(i -> {
          try {
            sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace(); // デバッグ用
          }
          Platform.runLater(() -> textArea.appendText("line" + i + "\n"));
        });
        Platform.runLater(() -> textArea.appendText("last\n"));
        Platform.runLater(() -> {
          try {
            join();
          } catch (InterruptedException e) {
            e.printStackTrace(); // デバッグ用
          }
        });
      }
    };
    thread.start();
  }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/08 18:32

    無事動きました!ありがとうございました!!!

    キャンセル

  • 2019/01/08 18:55 編集

    しまった。コード間違えてました。上記ではコンパイルエラーに・・・
    そのあたりはくみ取っていただき解決済みのようですが、一応コードを直しておきます。
    ---
    元のコードはnew Thread(() -> {...});のようにlambda式としてスレッド処理を書いたため本体の中でThreadに対するメソッド(sleep/join)を呼び出せないものになってました。回答のコードを匿名クラスに書き直しました。失礼しました。

    キャンセル

0

https://stackoverflow.com/questions/16868445/appending-text-to-a-textarea-over-time-with-delay

JavaFX does have a timer built in - it's called a Timeline. It's simple, straightforward, and provides extra functionality like Swing's Timer class, and, most importantly, executes code on the UI thread.

https://stackoverflow.com/questions/9966136/javafx-periodic-background-task/9966213#9966213

You can use Timeline for what matter:
~省略~
for background processes (which don't do anything to UI) you can use old good java.util.Timer:

こちらに書かれていますが、java.util.Timerを使用するのが良いのではないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る