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!
Related
Okay, so this problem has been plaguing me for the past few days. And before anyone comments about it, yes I have made the jframe visible and I have added all the components that I need to it. Also, all components are added well before the GUI is set to visible and activate() is called.
So I am trying to run a couple simeple lines of code.:
g.setVisible(true);
g.activate();
Simple enough. g is an object made from a class I made GUI which extends JFrame. and activate() is a method that runs an infinite loop that just does a bunch of things until the user tells it to exit. However, when the program gets to the g.setVisible() line it opens a JFrame that is the size I specified however is completely devoid of anything. Then it moves onto the g.activate(); which at the moment runs for a specific amount of iterations and stops. At which point it finally decides that it can go back and display my GUI. The issue with that is that the GUI is meant to be updated by the loop from active() and keep the user in the know of what is going on.
Any help is appreciated let me know if you need more detials and thank you in advance.
In many different UI frameworks, it's common for the methods you call to queue some work rather than perform it immediately. From your description, it seems like setVisible() may be creating a native window but then queuing the rendering of the components. Since this code is (presumably) running in the UI thread, it won't perform the work it queued until after running activate().
Long-running tasks should never be run on the UI thread. In Swing, you can use SwingWorker or explicitly create a background thread.
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 need to refactor my application, since I'm running into rendering issues which are probably a result of not properly using the event dispatch thread. In order to do things right, I try to gather information. I already started this thread, which was about the EDT:
When exactly are components realized in Swing
Now I would like to know more about the best way to nest Panels.
Let's say I have the following structure:
[PanelA [PanelB [PanelC ]]]
What would be more performant (less internal calls to invalidate())
Order 1 (first inner components then outer):
PanelB.add(PanelC);
PanelA.add(PanelB);
Order 2 (first outer components then inner):
PanelA.add(PanelB);
PanelB.add(PanelC);
If someone also has more info/links/hints etc on how to get the most performant UI I would really appreciate that. Most Tutorial just explain the basics.
A related question:
Since all JComponents are Containers, I consider saving some JPanels, by adding components to let's say a JButton. Is this good practice:
JButton b=new JButton();
b.setLayout(new BorderLayout(),BorderLayout.Right);
b.add(new MyComponent());
How can I know which layout a Component uses by default and what could possibly happen, when I change the Component's Layout?
Thanks a lot for your help.
You should not worry about the order of adding the components, the difference will not be noticeable to the user.
You should not worry about the performance of the UI in general. Swing code in itself will be "fast enough". Performance/responsiveness gets interesting only if you are starting long-running non-UI tasks from the UI.
If you add panels to buttons, it will confuse the user. You can check the source code of the components to see their layout managers (but this is very rarely necessary)
I realize that without code this might be hard to answer but the problem is I am not sure what code to post. My problem is that when I load an XML file, unmarshal it and then iterate a ArrayList of values loaded in to repopulate a JLabel array that get placed on a JPanel inside a JScrollPane, the graphics do not update until the entire processing is done. I discovered this when I created a JFrame that opened up with a JProgressBar. The bar does not even show or paint on the pane Frame until everything is done. This defeats the purpose since I am trying to have the progress bar show the progress of the decoding. While debugging that, I discovered that if I isolated the code (except for actually loading the file and doing the unmarshalling) the bar worked. That got me looking and I noticed that the other components mentioned were not updating when the individual JLabels were added and sized until all process was complete despite constant repaints. Since I am still new to JAVA I thought I would ask if anyone might know a reason that all of these components would not be graphically updating while this loop is going on.
As a note, in the loop, I call the add and repaint functions to place the newly decoded JLabel on the pane but it does not show until all is complete just like the bar. And all of this is running single thread (except for the JProgressBar which is spun into it's own thread).ll
I can post code if you like just please direct me to which code you want (IE the XML decoder and loop, the add and size of the JLabels, the instantiations, etc).
Your problem is that you're doing all processing on the Swing event thread or EDT. The solution is to use a background thread such as can be obtained by a SwingWorker object.
For more on this, please check out the Java Swing tutorial called Concurrency in Swing. It will describe the event thread, why it is important to respect and not to block, and how to do background processing with a SwingWorker object.
You are doing your work on the "event dispatch" thread, which is the same thread used to run swing.
You need to do your work in a separate thread. Take a look at the SwingWorker class
As most Java programmers know, updates to Swing GUIs should only be done on the AWT event dispatching thread and the recommendation is that long-running processes be executed on a "worker" thread, with updates sent to the event dispatching thread using SwingUtilities.invokeAndWait() or SwingUtilities.invokeLater().
How do you stop the user from proceeding with the application while the long-running process is completed? Do you gray out the controls and then have the worker thread reenable them using the SwingUtilities calls mentioned above?
Is there a better alternative pattern?
I would consider 3 solutions :
disable the components of the panel : it's generally what I do. Unfortunately, Swing does not provide a simple way to disable a panel and all its children, but it is easy to do the recursion (see this other SO answer for that). Another problem is that some Swing components look the same when enabled and disabled (JList, for example)
hide the panel with a CardLayout : in a panel with a CardLayout, add 2 components. The first is the panel that hosts the components to inactivate, and the second is a panel showing a "loading" or "please wait" message. A simple JLabel in a Gridbaglayout does the trick. Then, you just have to switch from one to another. I use this technique for places where a result of a computation/request is to be displayed.
put some kind of component on top of the panel that consumes the mouse events : you can do it yourself with a LayeredPane, or you can use a dedicated utility. JXLayer can do that (I read that JXLayer will be included in Java 7, so this may become the 'standard' solution to this kind of problem).
There are several ways and the selection of which, mostly depends on the design and layout of your GUI.
Use a Progress Bar - Replace the panel or an area that you don't want a user touching with a progress bar. This will prevent you from having to deal with events you don't want yet, while still making it clear to the user that something is happening in the background.
Disable buttons and add a Wait Cursor - Use setEnable(false) while work is being done and nd possibly change the cursor to a Wait Cursor. This again makes it clear that an option is not available yet only for a temporary period.
Don't respond to events or throw up a GlassPane - This is less user-friend as it makes the application look unresponsive, however it can acceptable in some situations.
One way I have seen it done is to use Jframe.setGlassPane() and set a component that eats all events. You can also be creative and use flash kind of rotating-wait gif in your glasspane. But note that setting a glass pane may not be all you want. For more advanced requirements, you may have to play around with event-queues.