I'm having a layout problem when adding a Label to a Pane which is added to another Pane.
Like in this example:
public class MyClass extends Pane {
private final Pane myPane;
public MyClass() {
this.myPane.prefWidthProperty().bind(this.widthProperty);
this.myPane.prefHeightProperty().bind(this.heightProperty);
this.getChildren().add(this.myPane);
}
#Override
layoutChildren() {
this.foo();
}
private void foo() {
this.myPane.getChildren().add(new Label("foo"));
}
}
The problem is that it just keeping calling layoutChildren infinitely. One thing that is weird is that if I add a Text instead of a Label, the "problem" doesn't occour.
I've checked every node sizes and they don't change. It seens to me that someone is expanding and for that the layout is called, but I just can't find where.
Is there something trivial that I'm missing?
layoutChildren is called constantly by the QuantumToolkit. The entire scene graph is traversed and each node has this method called during the lifetime of the application. It never ends.
From Oracle:
Threads
The system runs two or more of the following threads at any given time.
JavaFX application thread: This is the primary thread used by JavaFX application developers. Any “live” scene, which is a scene that is part of a window, must be accessed from this thread. A scene graph can be created and manipulated in a background thread, but when its root node is attached to any live object in the scene, that scene graph must be accessed from the JavaFX application thread. This enables developers to create complex scene graphs on a background thread while keeping animations on 'live' scenes smooth and fast. The JavaFX application thread is a different thread from the Swing and AWT Event Dispatch Thread (EDT), so care must be taken when embedding JavaFX code into Swing applications.
Prism render thread: This thread handles the rendering separately from the event dispatcher. It allows frame N to be rendered while frame N +1 is being processed. This ability to perform concurrent processing is a big advantage, especially on modern systems that have multiple processors. The Prism render thread may also have multiple rasterization threads that help off-load work that needs to be done in rendering.
Media thread: This thread runs in the background and synchronizes the latest frames through the scene graph by using the JavaFX application thread.
Pulse
A pulse is an event that indicates to the JavaFX scene graph that it is time to synchronize the state of the elements on the scene graph with Prism. A pulse is throttled at 60 frames per second (fps) maximum and is fired whenever animations are running on the scene graph. Even when animation is not running, a pulse is scheduled when something in the scene graph is changed. For example, if a position of a button is changed, a pulse is scheduled.
When a pulse is fired, the state of the elements on the scene graph is synchronized down to the rendering layer. A pulse enables application developers a way to handle events asynchronously. This important feature allows the system to batch and execute events on the pulse.
Layout and CSS are also tied to pulse events. Numerous changes in the scene graph could lead to multiple layout or CSS updates, which could seriously degrade performance. The system automatically performs a CSS and layout pass once per pulse to avoid performance degradation. Application developers can also manually trigger layout passes as needed to take measurements prior to a pulse.
The Glass Windowing Toolkit is responsible for executing the pulse events. It uses the high-resolution native timers to make the execution.
Layout Children is called 60 times/second on all nodes that have been altered in some way. So if a child deep in the graph is changed, all parents of that child will have layoutChildren called.
Your overriden layout method invokes foo, which in turn causes a new Label to be added. Unless I am mistaken, this will cause another layout pass to be initiated, which in turn will add a new Label, thus causing your infinite cycle.
Related
When loading in a new FXML and setting the center of a BorderPane there is a brief 'freeze' of the application where existing animation, whether from a Timeline, or from a gif in an image view, will stop. I'm using this code to change the centerView:
#FXML
public void handleChangeView(ActionEvent event) {
Task<Parent> loadTask = new Task<>() {
#Override
public Parent call() throws IOException {
String changeButtonID = ((ToggleButton) event.getSource()).getId();
Parent newOne = getFxmls().get(changeButtonID);
if (newOne == null) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/" + changeButtonID + ".fxml"));
newOne = loader.load();
getFxmls().put(changeButtonID, newOne);
}
return newOne ;
}
};
loadTask.setOnSucceeded(e -> {
getMainUI().setCenter(loadTask.getValue());
});
loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());
Thread thread = new Thread(loadTask);
thread.start();
}
And while this does the job in keeping the UI responsive in the load time, when the center stage displays for the first time there is a noticable lag. Looking at a CPU profile:
I'm not sure if the delay is from the initialize function running, or the loading of the elements. Here's a gif of the program, you can see the visible delay:
By loading it up in VLC and progresing frame-by-frame the delay looks to be 5 or 6 frames in a 30 fps video, meaning about a 200ms delay which implies the animation freezes for more than the 50ms or so initialize method. (Maybe the entire load method freezes the animation?)
The question is, is it possible to keep the animation smooth during this?
//********* EDIT **********//
So I went through the project and cut out as many methods and classes as possible, reducing the entire game to 6 minimal classes. I STILL have the delay in pressing the character button ('you' button). With such a minimal example I'm lost to what could be wrong. I've uploaded this to google drive for anyone to take a look.
https://drive.google.com/open?id=17A-PB2517bPJc8Dek-yp2wGXsjTyTj1d
There is nothing "wrong" with your code (at least in regards to this issue).
The issue you are experiencing also has nothing to do with the loading of the FXML (which is very slow and you have correctly handled off FX-Thread).
The stutter happens for These reasons:
relativly large hierarchy in character.fxml
lots of CSS (delete the main.css and you will notice; the stutter is slightly less prominent)
dynamically changing the scene graph (adding/removing Nodes during runtime)
Every time you replace the center of the mainView with some large Node, it causes the JavaFx runtime to completely re-layout and re-style (at least) that node. This happens on the FX-Thread, hence you notice the stutter.
One possible mitigation is a classic game dev technique: Pre-allocating as much as possible.
Simply load all necessary FMXLs once during startup and put them into the scene graph. In your click handlers then, change the visibility or (Z-)position of the Nodes you want to show/hide.
This is a good use case for a StackPane for example.
I adapted your code a little to demonstrate what I mean:
Prototype
Check out these ressources to learn more:
http://gameprogrammingpatterns.com/
https://stackoverflow.com/a/26537688/1271937
https://www.javaworld.com/article/2074652/core-java/javaone-2012-javafx-graphics-tips-and-tricks.html
It is bad practice to do big jobs on the UI thread as if you do, those big jobs will cause the program to hang (not accept user input or render any new data) until that job is finished.
I am looking to add a widget to our code base that will indicate to developers when they have committed this taboo. My idea, and one I've seen on a number of other applications, is to have some component that is constantly moving at a constant speed, such as a bar that is constantly twirling on the screen. With such a tool, if a developer is working and accidentally does something that is more computationally difficult than he expected on the UI thread, this spinning bar will become choppy, indicating to him, when he does functional testing, that he needs to implement mechanisms that will cause this job to be executed elsewhere.
One odd requirement on this code is that it should be completely non-existent in production builds, and only present in dev builds, since it is a widget not for users, but for developers.
I jumped into the Canvas objects and wrote up a quick component that simply spins a teal bar. The idea is that if a big job is dumped on the UI thread, the bar will stop spinning (since the FX job queue wont continue dispatching) and the bar will jump forward, rather than rotate smoothly (as it does when the program is at rest).
Below is a screen-shot of this first implementation:
(notice the teal bars, which, if you saw our application running, would be rotating slowly but steadily --hopefully anyways)
The issue here (as you might notice) is that our layout's been screwed up. This is because I'm modifying the scene graph from this:
Scene
RootComponent
Content
to
Scene
obnoxiousPane
Canvas
Spinner(s)
RootComponent
Content
Modifying the scene graph in such a way has things like preferred height, mouse events and (presumably) any number of other events getting dispatched to the spinners rather than the content components.
Of course, when we go to production, I would like to have the original scene graph in the version that we give to our users.
So my question is this: How should I go about correcting these problems?
I could go after each of them individually as they come up, writing a lot of custom code to do things like
obnoxiousPane.prefHeightProperty().bind(content.prefHeightProperty)
obnoxiousPane.prefWidthProperty()//...
spinner.setMouseTransparent(true)
spinner.setOtherEventsIProbablyCantEnumerateWithoutSeriousResearchTransparent(true)
Or I could try to go after this problem with reflection, attempting to bind every property in the content pane to the corresponding obnoxiousPane property, but this seems like a bug breeding ground.
Or... what? I'm hoping there's some LightWeight component or ImNotReallyHereProperty that I can leverage to add this development aid.
Thanks for any help!
Your approach seems fundamentally flawed. You shouldn't be stalling the JavaFX application thread.
Instead you should have a concurrent process and update the UI as appropriate as the process starts, progresses and completes.
See this java2s sample for using the JavaFX concurrency and progress indicator facilities for an example of such an alternate approach.
If you want to disable some portion of the UI for a time, nodes have a disabled property which you can set. You can use CSS to style a disabled node so the user has some indication that the thing hasn't just hung and is deliberately disabled.
I have a Swing application for creating RPG characters.
It has a nested JTabbedPane architecture, so there is a HeroTabsPanel which has HeroPanels and each of those has some more tabs like Stats, Items etc.
So the GUI is comprised of the upper tabbed pane for heroes, the lower tabbed pane for current hero tab and an EditViewPanel which displays an EditingView corresponding to each Tab when the tab is selected.
Performance has been bad from the start, but when I added the upper level hero-tabs (to have multiple heroes in editing simultaneously), switching between tabs became even slower.
It takes a few minutes for something to be displayed in the new JFrame after all the code has finished adding components. Could this be the layout?
I am using MigLayout with absolute positioning. Actually, before I added upper tabs, there had been some “Unstable Cyclic Dependency in absolute-linked values!” issues, but now there aren’t somehow.
In stateChanged() I have what amounts to this:
editViewPanel.activateView(currentTab.getLinkedView());
And in activateView():
removeAll();
currentView = heroView;
add(currentView, "pos 0 0");
currentView.refresh();
revalidate();
But like I said, all the code execution is finished in reasonable time, I've done my profiling, but after it is done, there a delay of considerable length, up to a few minutes in the case of first time adding to a new JFrame.
Another issue is that when I need to update a panel within the lower tabbedpane, especially StatsPanel which is made up of string-int elements added for each parameter in a few columns, I get yet another large delay. It also depends on MigLayout and for some reason (bad design, I know) has absolute positioning as well. But I have changed it so that no components are removed/added after initialization, only setText() is used and still there is a big delay after the code is finished executing.
I’m planning to use SwingWorkers but I would like to understand the problem better before I start solving it. I suspect it's simple, but I am a bit incredulous about how big the delays it's causing are. I'd appreciate some hints/examples about SwingWorkers.
I can add more code if you have some general idea where the issue might hide.
Any suggestions are welcome, thanks!
I never encountered a Swing UI which was slow due to the number of JComponents visible. What I do often see is a sluggish UI because the UI thread is used/abused to perform all kinds of work not related to UI updates.
In Swing, there is only one thread on which you may update the UI, and that same thread is responsible for painting the UI (the Event Dispatch Thread). If you block this thread by e.g. performing calculations on it, the UI will not be able to repaint or react on user input while your calculation is running. That is why you must perform all heavy work on a worker thread. This is clearly explained in the Concurrency in Swing tutorial.
Not 100% sure that is what happening in your case, but it definitely sounds like it. If you want to be sure, take a thread dump while you are waiting for your UI and see what the thread named AWT-EventQueue-0 is doing at that moment. If it really takes 5 minutes before your UI is updated, you must be able to locate fairly quickly what is blocking the UI.
Ok, I finally worked it out by going through the EDT dump. The freeze was due to layout, unlikely though it had seemed.
MigLayout was trying to figure out the sizes of all the components every time new tab was selected, and probably for all the components in all the tabs on init.
The solution is simply to override getPreferredSize() for the JTabbedPanel implementation.
#Robin, thanks for the thread dump hint!
I am combining Swing and Java3D together. Manipulations of swing components should be done using the Event Dispatcher Thread, while manipulations of Java3D components should be done in the BehaviourSchedulerThread.
Java3D renders the scene and then executes all of the behavior associated to the scene.
I have a MouseListener enabled on the Canvas3D. Events are posted into the AWT Event queue. I then want to modify the Java3D environment based on these events, so I use a special Behavior where I can post Runnable's to. This makes sure the Runnable's are executed during the Behavior cycle of Java3D (and do not modify anyting during the Render cycle).
Suppose some operations in the Behavior want to modify the Swing model. I then have to post a new Runnable to the EDT.
Is this the correct way to do it?
Using this technique, I experience a lot of problems on a mouse listener. I update a point in my Java3D model in the behaviour, and I update the swing GUI at the same time.
Update:
The problem can be more clearly defined as follows:
I have a JButton "spin cube" which has an ActionListener. Once the ActionListener is fired, it pushes the AWTEvent into a Java3D Behavior. Once the Behavior fires, it modifies the Scene graph and then modifies the JButton actionListener and text to become "Stop spinning".
Click on the JButton twice.
The first AWTEvent gets dispatched to SpinActionListener. The cube starts spinning and the JButton actionListener is modified to StopSpinningActionListener.
The second AWTEvent gets dispatched to StopSpinningActionListener. The cube stops spinning and the JButton actionListener is modified to SpinActionListener.
What actually happens is the following:
Click on a JButton twice. Both AWTEvents get dispatched to SpinActionListener. This creates a Runnable to execute inside the J3D Behavior.
The first AWTEvent starts a timer to spin the cube. It then posts a Runnable to the EDT to modify the button.
The second AWTEvent starts a timer to spin the cube. The cube will now spin twice as fast. It then posts a Runnable to the EDT to modify the button.
Obviously, I should not be depending on AWTEvent's getting processed sequentially. I cannot wait in the EDT for the behavior to fire, because any SwingUtilities.invokeAndWait() will then cause a deadlock.
Which WakeupCriterion is used to wake-up your special Behavior object?
Java 3D's source code includes the utility classes
com.sun.j3d.utils.behaviors.mouse.MouseBehavior/MouseRotate
which listens to Canvas3D's AWTEvents. One of two alternatives can be chosen:
MouseListener with WakeupOnBehaviorPost or
WakeupOnAWTEvent.
This code sample might be helpful.
Initiating a Swing component update from within the Behavior.processStimulus method via SwingUtilities.invokeLater shouldn't cause any problems.
I have a design related question that I am trying to find an answer to.
Here is the scenario.
Suppose that you want to do something expensive (time-consuming) as a result of user input (e.g. loading huge amounts data from some database, reading large files). The strongly recommended way is to do the time-consuming work in a separate thread and never ever block the EDT, or else the GUI will become unresponsive.
There are scenarios however when you should not provide inputs to the GUI unless the background task is finished. In my specific case, only after the background work is finished, I can determine which GUI elements should be visible and enabled/disabled. Only those GUI elements which should be visible and enabled should respond to the user inputs otherwise the behavior may be unpredictable in my specific case.
This is what I am doing to handle such a scenario.
Step 1: Before I am about to start a time-consuming operation.
Change the cursor to a busy cursor.
Add mouse listeners to the glasspane of component's top-level frame.
Make the glasspane visible so that it can receive mouse events. The glasspane doesn't do anything as a result of mouse inputs.
Step 2: Execute the time-consuming operation in a background thread. The background thread has a finally block that notifies the event thread when the job is finished (completed or aborted due to an error).
Step 3:
Switch the mouse cursor back to normal.
Remove listeners from the glass pane.
Make the glasspane invisible, so that mouse events go to their intended recipients.
Is this the correct approach to handle such situations?
What do you guys recommend?
SwingWorker can be used in this context. Related controls can be disabled when the background task is started and re-enabled in done(). In this related example, the run button is conditioned to toggle between "Run" and "Cancel".
Addendum: A back-port to Java 1.5 is available here.