Kill JFrame thread in Java on windowClosing event - java

I am trying to write application which will create new window when pressing button, show current window count and close window and thread on closing the window.
So basically, functionality is like this (and some of them are working):
Show window on launching application (OK)
Create new window on pressing button (OK)
Display current window count on pressing button (OK on creating, NOK on closing windows)
Destroy window when pressed "X" (OK)
Destroy main thread when original window is closed (NOK)
Here is my code:
package projectpackage;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.event.*;
class MyMouseEvent extends MouseAdapter
{
public void mousePressed(MouseEvent me)
{
MyWindowThread.incrementMyWindowThreadCount();
MyWindowThread obj1 = new MyWindowThread();
Thread thr1 = new Thread(obj1);
thr1.start();
}
}
class MyWindowThread extends JFrame implements Runnable
{
private int height;
private int width;
private static int MyWindowThreadCount = 1;
public static int getMyWindowThreadCount()
{
return MyWindowThreadCount;
}
public static void incrementMyWindowThreadCount()
{
MyWindowThreadCount++;
}
public static void decrementMyWindowThreadCount()
{
MyWindowThreadCount--;
}
public MyWindowThread()
{
super("Frame "+getMyWindowThreadCount());
this.setHeight(300);
this.setWidth(400);
this.setBounds(100,100,getWidth(),getHeight());
this.addWindowListener(new WindowAdapter()
{
#Override
public void windowClosing(WindowEvent e)
{
MyWindowThread.decrementMyWindowThreadCount();
//Thread.currentThread().interrupt();
return;
}
}
);
JPanel panel1 = new JPanel();
JButton button1 = new JButton("New window");
JButton button2 = new JButton("Count running windows");
this.getContentPane().add(panel1);
panel1.add(button1);
panel1.add(button2);
button1.addMouseListener(new MyMouseEvent());
button2.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
javax.swing.JOptionPane.showMessageDialog(null,"Window count = " + MyWindowThread.getMyWindowThreadCount());
}
}
);
this.setVisible(true); // show frame
}
public int getHeight()
{
return this.height;
}
public void setHeight(int h)
{
this.height = h;
}
public int getWidth()
{
return this.width;
}
public void setWidth(int w)
{
this.width = w;
}
public void run()
{
}
}
public class MyApp
{
public static void main(String[] args)
{
// Creating objects START
MyWindowThread obj1 = new MyWindowThread();
Thread thr1 = new Thread(obj1);
thr1.start();
}
}
manifest.mf:
Manifest-Version: 1.0
Main-Class: projectpackage.MyApp
I hope you know how to compile and run Java application in console. In case you don't, here it is:
(navigate in CLI to directory with "projectpackage" directory inside. *.java file must be inside "projectpackage" directory)
javac projectpackage\*.java
jar cfm MyApp.jar manifest.mf projectpackage\*
java -jar MyApp.jar
Can anyone enlighten me what am I doing wrong or just what I need to do to make the code work? Or am I just having fundamental misconception?

JFrame frame = new JFrame();
This is your problem, you extend the JFrame-class but try to use a new object.
You should replace this line by super(); or super("Frame " + getMyThreadCount()); if you want so.
Edit: Another issue to mention: you named your class MyThread, this is a quite irritating class-name for a window ;)
Edit 2: Few other things:
public void run() has no content, so why does MyThread implement Runnable?
You create some threads, but if I got that right, you do not start any of them.

If you simply want your application to exit when the JFrame is closed, then you are going about this all wrong. Get rid of all the Thread objects and Runnables. Swing applications run in the event dispatch thread (EDT). You should only use background threads if you need to do something like disk or network I/O.
Create/show the JFrame in the EDT. If you set the default close operation on the JFrame to DISPOSE_ON_CLOSE, then when all of the windows have been closed, the main thread will terminate normally.
public class MyApp {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
public void run() {
MyWindow w = new MyWindow();
w.setVisible( true );
}
} );
}
private static class MyWindow extends JFrame {
// Simplified reference counting
private static AtomicInteger frameCount = new AtomicInteger( 0 );
public MyWindow() {
super("Frame " + frameCount.incrementAndGet() );
this.setSize( 400, 300 );
this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
JButton button = new JButton("New Window");
button.addActionListener( new ActionListener() {
#Override
public void actionPerformed( ActionEvent e ) {
MyWindow w = new MyWindow();
w.setVisible( true );
}
} );
this.getContentPane().add( button );
// ignore reference count button for now
}
}
}

Related

How to break a loop using MouseListener java

I am trying to make an A* pathfinding algorithm my code goes something like this
public class Board extends JPanel implements MouseListener{
public void mousePressed(MouseEvent e){
if (StartAndEnd())
AStar();
}
}
Now the problem is that the AStar pathfinding algorithm takes alot of time to compute and I want to break it incase the user makes another choice. (another start and end input with the mouse).
How can I do that?
Here is a small demo program that creates a JFrame (window) with a Stop button. It loops for 10,000 seconds unless you press the stop button.
The stop button sets a flag which terminates the loop.
Hopefully you can figure out how to solve your original problem from this code. I've modified the example to call runLongTime in a new thread, leaving the Swing GUI thread alone.
import java.awt.event.*;
import javax.swing.*;
public class Stoppable extends Thread implements ActionListener
{
public static void main (String[] args)
{
final Stoppable app = new Stoppable ();
app.start ();
}
private final JFrame frame = new JFrame ("Stoppable");
private final JButton stop = new JButton ("Stop");
private final JPanel panel = new JPanel ();
private boolean isRunning = true;
public Stoppable ()
{
panel.add (stop);
frame.setContentPane (panel);
stop.addActionListener (this);
frame.pack ();
frame.setVisible (true);
}
#Override
public void run ()
{
try
{
runLongTime ();
}
catch (final InterruptedException e)
{
e.printStackTrace ();
}
System.exit (0);
}
private void runLongTime () throws InterruptedException
{
for (int i = 0; i < 10000 && isRunning; i++)
{
System.out.printf ("Iteration %d\n", i);
Thread.sleep (1000);
}
System.out.printf ("Stopped\n");
}
#Override
public void actionPerformed (ActionEvent e)
{
isRunning = false;
}
}

Java Swing: 'java.awt.AWTEventMulticaster.mouseMoved' error

I have been trying to create a 'catch me if you can' game: when I start it, it randomly chooses where to allocate a 'click me' button. I am not supposed to be able to click the button, the text should be re-assigned to another button before I am able to do that.
It works for a while but then it throws the following error: "java.awt.AWTEventMulticaster.mouseMoved".
I have been trying to fix the problem with removeListener() method but I don't seem to be able to find a solution. Any comments?
Here's my code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.lang.*;
public class Game extends JFrame {
//Panels
private JPanel mainPanel = new JPanel();
// Buttons
private JButton[] buttons = new JButton[9];
private JButton theChosenButton = new JButton();
// other
private int random = 0;
public Game() {
this.setTitle("Catch me if you can");
mainPanel.setLayout(new GridLayout(3, 3));
// creates buttons
for(int i = 0; i < 9 ; i++) {
buttons[i] = new JButton();
mainPanel.add(buttons[i]);
}
// Add everything to frame
this.getContentPane().add(mainPanel);
this.setSize(400, 400);
this.setVisible(true);
}
// generates random number between 1 and 9 to be used
public int clickMeGenerator(){
random = (int) Math.floor(Math.random() * 9);
return random;
}
// randomly assigns clickMeGenerator to a button
// add mouseMoved listener to the chosen button
public void assign(){
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
// inner class
class MouseHover implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
theChosenButton.setText("");
Game.this.assign();
}
public void mouseDragged(MouseEvent e) {
}
}
} // end of class
Test class:
public class GameTest {
public static void main (String args[]) {
Game myGame = new Game();
myGame.assign();
}
}
Thank you so much for your help!
Just for clarity, the "actual" error is ...
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
So looking through the code...
public void assign() {
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
You are repeatedly add a new MouseMotionListener to you buttons, over and over again, and...
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
is pointless, as you're trying to remove a new instance of MouseHover from the button, but it will never have been applied in the first place.
The first thing I would do is create an instance of MouseHover as an instance field in Game
private MouseHover mouseHover = new MouseHover();
and use it when calling addMouseMotionListener and removeMouseMotionListener.
I would then, remove the listener from the "currently" active button before adding it to the next one.
Personally, I would do this in the assign method
public void assign() {
int randomButton = this.clickMeGenerator();
if (theChosenButton != null) {
theChosenButton.removeMouseMotionListener(mouseHover);
}
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(mouseHover);
theChosenButton.setText("Click me");
}
I would also ensure that assign is called from within the Event Dispatching Thread when the class is first created, as the UI has been realised by the end of the constructor of Game, meaning the first call to assign is outside of the context of the EDT, which is not recommended.
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Game myGame = new Game();
myGame.assign();
}
});
}

java detect when any window is created or closed

Is it possible to be notified whenever any window in the application was created or closed?
At the moment I'm polling Window.getWindows() but I would prefer to get notified instead.
What I have:
List<Window> previousWindows = new ArrayList<>();
while (true) {
List<Window> currentWindows = Arrays.asList(Window.getWindows());
for (Window window : currentWindows) {
if (!previousWindows.contains(window)) {
//window was created
}
}
for (Window window : previousWindows) {
if (!currentWindows.contains(window)) {
//window was closed
}
}
previousWindows = currentWindows;
Thread.sleep(1000);
}
What I'd like:
jvm.addWindowListener(this);
#Override
public void windowWasDisplayed(Window w) {
//window was created
}
#Override
public void windowWasClosed(Window w) {
//window was closed
}
You can register listeners that receive any subset of types of AWT events via the windowing Toolkit. From those you can select and handle the WindowEvents for windows being opened and closed, something like this:
class WindowMonitor implements AWTEventListener {
public void eventDispatched(AWTEvent event) {
switch (event.getID()){
case WindowEvent.WINDOW_OPENED:
doSomething();
break;
case WindowEvent.WINDOW_CLOSED:
doSomethingElse();
break;
}
}
// ...
}
class MyClass {
// alternative 1
public void registerListener() {
Toolkit.getDefaultToolkit().addAWTEventListener(new WindowMonitor(),
AWTEvent.WINDOW_EVENT_MASK);
}
// alternative 2
public void registerListener(Component component) {
component.getToolkit().addAWTEventListener(new WindowMonitor(),
AWTEvent.WINDOW_EVENT_MASK);
}
}
I would recommend alternative 2, where the Component from which you obtain the Toolkit is the main frame of your application (there should be only one), but alternative 1 should work out for you if you have to do this without reference to any particular component (for instance, before any have been created).
Do note, however, that registering an AWTEventListener is subject to a security check.
If you create the additional windows (I assume JFrames) yourself, you can use the addWindowListener method. The WindowAdapter abstract class allows you to override methods for the events you are interested in:
import java.awt.event.*;
import javax.swing.*;
public class MultipleWindows {
public static void main(String[] arguments) {
SwingUtilities.invokeLater(() -> new MultipleWindows().createAndShowGui());
}
private void createAndShowGui() {
JFrame frame = new JFrame("Stack Overflow");
frame.setBounds(100, 100, 800, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(new JLabel("Testing multiple windows..."));
frame.getContentPane().add(panel);
WindowAdapter windowAdapter = new WindowAdapter() {
#Override
public void windowOpened(WindowEvent windowEvent) {
System.out.println("Window opened: "
+ windowEvent.getWindow().getName());
}
#Override
public void windowClosed(WindowEvent windowEvent) {
System.out.println("Window closed: "
+ windowEvent.getWindow().getName());
}
};
for (int windowIndex = 2; windowIndex < 6; windowIndex++) {
String title = "Window " + windowIndex;
JFrame extraFrame = new JFrame(title);
extraFrame.setName(title);
extraFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
extraFrame.addWindowListener(windowAdapter);
extraFrame.setVisible(true);
}
frame.setVisible(true);
}
}

Action / Keylisteners do not work properly

I have two frames (One main and one popup). On the main frame key & mouse listeners are added. Both work just fine. On the pop up there are 50 buttons. I have one action listener for all of them. It works fine. I also have key & mouse listeners. Mouse works. Key is flaky.
The same keyListener class used in main frame is added to the pop-up frame too.
As soon as the pop up shows, key listener works, once mouse click happens (Action listener kicks in) keylistener stops working. Please help. The code attached is a simplified version
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class key {
private static class toolsAction implements ActionListener {
public void actionPerformed (ActionEvent ae) {
log("Command " + Integer.parseInt(ae.getActionCommand()));
}
}
private static void log(String s) { System.out.println(s); }
private static class keyboardHandler implements KeyListener {
public void keyPressed( KeyEvent e) { log("KB Press called "); }
public void keyReleased(KeyEvent e) { log("KB Release called "); }
public void keyTyped (KeyEvent e) { log("KB Typed called "); }
}
public static void main(String [] args) {
JFrame pFrame = new JFrame("Frame");
pFrame.addKeyListener(new keyboardHandler());
Container pane = pFrame.getContentPane();
pane.setLayout(null);
pane.setVisible(true);
pFrame.setSize(650, 300);
pFrame.setVisible(true);
JButton[] buttons = new JButton[50];
toolsAction action = new toolsAction();
for (int i = 0; i < 50; i++) {
buttons[i] = new JButton("" + i);
buttons[i].setActionCommand("" + i);
buttons[i].addActionListener(action);
pane.add(buttons[i]);
buttons[i].setBounds(((i % 10) * 60), ((i / 10) * 40), 60, 40);
}
}
}
An alternative approach uses Action and key bindings. The example below binds 10 buttons to the number keys, also using the numbers as the MNEMONIC_KEY of each.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** #see http://stackoverflow.com/a/14182227/230513 */
public class Key {
public static final int SIZE = 10;
private static class ToolAction extends AbstractAction {
public ToolAction(int i) {
super(String.valueOf(i));
putValue(MNEMONIC_KEY, KeyEvent.VK_0 + i);
}
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println(ae.getActionCommand());
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, SIZE / 2));
for (int i = 0; i < SIZE; i++) {
final ToolAction toolAction = new ToolAction(i);
JButton b = new JButton(toolAction);
String name = b.getText();
b.getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_0 + i, 0), name);
b.getActionMap().put(name, toolAction);
f.add(b);
}
f.pack();
f.setVisible(true);
}
});
}
}
The KeyListener will only be executed if the component, to which the listener is added, has the (keyboard) focus. If you click on another component, that component will become the focus and the keyboard events will be sent to its KeyListeners.
Not sure if that is the problem without seeing the code of the popup, but it's the problem in the posted code...
EDIT
you can add an AWTEventListener to the Toolkit to intercept all events independently of the focused component:
private static class AWTListener implements AWTEventListener {
#Override
public void eventDispatched(AWTEvent event) {
log("AWT: " + event);
}
};
...
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addAWTEventListener(new AWTListener(), AWTEvent.KEY_EVENT_MASK);
I solved this problem by adding MouseMotionListener and in the mouseMoved method calling pFrame.requestFocusInWindow() & also adding focus request in the button's actionPerformed method.
Even with just one button in a frame as long as the ActionListener is added to the button, KeyListener does not work.
This is kludgy at best, but works for me. I would still love to hear from the community why my original code does not work.

How to quit or exit slave JWindow that was created from a master JWindow in Java?

How can i exit only they new MainGame that i created from Main?
Where Main is having an original layer of game. And the MainGame was a dialog window (such as modal windows).
Main.java: (main code)
public class Main extends JWindow
{
private static JWindow j;
public static MainGame mp;
public static void main(String[] args)
{
new Thread(new Runnable()
{
public void run()
{
mp = new MainGame();
mp.runit();
//mp.stopit();
}
}).start();
j = new Main();
j.setVisible(true);
}
}
MainGame.java: (this was extended by Main, and i would like to quite this only).
public class MainGame extends JWindow
{
private static JWindow j;
public MainGame()
{
// some GUI ...
}
public static void runit()
{
j = new MainGame();
j.setVisible();
}
}
1) better would be implements CardLayout, as create Top-Level Container for new Window, then you'll only to switch betweens Cards
2) don't create lots of Top-Level Container on Runtime, because there are still in JVM memory untill current instance exist,
create required number of and re-use that, to avoiding possible memory lacks
then you have to call setVisible(false) and setVisible(true)
JWindow missed methods for setting setDefaultCloseOperation(Whatever);
3) if you'll create constructor public JWindow(Frame owner), then you'll call directly
SwingUtilities.getAccessibleChildrenCount() and SwingUtilities.getWindowAncestor()
import javax.swing.*;
import java.awt.*;
public class Testing {
private JFrame f = new JFrame("Main Frame");
private JWindow splashScreen = new JWindow();
public Testing() {
splashScreen = new JWindow(f);
splashScreen.getContentPane().setLayout(new GridBagLayout());
JLabel label = new JLabel("Splash Screen");
label.setFont(label.getFont().deriveFont(96f));
splashScreen.getContentPane().add(label, new GridBagConstraints());
splashScreen.pack();
splashScreen.setLocationRelativeTo(null);
splashScreen.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
readDatabase();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}).start();
}
public void readDatabase() {
//simulate time to read/load data - 10 seconds?
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
public void createAndShowGUI() {
JLabel label = new JLabel("My Frame");
label.setFont(label.getFont().deriveFont(96f));
f.add(label);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
System.out.println("JFrame getAccessibleChildrenCount count -> "
+ SwingUtilities.getAccessibleChildrenCount(f));
System.out.println("JWindow getParent -> "
+ SwingUtilities.getWindowAncestor(splashScreen));
splashScreen.dispose();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Testing t = new Testing();
}
});
}
}
I did not go really into your design. but there is 'j.dispose();'.
this should work. here is the java documentation.
notice:
dispose(); - deletes the window from memory.
setVisibilty(false); - just hides it from the screen.
You can override the 'dispose()' function to do some stuff while the widow is closing (updating scores if its a game) but at the end of the overriden function you have to call 'super.dispose();' so the function of the class Window is called.
And the MainGame was a dialog window
But thats not what your code uses. You use a JWindow.
You should be using a JDialog for a modal window. Then you just dispose() the window.

Categories