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
JavaFX: Redirect console output to TextArea that is created in SceneBuilder
提问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 ps
with the value of console
before 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 console
is still null
when you pass it to new Console(...)
.
显然console
仍是null
当你通过它来new Console(...)
。
You need to initialize ps
after the FXMLLoader
has initialized the injected fields, which you can do using the initialize
method.
您需要在初始化注入的字段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 FXMLLoader
to create your ytdlLogger
, add the attribute fx:controller="application.ytdlLogger"
(where fx
is 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:
如果你想这样做,你还需要改变一些东西:
ytdlLogger
needs a default constructor (i.e. either remove your constructor or create a new one without arguments).Add the
@FXML
annotation to yourloggerPane
field to allow theFXMLLoader
to access that field to assign theTextArea
with thefx:id="loggerPane"
attribute to it.- better remove the base class
OutputStream
from the controller, since you don't use it. - Add some code that prints to
System.out
orSystem.err
. Otherwise it's not surprising nothing is written to yourTextArea
. Make sure you do this after the controller is initialized.
ytdlLogger
需要一个默认构造函数(即删除您的构造函数或创建一个不带参数的新构造函数)。添加
@FXML
注释到你的loggerPane
领域,允许FXMLLoader
访问该字段指定TextArea
与fx: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>