4

I have a class (called Class_GUI) which has a panel with lots of buttons on it. Class_GUI has some methods that change the text and colour of the buttons.

I have also have a program with the actionPerformed method. When this is called it creates an instance of Class_GUI and repeatedly calls Class_GUI methods, changing the buttons etc.

The issue I'm having is that the buttons only display properly once the actionPerformed method has finished entirely whereas I want it to change after each Class_GUI method is called.

My attempt so far is in each Class_GUI method I do this at the end of the method:

SwingUtilities.invokeLater(Refresh_GUI);

Where Refresh_GUI is defined:

Runnable Refresh_GUI = new Runnable(){
    public void run(){
        frame.revalidate();
        frame.repaint();
    }
};
BenMorel
  • 34,448
  • 50
  • 182
  • 322
user1883352
  • 53
  • 2
  • 5

4 Answers4

3

Assuming that your actionPerformed method is being called within the context of the Event Dispatching Thread, no UI updates will occur until AFTER the actionPerformed method has competed, even using SwingUtilities#invokeLater won't change that, because until the actionPerformed method exits, the EDT won't be able to continue processing (amongst other things) repaint requests.

The best you can do, is start a second thread and from within that thread, update your UI components...but, you area going to be forced to use SwingUtilities#invokeLater as you should NEVER update any UI component outside the EDT.

The advantage though, is that the thread does not need to compete in order for the EDT to start processing the repaint request

UPDATED with Example

public class SwingThreadUpdate {

    public static void main(String[] args) {
        new SwingThreadUpdate();
    }

    public SwingThreadUpdate() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new BlinkPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class BlinkPane extends JPanel {

        private JLabel label;
        private JButton button;

        public BlinkPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;

            label = new JLabel("Blinky");
            label.setBackground(Color.RED);
            button = new JButton("Click me");

            add(label, gbc);
            gbc.gridy++;
            add(button, gbc);

            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    button.setEnabled(false);
                    new Thread(new BlinkTask(BlinkPane.this)).start();
                }
            });

        }

        private void setBlink(boolean blink) {
            label.setOpaque(blink);
        }

        private void reset() {
            button.setEnabled(true);
            label.setOpaque(false);
        }
    }

    public class BlinkTask implements Runnable {

        private BlinkPane blinkPane;

        protected BlinkTask(BlinkPane blinkPane) {
            this.blinkPane = blinkPane;
        }

        @Override
        public void run() {
            Blink blinkOn = new Blink(blinkPane, true);
            Blink blinkOff = new Blink(blinkPane, false);

            for (int index = 0; index < 10; index++) {
                if (index % 2 == 0) {
                    SwingUtilities.invokeLater(blinkOn);
                } else {
                    SwingUtilities.invokeLater(blinkOff);
                }
                try {
                    Thread.sleep(125);
                } catch (InterruptedException ex) {
                }
            }

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    blinkPane.reset();
                }
            });

        }
    }

    public class Blink implements Runnable {

        private BlinkPane blinkPane;
        private boolean blink;

        public Blink(BlinkPane blinkPane, boolean blink) {
            this.blinkPane = blinkPane;
            this.blink = blink;
        }

        @Override
        public void run() {
            blinkPane.setBlink(blink);
            blinkPane.repaint();
        }
    }
}

You might like to have a read through Painting in AWT and Swing for more information.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks. So I should start a new thread that calls SwingUtilities.invokeLater(Refresh_GUI); ? – user1883352 Dec 06 '12 at 19:26
  • It's more involved than you present it: any call into a Swing method may trigger listeners, which may well result in a repaint before control returns from the Swing method. – Marko Topolnik Dec 06 '12 at 19:28
  • 1
    @Marko: They can cause a repaint *request*; the painting itself is done via a discreet event. – Lawrence Dol Dec 06 '12 at 20:01
  • @user1883352 It all comes down to exactly what it is you are trying to achieve. We have no context so we are just stabbing in the dark... – MadProgrammer Dec 06 '12 at 22:18
  • @user1883352 I've added an example. Again, without context as to what is you are trying to do, we're just guessing – MadProgrammer Dec 06 '12 at 22:29
  • @SoftwareMonkey As Marko as pointed out, a `repaint` is a request, which is past to the repaint manager, these requests may be consolidated. When the repaint manager decides, it will seed a paint request to the EDT. This means that while you block the EDT, it can not process paint requests – MadProgrammer Dec 06 '12 at 22:30
  • +1 [against to put the SwingWorker everywhere](http://stackoverflow.com/a/8615861/714968), but not an answer to original idea , I think – mKorbel Dec 07 '12 at 06:58
  • @mKorbel To be honest, I'm not sure what the original idea was and I think a swing timer would have being easier – MadProgrammer Dec 07 '12 at 07:04
  • hehehe not Swing timer creates enless EDT, only Runnable#Thread with Thread.sleep() can to lock that in proper time / period (as I demonstrating in linked code), bunch of code lines required to wrote similair output to the screen by using Swing Timer, – mKorbel Dec 07 '12 at 07:14
2

Incase your actionPerform method calls code to update buttons in a for loop you could also add the updation code in the invokeLater that way both the updation and painting code will run one by one. Invoke later will execute only after current method completes its execution so only way to ensure painting happens faster is to break your tasks into smaller peices.

Achintya Jha
  • 12,735
  • 2
  • 27
  • 39
1

First, make sure you are only accessing any GUI components from the Event Dispatch thread (via invokeLater or as part of handling a GUI event).

Second, if you change any properties of a GUI component, it should automatically post an event to repaint itself. If not you can try invoking component.repaint(). But it's critical that the changes to the component properties happen on the EDT.

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189
  • Currently the Class_GUI methods look like this: private void Some_Method(){ Some_Button.setBackground(Color.WHITE); SwingUtilities.invokeLater(Refresh_GUI); } Are you saying this is wrong? – user1883352 Dec 06 '12 at 19:18
  • It is wrong if `SomeMethod()` (s/be `someMethod()`, BTW) is invoked from a thread other than the EDT. – Lawrence Dol Dec 06 '12 at 19:59
0

A simple solution is execute the entire ActionPerformed event less task to clean the screen at the end of the event queue. So, first it executes the cleanScreen() function because the rest of the event waits for all events finish.

    AnyButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            cleanScreen();        //Modify components before action performer event
            EventQueue.invokeLater( new Runnable() {
                @Override public void run() {
                    anytask();    //Action performer event
                }
            });                     
        }
    });
josepmra
  • 617
  • 9
  • 25