3

(In my applicaton with Swing GUI) I want to display GlassPane during some work performed in a loop or method, which is called after clicking JButton.

For example: (action performed after clicking a button)

if (item.equals(button)) {

    glassPane.setVisible(true);

    someTimeConsumingMethod();

    glassPane.setVisible(false);

}

Running this code results in not showing the glassPane during execution of someTimeConsumingMethod() - GUI just freezes for a moment, before result is displayed. Removing last line in that loop (glassPane.setVisible(false);) results in showing glassPane after the method is done (when GUI unfreezes).

Is there a simple way to show that glassPane before GUI freezes, or I need to use some advanced knowledge here? (threads?)

UPDATE1:

I've updated my code according to davidXYZ answer (with two changes):

(action performed after clicking a button)

if (item.equals(button)) {

    glassPane.setVisible(true);

        new Thread(new Runnable(){
            public void run(){
                someTimeConsumingMethod(); // 1st change: running the someTimeConsumingMethod in new Thread
                                           //             instead of setting glassPane to visible
            }
        }).start();

    // 2nd change: moved glassPane.setVisible(false); inside the someTimeConsumingMethod(); (placed at the end of it).

}

The point of 1st change is that setting glassPane visible in new thread right before running someTimeConsumingMethod in my GUI thread was revealing the glassPane after someTimeConsumingMethod finished (double-checked this).

Now it works fine, thank you for all answers. I will definitely check all the links you provided to actually understand threads!

UPDATE2: Some more info: someTimeConsumingMethod(); in my application is prepering new Swing Components accoriding to the XML data (cards builded from JButtons and JLabels with few JPanels where needed, and adding them in correct places).

UPDATE3: I am trying to make it work using SwingWorker's invokeLater method. Now it looks like that:

(action performed after clicking a button)

if (item.equals(button)) {

    glassPane.setVisible(true);

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() { 
            someTimeConsumingMethod();
            glassPane.setVisible(false);
        }
    });

}

It works not that good as code from UPDATE1 (but still - it works). Problems are:

  • glassPane loads without .gif animation (file is setted up in custom glassPane class - it works with UPDATE1 code)

  • there is small delay at the end of "working" process - first cursor changes to normal (from the WAIT_CURSOR), and after very short moment glassPane disappear. Cursor is changed by the custom glassPane class on activation/deactivation (no delay using new Thread way).

Is it correct way of using SwingWorker's invokeLater method?

EDIT: My mistake, I confused SwingWorker with SwingUtilities.invokeLater(). I guess the image issue is due to GUI freezing when the someTimeCOnsumingMethod starts.

TV.
  • 232
  • 1
  • 3
  • 14
  • Need to clarify. Do you want the `glassPane` to be visible while `someTimeConsumingMethod` is running and then close it right after the function is done? – davidXYZ Sep 26 '12 at 17:30
  • Yes, that is exactly what I want to achieve. So far I was thinking that cade execution is ordered from the top to bottom (looking at the code in some editor) - when one instruction end, next one starts, but I guess its not that simple. Will try to solve my problem with provided answers. – TV. Sep 26 '12 at 17:46
  • In that case, you definitely need another thread to handle some of the tasks. I have given an answer below with an example. – davidXYZ Sep 26 '12 at 18:16

4 Answers4

5

GUI just freezes for a moment, before result is displayed. Removing last line in that loop (glassPane.setVisible(false);) results in showing glassPane after the method is done (when GUI unfreezes).

this is common issue about Event Dispath Thread, when all events in EDT are flushed to the Swing GUI in one moment, then everything in the method if (item.equals(button)) { could be done on one moment,

but your description talking you have got issue with Concurency in Swing, some of code blocking EDT, this is small delay, for example Thread.sleep(int) can caused this issue, don't do that, or redirect code block to the Backgroung taks

Is there a simple way to show that glassPane before GUI freezes, or I need to use some advanced knowledge here? (threads?)

this question is booking example why SwingWorker is there, or easier way is Runnable#Thread

  • methods implemented in SwingWorker quite guarante that output will be done on EDT

  • any output from Runnable#Thread to the Swing GUI should be wrapped in invokeLater()

easiest steps from Jbuttons Action could be

  • show GlassPane

  • start background task from SwingWorker (be sure that listening by PropertyChangeListener) or invoke Runnable#Thread

  • in this moment ActionListener executions is done rest of code is redirected to the Backgroung taks

  • if task ended, then to hide GlassPane

  • create simple void by wrapping setVisible into invokeLater() for Runnable#Thread

  • in the case that you use SwingWorker then you can to hide the GlassPane on proper event from PropertyChangeListener or you can to use any (separate) void for hidding the GlassPane

best code for GlassPane by @camickr, or my question about based on this code

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Thanks for very detailed answer, I've got some reading now ;-). – TV. Sep 26 '12 at 19:19
  • as mentioned @Guillaume Polet and I too, move code execution e.g. `someTimeConsumingMethod();` to the `SwingWorker` or `Runnable#Thread` – mKorbel Sep 27 '12 at 11:12
  • You can see in **UPDATE3**, that someTimeConsumingMethod(); is running from SwingWorker right now, which is working with small issues described under the example. I don't know if I am using SwingWorker's invokeLater method in correct way. – TV. Sep 27 '12 at 11:29
  • you can to [use this code for simulating any of issues with GlassPane](http://stackoverflow.com/a/7049095/714968), there are four different methods – mKorbel Sep 27 '12 at 11:35
  • @TV You are confusing SwingWorker and SwingUtilities.invokeLater. They are very different things but are both related to Swing-thread. Consider using SwingWorker rather than SwingUtilities.invokeLater as I described in my answer – Guillaume Polet Sep 27 '12 at 12:33
3

You are blocking the EDT (Event Dispatching Thread, the single thread where all UI events are handled) with your time consuming job.

2 solutions:

  1. Wrap the calls to:someTimeConsumingMethod();glassPane.setVisible(false); in SwingUtilities.invokeLater(), this will allow the frame to repaint itself once more. However this will still freeze your GUI.

  2. Move your someTimeConsumingMethod() into a SwingWorker (this is the recommended option). This will prevent your GUI from ever freezing.

Read the javadoc of SwingWorker to understand better what is going on and how to use it. You may also learn a lot in this tutorial about Swing and multi-threading

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • there not reason why thanking, nor me, I'm talking about describtion :-) – mKorbel Sep 26 '12 at 17:35
  • @TV. then try to avoid use the way that you accepted, everything about Swing JComponents (in your case GlassPane) must be doe on EDT, or wrapped into invokeLater, and hard and long running code must be redirected to the runnable#thread, I can show you 1Mio codes where is required to block EDT, but works only in this form, have to calculating with big throubles, nor in case that there is multithreading, caused locked current JVM instance until is killed by using Task manager – mKorbel Sep 26 '12 at 20:17
  • Thank you for the warning. I accepted the answer maybe too early. I will keep it open for some more time and try to get through all materials provided. Please check UPDATE2 for some new info about my time consuming method. – TV. Sep 26 '12 at 20:47
2
 JButton startB = new JButton("Start the big operation!");
    startB.addActionListener(new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent A) {
        // manually control the 1.2/1.3 bug work-around
        glass.setNeedToRedispatch(false);
        glass.setVisible(true);
        startTimer();
      }
    });

glasspane here used here is FixedGlassPane glass;

ref: http://www.java2s.com/Code/Java/Swing-JFC/Showhowaglasspanecanbeusedtoblockmouseandkeyevents.htm

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
Alvin Pradeep
  • 618
  • 4
  • 13
0

Guillaume is right. When you are on the main thread, each line will finish before the next line. You definitely need another thread.

An easy way to solve your problem is to spin off the display of the glasspane in another thread (normal thread or Swing threads - either will work fine).

if (item.equals(button)) {
    new Thread(new Runnable(){
        public void run(){
            glassPane.setVisible(true);
        }
    }).start();
    someTimeConsumingMethod();
    glassPane.setVisible(false);
}

That way, a different thread is blocked by setvisible(true) while someTimeConsumingMethod() runs on the main thread. When it's done, glasspane will disappear. The anonymous thread reaches the end of the run method and stops.

davidXYZ
  • 719
  • 1
  • 8
  • 16
  • I used your example in my code, check my updated question post if interested. – TV. Sep 26 '12 at 19:19
  • Okay, so lets violate the EDT shell we...It would be better to use a `SwingWorker` and turn off the glass pane in the `done` method... – MadProgrammer Sep 26 '12 at 20:19
  • -1. This is really a bad advice. UI-manipulation should be done on the EDT. @MadProgrammer is right. – Guillaume Polet Sep 26 '12 at 20:22
  • If it's not how you do it, then it's bad advice? – davidXYZ Sep 26 '12 at 20:23
  • @davidXYZ You are still blocking the EDT with the `someTimeConsumingMethod`. Try resizing the window while the method is running, it won't work properly, because repaint request are no longer been processed due to the fact the the `someTimeConsumingMethod` is now block the event queue – MadProgrammer Sep 26 '12 at 20:24
  • @MadProgrammer: in my application whole frame is locked for a moment, when XML data is loading (thats when Glass Pane is visible - blocking input and providing information that application is reading data). Frame is not-resizable. Is it still bad way to do this in that case? – TV. Sep 26 '12 at 20:40
  • @MadProgrammer I've not actually tried resizing the window. If what you are saying is true, then I'll take note of that. I just knew the display had to be in a diff thread than the method. Anyway, look at the question's update. TV kept the UI stuff on the main thread and method on the spin-off. That works. Thanks for the info. – davidXYZ Sep 26 '12 at 20:40
  • @davidXYZ Your assumption _"I just knew the display had to be in a diff thread than the method."_ is correct. But on top of that, everything related to UI must be done on the EDT because stuffs like RepaintEvent, InputEvent, etc... are dispatched on that thread. Eventually, this means that everything that is time-consuming must move off the EDT. For advices on how to do, take a look at the other answers. – Guillaume Polet Sep 26 '12 at 20:46
  • When you open any program and start loading data, do you usually want to be resizing your window? What for???? I wonder why anyone would complain about that. TV, I dont think there's a problem with not touching your frame while data is loading. Can you always drag around MSWord, Eclipse, web browser or any other software while data is loading?? It's funny sometimes what people pick on. – davidXYZ Sep 26 '12 at 20:48
  • Please check UPDATE2 for some new info about my time consuming method. – TV. Sep 26 '12 at 20:48
  • davidXYZ, for now I am happy with your answer, and I don't think, that your are not right, because it works, but it may be due to the lack of (my) knowledge. – TV. Sep 26 '12 at 20:51
  • @GuillaumePolet I've learned that today. I didn't know it was always better to leave the UI stuff on the EDT. Thanks – davidXYZ Sep 26 '12 at 20:52
  • @davidXYZ the problem with your example is that it is for `someTimeConsumingMethod` and `glassPane.setVisible(false);` to be executed BEFORE your update thread runs (to call `glassPane.setVisible(true)` which would not be desirable at all. The other problem you run into is dirty updates, where one part of the screen is updated out of sync with the rest. You see this a lot when people use table row sorters and try adding/removing the table model out side the EDT. – MadProgrammer Sep 26 '12 at 22:20
  • 1
    @davidXYZ *When you open any program and start loading data, do you usually want to be resizing your window?* is irreverent, you're code will make the program look like it's hung and the user will be tempted to forcefully terminate it. IMHO it looks unprofessional and shabby. I agree, you should provide feedback to the user that you executing a long running task, but to the determinent of the usability of the program. The 1st rule of Swing, is updates to the UI (any part of the UI) must be done on the EDT, the 2nd rule is, to block the EDT. – MadProgrammer Sep 26 '12 at 22:22
  • 1
    @MadProgrammer For the last part of your comment, I think you meant "Do NOT block the EDT" – Guillaume Polet Sep 26 '12 at 22:31
  • @GuillaumePolet Good morning :P Yes, you right, I will have a chat to my fat fingers shortly – MadProgrammer Sep 26 '12 at 22:38