Swing timer synchronization - java

I'm confused about how the Swing timer work. In the code below, I want to display from 0~9 every 400ms in the first text field when press START (once). After that the second text field will display "Finished".
public class Main extends JPanel{
private static final long serialVersionUID = 1L;
private JButton bStart;
private JTextField tTest;
private JTextField tNumber;
Main(){
bStart = new JButton("Start");
bStart.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
displayNumbers();
}
});
tTest = new JTextField(null, 30);
tNumber = new JTextField(" ", 30);
tNumber.setEditable(false);
this.setSize(300, 100);
this.add(bStart);
this.add(tNumber);
this.add(tTest);
}
public void displayNumbers(){
new Timer(400, new ActionListener() {
int i = 0;
public void actionPerformed(ActionEvent evt) {
if(i<10){
tNumber.setText(Integer.toString(i));
i++;
}
else
((Timer)evt.getSource()).stop();
}
}).start();
tTest.setText("Finished");
}
public static void createAndShowGUI(){
JFrame frame = new JFrame("test");
frame.add(new Main());
frame.setSize(400, 150);
frame.setVisible(true);
}
public static void main(String args[]){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
createAndShowGUI();
}
});
}
}
However, it first displays "Finished" before finishing displaying 0 ~ 9. I think the Swing timer works also in EDT, so "tTest.setText("Finished");" will be executed after timer thread. Why does not it work? How do I wait finishing displaying 0 ~ 9 then print "Finished"? Thanks!
Thanks for your answers. In fact what I want to ask is in general:
new Timer(delay, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doSomething();
}
}).start();
doOthers();
How to let doOthers() execute after all the doSomething()? (In some cases, we cannot put doOthers() inside actionPerformed function, as some answers mentioned).

The timer works concurrently. So the timer is started, then the text is set to finished, and then the timer fires and the first number appears.
To make the timer display finished after it is finished, put the tTest.setText("Finished"); in the else clause of if(i<10).

Related

Timer works with println but not label using java

I have some labels that become visible when the letter a is pressed.
private void formKeyPressed(java.awt.event.KeyEvent evt) {
// TODO add your handling code here:
if(evt.getKeyCode()==KeyEvent.VK_A){
jLabel7.setVisible(true);
jLabel8.setVisible(true);
jLabel9.setVisible(true);
myBlink();
}
I have Label8 on a timer myBlink()
public void myBlink()
{
new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("begin");
jLabel8.setVisible(false);
jLabel8.setVisible(true);
System.out.println("Timer");
}
}).start();
}
I have placed printlns to see if timer begins and ends and when I press key "a" my output shows begin Timer multiple times but my label does not appear and disappear. What tweak does this code need? What am I missing? Thanks for the extra set of eyes.
This is probably because you call successively setVisible(false) and setVisible(true) which is done too fast to be seen, you should use a variable and modify its value any time the action of the Timer is called as next:
public void myBlink()
{
new Timer(1000, new ActionListener() {
boolean visible = true;
public void actionPerformed(ActionEvent e) {
jLabel8.setVisible(visible = !visible);
}
}).start();
}

How to get Mouse hover event in `Java Swing`

I have a JPanel with multiple components in it - like a few JLabels, JTextBoxes, JComboBoxes, JCheckBoxes etc.
I want to display a pop up help window if the user hovers over these components for say 3 secs.
So far I added a MouseListener to one of my Components and it does display the required pop up and help. However I can't achieve it after 3 sec delay. As soon as the user moves the mouse to through that area of the component the pop up displays. This is very annoying as the components are almost unusable. I have tried using MouseMotionListener and having the below code in mouseMoved(MouseEvent e) method. Gives the same effect.
Any suggestion on how can I achieve the mouse hover effect - to display the pop up only after 3 sec delay?
Sample Code:(Mouse Entered method)
private JTextField _textHost = new JTextField();
this._textHost().addMouseListener(this);
#Override
public void mouseEntered(MouseEvent e) {
if(e.getSource() == this._textHost())
{
int reply = JOptionPane.showConfirmDialog(this, "Do you want to see the related help document?", "Show Help?", JOptionPane.YES_NO_OPTION);
if(reply == JOptionPane.YES_OPTION)
{
//Opens a browser with appropriate link.
this.get_configPanel().get_GUIApp().openBrowser("http://google.com");
}
}
}
Use a Timer in mouseEntered(). Here's a working example:
public class Test {
private JFrame frame;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Test test = new Test();
test.createUI();
}
});
}
private void createUI() {
frame = new JFrame();
JLabel label = new JLabel("Test");
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent me) {
startTimer();
}
});
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
private void startTimer() {
TimerTask task = new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(frame, "Test");
}
});
}
};
Timer timer = new Timer(true);
timer.schedule(task, 3000);
}
}

Java Timer usage with JButtons

I have problem that I don't understand, how to correctly use the Java timer with JButton.
The idea of what I need -
When I click on JButton with text "0" then starts the timer counting from two seconds till zero.
When button is released program checks the situation:
if timer now is 0 then it shows in JTextField sign "+", else it shows "0".
Here is my code of program. Can someone please add the things that I need to make the program work like the idea I want?
public class DialPanel extends JPanel {
private MainFrame frame;
public DialPanel(MainFrame frame) {
this.frame = frame;
this.setLocation(0, 90);
this.setSize(300, 290);
this.setLayout(null);
this.setBackground(color);
this.initContent();
}
// -------------------------------------------------------------------------
private JButton btnNumZero;
private JTextField txfNumber;
private void initContent() {
txfNumber = new JTextField();
this.add(txfNumber);
txfNumber.setSize(190, 30);
txfNumber.setLocation(30, 0);
txfNumber.setFocusable(false);
txfNumber.addActionListener(controller);
btnNumZero = new JButton();
this.add(btnNumZero);
btnNumZero.setText("0");
btnNumZero.setFocusable(false);
btnNumZero.setSize(30, 30);
btnNumZero.setLocation(10, 10);
btnNumZero.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//Start someTimer countdown from two seconds
}
});
btnNumZero.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
//Stop someTimer
//if someTimer == 0 seconds then do this line:
txfNumber.setText("+");
//else do this line:
txfNumber.setText("0");
}
});
}
}
Excuse me if there is some unnecessary error with code. I deleted and changed a lot of things from the real one code so that this could be more understandable and clear for reading.
Instead of using a Timer, you may want to just make use of System.currentTimeMillis(). The Timer may not be a timer in the sense you are looking for, as a stopwatch type object.
You could do something like this
long startTime;
long endTime;
btnNumZero.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
startTime = System.currentTimeMillis();
}
});
btnNumZero.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
endTime = System.currentTimeMillis();
long difference = endTime - startTime;
if (difference > 2000)
txfNumber.setText("+");
else
txfNumber.setText("0");
}
});

Run and pause a GUI background thread by clicking a button

I need to run a background thread in my Java GUI that only runs when I click a button and pauses when I click that button again. I am not exactly sure how to set this up, but I have placed a thread in my constructor and the while loop within is set to go through when I set a specific boolean to TRUE. One button switches from setting this boolean TRUE or FALSE.
Everything else I have in this GUI works fine. When I tried debugging the thread, it actually works as I step through the thread but nothing when I try running the GUI completely. The GUI is rather large so I'm gonna put up a portion of the constructor and the action listener of the button. The rest of the code is unnecessary since it works just fine. I need to know what I am doing wrong here:
public BasketballGUI() {
// certain labels and buttons
Thread runningSim = new Thread() {
public void run() {
while(simRun) {
// do stuff here
}
}
};
runningSim.start();
}
// other GUI stuff
// actionListener that should run the thread.
class SimButtonListener implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
if(!simRun) {
simRun = true;
sim.setText("Pause Simulator");
}
else if(simRun) {
simRun = false;
sim.setText("Run Simulator");
}
// other stuff in this actionListener
}
}
Establish a Swing based Timer with an ActionListener that will be called repeatedly.
In the actionPerformed(ActionEvent) method call repaint().
Start the timer (Timer.start()) when the user clicks Start
Stop the timer (Timer.stop()) when the user clicks Stop
If you cannot get it working from that description, I suggest you post an SSCCE of your best attempt.
I thought I had one 'lying around'.. Try this working SSCCE which uses images created in this SSCCE.
I could see this background thread useful for a Java GUI when handling button events to affect something like a text area or progress bar.
For the sake of argument, I will build you a tiny GUI that affects a Text Area. I hope this helps you.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;
public class TestClass extends JPanel {
super("TestClass - Title");
private AtomicBoolean paused;
private JTextArea jta;
private JButton btn;
private Thread thread;
public TestClass() {
paused = new AtomicBoolean(false);
jta = new JTextArea(100, 100);
btn = new JButton();
initialize();
}
public void initialize() {
jta.setLineWrap(true);
jta.setWrapStyleWord(true);
add(new JScrollPane(jta));
btn.setPreferredSize(new Dimension(100, 100));
btn.setText("Pause");
btn.addActionListener(new ButtonListener());
add(btn);
Runnable runnable = new Runnable() {
#Override
public void run() {
while(true) {
for(int i = 0; i < Integer.MAX_VALUE; i++) {
if(paused.get()) {
synchronized(thread) {
try {
thread.wait();
} catch(InterruptedException e) {
}
}
}
}
jta.append(Integer.toString(i) + ", ");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
};
thread = new Thread(runnable);
thread.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 30);
}
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
if(!paused.get()) {
btn.setText("Start");
paused.set(true);
} else {
btn.setText("Pause");
paused.set(false);
synchronized(thread) {
thread.notify();
}
}
}
}
}
Main class to call everything.
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class MainClass {
public static void main(final String[] arg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestClass());
frame.pack();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
}
I did not test this code to see if it works exactly, Its main goal is to break you through your coders block and use my components to fix your issue. Hope this helped. Need anything else Email me at DesignatedSoftware#gmail.com

In Java how do I repaint a panel from an actionPerformed thread while it is currently running?

I have a class (called Class_GUI) which has a panel with lots of buttons on it. Class_GUI has some methods that change the text and colour of the buttons.
I have also have a program with the actionPerformed method. When this is called it creates an instance of Class_GUI and repeatedly calls Class_GUI methods, changing the buttons etc.
The issue I'm having is that the buttons only display properly once the actionPerformed method has finished entirely whereas I want it to change after each Class_GUI method is called.
My attempt so far is in each Class_GUI method I do this at the end of the method:
SwingUtilities.invokeLater(Refresh_GUI);
Where Refresh_GUI is defined:
Runnable Refresh_GUI = new Runnable(){
public void run(){
frame.revalidate();
frame.repaint();
}
};
Assuming that your actionPerformed method is being called within the context of the Event Dispatching Thread, no UI updates will occur until AFTER the actionPerformed method has competed, even using SwingUtilities#invokeLater won't change that, because until the actionPerformed method exits, the EDT won't be able to continue processing (amongst other things) repaint requests.
The best you can do, is start a second thread and from within that thread, update your UI components...but, you area going to be forced to use SwingUtilities#invokeLater as you should NEVER update any UI component outside the EDT.
The advantage though, is that the thread does not need to compete in order for the EDT to start processing the repaint request
UPDATED with Example
public class SwingThreadUpdate {
public static void main(String[] args) {
new SwingThreadUpdate();
}
public SwingThreadUpdate() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BlinkPane extends JPanel {
private JLabel label;
private JButton button;
public BlinkPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
label = new JLabel("Blinky");
label.setBackground(Color.RED);
button = new JButton("Click me");
add(label, gbc);
gbc.gridy++;
add(button, gbc);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
new Thread(new BlinkTask(BlinkPane.this)).start();
}
});
}
private void setBlink(boolean blink) {
label.setOpaque(blink);
}
private void reset() {
button.setEnabled(true);
label.setOpaque(false);
}
}
public class BlinkTask implements Runnable {
private BlinkPane blinkPane;
protected BlinkTask(BlinkPane blinkPane) {
this.blinkPane = blinkPane;
}
#Override
public void run() {
Blink blinkOn = new Blink(blinkPane, true);
Blink blinkOff = new Blink(blinkPane, false);
for (int index = 0; index < 10; index++) {
if (index % 2 == 0) {
SwingUtilities.invokeLater(blinkOn);
} else {
SwingUtilities.invokeLater(blinkOff);
}
try {
Thread.sleep(125);
} catch (InterruptedException ex) {
}
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
blinkPane.reset();
}
});
}
}
public class Blink implements Runnable {
private BlinkPane blinkPane;
private boolean blink;
public Blink(BlinkPane blinkPane, boolean blink) {
this.blinkPane = blinkPane;
this.blink = blink;
}
#Override
public void run() {
blinkPane.setBlink(blink);
blinkPane.repaint();
}
}
}
You might like to have a read through Painting in AWT and Swing for more information.
Incase your actionPerform method calls code to update buttons in a for loop you could also add the updation code in the invokeLater that way both the updation and painting code will run one by one. Invoke later will execute only after current method completes its execution so only way to ensure painting happens faster is to break your tasks into smaller peices.
First, make sure you are only accessing any GUI components from the Event Dispatch thread (via invokeLater or as part of handling a GUI event).
Second, if you change any properties of a GUI component, it should automatically post an event to repaint itself. If not you can try invoking component.repaint(). But it's critical that the changes to the component properties happen on the EDT.
A simple solution is execute the entire ActionPerformed event less task to clean the screen at the end of the event queue.
So, first it executes the cleanScreen() function because the rest of the event waits for all events finish.
AnyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cleanScreen(); //Modify components before action performer event
EventQueue.invokeLater( new Runnable() {
#Override public void run() {
anytask(); //Action performer event
}
});
}
});

Categories

Resources