I have a method inside of one of my controllers that requires some seconds to process. I would like to have a gif animation popup while this occurs but I only get a static image. This is my code:
#FXML
public void search(ActionEvent e) {
final Stage dialog = new Stage();
Group popup = new Group();
Image image = new Image("file:resources/images/bender.gif");
ImageView view = new ImageView(image);
popup.getChildren().add(view);
Scene dialogScene = new Scene(popup);
dialog.setScene(dialogScene);
dialog.show();
Platform.runLater(new Runnable() {
#Override
public void run() {
Match msg = stablishSearchConditions();
TreeItem<String> root = new TreeItem<>("ROOT");
int indexName = 1;
String mensaje = "Mensaje ";
treeLabelResults.setText("");
arbol.setRoot(root);
for (Match message : msg.each()) {
TreeItem<String> nodo = new TreeItem<String>(mensaje + indexName);
root.getChildren().add(nodo);
root.setExpanded(true);
String mens = message.getMessage();
TreeItem<String> nodo2 = new TreeItem<String>(mens);
nodo.getChildren().add(nodo2);
indexName++;
}
dialog.close();
}
});
}
You are blocking the fx application thread by running the expensive operation on this thread. This prevents your UI from updating, including animating the GIF.
Move the expensive operations to a non-application thread instead and only use Platform.runLater() to "commit" the ui updates:
Runnable expensiveTask = () -> {
// expensive operations that should not run on the application thread
Match msg = stablishSearchConditions();
TreeItem<String> root = new TreeItem<>("ROOT");
int indexName = 1;
String mensaje = "Mensaje ";
for (Match message : msg.each()) {
TreeItem<String> nodo = new TreeItem<String>(mensaje + indexName);
root.getChildren().add(nodo);
root.setExpanded(true);
String mens = message.getMessage();
TreeItem<String> nodo2 = new TreeItem<String>(mens);
nodo.getChildren().add(nodo2);
indexName++;
}
// update ui -> application thread
Platform.runLater(() -> {
treeLabelResults.setText("");
arbol.setRoot(root);
dialog.close();
});
};
// start new thread for expensiveTask
new Thread(expensiveTask).start();
Related
apologies for the length of my code. I realized last night that I was on the wrong path and now have gotten stuck on an issue that I think relates to JavaFX event handling. Initially I had the logic functioning outside a GUI in a basic loop that depended on interaction through the console. Everything was working great. I've now tried to get this to work in a GUI with interaction from the user.
I have two main problems with the code below.
The first is that the text in textArea is not updating with additional text after the startButton executes the start of my main logic sequence. The first append starts right under the first while loop. I was hoping to have this show up in the GUI as the logic executes. I'm not sure if I need to tell the GUI to update at certain intervals or if there's something else wrong.
Second, I'm not sure how to get the program to wait for the user to type in something into textField before hitting the textButton I created to continue on. I used to have a scanner created which caused the program to wait in the console for input. I realize I need some way of telling it to wait for a button press when it's running inside JavaFX.
I chose not to include the rest of the code to make things easier to read, but I can add it on if it will help resolve this issue.
Thank you everyone for your help!
public class FxApp extends Application {
//Creates FileParser object with methods that alter the incoming Array of Strings into the format we need
FileParser fileParser = new FileParser();
Configure configure = new Configure();
private String text;
private String initialState;
private ArrayList<Machine> machines = new ArrayList<Machine>();
private Map<String, String> initialStates = new HashMap<String, String>();
private Map<String, String> states = new HashMap<String, String>();
private Map<String, ArrayDeque<String>> queues = new HashMap<String, ArrayDeque<String>>();
private Map<Integer, ArrayList<String>> parsedData = new HashMap<Integer, ArrayList<String>>();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("File Chooser");
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().addAll(new ExtensionFilter("Text Files", "*.txt"));
Button startButton = new Button("Start");
Button openButton = new Button("Click to open a file...");
openButton.setPrefSize(200, 80);
Button textButton = new Button("Enter");
TextArea textArea = new TextArea();
textArea.setWrapText(true);
TextField textField = new TextField();
Label lbl = new Label();
VBox vbox = new VBox(lbl, openButton, startButton, textArea, textField, textButton);
vbox.setSpacing(10);
vbox.setPadding(new Insets(15));
lbl.setText("This tool creates virtual automata based \ron the file.");
Scene scene = new Scene(vbox, 640, 480);
primaryStage.setScene(scene);
primaryStage.show();
openButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
//Execute the method to convert to string array before sending to file parser
try {
fileParser.convertFile(file);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
textButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
text = textField.getText();
}
});
startButton.setOnAction(new EventHandler <ActionEvent>()
{
public void handle(ActionEvent event)
{
machineCreation();
String exit = "no";
String nextLine = null;
ArrayList<String> listOfCurrentTransitions = new ArrayList<String>();
int nextInt = 0;
states = initialStates;
while(!(exit.toLowerCase().equals("yes"))) {
textArea.appendText("Choose a state to load");
//Print out the states possible for each machine
ArrayList<String> tempTrans = machines.get(nextInt).getTransitions();
//This loops through the list of transitions of the machine and pulls possible transitions from its current state
for(int i = 0; i < tempTrans.size(); i++) {
String pull = tempTrans.get(i);
String[] apart = pull.split(" ");
pull = apart[0];
if(states.get(Integer.toString(nextInt)).equals(pull)) {
listOfCurrentTransitions.add(tempTrans.get(i));
}
}
if(!(listOfCurrentTransitions.isEmpty())) {
textArea.appendText("The following transitions are possible. Choose one: " + listOfCurrentTransitions);
}
else {
textArea.appendText("No transitions for this machine exist from its current state");
}
//Tell GUI to wait for user input in textField and execute textButton which assigns to String text. Resume on button click.
The while loop blocks the JavaFX application thread which prevents updates of the GUI and handling of events.
You need to execute the logic of a single iteration of the loop on each "text commit" instead:
private TextArea textArea;
private void activateState(int nextInt) {
ArrayList<String> listOfCurrentTransitions = new ArrayList<String>();
textArea.appendText("Choose a state to load");
//Print out the states possible for each machine
ArrayList<String> tempTrans = machines.get(nextInt).getTransitions();
//This loops through the list of transitions of the machine and pulls possible transitions from its current state
for(int i = 0; i < tempTrans.size(); i++) {
String pull = tempTrans.get(i);
String[] apart = pull.split(" ");
pull = apart[0];
if(states.get(Integer.toString(nextInt)).equals(pull)) {
listOfCurrentTransitions.add(tempTrans.get(i));
}
}
if(listOfCurrentTransitions.isEmpty()) {
textArea.appendText("No transitions for this machine exist from its current state");
} else {
textArea.appendText("The following transitions are possible. Choose one: " + listOfCurrentTransitions);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
...
textArea = new TextArea();
...
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
machineCreation();
activateState(0);
}
});
textButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// read input and ask for more input...
int nextState = Integer.parseInt(textField.getText()); // TODO: deal with invalid input
activateState(nextState);
}
});
You probably need to fix the logic a bit to verify a transition is valid, change the values of some fields ect...
I'm using a translator.
enter image description here
Windows desktop appears unexpectedly when you click on the image.
Scrollpane -> BorderPane
Normal at first Occurs like an image later
FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Main_fx.fxml"));
Parent root = loader.load();
st = new Stage();
final UndecoratorScene undecorator = new UndecoratorScene(st, (Region) root);//NewMainScene lib 사용(프로젝트 및 패키지 명 :Newtable)
undecorator.getStylesheets().add(getClass().getResource("/View/winDec.css").toExternalForm());
st.setScene(undecorator);
stage = (Stage) lb.getScene().getWindow();
st.getIcons().add(new Image(config2.class.getResourceAsStream("/View/207411.jpg" )));
st.initStyle(StageStyle.TRANSPARENT); //스타일 미적용.
st.setResizable(resize);
st.setMaximized(maximized);
st.setTitle(judul);
st.sizeToScene();
// Undecorator undecor = undecorator.getUndecorator();
//최소로 줄이수 있는 화면 크기 값 .
st.setMinWidth(350);
st.setMinHeight(200);
GetStage gs = new GetStage();//트레이 창으로 보낼때 현재 Stage 값 전달.
gs.SetStage(st);
st.toFront();
st.show();
Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
st.setX((primScreenBounds.getWidth() - st.getWidth()) / 2);
st.setY((primScreenBounds.getHeight() - st.getHeight()) / 2);
createTrayIcon(st);//트레이 창 이벤트 메소드
firstTime = true;
Platform.setImplicitExit(false);
stage.close();//controllSplash stage 종료
Hander_Main controller = (Hander_Main)loader.getController();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:00");
Date sys_date = new Date();
String now= formatter.format(sys_date);
controller.setPrimaryStage(st);
controller.setScene(undecorator);
controller.setFirstDate(now);
st.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we)
{
hide(st);
}
});
} catch (Exception e) {
st.hide();
dialog(Alert.AlertType.ERROR,"Main_fx Stage ERR\n"+e);
System.exit(0);
}
First i am sorry for my poor english...
i made Media Player Application with Javafx.
this player can get mulit file media. and play files out of all limits.
it work well. but sometimes not work..
it is not media error. it is mediaplayer error.
error message is 'mediaPlayer Unknown, media Invalid..' why.??
i played same video file(1920 * 1080), sometimes work and sometimes not work..
and javafx is depend on OS ??
player works perfectly on windown7 computer
but player have this error on windown10 computer...
please give me advice..
MediaPlayer mediaPlayer = null;
Stage stage = new Stage();
AnchorPane pane = new AnchorPane();
Scene scene = new Scene(pane);
MediaView mediaView = new MediaView();
int mNextFileIndex = -1;
List<File> fileLists = new ArrayList<>();
Media media;
mediaplayer play Method
public void playNextMedia() {
if (mediaPlayer != null) {
mediaPlayer.dispose();
mediaView.setMediaPlayer(null);
}
mNextFileIndex = (mNextFileIndex + 1) % fileLists.size();
media =new Media(fileLists.get(mNextFileIndex).toURI().toString());
media.setOnError(()-> {
MainApp.makeLog("media error");
});
mediaPlayer = new MediaPlayer(media);
mediaView.setMediaPlayer(mediaPlayer);
mediaPlayer.setOnReady(() -> {
mediaPlayer.play();
});
mediaPlayer.setOnEndOfMedia(() -> {
playNextMedia();
});
mediaPlayer.setOnError(() -> {
systom.out.println("mediaPlayer error");
Systeom.out.println(mediaPlayer.getError().getMessage());
playNextMedia();
});
}
Button Method
#FXML
private void playMedia(ActionEvent event) {
mNextFileIndex = -1;
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().addAll(new
FileChooser.ExtensionFilter("Select a File (*.mp4)", "*.mp4"),
new FileChooser.ExtensionFilter("All Files", "*.*"));
List<File> list = fileChooser.showOpenMultipleDialog(null);
if (list != null) {
for (File file : list) {
fileLists.add(file)
}
playNextMedia();
pane.getChildren().add(mediaView);
stage.setScene(scene);
stage.show();
}
I have developed a very simplified but fully functional JavaFX web browser for illustrating what I want to ask. Here follows the source code.
public class OpenInNewTab extends Application {
#Override
public void start(Stage primaryStage)
{
TabPane tabPane = new TabPane();
WebView webView = new WebView();
Tab tab = new Tab("Home Tab");
tab.setContent(webView);
tabPane.getTabs().add(tab);
webView.getEngine().load("https://www.google.co.in/?gws_rd=ssl#q=javafx");
BorderPane root = new BorderPane();
root.setTop(tabPane);
Scene scene = new Scene(root, 600, 339);
primaryStage.setTitle("Basic browser");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
} }
This browser loads a web page from this link. I want that when I click on any of the search results, the clicked hyperlink should open in new tab adjacent to the existing "Home tab".
After searching on the internet extensively, I reached nowhere.
Please help me with relevant code. Thanks.
First of all, you need to find a way to handle all outgoing click events on the displayed page. I've made a helper method to achieve that. It creates an event listener for each link, clicking on which will open a new tab.
private void handleUrls(Document doc) {
NodeList links = doc.getElementsByTagName("a");
for (int i = 0; i < links.getLength(); i++) {
EventTarget eventTarget = (EventTarget) links.item(i);
String link = links.item(i).toString();
eventTarget.addEventListener("click", e -> {
WebView webView = new WebView();
alterWebView(webView);
Tab tab = new Tab(link);
tab.setContent(webView);
tab.setClosable(true);
webView.getEngine().load(link);
tabPane.getTabs().add(tab);
e.preventDefault();
}, false);
}
}
You may want to set a better name for your tab... I've used a link as a makeshift solution, but it's long and messy.
webView.getEngine().load(link);
The alterWebView referenced above is another helper function. It's responsible for tracking the loading progress. Once the Worker does nothing, it will add listeners to all urls.
private void alterWebView(WebView webView) {
WebEngine engine = webView.getEngine();
Worker worker = engine.getLoadWorker();
worker.stateProperty().addListener((oldVal, newVal, o) -> {
if (newVal.equals(State.RUNNING)) {
handleUrls(engine.getDocument());
}
});
}
So basically, the logic behind the code above is as follows:
We track our WebEngine's State until the page is fully loaded and then we add event listeners to every url.
These listeners, when invoked, will create a new WebView, handle it accordingly, and add it to the TabPane inside a new Tab.
And here's the complete, working example based on your code:
public class JavaFXTest extends Application {
private TabPane tabPane;
#Override
public void start(Stage primaryStage) {
tabPane = new TabPane();
WebView webView = new WebView();
alterWebView(webView);
Tab tab = new Tab("Home Tab");
tab.setContent(webView);
tab.setClosable(true);
tabPane.getTabs().add(tab);
webView.getEngine().load("https://www.google.co.in/?gws_rd=ssl#q=javafx");
BorderPane root = new BorderPane();
root.setTop(tabPane);
Scene scene = new Scene(root, 600, 339);
primaryStage.setTitle("Basic browser");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void alterWebView(WebView webView) {
WebEngine engine = webView.getEngine();
Worker worker = engine.getLoadWorker();
worker.stateProperty().addListener((oldVal, newVal, o) -> {
if (newVal.equals(State.RUNNING)) {
handleUrls(engine.getDocument());
}
});
}
private void handleUrls(Document doc) {
NodeList links = doc.getElementsByTagName("a");
for (int i = 0; i < links.getLength(); i++) {
EventTarget eventTarget = (EventTarget) links.item(i);
String link = links.item(i).toString();
eventTarget.addEventListener("click", e -> {
WebView webView = new WebView();
alterWebView(webView);
Tab tab = new Tab(link);
tab.setContent(webView);
tab.setClosable(true);
webView.getEngine().load(link);
tabPane.getTabs().add(tab);
e.preventDefault();
}, false);
}
}
}
When I start my mediaplayer, and click the next-song button, it starts at the first song and goes down the list as intended. But if I start the program, double-click an mp3 file to play it, then click the next-song button, the first song in the list plays, not the mp3 after the mp3 I double clicked.
btn.setOnAction((ActionEvent e) ->
{
if(doubleClicked)
{
player.stop();
media = new Media(rowData.toURI().toString()); // needs to go to next song
player = new MediaPlayer(media);
player.play();
return;
}
if(music.hasNext())
{
try
{
player.stop();
media = new Media(music.next());
player = new MediaPlayer(media);
player.play();
lbl.setText(media.getSource());
}
catch(MediaException a)
{
System.out.println("Unsupported Format");
}
}
});
rowData is the mp3 file when double clicked. I've tried a bunch of things but nothing seems to work. I have my own data structure for this, but its pretty much the same as the built in Iterator and ArrayList code.
You don't adjust the Iterator's position according to the new position in the playlist. Given the index of the item clicked by the user you can simply replace the Iterator by a new one using List.listIterator.
Here's an simplified example using a ListView and Strings that are displayed in a label:
private Iterator<String> songIterator;
#Override
public void start(Stage primaryStage) {
ObservableList<String> songs = FXCollections.observableArrayList("Song 1", "Song 2", "Song 3", "Song 4");
// start with first song
songIterator = songs.iterator();
Label songLabel = new Label(songIterator.next());
ListView<String> lv = new ListView(songs);
MultipleSelectionModel<String> selectionModel = lv.getSelectionModel();
lv.setCellFactory(t -> new ListCell<String>() {
{
setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.PRIMARY && evt.getClickCount() == 2) {
evt.consume();
// update label
songLabel.setText(getItem());
// iterator should return next item next
songIterator = songs.listIterator(selectionModel.getSelectedIndex()+1);
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
}
});
Button btn = new Button();
btn.setText("Next");
btn.setOnAction((ActionEvent event) -> {
if (songIterator.hasNext()) {
songLabel.setText(songIterator.next());
}
});
Scene scene = new Scene(new VBox(songLabel, lv, btn));
primaryStage.setScene(scene);
primaryStage.show();
}