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

时间:2015-11-03 08:01:28

标签: java javafx console textarea scenebuilder

编辑4

我创建了一个简单的例子,可以让你了解现在发生了什么。

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

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

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));
        }
    }
}

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

编辑3

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

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

这是我的(当前)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

    }
}

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

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

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

编辑1

这是Main类:

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);
    }
}

2 个答案:

答案 0 :(得分:2)

ps初始化console之前,您正在初始化FXMLLoader,其值为@FXML private TextArea console; private PrintStream ps = new PrintStream(new Console(console)); 。你有

console

当您将null传递给new Console(...)时,ps仍然是FXMLLoader

您需要在initialize初始化注入字段后初始化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)); } } } ,您可以使用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); } } 方法执行此操作。

SSCCE:

MainController.java:

<?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>

Main.java:

<%=userNameTextBox.ClientID%>

test.fxml:

<%=userNameTextBox.UniqueID%>

答案 1 :(得分:1)

您不能将控制器与FXMLLoader一起使用。否则你会得到一个异常,因为该类没有默认的构造函数。

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

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

  • ytdlLogger需要一个默认构造函数(即删除构造函数或创建一个没有参数的新构造函数)。

  • @FXML注释添加到loggerPane字段,以允许FXMLLoader访问该字段,为TextArea分配fx:id="loggerPane"属性它。

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

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

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));
    }

}

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>