问题
EDIT 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.
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));
}
}
}
EDIT 2: It seems that my question is way too long and hard to understand. I'm in the middle of restructuring this one.
EDIT 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.
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.
Here's my (current) logger.java Class:
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.
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.
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.
EDIT 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);
}
}
回答1:
You are initializing ps
with the value of console
before it has been initialized by the FXMLLoader
. I.e you have
@FXML
private TextArea console;
private PrintStream ps = new PrintStream(new Console(console));
Clearly console
is still null
when you pass it to new Console(...)
.
You need to initialize ps
after the FXMLLoader
has initialized the injected fields, which you can do using the initialize
method.
SSCCE:
MainController.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:
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:
<?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>
回答2:
You do not use your controller with an FXMLLoader
. Otherwise you'd get an exception, since the class has no default constructor.
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.
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.
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
<?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>
来源:https://stackoverflow.com/questions/33494052/javafx-redirect-console-output-to-textarea-that-is-created-in-scenebuilder