I have a small program which gets called by another program, downloads/renames some files and closes itself afterwards. I want to show the user the progress with some simple text. My problem is, that the logic of the code runs before the view is completely shown. The window is visible but the content (in this case the sample text) is not.
I have already tried setOnShown() (as seen in my example) or setOnShowning(). I also tried not using a fxml file for the layout. But nothing seems to work.
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
Text t = new Text();
t.setText("sample Text");
root.getChildren().add(t);
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("FXML Welcome");
primaryStage.setScene(scene);
primaryStage.setOnShown(e -> {
updaterLogic(); //do some work
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void updaterLogic(){
try {
Thread.sleep(10 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
I want the view to be completed before running the actual logic. How can i archieve this?
To execute something after a window has been shown you can:
Listen for a WindowEvent.WINDOW_SHOWN event.
See Window.onShown and Window.addEventHandler
Listen to the Window.showing property and react when it changes to true.
Put the code after the call to show().
But not showAndWait() as that method doesn't return until the window is closed (and is illegal to call on the primary stage).
However, how to do this is not your problem (you're already using onShown). Your problem is caused by how you execute the "updater logic". Inside your updaterLogic() method you are calling Thread.sleep (simulating work). Since event handlers are executed on the JavaFX Application Thread that causes said thread to block. While the FX thread is blocked it can't do anything related to the GUI (e.g. layout passes, trigger rendering pulses, process user events, etc...)—thus the frozen window with no content rendered.
Move the "updater logic" onto a background thread so that the FX thread remains free to do its work. For general information about concurrency in Java, see Lesson: Concurrency and the java.util.concurrent package. For JavaFX specifics, see Concurrency in JavaFX, the javafx.concurrent package, and Platform.runLater(Runnable).
Here are two golden rules of JavaFX:
You must never block, nor execute long running work on, the JavaFX Application Thread.
You must always access and modify a live scene graph on the JavaFX Application Thread.
This is documented by Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
Note: Some nodes, such as WebView, must be created on the FX thread; this will be documented as appropriate.
Related
If my controller class is
public class FXMLDocumentController implements Initializable {
#FXML
private TextArea msgArea;
public void initialize(URL url, ResourceBundle rb) {
someThread.start();
}
}
How can I change the value of my TextArea from the thread?
EDIT: I used tasks to to solve this problem. Thanks to everyone who tried to help.
Whenever needing to update a UI element from a thread, you must get the JavaFX UI thread to do so. Attempting to update an element from a different thread may lead to an exception, but could also lead to some unexpected behaviour.
Fortunately, JavaFX includes a useful way to do this. Simply add this in the code that runs on your separate thread:
Platform.runLater(() -> {
msgArea.setText("Your text");
});
It is better to use a task or service, because those provide in-built means of updating UI elements. For example, a task allows one to call updateMessage("...") or updateProgress("..."), which updates a bound element without you even needing to call the UI thread.
Im having issues with understanding what should be the model in my ViewLoader statement while making a JavaFXML MVC project
I have a button on the main menu in which when the user clicks it will take me to another window called known as the build menu.
Ive tried a multitude of possible models that I think would work including getBuild etc.
https://imgur.com/Ubz43CI
Here is a screenshot of my Controller and View file
https://imgur.com/0C3FUG3
Here is a screenshot of my Model file
The expected result based off a similar project ive found online is that when the button is clicked a new window pops up. Im assuming the reason this doesnt work is because the getBuild method/statement needs to be initialised in the Controller class however I am unsure as of how to do that as getBuild is a method in my Model class
If you are only Navigating from one scene (window) to another the code is as listed below. Why do you think you need to have a MVC pattern to do this is is confusing.
Here is the code to go from one scene to another we name all our Anchor Pane's somenamePane so we know where we are and where we are going.
public void goTO() throws IOException, SQLException{
stage = (Stage)paneStart.getScene().getWindow();// pane you are ON
ckbookPane = FXMLLoader.load(getClass().getResource("manager.fxml"));// pane you are GOING TO
Scene scene = new Scene(ckbookPane);// pane you are GOING TO
scene.getStylesheets().add(getClass().getResource("checkbook.css").toExternalForm());
stage.setScene(scene);
stage.setTitle("Check Book Manager");
stage.show();
stage.sizeToScene();
stage.centerOnScreen();
}
I am trying to make a JavaFX application (running in the background to show up (set visible)) by a specific keystroke and to make the window the active window immediately. Therefore I set the primary stage alwaysOnTop-property true, call stage.toFront() and finally call stage.requestFocus(). Afterwards I request focus for a text field. When the window is made visible I would like to instantly start typing into the text field.
However, when I for example have a Ubuntu-terminal selected and make the window visible and start typing, the application is shown on top of everything, however, the typing goes to the terminal. The application window is not active! Nevertheless, the focused property of the stage is true. Is that a bug or am I missing something? Is it OS related?
Edit: I am willed to give my little hack-around for this problem that I am using at the moment, because the internet is suggesting, that a lot of people are facing this problem: Since I am working on a linux maschine I have access to the wonderful tool wmctrl. It is part of most standard repositories. wmctrl -a WINDOWNAME sets the window with the name WINDOWNAME active. For now, I simply call this tool from my source code when I need the window to be active. Since this is more of a dirty hack than any thing else, I sure want to get rid of it.
Not perfect, but it works:
Platform.runLater(() ->
{
//primaryStage.setAlwaysOnTop(true);
//primaryStage.setAlwaysOnTop(false);
primaryStage.setIconified(true);
primaryStage.setIconified(false);
});
If your node is not getting focused, try wrapping requestFocus() in a Runnable and call Platform.runLater():
final TextField text = new TextField();
Platform.runLater(new Runnable() {
#Override
public void run() {
text.requestFocus();
}
});
I have an Mfc application where I want to open a Qt modal window, while the Mfc application is still running behind.
To start a Qt application from an Mfc, I got the how to there. This page creates a Dll but my solution creates a static library instead.
As for creating a Qt modal window with main application running behind, it looks easy.
My problem is that it looks like that I cannot combined both solution.
The following code starts the Qt window, but I strongly suspect that I am blocking my main thread and main thread is not able to receive events.
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget win( hWnd );
win.showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI(&win);
devicesConfigurationWizardUI.exec();
}
exec function is probably blocking main thread
Following solution should not block main thread, but the Qt windows appears only for few miliseconds and disappeared
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget win( hWnd );
win.showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI(&win);
devicesConfigurationWizardUI.show();
}
So, the question is, is it possible to use show function instead of exec function?
(This works if I am creating a Dll and calling the Dll in a worker thread from my Mfc application, but I do prefer using static library)
I did not read enough the walkthough.
It is said : "Since we want the dialog to be modeless we cannot create the QWinWidget on the stack, since it would be deleted when it leaves the scope, and all its children, including the dialog, would be deleted as well. Instead we create QWinWidget on the heap, using operator new"
So, to make my window non modal while giving control back to main thread, I should do this :
void QtMfcFacade::startDevicesConfigurationWizard(HWND hWnd)
{
QWinWidget *win = new QWinWidget( hWnd );
win->showCentered();
DevicesConfigurationWizard devicesConfigurationWizardUI = new DevicesConfigurationWizard (win);
devicesConfigurationWizardUI->show();
}
I am trying to move my QSystemTrayIcon module to a separate thread. I am getting the below error while running the application. The Initialize() method addAction causing the error.
QWidget: Must construct a QApplication before a QPaintDevice
My sample code snippets:
Declared the global variable
QMenu myMenu;
Member Variable in the header file
QAction* openFile;
In the constructor
openFile = new QAction(parent);
Initialize()
{
myMenu.addAction(openFile);
}
void myApp::run()
{
Initialize()
}
You must not use any GUI-related classes outside your main thread, i.e. the one QApplication was created in. Hence you cannot move your QSystemTrayIcon stuff in a separate thread. And no, there is no workaround.
You are approaching this problem backwards. The reason that "The TrayIcon menus are not displaying if some functionality is running at the background Menus will display after the process completion." is because you are blocking the GUI thread when you wait for things. Do not block the GUI thread by waiting on things. Most of Qt provides signals that fire when things get accomplished (when they succeed or fail). Those are the nonblocking APIs that you should be using.