Repainting multiple JPanels from one control panel

so I am trying to set up an application where I have multiple panels inside a jframe. lets say that 3 of them are for display only and one of them is for control purposes. I'm using borderLayout, but I don't think the layout should really affect things here.

my problem is this: I want the repainting of the three display panels to be controlled by the buttons on the control panel, and I want them all to run synchronously whenever a button on the control panel is pressed. for this i have installed this little method:

public void update(){
            while(ButtonIsOn){
                a.repaint();
                b.repaint()
                c.repaint();
                System.out.println("a,b, and c should have repainted");

                }
    }

      

where a, b and c are all display panels and I want a, b and c to redraw continuously until I press the button again. the problem is that when I loop, the message is printed in an infinite loop, but none of the panels do anything, i.e. none of them are redrawn.

I've read on the event dispatch thread and rocked multithreading, but nothing I've found so far has actually solved my problem. can anyone give me a gist of what I am doing wrong here, or even better, some sample code that handles the situation I am describing? thank...

+3


source to share


3 answers


The java.util.concurrent package provides very powerful concurrent programming tools.

In the code below, I am using ReentrantLock

(which works the same as the Java keyword synchronized

, providing mutual, exclusive access by multiple threads to the same block of code). Another great thing it ReentrantLock

provides is that it Conditions

allows you to Threads

wait for a specific event before continuing.

Here are RepaintManager

just loops calling repaint()

on JPanel

. However, when called toggleRepaintMode()

, it blocks, waiting for modeChanged Condition

until called toggleRepaintMode()

.



You should be able to run the following code out of the box. Clicking JButton

toggle dragging JPanel

(which you can see with operators System.out.println

).

In general, I highly recommend that you familiarize yourself with the capabilities offered by java.util.concurrent . There is a lot of very strong material. There's a good tutorial at http://docs.oracle.com/javase/tutorial/essential/concurrency/

import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class RepaintTest {
    public static void main(String[] args) {

        JFrame frame = new JFrame();
        JPanel panel = new JPanel()
        {
            @Override
            public void paintComponent( Graphics g )
            {
                super.paintComponent( g );

                // print something when the JPanel repaints
                // so that we know things are working
                System.out.println( "repainting" );
            }
        };

        frame.add( panel );

        final JButton button = new JButton("Button");
        panel.add(button);

        // create and start an instance of our custom
        // RepaintThread, defined below
        final RepaintThread thread = new RepaintThread( Collections.singletonList( panel ) );
        thread.start();

        // add an ActionListener to the JButton
        // which turns on and off the RepaintThread
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                thread.toggleRepaintMode();
            }
        });

        frame.setSize( 300, 300 );
        frame.setVisible( true );
    }

    public static class RepaintThread extends Thread
    {
        ReentrantLock lock;
        Condition modeChanged;
        boolean repaintMode;
        Collection<? extends Component> list;

        public RepaintThread( Collection<? extends Component> list )
        {
            this.lock = new ReentrantLock( );
            this.modeChanged = this.lock.newCondition();

            this.repaintMode = false;
            this.list = list;
        }

        @Override
        public void run( )
        {
            while( true )
            {
                lock.lock();
                try
                {
                    // if repaintMode is false, wait until
                    // Condition.signal( ) is called
                    while ( !repaintMode )
                        try { modeChanged.await(); } catch (InterruptedException e) { }
                }
                finally
                {
                    lock.unlock();
                }

                // call repaint on all the Components
                // we're not on the event dispatch thread, but
                // repaint() is safe to call from any thread
                for ( Component c : list ) c.repaint();

                // wait a bit
                try { Thread.sleep( 50 ); } catch (InterruptedException e) { }
            }
        }

        public void toggleRepaintMode( )
        {
            lock.lock();
            try
            {
                // update the repaint mode and notify anyone
                // awaiting on the Condition that repaintMode has changed
                this.repaintMode = !this.repaintMode;
                this.modeChanged.signalAll();
            }
            finally
            {
                lock.unlock();
            }
        }
    }
}

      

+2


source


jComponent.getTopLevelAncestor().repaint();

      



+1


source


You can use SwingWorker for this. SwingWorker was designed to run long-running tasks in the background without blocking the event dispatcher thread. So, you need to extend SwingWorker

and implement certain methods that make sense to you. Note that all long-running actions must be performed in a method doInBackground()

, and Swing UI elements must only be updated by the method done()

.

So here's an example:

class JPanelTask extends SwingWorker<String, Object>{
    JPanel panel = null;
    Color bg = null;
    public JPanelTask(JPanel panel){
        this.panel = panel;
    }
    @Override
    protected String doInBackground() throws Exception {
        //loooong running computation.
        return "COMPLETE";
    }
    @Override
    protected void done() {
        panel.repaint();
    }

}

      

Now, in your control action, you can do the following:

    controlButton.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            JPanelTask task1 = new JPanelTask(panel1);
            task1.execute();
            JPanelTask task2 = new JPanelTask(panel2);
            task2.execute();
           //so on..
        }
    });

      

Another way: javax.swing.Timer . A timer helps you trigger changes to your ui elements in a timely manner. This may not be the most appropriate solution. But that does the job too. Again, you have to be careful about updating the UI elements in the correct places.

0


source







All Articles