java JavaFX:将控制台输出重定向到在 SceneBuilder 中创建的 TextArea

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33494052/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:45:37  来源:igfitidea点击:

JavaFX: Redirect console output to TextArea that is created in SceneBuilder

javajavafxconsoletextareascenebuilder

提问by Deo Favente

EDIT 4

编辑 4

I've created a simple example that should give you an idea about what's happening right now.

我已经创建了一个简单的示例,它应该可以让您了解当前正在发生的事情。

What's happening right now is that whenever I click the button to print "HELLO WORLD" to the TextArea, the program will hang and use 100% of the CPU. There's also no output in the Eclipse console panel too.

现在发生的事情是,每当我单击按钮将“HELLO WORLD”打印到 TextArea 时,程序将挂起并使用 100% 的 CPU。Eclipse 控制台面板中也没有输出。

Main.java

Main.java

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/application/test.fxml"));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();


        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

MainController.java

MainController.java

public class MainController {

    @FXML
    private TextArea console;
    private PrintStream ps = new PrintStream(new Console(console));

    public void button(ActionEvent event) {
        System.setOut(ps);
        System.setErr(ps);
        System.out.println("Hello World");
    }

    public class Console extends OutputStream {
        private TextArea console;

        public Console(TextArea console) {
            this.console = console;
        }

        public void appendText(String valueOf) {
            Platform.runLater(() -> console.appendText(valueOf));
        }

        public void write(int b) throws IOException {
            appendText(String.valueOf((char)b));
        }
    }
}


EDIT 2: It seems that my question is way too long and hard to understand. I'm in the middle of restructuring this one.

编辑 2:我的问题似乎太长且难以理解。我正在重组这个。



EDIT 3

编辑 3

I guess I should just show everything here. What I'm trying to do is a simple GUI front-end for a CLI application. I'm a CS student and Java is our main language, so this is mainly for practice.

我想我应该在这里展示所有内容。我正在尝试为 CLI 应用程序创建一个简单的 GUI 前端。我是CS学生,Java是我们的主要语言,所以这主要是为了练习。



I've been looking every where for hours and hours but there's still no solution to this. I've tried doing the same like I did previously with Swing. The method worked fine with Swing but not with JavaFX.

我一直在寻找每个地方几个小时,但仍然没有解决方案。我尝试过像以前使用 Swing 那样做同样的事情。该方法适用于 Swing,但不适用于 JavaFX。

Here's my (current) logger.java Class:

这是我的(当前)logger.java 类:

package application;

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

import javafx.application.Platform;
import javafx.fxml.Initializable;
import javafx.scene.control.*;

public class ytdlLogger extends OutputStream implements Initializable
{
    private TextArea loggerPane;

    public ytdlLogger(TextArea loggerPane) {
        this.loggerPane = loggerPane;
    }

    public void appendText(String valueOf) {
        Platform.runLater(() -> loggerPane.appendText(valueOf));
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        OutputStream out = new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                appendText(String.valueOf((char)b));
            }
        };
        System.setOut(new PrintStream(out, true));
        System.setErr(new PrintStream(out, true));
    }

    @Override
    public void write(int b) throws IOException {
        // TODO Auto-generated method stub

    }
}

I don't think there's any actual problems with this. I also did add the PrintStream object to redirect System.setOut and System.setErr in the MainController class to the TextArea, but it didn't work either.

我不认为这有任何实际问题。我也确实添加了 PrintStream 对象以将 MainController 类中的 System.setOut 和 System.setErr 重定向到 TextArea,但它也不起作用。

I also have another Main class, which is the main thing that loads the FXML. I tried redirecting the output from there, it almost worked. Just almost, because i stopped seeing the console outputs inside Eclipse and I knew that was a great progress.

我还有另一个 Main 类,它是加载 FXML 的主要内容。我尝试从那里重定向输出,它几乎起作用了。差不多,因为我不再看到 Eclipse 中的控制台输出,我知道这是一个很大的进步。

So, what seems to be the problem here? Is it because of the FXML? I'm absolute beginner in Java and also JavaFX, this is my first JavaFX application. Any guidance is very much appreciated. Thank you in advance.

那么,这里的问题似乎是什么?是因为 FXML 吗?我绝对是 Java 和 JavaFX 的初学者,这是我的第一个 JavaFX 应用程序。非常感谢任何指导。先感谢您。



EDIT 1

编辑 1

Here's the Main class:

这是主类:

package application;

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

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/application/Main.fxml"));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

采纳答案by James_D

You are initializing pswith the value of consolebefore it has been initialized by the FXMLLoader. I.e you have

要初始化ps与价值console之前已被初始化FXMLLoader。即你有

@FXML
private TextArea console;
private PrintStream ps = new PrintStream(new Console(console));

Clearly consoleis still nullwhen you pass it to new Console(...).

显然console仍是null当你通过它来new Console(...)

You need to initialize psafter the FXMLLoaderhas initialized the injected fields, which you can do using the initializemethod.

您需要在初始化注入的字段ps后进行FXMLLoader初始化,您可以使用该initialize方法进行初始化。

SSCCE:

SSCCE:

MainController.java:

主控制器.java:

package application;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;

public class MainController {

    @FXML
    private TextArea console;
    private PrintStream ps ;

    public void initialize() {
        ps = new PrintStream(new Console(console)) ;
    }

    public void button(ActionEvent event) {
        System.setOut(ps);
        System.setErr(ps);
        System.out.println("Hello World");
    }

    public class Console extends OutputStream {
        private TextArea console;

        public Console(TextArea console) {
            this.console = console;
        }

        public void appendText(String valueOf) {
            Platform.runLater(() -> console.appendText(valueOf));
        }

        public void write(int b) throws IOException {
            appendText(String.valueOf((char)b));
        }
    }
}

Main.java:

主.java:

package application;

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) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("test.fxml"));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

test.fxml:

测试.fxml:

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

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
    <center>
        <TextArea fx:id="console"/>
    </center>
    <bottom>
        <Button  onAction="#button" text="Output">
            <BorderPane.alignment>CENTER</BorderPane.alignment>
            <BorderPane.margin><Insets top="5" left="5" right="5" bottom="5"/></BorderPane.margin>
        </Button>
    </bottom>
</BorderPane>

回答by fabian

You do not use your controller with an FXMLLoader. Otherwise you'd get an exception, since the class has no default constructor.

您不使用带有FXMLLoader. 否则你会得到一个异常,因为这个类没有默认的构造函数。

If you want to use the FXMLLoaderto create your ytdlLogger, add the attribute fx:controller="application.ytdlLogger"(where fxis the fxml namespace prefix) to the root element of your fxml file.

如果您想使用FXMLLoader来创建您的ytdlLogger,请将属性fx:controller="application.ytdlLogger"(其中fx是 fxml 命名空间前缀)添加到您的 fxml 文件的根元素。

If you want to do this, you also need to change some things:

如果你想这样做,你还需要改变一些东西:

  • ytdlLoggerneeds a default constructor (i.e. either remove your constructor or create a new one without arguments).

  • Add the @FXMLannotation to your loggerPanefield to allow the FXMLLoaderto access that field to assign the TextAreawith the fx:id="loggerPane"attribute to it.

  • better remove the base class OutputStreamfrom the controller, since you don't use it.
  • Add some code that prints to System.outor System.err. Otherwise it's not surprising nothing is written to your TextArea. Make sure you do this after the controller is initialized.
  • ytdlLogger需要一个默认构造函数(即删除您的构造函数或创建一个不带参数的新构造函数)。

  • 添加@FXML注释到你的loggerPane领域,允许FXMLLoader访问该字段指定TextAreafx:id="loggerPane"属性吧。

  • 最好OutputStream从控制器中删除基类,因为您不使用它。
  • 添加一些打印到System.out或的代码System.err。否则,没有任何内容写入您的TextArea. 确保在控制器初始化后执行此操作。

Your controller should look like this after the changes:

更改后,您的控制器应如下所示:

public class ytdlLogger implements Initializable
{

    @FXML
    private TextArea loggerPane;

    public void appendText(String valueOf) {
        Platform.runLater(() -> loggerPane.appendText(valueOf));
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        OutputStream out = new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                appendText(String.valueOf((char)b));
            }
        };
        System.setOut(new PrintStream(out, true));
        System.setErr(new PrintStream(out, true));
    }

}

And the fxml should look similar to this

并且 fxml 应该与此类似

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

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

<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" 
            fx:controller="application.ytdlLogger"> <!-- controller goes here -->
   <children>
      <TextArea fx:id="loggerPane" /> <!-- the TextArea you want to use for logging -->
   </children>
</AnchorPane>