Window JPanel is transparent? - java

I have a main window with 4 JButtons and an action listener, and three of them call another window and the fourth exits. Two of my windows work fine, but for some reason my window that waits for a client to connect opens and you can see the boundary of the window, but the inside of the window is transparent.
I tried saying new HostWindow() from my main class, and that worked fine; it's just when I call it from my StartWindow class that it doesn't work.
code:
StartWindow:
public class StartWindow extends JFrame{
private JPanel pane;
private JButton host;
private JButton join;
private JButton comp;
private JButton exit;
public StartWindow()
{
this.setSize(220, 110);
this.setTitle("Closed Arena");
pane = new JPanel();
this.add(pane);
host = new JButton("Host Match");
host.addActionListener(new myButtonListener());
join = new JButton("Join Match");
join.addActionListener(new myButtonListener());
comp = new JButton("Play Computer");
comp.addActionListener(new myButtonListener());
exit = new JButton("Exit");
exit.addActionListener(new myButtonListener());
pane.add(host);
pane.add(join);
pane.add(comp);
pane.add(exit);
this.setResizable(false);
pane.setVisible(true);
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//If I make a new Hostwindow here it displays properly
//new HostWindow();
}
private class myButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
if(source.equals(host))
{
close();
//But here it displays improperly.
new HostWindow();
}
if(source.equals(join))
{
close();
new JoinWindow();
}
if(source.equals(comp))
{
close();
new Arena();
}
if(source.equals(exit))
{
close();
}
}
}
public void close()
{
this.dispose();
}
}
HostWindow:
private JPanel panel;
private JLabel text;
private JButton stop;
private LabelEditor edit;
private Thread editThread;
private ServerSocket server;
private Socket mySocket;
public HostWindow()
{
panel = new JPanel();
text = new JLabel("Waiting for client");
stop = new JButton("Stop");
stop.addActionListener(new buttonList());
panel.add(text);
panel.add(stop);
this.add(panel);
this.setResizable(false);
this.setSize(160, 90);
this.setTitle("Server");
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
edit = new LabelEditor(text, "Waiting for client", 700);
editThread = new Thread(edit);
editThread.start();
try
{
server = new ServerSocket(4011);
mySocket = server.accept();
server.close();
new Arena(mySocket, true);
}
catch (IOException e) {
System.out.print("Failed to set up server!");
}
editThread.interrupt();
this.dispose();
}
Edit: HostWindow does extend JFrame, I jsut didn't copy-paste the heading, but it looks like this:
public class HostWindow extends JFrame {
Edit2: Thank you answerers, it was fixed when I made it so that the serer starts in a separate thread:
if(source.equals(host))
{
close();
HostWindow hoster = new HostWindow();
Thread hosterThread = new Thread(hoster);
hosterThread.start();
}
and in hostwindow:
I moved the server stuff into the run.
public void run() {
try
{
server = new ServerSocket(4011);
mySocket = server.accept();
server.close();
new Arena(mySocket, true);
}
catch (IOException e) {
System.out.print("Failed to set up server!");
}
editThread.interrupt();
this.dispose();
}

The problem here is that you're performing a long lasting blocking operation inside the UIThread.
Threads are simultanous sequences of commands that run inside a single program (I recommend reading up on Java concurrency ). There is a thread usually refered to as the UIThread that performs drawing of the UI elements and works with their code.
Creating a socket - a connection to a remote host means you're starting a process that can take several seconds to perform. And this is happening inside the constructor of the UI class, inside the UIThread. Until the connection is established the rest of the code in the constructor cannot run. This is because the connection process is a blocking operation - the code after the socket creation won't run until the socket creation is finishsed.
So you should move the socket creation to a different thread.

Firstly, I think your example is incorrect HostWindow doesn't extend from anything, yet you seem to be treating it like a window...
Secondly
try
{
server = new ServerSocket(4011);
mySocket = server.accept();
server.close();
new Arena(mySocket, true);
}
catch (IOException e) {
System.out.print("Failed to set up server!");
}
Will stop the window from getting painted until a connection is made, at which time you dispose of the window any way.
You might like to take a read through
The Event Dispatching Thread
Concurrency in Swing
I'd suggest moving the the "connection" code into a SwingWorker and using it's done method to dispose of the window.

Related

how to make JFrame "wait"? [duplicate]

I need some help with a simple java application which makes use of two jframe to get some input parameters. Here's a sketch of my code:
//second jframe, called when the button OK of the first frame is clicked
public class NewParamJFrame extends JFrame{
...
}
//first jframe
public class StartingJFrame extends JFrame{
private static NewParamJFrame newPFrame = null;
private JTextField gnFilePath;
private JButton btnOK;
public StartingJFrame(){
//..
initComponents();
}
private void initComponents(){
btnOK.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
try{
EventQueue.invokeAndWait(new Runnable(){
public void run() {
try {
newPFrame = new NewParamJFrame();
newPFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
catch(InvocationTargetException e2) {}
catch(InterruptedException e1){}
dispose();
}
}
public String getText(){
return gnFilePath.getText();
}
}
public class Main {
private static StartingJFrame begin = null;
public static void main(String[] args) {
try{
EventQueue.invokeAndWait(new Runnable(){
public void run() {
try {
begin = new StartingJFrame();
begin.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
catch(InvocationTargetException e) {}
catch(InterruptedException e1){}
String s= begin.getText();
//...use s ...
}
}
The call to getText() causes a NullPointerException. I want the main class to wait until the frames are closed but I don't know how to do. I'm using swing for the first time.
I want the main class to wait until the frames are closed but I don't
know how to do. I'm using swing for the first time.
If I understand your problem correctly, you need StartingJFrame to stay waiting until NewParamJFrame is closed and then continue its execution. If this is the case then it won't happen because JFrame doesn't support modality. But JDialog does, so you can have just one JFrame and do the parameters request in a JDialog whose parent is this JFrame.
For a better explanation about modality, take a read to How to Use Modality in Dialogs.
Also take a look to this topic: The Use of Multiple JFrames, Good/Bad Practice?
In any case you'll probably face a new problem: what should the JFrame do if the user closes/cancels the dialog withouth input any parameter? How could this JFrame know what just happened in that dialog? One approach is described in this answer. You'll see the example is about a login dialog but the problem is similar to this one: How could a dialog notify to its parent frame on how the process went?
The easiest way to wait for close without modifying the code flow is to use a modal JDialog. So you have to change your StartingJFrame class to make it a subclass of JDialog instead of JFrame, and add the following to the begin of its constructor:
super((Window)null);
setModal(true);
Then the setVisible(true); invocation on the StartingJFrame instance will wait until the dialog has been closed and hence the invokeAndWait invocation will wait too.
The call to getText() causes a NullPointerException.
Because, gnFilePath of JTextField is null.
private JTextField gnFilePath;
public String getText(){
return gnFilePath.getText();// NullPointerException is throw here.
}
To avoid NPE, you need to initialize JTextField and JButton like below.
private JTextField gnFilePath=new JTextField();
private JButton btnOK=new JButton()
Try putting this:
import java.awt.event.*;
import javax.swing.*;
public class MyWindow extends JFrame{
MyWindow(){
setSize(300, 200);
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton b = new JButton("Close");
b.setBounds((300-80)/2, (200-30)/2, 80, 30);
//
final MyWindow frame = this;
b.addActionListener(
new ActionListener(){
#Override
public void actionPerformed(ActionEvent ev){
synchronized(frame){
frame.notify();
}
frame.setVisible(false);
frame.dispose();
}
}
);
//
getContentPane().add(b);
setVisible(true);
synchronized(this){
try{
this.wait();
}
catch(InterruptedException ex){ }
}
}
public static void main(String args[]) {
new MyWindow();
System.out.println("You are here");
}
}
The code above is checked.
Using a JDialog is probably the simplest solution, but in some cases it's desirable to have a JFrame, for example to show the window in the taskbar. Using a synchronization mechanism as suggested by Octavio is a way to achieve this, here is an alternative using a CountDownLatch blocking the main thread until the frame is closed:
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setSize(300, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
latch.countDown();
}
});
});
latch.await();
System.out.println("Main thread released");
}
You can use a loop (preferably do-while loop) to put a frame on hold until the other frame closes or hides. Make sure to break the loop or increment the variable used for the loop by specific amount when the other frame is disposed or hidden. This way you can keep your StartingJFrame class to remain as a subclass of JFrame.
do {
if (changeLog.isVisible()) {
} else {
changeLog.dispose();
break;
}
} while (hold < 1);
or
do {
if (changeLog.isActive()) {
} else {
break;
}
} while (hold < 1);
The first one would require the previous frame to be hidden (JFrame.HIDE_ON_EXIT or Window.setVisible(false)) before the codes can be run. The last one would require the previous frame to be "disposed" (JFrame.DISPOSE_ON_EXIT or (subclass of JFrame).dispose(). Add any of those codes on StartingJFrame, though, since you created a NewParamJFrame in that class and have the corresponding field(s) set to private.

Creating and Discarding JFrames that run Threads

I'm writing my Tetris using Java Swing. The Game class revolves around a JFrame (frame), which consists of a TetrisPanel extending JPanel (panel) where the blocks fall, a JLabel (pontok) point counter, a JTextArea (rekord_text) showing high scores, and another JPanel (kovi) showing the next block to fall. My idea is that the game has 3 difficulty levels, where the blocks fall with different speed.
I thought the best way of approaching this problem is to create a new JFrame with the components above, but with the blocks' speed set different. I am able to close the old JFrame. However, when the new JFrame opens up, it is only a blank frame, and it won't respond to closing the window.
I should add that TetrisPanel is running a thread, but I am 90% sure I stop that with a volatile boolean.
Constructor of the Game class:
this.difSet(nehezseg); //this function sets the falling velocity
TetrisPanel.stopped = true; //this static member is the volatile boolean responsible for stopping the thread
new_game = false;
frame = new JFrame("Tetris_alpha");
frame.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
panel = new TetrisPanel();
TetrisPanel.stopped = false;
new Thread(panel).start();
frame.add(panel, c);
pontok = new JLabel ("0");
frame.add(pontok, c);
rekord_text = new JTextArea();
//i set up the area
frame.add(rekord_text, c);
kovi = new NextAktualPanel();
frame.add(kovi, c);
menu = new MyMenu(this);
frame.setJMenuBar(menu);
frame.addWindowListener(new WindowAdapter()
{
#Override
public void windowClosing(WindowEvent e)
{
rekordok.add(panel.getPont());
rekordok.write(f);
e.getWindow().dispose();
System.exit(0);
}
}
);
frame.pack();
frame.setVisible(true);
}
The Game.start() function containing the game loop:
public void start()
{
//game_loop
while (!panel.GameOver() && !new_game)
{
if (panel.aktualLeertDetector())
{
panel.addAktualToBlocks();
panel.addNewAktual(next);
Elem temp = new Elem(0,0,rand.nextInt(7));
while (temp.getTipus() == next.getTipus())
temp = new Elem(0,0,rand.nextInt(7));
next = temp;
kovi.setNextAktual (next);
}
if (!paused)
pontok.setText(Integer.toString(panel.getPont()));
kovi.repaint();
panel.repaint();
}
The function which opens the new frame:
Public void newGame (Game g)
{
Game.new_game = true;
g.frame.dispose();
Game new_game = new Game("easy");
g = new_game;
g.start();
}
And the run() function of TetrisPanel:
public static volatile boolean stopped = false;
#Override
public void run() {
while (!stopped)
{
aktual.zuhan();
this.sorTeleAction();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Any help would be appreciated, including ideas about different a approach.
Do not use volatile boolean as status variable, use AtomicBoolean instead, volatile it's not the correct way to do this kind of things, and it does not either cause "immediate variable updating"... this is not volatile purpose.
It's not a good idea to start a thread on main AWT thread, you still have to use SwingUtilities.invokeLater(Runnable runnableAction). You can use something like this when launching a Gui Thread:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new Thread(threadAction).start();
} catch (Exception e) {
}
}
});
Hope that this will solve you problem!

java actionListener: retrieve TextField in a separate thread

i have a Frame which look like this:
public class Load_Frame extends JFrame implements ActionListener{
private JButton uploadButton, downloadButton;
private JTextField uploadField;
private String filename;
private Client client;
public Load_Frame(String username, Socket socket) {
this.client = new Client(username, socket);
uploadField = new JTextField ();
uploadField.setBounds(60,100,450,30);
uploadButton = new JButton ("Upload");
uploadButton.setBounds(410,150,100,30);
uploadButton.addActionListener(this);
downloadButton = new JButton ("Download");
downloadButton.setBounds(390,300,120,30);
downloadButton.addActionListener(this);
this.add(uploadField);
this.add(uploadButton);
this.add(downloadButton);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
//Upload:
if (e.getSource()== uploadButton) {
this.filename = uploadField.getText();
File file = new File(filename);
client.upload(file);
}
//Download
else if (e.getSource()== downloadButton) {
filename = (String) filesList.getSelectedItem();
client.download(filename);
}
}
My problem is: i have been said that the frame and the "process" should be separated in different thread, so that when the process fail the frame don't freeze. So i need my Client to be a new thread.
But then, i still need acess to those "upload" and "download" button. I've read that i can easily do that like it:
public class Client implements Runnable, ActionListener{
...
public void actionPerformed(ActionEvent e){
if(e.getSource() == uploadButton){
File file = new File(filename); //how can i retrieve the filename??
upload(file);
}
}
and i'll just need to add another actionListener in my Frame class like that:
uploadButton.addActionListener(client);
(same for download of course)
My prolem is: how can i get the filename, the text written in the TextField of my Frame?? Should i give this TextField as a parameter for my client ? This will make the code look weird, and by weird i mean not very logical, so i hope there is another way to do so.
You can create two thread one for download and one for upload like below
public void actionPerformed(ActionEvent e){
if(e.getSource()==uploadButton){
new Thread(){
public void run(){
this.filename = uploadField.getText();
File file = new File(filename);
client.upload(file);
}
}.start();
}
else if(e.getSource() == downloadButton){
new Thread(){
public void run(){
this.filename = downloadField.getText();
File file = new File(filename);
client.download(file);
}
}.start();
}
}

Adopting another process's child window

I'm writing a sort of web applet emulator. I read a web page, find the applet parameters, download the applet and run it. It is very important that the applet runs in its own process (i.e. not the emulator process). It should, however, render in the emulator process window.
How does the Java plugin do it? When the separate_jvm flag is set, the plugin loads the applet in a separate JVM process but the applet still appears in the same browser panel.
I've made some progress by creating a loader class that, on another JVM, adds the target Applet to an undecorated, invisible frame and messages the frame's window handle to the emulator JVM. The latter binds it to a Canvas instance with user32.SetParent via JNA, and the display works perfectly.
However, only mouse events are being sent: keyboard input is not forwarded. The applet reports Component#isFocusOwner as false, and requestFocusInWindow does not make it the focus owner, returning false. How can I pass keyboard focus to the Applet window handle? My current approach involves a server (the emulator), which receives window handles from the client (the applet). Only mouse events appear to work, since the Applet cannot gain focus.
The server class handles the display of the applet.
import com.sun.jna.*;
import com.sun.jna.platform.win32.User32;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import static com.sun.jna.platform.win32.User32.*;
public class Loader {
private static final String APPLET_DIRECTORY = ""; // TODO: Set this to the directory containing the compiled applet
private static ServerSocket serverSocket;
private static JFrame frame;
private static Canvas nativeDisplayCanvas;
public static void main(String[] argv) throws Exception {
nativeDisplayCanvas = new Canvas();
frame = new JFrame("Frame redirect");
frame.setLayout(new BorderLayout());
frame.add(nativeDisplayCanvas, BorderLayout.CENTER);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
(new Thread() {
public void run() {
try {
serve();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
spawnAltJVM(APPLET_DIRECTORY, "AppletDemo");
}
public static void serve() throws Exception {
serverSocket = new ServerSocket(6067);
serverSocket.setSoTimeout(10000);
while (true) {
try {
System.out.println("Waiting for applet on port " + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Connected to " + server.getRemoteSocketAddress());
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
DataOutputStream out = new DataOutputStream(server.getOutputStream());
while (true) {
String msg = in.readLine();
if (msg != null && msg.startsWith("child_hwnd")) {
windowCreatedHandler(msg);
out.writeUTF("hwnd_recv\n");
out.flush();
}
}
} catch (IOException ex) {
System.out.println("Something happened to the socket...");
break;
}
}
}
public static void windowCreatedHandler(String message) {
String[] tokens = message.split(":");
final User32 user32 = User32.INSTANCE;
HWND child_applet = new HWND(Pointer.createConstant(Long.parseLong(tokens[1])));
final HWND child_frame = new HWND(Pointer.createConstant(Long.parseLong(tokens[2])));
frame.addComponentListener(
new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
user32.SetWindowPos(child_frame, new HWND(Pointer.NULL), 0, 0, frame.getWidth(), frame.getHeight(), 0);
}
}
);
HWND parent = new HWND(Native.getComponentPointer(nativeDisplayCanvas));
user32.SetParent(child_applet, parent);
int style = user32.GetWindowLong(child_frame, GWL_STYLE) & ~WS_POPUP | (WS_CHILD | WS_VISIBLE);
user32.SetWindowLong(child_applet, GWL_STYLE, style);
user32.SetWindowPos(child_applet, new HWND(Pointer.NULL), 0, 0, nativeDisplayCanvas.getWidth(), nativeDisplayCanvas.getHeight(), 0);
}
public static void spawnAltJVM(String cp, String clazz) throws IOException, InterruptedException, ClassNotFoundException {
ProcessBuilder processBuilder = new ProcessBuilder(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java", "-cp", cp, clazz);
Process applet = processBuilder.start();
final BufferedReader in = new BufferedReader(new InputStreamReader(applet.getInputStream()));
final BufferedReader err = new BufferedReader(new InputStreamReader(applet.getErrorStream()));
(new Thread() {
public void run() {
while (true) {
try {
System.out.println("[client] " + in.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
Meanwhile, the client class just instantiates and messages the handles.
import sun.awt.windows.WComponentPeer;
import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingDeque;
public class AppletDemo extends Applet {
private Canvas canvas;
private static Color backgroundColor = Color.RED;
public AppletDemo() {
setLayout(new BorderLayout());
canvas = new Canvas();
add(canvas, BorderLayout.CENTER);
setBackground(Color.CYAN);
canvas.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
refreshColors();
}
});
canvas.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
refreshColors();
}
});
}
private void refreshColors() {
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
backgroundColor = (backgroundColor == Color.RED ? Color.GREEN : Color.RED);
canvas.setBackground(backgroundColor);
}
}
);
}
public static void main(String[] argv) throws Exception {
System.setErr(System.out);
final AppletDemo app = new AppletDemo();
Frame frame = new Frame("AppletViewer");
frame.setLayout(new BorderLayout());
frame.add(app, BorderLayout.CENTER);
frame.setUndecorated(true);
frame.pack(); // Create the native peers
frame.setSize(300, 200);
final Socket client = new Socket("localhost", 6067);
final LinkedBlockingDeque<String> messageQueue = new LinkedBlockingDeque<>();
final DataOutputStream out = new DataOutputStream(client.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
(new Thread() {
public void run() {
while (true) {
try {
out.writeBytes(messageQueue.take() + "\n");
out.flush();
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}
}
}
}).start();
(new Thread() {
public void run() {
while (true) {
try {
if ("hwnd_recv".equals(in.readLine())) {
// Attempt to grab focus in the other process' frame
System.out.println("Trying to request focus...");
System.out.println(app.requestFocusInWindow());
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}).start();
messageQueue.add("child_hwnd:" + ((WComponentPeer) app.getPeer()).getHWnd() + ":" + ((WComponentPeer) frame.getPeer()).getHWnd());
}
}
They're both a bit lengthy because they require some socket work, but they are compilable and should demonstrate the issue. They require JNA to compile. I've shortened them as much as possible at the cost of some good practices.
When Loader is ran, a window redirecting the AppletDemo's canvas should appear. Mouse events are sent: the canvas toggles between red and green on a mouse press. Ideally, the same behavior should occur for keystrokes too.
I've used WinSpy to get the handles of a notepad.exe window and text pane, and hardcoding the handles into Loader. Keyboard focus works perfectly with the multiline edit control, but not with the toplevel window itself. Why? Is this related to the issue I'm having?
I opened up a Chrome window running an applet in WinSpy, and found that the plugin creates no dummy Frame — the applet canvas is directly set as a child of Chrome. However, I haven't been able to create a native peer for the Applet, since it seems to require it to be displayable.
I've read about the dangers of cross-process parent/child or owner/owned window relationship, but I can't think of a better way to graft the child applet into the emulator.
Since what you really want is to create the applet as a child window, the easy solution would be to convince the applet to be your children, not forcefully adopting it, and working against both Windows and the JVM.
Luckily, the Sun/Oracle Java VM comes with a class called WComponentFrame (Windows-only as implied from the name). It can constructed from an hwnd, which you can send from your parent process. The applet can then be added as a child of your window.
import sun.awt.windows.WComponentPeer;
frame = new WEmbeddedFrame(hwnd);
frame.setLayout(new BorderLayout());
frame.add(applet, BorderLayout.CENTER);
It looks like you are trying to pass the event to the Canvas object, which you do not explicitly setFocusable(true) for.
If this is the case, then in your AppletDemo constructor, try:
canvas.setFocusable(true);
canvas.requestFocus();
Also it seems like you want to pass key events to your Applet rather than your Canvas from your question.
In this case, try this in your AppletDemo constructor:
this.setFocusable(true);
this.requestFocus();
After that, you should receive keyboard input by default to the component that is focused.
With JNA it is as easy as
HWND hwnd1 = User32.INSTANCE.FindWindow(null, "JFrame1");
HWND hwnd2 = User32.INSTANCE.FindWindow(null, "JFrame2");
HWND hwnd3 = User32.INSTANCE.SetParent(hwnd2, hwnd1);
see also
Good or evil - SetParent() win32 API between different processes

Swing ProgressMonitor not working

I am trying to learn ProgressMonitor in Java Swing.
I created this simple test code -
public class ProgressMonitorTest extends JFrame
{
private JPanel contentPane;
private ProgressMonitor progressMonitor;
private JButton button;
private static ProgressMonitorTest frame;
private static boolean isFrameReady;
public JButton getButton()
{
return button;
}
public ProgressMonitor getProgressMonitor()
{
return progressMonitor;
}
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
frame = new ProgressMonitorTest();
frame.setVisible(true);
isFrameReady = true;
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
while(!isFrameReady)
{
//
}
frame.getButton().addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
try
{
for(int i=0;i<=10;i++)
{
final int percent = i;
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
frame.getProgressMonitor().setProgress(percent * 10);
frame.getProgressMonitor().setNote("Completed " + percent*10 + "%.");
}
});
try
{
Thread.sleep(1000);
}
catch(Exception ee)
{
//
}
}
}
catch(Exception es)
{
//
}
}
});
}
/**
* Create the frame.
*/
public ProgressMonitorTest()
{
isFrameReady = false;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
setTitle("Progress Monitor");
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
progressMonitor = new ProgressMonitor(frame, "Update in progress...", "", 0, 10);
button = new JButton("Click Here");
contentPane.add(button);
setContentPane(contentPane);
}
}
A few questions regarding this-
If I remove the isFrameReady check, the program says a NullPointerException at the line where I assign the button's action listener.
If I keep the above check, then clicking on the button does nothing.
Keeping the above check and then debugging this, I let it wait for some time before it gets to the line where the action listener. In this case, it works but immediately quits saying it can't call invokeAndWait from the event handling thread.
What am I missing in all this ? Can someone explain how to get this to work.
If I remove the isFrameReady check, the program says a
NullPointerException at the line where I assign the button's action
listener.
your use of isFrameReady ensures that you have created your frame successfully. inside your main, your posted request to event dispatch thread(EDT) using call EventQueue.invokeLater(new Runnable(){}): removing the check isFrameReady, you were going to call frame.getButton() in main thread but the frame have not been yet created by frame = new ProgressMonitorTest(); in the EDT and thus a NullPointerException occurs.
If I keep the above check, then clicking on the button does nothing.
you should understand by now, that above check is nothing to do with button click. The button is not doing anything because the GUI got freezed for violating swing's single threading rule. Put your incrementing for loop of the actionPerformed method inside another thread as the following code fragement shows and execute it from there. you will see that it works fine.
new Thread(){
public void run()
{
for(int i=0; i<10; i++)
{
//whatever you were doing.
}
}
}.start();
Keeping the above check and then debugging this, I let it wait for
some time before it gets to the line where the action listener. In
this case, it works but immediately quits saying it can't call
invokeAndWait from the event handling thread.
SwingUtitlies.invokeAndWait() blocks the current thread and waits until the EDT is done executing the task given to it. As actionPerformed() function is already running inside EDT, so calling SwingUtitlies.invokeAndWait() from the current thread:EDT would block the current thread:EDT which should not be allowed. Don't use invokeAndWait for this case. you should call SwingUtilities.invokeLater() instead.
However I don't think you will get anything until you understand Swing threading model. Read the javadoc and some internet resource. DO HAVE The book Filthy Rich Clients and try the example the book offered: You will have a greater knowledge in graphical effects then any other resource can provide.

Categories

Resources