views:

459

answers:

2

Here is the code:

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.event.*;
import java.awt.*;

public class GameWindow {

 private String[] players;
 private JFrame frame;

 // Constructor.
 public GameWindow(String[] players) {
  this.players = players;
 }

 // Start the window in the EDT.
 public void start() {
  SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    showWindow();
    controller.start();
   }
  });
 }

 // Defines the general properties of and starts the window.
 public void showWindow() {
  frame = new JFrame("Game");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  frame.setSize(600,400);
  frame.setVisible(true);
 }

 // The thread controlling changes of panels in the main window.
 private Thread controller = new Thread() {
     public void run() {
      frame.add(generatePartnerSelectionPanel());
      frame.invalidate();
      frame.validate();
     }
 };

 // Generate the panel for the selection of a partner.
 private JPanel generatePartnerSelectionPanel() {
  JPanel panel = new JPanel();
  panel.add(new JLabel("Pleas select a partner:"));
  return panel;
 }

}

I should see "Pleas select the partner" and I don't. Why?

I suppose that it's because I do not see frame from the run method of the Thread.

ADDED:

May be I need to do all updates in the event dispatch thread... I will check it right now.

ADDED 2:

I tried to modify the code for the controller. It did not help:

 private Thread controller = new Thread() {
 public void run() {
      SwingUtilities.invokeLater(new Runnable() {
       public void run() {
        frame.getContentPane().add(generatePartnerSelectionPanel());
        frame.invalidate();
        frame.validate();
   }
      });
 }
 };

ADDED 3:

OK. Here is the complete version of the code (which does not work):

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.event.*;
import java.awt.*;

public class GameWindow {

    private String[] players;
    private JFrame frame;

    // Constructor.
    public GameWindow(String[] players) {
        this.players = players;
    }

    // Start the window in the EDT.
    public void start() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                showWindow();
                controller.start();
            }
        });
    }

    // Defines the general properties of and starts the window.
    public void showWindow() {
        frame = new JFrame("Game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       
        frame.setSize(600,400);
        frame.setVisible(true);
    }

    // The thread controlling changes of panels in the main window.
    private Thread controller = new Thread() {
        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    frame.getContentPane().add(generatePartnerSelectionPanel());
                    frame.invalidate();
                    frame.validate();
                }
            });
        }
    };

    // Generate the panel for the selection of a partner.
    private JPanel generatePartnerSelectionPanel() {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Pleas select a partner:"));
        return panel;
    }

}

ADDED 4:

The following code also does not work. By the way, why should I remove invokeLater from the start? I do need to start the GUI in the event dispatch thread and invokelater does it.

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.event.*;
import java.awt.*;

public class GameWindow {

    private String[] players;
    private JFrame frame;

    // Constructor.
    public GameWindow(String[] players) {
        this.players = players;
    }

    // Start the window in the EDT.
    public void start() {
//      SwingUtilities.invokeLater(new Runnable() {
//          public void run() {
                showWindow();
                controller.start();
//          }
//      });
    }

    // Defines the general properties of and starts the window.
    public void showWindow() {
        frame = new JFrame("Game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);       
        frame.setSize(600,400);
        frame.setVisible(true);
    }

    // The thread controlling changes of panels in the main window.
    private Thread controller = new Thread() {
        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    frame.getContentPane().add(generatePartnerSelectionPanel());
                    frame.invalidate();
                    frame.validate();
                }
            });
        }
    };

    // Generate the panel for the selection of a partner.
    private JPanel generatePartnerSelectionPanel() {
        JPanel panel = new JPanel();
        panel.add(new JLabel("Pleas select a partner:"));
        return panel;
    }

}

ADDED 5:

I solved the problem.

  1. In the class I had start and showWindow methods. From the main program I called the wrong method (showWindow instead of the start). So, I replaces the start method (to avoid confusions with the start of thread) and then I called startWindow from the main class and it solved the problem.
+3  A: 

You are creating your update thread from the invokeLater call. This is not how you use invokeLater. invokeLater is for updating UI components from a separate thread. Call invokeLater, passing a Runnable that updates your UI components, from your separate thread.

For additional information, see the JavaDocs

For example:

// Start the window in the EDT. 
public void start() {   
    showWindow(); 
    controller.start();  
} 

// Defines the general properties of and starts the window. 
public void showWindow() { 
    frame = new JFrame("Game"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
    frame.setSize(600,400); 
    frame.setVisible(true); 
} 

// The thread controlling changes of panels in the main window. 
private Thread controller = new Thread() { 
    public void run() { 

        // some long running process, I assume, but at 
        // some point you want to update UI:
        SwingUtilities.invokeLater(new Runnable() { 
            public void run() { 
                frame.add(generatePartnerSelectionPanel()); 
                frame.invalidate(); 
                frame.validate(); 
            } 
        });
    } 
}; 
Randolpho
You are right. I agree that I need to update components of GUI using the invokeLater. I did this changes in the code (I also mentioned it in the correction of the question). But it did not solve the problem.
Roman
Did you change `start` as I suggested?
Randolpho
Well, I removed `invokeLater` from the `start` and it did not help. Moreover, I think we should use the `invokeLater` there. As far as I know the GUI should be started in the event dispatch thread and invokeLater does it.
Roman
A: 

Since The Single-Thread Rule states

Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.

Rule found here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

I've written a sample based on your code....

public class GameWindow {
    private String[] players;
    private JFrame frame;

    // Constructor.
    public GameWindow(String[] players) {
      this.players = players;
      start();
    }

  public void start() {
      Runnable jFrameWorkerThread = new Runnable() {
         void run() 
         {
             JFrame frame = new JFrame("Game");
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
             frame.add(generatePartnerSelectionPanel()); // works only in java 6
             //frame.getContentPane().add(generatePartnerSelectionPanel()); // works in any version of java
             frame.pack(); // assuming your pane has preferred size 
             frame.setVisible(true); //For other old Java, frame.show() does the same thing (if the show() method isn't deprecated)
         }
    };

      SwingUtilities.invokeLater(jFrameWorkerThread());
  }
}

PS when do you ever call GameWindow.start() method?

// Start the window in the EDT.
 public void start() {...}
The Elite Gentleman
@Downvoter, how is this wrong?
The Elite Gentleman
see @Powerlords comment to @dbingham's answer.
Randolpho