accessing variables and swing components through different threads

前端 未结 3 1242
时光取名叫无心
时光取名叫无心 2020-12-07 02:43

This question is related somewhat to the one i asked HERE. Now, i have a class "Controller" which consists of the main method and all the swing components. there i

相关标签:
3条回答
  • 2020-12-07 03:03

    Whenever you modify a Swing component, you need to ensure that this event happens in the Event Dispatch Thread (i.e. EDT).

    0 讨论(0)
  • 2020-12-07 03:07

    A solution is to use a SwingPropertyChangeSupport object, to make altitude a "bound" property with this support object, to have your GUI listener to this model class and to thereby notify the GUI of changes in altitude.

    e.g.,

    import java.beans.PropertyChangeListener;
    import javax.swing.event.SwingPropertyChangeSupport;
    
    public class Gravity implements Runnable {
       public static final String ALTITUDE = "altitude";
       private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
       private volatile double altitude;
    
       @Override
       public void run() {
          while (true) {
             double temp = altitude + 10;
             setAltitude(temp); // fires the listeners
             try {
                Thread.sleep(10);
             } catch (InterruptedException e) {
                e.printStackTrace();
             }
          }
    
       }
    
       public double getAltitude() {
          return altitude;
       }
    
       public void setAltitude(double altitude) {
          Double oldValue = this.altitude;
          Double newValue = altitude;
    
          this.altitude = newValue;
    
          // this will be fired on the EDT since it is a SwingPropertyChangeSupport object
          swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
       }
    
       public void addPropertyChangeListener(PropertyChangeListener listener) {
          swingPcSupport.addPropertyChangeListener(listener);
       }
    
       public void removePropertyChangeListener(PropertyChangeListener listener) {
          swingPcSupport.removePropertyChangeListener(listener);
       }
    
    
    }
    

    For a more complete runnable example:

    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.*;
    import javax.swing.event.SwingPropertyChangeSupport;
    
    public class GravityTestGui extends JPanel {
       private static final long ALT_SLEEP_TIME = 400;
       private static final double ALT_DELTA = 5;
       JLabel altitudeLabel = new JLabel("     ");
       private Gravity gravity = new Gravity(ALT_SLEEP_TIME, ALT_DELTA);
    
       public GravityTestGui() {
          add(new JLabel("Altitude:"));
          add(altitudeLabel);
    
          gravity.addPropertyChangeListener(new PropertyChangeListener() {
    
             @Override
             public void propertyChange(PropertyChangeEvent pcEvt) {
                if (Gravity.ALTITUDE.equals(pcEvt.getPropertyName())) {
                   String altText = String.valueOf(gravity.getAltitude());
                   altitudeLabel.setText(altText);
                }
             }
          });
    
          new Thread(gravity).start();
       }
    
       private static void createAndShowGui() {
          GravityTestGui mainPanel = new GravityTestGui();
    
          JFrame frame = new JFrame("GravityTest");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(mainPanel);
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    
    
    }
    
    class Gravity implements Runnable {
       public static final String ALTITUDE = "altitude";
       private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
       private volatile double altitude;
       private long sleepTime;
       private double delta;
    
       public Gravity(long sleepTime, double delta) {
          this.sleepTime = sleepTime;
          this.delta = delta;
       }
    
       @Override
       public void run() {
          while (true) {
             double temp = altitude + delta;
             setAltitude(temp); // fires the listeners
             try {
                Thread.sleep(sleepTime);
             } catch (InterruptedException e) {
                e.printStackTrace();
             }
          }
    
       }
    
       public double getAltitude() {
          return altitude;
       }
    
       public void setAltitude(double altitude) {
          Double oldValue = this.altitude;
          Double newValue = altitude;
    
          this.altitude = newValue;
    
          // this will be fired on the EDT since it is a SwingPropertyChangeSupport object
          swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
       }
    
       public void addPropertyChangeListener(PropertyChangeListener listener) {
          swingPcSupport.addPropertyChangeListener(listener);
       }
    
       public void removePropertyChangeListener(PropertyChangeListener listener) {
          swingPcSupport.removePropertyChangeListener(listener);
       }
    }
    
    0 讨论(0)
  • 2020-12-07 03:22

    A third approach would be to have your Swing component know about the model, VTOL.

    In Gravity, you'd update VTOL.altitude, then call repaint on the component. e.g.

    while (true) {
      VTOL.altitude -= 0.01;
      VTOL.makeAnyOtherChangesHereAsWell();
    
      controller.repaint();
      // sleep, break etc. left as an exercise for the reader
    }
    

    Then, in the paintComponent() method (or maybe somewhere else in all the paint calls, there's a slight chance it needs to be elsewhere...) of Controller, which you know is running on the EDT

    // update my widgets from the VTOL model - may want this in a method
    String altStr=new Integer(VTOL.altitude).toString();
    this.lblAltitude.setText(altStr);  
    // may be more, e.g. ...
    this.lblFuelSupply.setText(VTOL.getFuelSupply());
    
    super.paintComponent();  // now go draw stuff...
    

    This is a bit tighter coupled than SwingPropertyChangeSupport, but the coupling is all between very related classes, so it is "reasonable", and in some ways this may be "clearer". And the Event Dispatch Queue will combine multiple repaints so this isn't as inefficient as it first appear. If multiple threads are updating stuff and queuing up multiple repaints(), only the last repaint() actually does anything.

    A disadvantage is that if your GUI has a gazillion widgets and you update all of them every time this may get a bit slow. But processors are amazingly fast nowadays.

    0 讨论(0)
提交回复
热议问题