I need to use a thread to change the position of my JLabel (movingDisplay) every second when I click on my button (btnDisplay) and for the thread to stop when I click on my other button (btnDStop). I have a class called MoveDisplay that implements Runnable and does this action when I click on btnDisplay. After MoveDisplay has randomized x and y for my JLabel, it's supposed to send x and y back to my main class where it updates the position of the JLabel. I have a method in my mainclass to update the JLabel position however I get a NullPointerException when trying to do so. In fact it doesn't work changing any component at all from MoveDisplay class.
public class Main {
public static void main(String[] args) {
GUIFrame test = new GUIFrame();
}
}
MoveDisplay class:
public class MoveDisplay implements Runnable {
private GUIFrame gui;
private boolean moving = true;
private Thread thread;
public void run() {
gui = new GUIFrame();
if (moving) {
Random rand = new Random();
while (moving) {
int x = rand.nextInt(150) + 1;
int y = rand.nextInt(150) + 1;
gui.moveDisplay(x, y, 100, 100);
try {
thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
}
public void start() {
thread = new Thread(new MoveDisplay());
thread.start();
}
public void stop() {
thread.interrupt();
System.out.println("Stopped");
}
}
GUIFrame class:
public class GUIFrame {
private JFrame frame; // The Main window
private JLabel movingDisplay;
private boolean playing = true;
private boolean moving = true;
private MoveDisplay moveDisplay = new MoveDisplay();
/**
* Starts the application
*/
public void Start() {
frame = new JFrame();
frame.setBounds(0, 0, 494, 437);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setTitle("Multiple Thread Demonstrator");
InitializeGUI(); // Fill in components
frame.setVisible(true);
frame.setResizable(false); // Prevent user from change size
frame.setLocationRelativeTo(null); // Start middle screen
}
public void InitializeGUI() {
// The moving display outer panel
JPanel pnlDisplay = new JPanel();
Border b2 = BorderFactory.createTitledBorder("Display Thread");
pnlDisplay.setBorder(b2);
pnlDisplay.setBounds(12, 118, 222, 269);
pnlDisplay.setLayout(null);
// Add buttons and drawing panel to this panel
btnDisplay = new JButton("Start Display");
btnDisplay.setBounds(10, 226, 121, 23);
pnlDisplay.add(btnDisplay);
btnDStop = new JButton("Stop");
btnDStop.setBounds(135, 226, 75, 23);
pnlDisplay.add(btnDStop);
pnlMove = new JPanel();
pnlMove.setBounds(10, 19, 200, 200);
Border b21 = BorderFactory.createLineBorder(Color.black);
pnlMove.setBorder(b21);
pnlDisplay.add(pnlMove);
// Then add this to main window
frame.add(pnlDisplay);
movingDisplay = new JLabel("DisplayThread");
pnlMove.add(movingDisplay);
btnDStop.setEnabled(false);
btnDisplay.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
moving = true;
btnDisplay.setEnabled(false);
btnDStop.setEnabled(true);
startMoveDisplay();
}
});
btnDStop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
moving = false;
btnDisplay.setEnabled(true);
btnDStop.setEnabled(false);
startMoveDisplay();
}
});
}
public void startMoveDisplay() {
if(moving) {
moveDisplay.start();
}
else {
moveDisplay.stop();
}
}
public void moveDisplay(int x, int y, int a, int b) {
movingDisplay.setBounds(10,10,150,150);
}
}
I've checked the code and in the MoveDisplay.run() you were creating a new frame, but in it's constructor the panel wasn't initialized, which triggered the NPE. Because of this I've refactored the GUIFrame constructor to initialeze all components (invoked the Start() method) and removed the new frame's initialization from the run method. Here are the modified classed
public class MoveDisplay {
private GUIFrame gui;
private volatile boolean moving;
public MoveDisplay(GUIFrame gui) {
this.gui = gui;
}
public void start() {
moving = true;
Random rand = new Random();
while (moving) {
int x = rand.nextInt(150) + 1;
int y = rand.nextInt(150) + 1;
gui.moveDisplay(x, y, 100, 100);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
moving = false;
}
}
And here is the new frame class
public class GUIFrame {
private JFrame frame; // The Main window
private JLabel movingDisplay;
private boolean playing = true;
private boolean moving = true;
private MoveDisplay moveDisplay;
public GUIFrame() {
Start();
}
/**
* Starts the application
*/
private void Start() {
frame = new JFrame();
frame.setBounds(0, 0, 494, 437);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setTitle("Multiple Thread Demonstrator");
InitializeGUI(); // Fill in components
frame.setVisible(true);
frame.setResizable(false); // Prevent user from change size
frame.setLocationRelativeTo(null); // Start middle screen
moveDisplay = new MoveDisplay(this);
}
public void InitializeGUI() {
// The moving display outer panel
JPanel pnlDisplay = new JPanel();
Border b2 = BorderFactory.createTitledBorder("Display Thread");
pnlDisplay.setBorder(b2);
pnlDisplay.setBounds(12, 118, 222, 269);
pnlDisplay.setLayout(null);
// Add buttons and drawing panel to this panel
JButton btnDisplay = new JButton("Start Display");
btnDisplay.setBounds(10, 226, 121, 23);
pnlDisplay.add(btnDisplay);
JButton btnDStop = new JButton("Stop");
btnDStop.setBounds(135, 226, 75, 23);
pnlDisplay.add(btnDStop);
JPanel pnlMove = new JPanel();
pnlMove.setBounds(10, 19, 200, 200);
Border b21 = BorderFactory.createLineBorder(Color.black);
pnlMove.setBorder(b21);
pnlDisplay.add(pnlMove);
// Then add this to main window
frame.add(pnlDisplay);
movingDisplay = new JLabel("DisplayThread");
pnlMove.add(movingDisplay);
btnDStop.setEnabled(false);
btnDisplay.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
moving = true;
btnDisplay.setEnabled(false);
btnDStop.setEnabled(true);
startMoveDisplay();
}
});
btnDStop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
moving = false;
btnDisplay.setEnabled(true);
btnDStop.setEnabled(false);
startMoveDisplay();
}
});
}
public void startMoveDisplay() {
if(moving) {
new Thread(new Runnable() {
#Override
public void run() {
moveDisplay.start();
}
}).start();
} else {
moveDisplay.stop();
}
}
public void moveDisplay(int x, int y, int a, int b) {
movingDisplay.setBounds(x, y, a, b);
}
}
You have to get a reference to the existing GUIFrame instance instead of creating a new one, try this:
MoveDisplay
public class MoveDisplay implements Runnable {
private GUIFrame gui;
Random rand = new Random();
volatile boolean moving;
public MoveDispaly(GUIFrame gui){
this.gui = gui;
}
public void run() {
while (moving) {
int x = rand.nextInt(150) + 1;
int y = rand.nextInt(150) + 1;
try {
SwingUtilities.invokeAndWait(new Runnable(){
public void run(){
gui.moveDisplay(x, y, 100, 100);
}
});
thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
public void start() {
moving = true;
thread = new Thread(new MoveDisplay());
thread.start();
}
public void stop() {
moving = false;
}
}
GUIFrame
public class GUIFrame{
MoveDisplay moveDisplay = new MoveDisplay(this);
...
}
Also, you have to run the part of the code that changes the GUI on the EDT thread.
Is not a good idea to stop a thread by interrupting it, you can stop it by setting the moving variable to false.
Maybe you can also use the Singleton Pattern to get the original instance of GUIFrame:
public static class instanceClassA {
private static instanceClassA = null;
public static instanceClassA(){
if(instance == null){
instance = instanceClassA();
}
return instance;
}
}
Related
I would like to make my JToolBar impossible to detach from its container but still let the user drag it to one of the container's sides.
I know about
public void setFloatable( boolean b )
but this won't allow the user to move the JToolBar at all.
Is there any way of doing this without overwriting ToolBarUI?
Also, is there an option to highlight its new position before dropping it?
It's not the most elegant solution, but it works.
public class Example extends JFrame {
BasicToolBarUI ui;
Example() {
JToolBar tb = new JToolBar();
tb.add(new JButton("AAAAA"));
tb.setBackground(Color.GREEN);
ui = (BasicToolBarUI) tb.getUI();
getContentPane().addContainerListener(new Listener());
getContentPane().add(tb, BorderLayout.PAGE_START);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 300);
setLocationRelativeTo(null);
setVisible(true);
}
class Listener implements ContainerListener {
#Override
public void componentAdded(ContainerEvent e) {}
#Override
public void componentRemoved(ContainerEvent e) {
if (ui.isFloating()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ui.setFloating(false, null);
}
});
}
}
}
public static void main(String[] args) {
new Example();
}
}
Explanation:
Whenever the toolbar is moving to a floating state, it is instructed not do so. The only problem is that you have to wait for the EDT to finish the process for creating the floating window, and only then can you tell it not to float. The result is that you actually see the window created and then hidden.
Note:
I think that overriding the UI for the toolbar is a better solution, though it's possible that with a more intricate approach doing something similar to what I did will also work well.
works for me quite correctly on WinOS, old code from SunForum
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CaptiveToolBar {
private Robot robot;
private JDialog dialog;
private JFrame frame;
public static void main(String[] args) {
//JFrame.setDefaultLookAndFeelDecorated(true);
//JDialog.setDefaultLookAndFeelDecorated(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new CaptiveToolBar().makeUI();
}
});
}
public void makeUI() {
try {
robot = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
}
final JToolBar toolBar = new JToolBar();
for (int i = 0; i < 3; i++) {
toolBar.add(new JButton("" + i));
}
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.add(toolBar, BorderLayout.NORTH);
final ComponentListener dialogListener = new ComponentAdapter() {
#Override
public void componentMoved(ComponentEvent e) {
dialog = (JDialog) e.getSource();
setLocations(false);
}
};
toolBar.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
Window window = SwingUtilities.getWindowAncestor(toolBar);
if (window instanceof JDialog) {
boolean listenerAdded = false;
for (ComponentListener listener : window.getComponentListeners()) {
if (listener == dialogListener) {
listenerAdded = true;
break;
}
}
if (!listenerAdded) {
window.addComponentListener(dialogListener);
}
}
}
});
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentMoved(ComponentEvent e) {
if (dialog != null && dialog.isShowing()) {
setLocations(true);
}
}
});
frame.setVisible(true);
}
private void setLocations(boolean moveDialog) {
int dialogX = dialog.getX();
int dialogY = dialog.getY();
int dialogW = dialog.getWidth();
int dialogH = dialog.getHeight();
int frameX = frame.getX();
int frameY = frame.getY();
int frameW = frame.getWidth();
int frameH = frame.getHeight();
boolean needToMove = false;
if (dialogX < frameX) {
dialogX = frameX;
needToMove = true;
}
if (dialogY < frameY) {
dialogY = frameY;
needToMove = true;
}
if (dialogX + dialogW > frameX + frameW) {
dialogX = frameX + frameW - dialogW;
needToMove = true;
}
if (dialogY + dialogH > frameY + frameH) {
dialogY = frameY + frameH - dialogH;
needToMove = true;
}
if (needToMove) {
if (!moveDialog && robot != null) {
robot.mouseRelease(InputEvent.BUTTON1_MASK);
}
dialog.setLocation(dialogX, dialogY);
}
}
}
I am trying to create a slideshow with Java Swing.
I began with implementing a class PicturePanel
public class PicturePanel extends JPanel {
private int counter = 0;
private ImageIcon[] images = new ImageIcon[10];
private JLabel label;
public PicturePanel()
{
for(int i = 0 ; i <images.length;i++)
{
images[counter] = new ImageIcon("check.png");
label = new JLabel();
add(label);
Timer timer = new Timer(100, new TimerListener());
}
}
private class TimerListener implements ActionListener {
public TimerListener() {
}
#Override
public void actionPerformed(ActionEvent ae) {
counter++;
//counter% =images.length;
label.setIcon(images[counter]);
}
}
}
Then I am calling this class in my Jframe through this code :
panProfil= new PicturePanel();
panProfil is a Jpanel in my form
When I run my project, I don't get any errors, but there is nothing in my form. Can someone point me in the right direction?
So you haven't started your Timer that's the problem (as #ItachiUchiha pointed out). But another thing you need to do is know when to stop() the Timer or else it will keep running
You want to start() it in the constructor after you create the Timer. In you ActionListener, to stop it, you'll want to do something like this.
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae) {
if (counter == images.length) {
((Timer)e.getSource()).stop();
} else {
label.setIcon(images[counter]);
counter++;
}
}
}
If you want to access the Timer from your main GUI class, so you can control it, you want to have a getter for it, and declare it globally
public class PicturePanel extends JPanel {
private Timer timer = null;
public PicturePanel() {
timer = new Timer(1000, new TimerListener());
}
public Timer getTimer() {
return timer;
}
}
Then you can start and stop it from your main GUI class
DrawPanel panel = new DrawPanel();
Timer timer = panel.getTimer();
Also, I don't see the point of create a JLabel every iteration and adding it to the JPanel. You only need one.
public class Project2 {
int c=0;
public static void main(String arg[]) throws InterruptedException {
JFrame login = new JFrame("Login");
// creating a new frame
login.setSize(700, 500);
JPanel addPanel = new JPanel();
JLabel pic = new JLabel();
Project2 p2= new Project2();
String[] list = {"C:\\Users\\divyatapadia\\Desktop\\pic1.jpg", "C:\\Users\\divyatapadia\\Desktop\\benefit.PNG" , "C:\\Users\\divyatapadia\\Desktop\\pic2.jpg"};
pic.setBounds(40, 30, 500, 300);
JButton log = new JButton("SHOW");ImageIcon[] img = new ImageIcon[3];
for(int time = 0;time<3;time++) {
img[time]= new ImageIcon(list[time]);
}
addPanel.add(pic);
addPanel.add(log);
login.add(addPanel);
login.setVisible(true);
try {
log.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Timer t ;
t= new Timer(1000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(p2.c<3) {
pic.setIcon(img[p2.c]);
p2.c++;
}}
});
t.start();
}
}
);
}catch(Exception ex)
{
System.out.println(ex.toString());
}
}
}
I have this set up to update pretty constantly on the timer, but I want to be able to pause the timer with the spacebar. I have attempted to implement an actionListener, but I am not sure what to apply it to. Most of the examples I can find relate to buttons or text boxes, not keyboard presses inside a jpanel. I have printed src to the console and it doesn't appear to be registering my spacebar as an event... I have tried adding the actionListener, but I am not getting something about the syntax. Any help would be appreciated.
public class Arena extends JFrame {
private PaintPanel paintPanel;
public Arena() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setMinimumSize(new Dimension(1000, 720));
paintPanel = new PaintPanel();
getContentPane().add(paintPanel, BorderLayout.CENTER);
paintPanel.setBackground(Color.black);
paintPanel.setFocusable(true);
//paintPanel.addActionListener(this);
pack();
paintPanel.initGame();
}
class PaintPanel extends JPanel implements ActionListener {
private List<Gladiator> gladiators;
private Timer timer;
private Ai AI;
private Load loadObject;
public void initGame() {
timer = new Timer(500, this);
timer.start();
AI = new Ai(gladiators);
loadObject = new Load();
}
#Override
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
System.out.println("************* "+src);
// if (src == spacebar) {
// } else if (src = timer) {
AI.runAI();
try {
Thread.sleep(100);
System.out.println("sleeping");
} catch (InterruptedException d) {
System.err.println("Caught : InterruptedException" + d.getMessage());
}
repaint();
// }
}
public PaintPanel(){
super();
gladiators = new ArrayList<Gladiator>();
String[][] gladiatorInfo = new String[100][25];
String[] gladiatorRaw = new String[100];
String[][] parsedInfo = new String[250][100];
Gladiator[] gladiator = new Gladiator[20];
int[] matchInfo = new int[3];
int numberofcontestants = 0;
gladiatorRaw = loadObject.getGladiators(gladiatorRaw);
parsedInfo = loadObject.parseStats(gladiatorRaw);
Venue venue = new Venue();
matchInfo = loadObject.getMatchSettings(matchInfo);
venue.populateVenue(matchInfo);
gladiator = createGladiators(venue);
for (int a = 0; a < venue.contestants; a++) {
System.out.println("Populating Gladiator "+a);
gladiator[a].populategladiators(parsedInfo,a);
gladiator[a].getEquipment();
gladiator[a].contestantNumber = a;
gladiators.add(gladiator[a]);
}
}
public Gladiator[] createGladiators(Venue venue) {
int[][] initialPlacement = new int[20][2];
Gladiator[] gladiator = new Gladiator[(venue.contestants)];
initialPlacement = loadObject.loadInitialPlacement(venue.contestants);
for (int a = 0; a < venue.contestants; a++) {
System.out.println("Add gladiator "+a);
gladiator[a] = new Gladiator(initialPlacement[a][0],initialPlacement[a][1]);
System.out.println(initialPlacement[a][0]+","+initialPlacement[a][1]);
}
return gladiator;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
try {
BufferedImage background = ImageIO.read(new File("background.png"));
g.drawImage(background,0,0,this);
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
}
for (Gladiator s : gladiators){
s.draw(g2);
}
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Arena gamePanel = new Arena();
gamePanel.setVisible(true);
}
});
}
}
Also, is there a getEvent() key code for the spacebar? Can't seem to find one. Thanks
You should use the key bindings API
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space");
am.put("space", new AbstractAction() {
public void actionPerformed(ActionEvent evt) {
if (timer.isRunning()) {
timer.stop();
} else {
timer.start();
}
}
});
The InputMap/ActionMap can be applied to any component that extends from JComponent, but in your case, I'd suggest attaching it to your PaintPane
f.e. I have an email client, it receives new message, button with incoming messages starts doing something, until user clicks it to see whats up.
I'm trying to make button attract attention by selecting, waiting and then deselecting it, but this does nothing!
do{
button.setSelected(true);
Thread oThread = new Thread() {
#Override
public void run() {
synchronized (this) {
try {
wait(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
button.setSelected(false);
}
};
oThread.start();
}while(true);
You should use Swing timers for that. Don't interact with GUI objects from foreign threads.
There's some docs in the Java tutorial: How to use Swing timers.
Here's an example way you could do this playing with the button's icon.
// member var
Icon buttonIcon;
Timer timer;
// in constructor for example
buttonIcon = new ImageIcon("resources/icon.png");
button.setIcon(buttonIcon);
timer = new Timer(1000, this);
timer.start();
// in the actionPerformed handler
if (button.getIcon() == null)
button.setIcon(icon);
else
button.setIcon(null);
Your class will need to implement ActionListener for this to work like that. Add some logic to stop the flashing when you need it.
hafl_workaround to your questions
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ShakingButtonDemo implements Runnable {
private JButton button;
private JRadioButton radioWholeButton;
private JRadioButton radioTextOnly;
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new ShakingButtonDemo());
}
#Override
public void run() {
radioWholeButton = new JRadioButton("The whole button");
radioTextOnly = new JRadioButton("Button text only");
radioWholeButton.setSelected(true);
ButtonGroup bg = new ButtonGroup();
bg.add(radioWholeButton);
bg.add(radioTextOnly);
button = new JButton(" Shake with this Button ");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
shakeButton(radioWholeButton.isSelected());
}
});
JPanel p1 = new JPanel();
p1.setBorder(BorderFactory.createTitledBorder("Shake Options"));
p1.setLayout(new GridLayout(0, 1));
p1.add(radioWholeButton);
p1.add(radioTextOnly);
JPanel p2 = new JPanel();
p2.setLayout(new GridLayout(0, 1));
p2.add(button);
JFrame frame = new JFrame();
frame.setTitle("Shaking Button Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(p1, BorderLayout.NORTH);
frame.add(p2, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void shakeButton(final boolean shakeWholeButton) {
final Point point = button.getLocation();
final Insets margin = button.getMargin();
final int delay = 75;
Runnable r = new Runnable() {
#Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
if (shakeWholeButton) {
moveButton(new Point(point.x + 5, point.y));
Thread.sleep(delay);
moveButton(point);
Thread.sleep(delay);
moveButton(new Point(point.x - 5, point.y));
Thread.sleep(delay);
moveButton(point);
Thread.sleep(delay);
} else {// text only
setButtonMargin(new Insets(margin.top, margin.left + 3, margin.bottom, margin.right - 2));
Thread.sleep(delay);
setButtonMargin(margin);
Thread.sleep(delay);
setButtonMargin(new Insets(margin.top, margin.left - 2, margin.bottom, margin.right + 3));
Thread.sleep(delay);
setButtonMargin(margin);
Thread.sleep(delay);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
};
Thread t = new Thread(r);
t.start();
}
private void moveButton(final Point p) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button.setLocation(p);
}
});
}
private void setButtonMargin(final Insets margin) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button.setMargin(margin);
}
});
}
}
When setting a busy cursor on the glass pane of the application frame after closing a modal dialog, the busy cursor is not always displayed. Sometimes it works (the first time it is mostly always working), sometimes not.
Even better, when setting the busy cursor before opening the dialog. The busy cursor get displayed but when moving the mouse inside and then outside the dialog the busy cursor is not displayed anymore.
Note that I observe the following bug on Linux only. On Mac OS X or Windows the behavior is deterministic and consistent.
Another hint, in the first case of the code sample, when the mouse is NOT entering the dialog and the YES_OPTION is selected using the keyboard, the busy mouse cursor is always shown. Also in this case, the "Please wait..." label on the glass pane get never painted.
Here a SSCCE demonstrating these bugs:
import java.awt.event.*;
import javax.swing.*;
public class TestFrame extends JFrame {
private JPanel panel;
private JPanel glassPane;
public TestFrame() {
final JButton button1 = new JButton(new AbstractAction("Start activity indicator after closing the dialog") {
#Override
public void actionPerformed(ActionEvent e) {
doAction1();
}
});
final JButton button2 = new JButton(new AbstractAction("Start activity indicator before opening the dialog") {
#Override
public void actionPerformed(ActionEvent e) {
doAction2();
}
});
panel = new JPanel();
panel.add(button1);
panel.add(button2);
getContentPane().add(panel, BorderLayout.NORTH);
glassPane = (JPanel) getGlassPane();
glassPane.setLayout(new BorderLayout());
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
glassPane.add(new JLabel("Please Wait..."), BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setVisible(true);
}
public void doAction1() {
System.out.println("IsStartingInEDT?: "+ SwingUtilities.isEventDispatchThread());
final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
if (JOptionPane.YES_OPTION == response) {
startActivity();
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
}
stopActivity();
}
}
public void doAction2() {
startActivity();
System.out.println("IsStartingInEDT?: "+ SwingUtilities.isEventDispatchThread());
final int response = JOptionPane.showConfirmDialog(this, "Move the mouse inside the dialog (me) and then outside, the busy indicator is not shown anymore");
if (JOptionPane.YES_OPTION == response) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
}
}
stopActivity();
}
public void startActivity() {
System.out.println("TestFrame.startActivity()");
glassPane.setVisible(true);
}
public void stopActivity() {
System.out.println("TestFrame.stopActivity()");
glassPane.setVisible(false);
}
/**
* #param args
*/
public static void main(String[] args) {
new TestFrame();
}
}
At the moment I did not find any related issues in the JavaBug parade. I will search further before opening a new one.
I also already read the following article but it is not very convenient as making a good modal dialog from a non-modal one is not straightforward:
http://www.javaspecialists.eu/archive/Issue065.html
Can anyone provide some help?
Thanks in advance, Pierre
You have some threading issue here.
Is IsStartingInEDT true?
If yes, you are doing it wrong because:
You should not sleep in UI thread. This would stop the screen update.
If no, you are doing it wrong because:
OptionPane.showConfirmDialog() must be called from the UI thread.
you should do something like this:
public void doAction1() {
if (!SwingUtilities.isEventDispatchThread()) {
System.err.println("error, must be edt");
return;
}
final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
if (JOptionPane.YES_OPTION == response) {
startActivity(); // change glass panel in edt
// new thread for long standing task
new Thread( new Runnable() { public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
}
SwingUtilities.invokeAndWait(new Runnable(){ public void run() {
// changing glass panel need edt
stopActivity();
});
}).start();
}
}
1st. by using Tread.sleep(int) pretty sure to block EDT, with all issues desribed about Concurrency in Swing
2.nd works because initializations for JOptionPane create a new EDT
here is simple demonstrations about ...., please that only example and be sure that is against all Swing rules, but demostrated lock and unlock EDT by usage Tread.sleep(int) during EDT
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class ShakeComponents1 {
private JFrame frame = new JFrame();
private final String items[] = {"One", "Two", "Three"};
private Timer timer;
private JPanel panel = new JPanel();
private JPanel buttonPanel = new JPanel();
private JButton button = new JButton(" Exit ");
private boolean repeats = true;
private boolean runs = false;
private Color clr[] = {Color.red, Color.blue, Color.magenta};
private Insets initMargin;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ShakeComponents1().makeUI();
}
});
}
public void makeUI() {
buttonPanel = new JPanel();
buttonPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
buttonPanel.setLayout(new BorderLayout());
button.setPreferredSize(new Dimension(100, 45));
button.setForeground(Color.darkGray);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
Runnable doRun = new Runnable() {
#Override
public void run() {
System.exit(0);
}
};
SwingUtilities.invokeLater(doRun);
}
});
button.addMouseListener(new java.awt.event.MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
if (runs) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
runs = false;
timer.stop();
changePnlBorder(new EmptyBorder(5, 5, 5, 5));
changeBtnForegroung(Color.darkGray);
}
});
}
}
#Override
public void mouseExited(MouseEvent e) {
if (!runs) {
timer.start();
runs = true;
}
}
});
buttonPanel.add(button);
final Insets margin = button.getMargin();
panel.add(buttonPanel);
for (int i = 0; i < 2; i++) {
JComboBox combo = new JComboBox(items);
combo.setMinimumSize(new Dimension(50, 25));
combo.setMaximumSize(new Dimension(150, 25));
combo.setPreferredSize(new Dimension(100, 25));
combo.addActionListener(new ShakeAction());
panel.add(combo);
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocation(50, 50);
frame.setVisible(true);
timer = new Timer(500, new ShakeAction());
timer.setRepeats(repeats);
initMargin = button.getMargin();
}
private class ShakeAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private int noColor = 0;
private Border border;
private int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
if (count > 5) {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(500);
changeBtnForegroung(Color.darkGray);
Thread.sleep(500);
count = 0;
Thread.sleep(750);
} catch (Exception e) {
System.out.println(e);
}
}
}).start();
} else {
new Thread(new Runnable() {
#Override
public void run() {
try {
runs = true;
if (noColor < 2) {
noColor++;
changeBtnForegroung(clr[noColor]);
} else {
noColor = 0;
changeBtnForegroung(clr[noColor]);
}
changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
border = new EmptyBorder(0, 5, 10, 5);
changePnlBorder(border);
Thread.sleep(100);
changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
border = new EmptyBorder(0, 0, 10, 10);
changePnlBorder(border);
Thread.sleep(100);
changeBtnMargin(new Insets(initMargin.top, initMargin.left + 10, initMargin.bottom, initMargin.right - 10));
border = new EmptyBorder(5, 10, 5, 0);
changePnlBorder(border);
Thread.sleep(100);
changeBtnMargin(new Insets(initMargin.top, initMargin.left - 10, initMargin.bottom, initMargin.right + 10));
border = new EmptyBorder(10, 10, 0, 0);
changePnlBorder(border);
Thread.sleep(100);
changeBtnMargin(new Insets(initMargin.top, initMargin.left, initMargin.bottom, initMargin.right));
border = new EmptyBorder(5, 5, 5, 5);
changePnlBorder(border);
Thread.sleep(100);
count++;
} catch (Exception e) {
System.out.println(e);
}
}
}).start();
}
}
}
private void changePnlBorder(final Border b) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
buttonPanel.setBorder(b);
buttonPanel.revalidate();
buttonPanel.repaint();
}
});
}
private void changeBtnForegroung(final Color c) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button.setForeground(c);
}
});
}
private void changeBtnMargin(final Insets margin) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button.setMargin(margin);
}
});
}
}
conclusion -> you can create new Thread as BackGroung Task(s) wrapped into Runnable, if you wnat to simulate LongRunning Task and with Thread.sleep(int), maybe answer to your question is here
sure correct way would be by using SwingWorker for that, with Thread.sleep(int) too