make button unresponsive for a while java [duplicate] - java

This question already has an answer here:
Using a Swing Timer to hide a notification temporarily
(1 answer)
Closed 9 years ago.
I was trying a simple code in java for test, just a button when u click it it sleeps for 5 seconds, here's the handler
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
I want this button doesn't receive any events until it finishes working (5 seconds), I tried to disable and enable it in the handler but in vain
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jButton1.setEnabled(false);
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
jButton1.setEnabled(true);
}

You have to move any long-running task (in your case it is a simple sleep call) outside the EDT (Event Dispatch Thread) - that thread is used to render UI, if you block it with some operation - you block all your UI at once.
Here is a proper example how you can disable/enable button:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* #author Mikle Garin
* #see http://stackoverflow.com/a/18590057/909085
*/
public class ButtonSleep
{
public static void main ( String[] args )
{
JFrame frame = new JFrame ( "Custom list renderer" );
final JButton button = new JButton ( "Make me sleep 5 seconds" );
button.addActionListener ( new ActionListener ()
{
#Override
public void actionPerformed ( ActionEvent e )
{
button.setEnabled ( false );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
Thread.sleep ( 5000 );
}
catch ( InterruptedException e1 )
{
//
}
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
button.setEnabled ( true );
}
} );
}
} ).start ();
}
} );
frame.add ( button );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
}
Yes, it seems weird, but you have to move your operations to a separate thread (doesn't really matter which thread) and you have to execute any operations with UI (in this case - enabling/disabling button) inside the EDT - that is why i call setEnabled in invokeLater call and not inside the separate thread.
And yes, all Swing component listeners event calls are executed inside EDT from the beginning, so you are already inside EDT when you start executing your code inside actionPerformed method of your action listener - that is why you block the whole UI if you call sleep there.

Related

Updating SWT periodically causes GUI to freeze

Problem: SWT freezes when GUI field is periodically updated.
I would like to have a SWT-based GUI with text field were values are periodically incremented.
Initially I accessed textField from separate Thread what led to throwing exception:
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid
thread access at org.eclipse.swt.SWT.error(SWT.java:4533) at
org.eclipse.swt.SWT.error(SWT.java:4448) at
org.eclipse.swt.SWT.error(SWT.java:4419) at
org.eclipse.swt.widgets.Widget.error(Widget.java:482) at
org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:373) at
org.eclipse.swt.widgets.Text.setText(Text.java:2311) at
regreon.Incrementing.lambda$0(Incrementing.java:62) at
java.lang.Thread.run(Thread.java:745)
After reading SWT documentation (thanks to #marko-topolnik ) - I tried using display.SyncExec(Runnable r) or display.AsyncExec(Runnable r) with runnable that called Thread.sleep in the loop. But this caused the whole thing to freeze.
Here is the code:
package whatever;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.SWT;
public class FreezingGUI {
protected Shell shell;
private Text text;
public static void main(String[] args) {
try {
FreezingGUI window = new FreezingGUI();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
// HOW TO DO THAT???
display.syncExec(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Integer i = Integer.parseInt(text.getText()) + 1;
text.setText(i.toString());
}
}
}
);
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
protected void createContents() {
shell = new Shell();
shell.setSize(450, 300);
shell.setText("SWT Application");
text = new Text(shell, SWT.BORDER);
text.setEditable(false);
text.setText("0");
text.setBounds(30, 32, 78, 26);
}
}
How to avoid freezing and throwing exception?
Any SWT operation which changes a UI object must be run on the SWT User Interface thread.
In your case the text.setText(i.toString()); line is an SWT UI operation and is running in a different thread.
You can use the asyncExec or syncExec methods of Display to run some code in the UI thread. So replace:
text.setText(i.toString());
with
final String newText = i.toString();
Display.getDefault().asyncExec(() -> text.setText(newText));
(this is assuming you are using Java 8).
Using asyncExec will do the UI update asynchronously. Use syncExec instead if you want to pause the thread until the update is done.
If you are using Java 7 or earlier use:
final String newText = i.toString();
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
text.setText(newText);
}
});
Note you should also be checking for the Shell being disposed and stopping your background thread. If you don't do this you will get an error when you close the app. Your code incrementing i is also wrong. This thread works:
new Thread(() -> {
for (int i = 1; true; i++) {
try {
Thread.sleep(1000);
} catch (final InterruptedException e) {
return;
}
if (shell.isDisposed()) // Stop thread when shell is closed
break;
final String newText = Integer.toString(i);
Display.getDefault().asyncExec(() -> text.setText(newText));
}
}).start();
Looking at your code the reason your UI is freezing is because you are executing a runnable on Display.sync that never returns because of the while(true) loop, as a result, other UI threads don't get a chance to execute and the UI freezes. What you need to do is use Displasy.asyncRunnable and instead of having a runnable with while(true) make a scheduler or another thread that sleeps every x seconds that executes the runnable to make the update you want, this way the other UI threads can run preventing your UI from freezing.

How to call a heavy algorithm from JButton ActionListener

I'm writing a Java program that acts as both a server and a client. Leaving out the irrelevant bits it has three classes: Main, Server and Client. Main just sets up a menu and contains the main method. Server and Client hold the algorithms for the server and the client respectively.
What I'm trying to do is to call the algorithm from the server and client classes and their GUIs depending on the button pressed. The code to call the server currently looks like this:
serverButton = new JButton();
serverButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) {
server.showGUI();
server.run();
}
});
The problem is that server.run() runs continuously for quite a long while and is a lot of heavy lifting. This bugs out the GUI, which from my understanding is because I'm calling the method from the EDT.
How can I call this method from the main thread? Do I need to create a SwingWorker and leave it there until the end of server.run()?
How can I call this method from the main thread?
This is how it is usually done in Swing.
public class WhatEverServer {
private UserInterface userInterface;
[...]
private static void createAndShowGUI() {
if( GraphicsEnvironment.isHeadless() )
logger.log( Level.FATAL, "This system seems to be 'headless'. Aborting now." );
else {
userInterface = UserInterface.getInstance();
userInterface.createAndShowUI();
}
}
public static void main( String[] args ) {
// schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater( new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
public class UserInterface {
...
public void createAndShowUI() {
// make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() );
// create and set up the window.
JFrame frame = new JFrame( "Whatever Server" );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set UI components, i.e
// set main menu bar
frame.setJMenuBar( this.mainMenuBar );
// set layout
frame.getContentPane().setLayout( new BorderLayout() );
// add UI components
// display the window.
frame.pack();
frame.setVisible(true);
}
}
This bugs out the GUI, which from my understanding is because I'm
calling the method from the EDT.
Yes, since the action is triggered by an event, the actionPerformed() is invoked by (or on) the EDT. I don't know what you are doing in server.run(), but I suppose this should not end up on the EDT.
Do I need to create a SwingWorker and leave it there until the end of
server.run()?
I would use SwingWorker or SwingUtilities in that case. You can write an ActionHandler in this way, using two threads, one for doing some of the 'heavy lifting', one for setting up the UI :
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable {
public void run() {
...
// do some 'heavy lifting' here ...
SwingUtilities.invokeLater(new Runnable() {
public void run() {
server.setupUI();
}
)
...
// or do some 'heavy lifting' here
});
}
}
Make sure the server object reference is final and then invoke the method in a new thread in your actionPerformed method.
Runnable task = () -> {server.run();};
Thread thread = new Thread(task);
thread.start();
It depends on your requirement, if you want the user do not want anything to do until server returns, it is best to do it in a Busyindicator like :
public void actionPerformed( ActionEvent e )
{
BusyIndicator.showWhile(Display.getCurrent(), new Runnable()
{
#Override
public void run()
{
server.run();
}
});
}
This will show user a hour glass while the server run is going on and user is blocked from using UI.
Or
if you want the UI to be responsive, you need to call server.run() in a separate thread.
MyThread t = new MyThread()
{
public void run()
{
server.run();
}
}
t.start();
and it is good practice to add a listener to thread to notify completion of server response so UI can do its things.
t.addListener( new MyThreadListener()
{
public void serverDone()
{
Display.getDefault().asyncExec( new Runnable()
{
public void run()
{
}
});
}
});
Please note this is not complete code for thread listener, just for idea sake.

Java Swing - Flashing icon not showing up

I am trying to flash the icon to the user using a GlassPane. I am running a javax.swing.Timer which basically performs this:
for (int i = 0; i < 3; i++) {
frame.getGlassPane().setVisible(true);
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
//To change body of catch statement use File | Settings | File Templates.
e1.printStackTrace();
}
frame.getGlassPane().setVisible(false);
}
Unfortunatly, if I sleep the EDT (current thread within the timer), the icon does not show, as in the paintComponent method did not manage to get invoked fully before the thread went to sleep. Therefore, when the next instruction kicks in, the glass pane is hidden, and, as a result, the icon is never shown. Is there a way to achieve what I want using this (similiar) approach?
You could use a javax.swing.Timer
public FlashTimer() {
javax.swing.Timer flashTimer = new javax.swing.Timer(500, new FlashHandler());
flashTimer.setCoalesce(true);
flashTimer.setRepeats(true);
flashTimer.setInitialDelay(0);
}
public class FlashHandler implements ActionListener {
private int counter;
#Override
public void actionPerformed(ActionEvent ae) {
countrol.setVisible(counter % 2 == 0);
counter++;
if (counter > 3) {
((Timer)ae.getSource()).stop();
}
}
}
It should be obvious - use a separate Thread and do the "blinking logic" there but modify the UI in EDT. Here is a simple example (should be enough to understand the idea):
public static void main ( String[] args )
{
JFrame frame = new JFrame ();
final JLabel label = new JLabel ( "X" );
label.setBorder ( BorderFactory.createEmptyBorder ( 90, 90, 90, 90 ) );
frame.add ( label );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
new Thread ( new Runnable ()
{
public void run ()
{
for ( int i = 0; i < 15; i++ )
{
try
{
setVisible ( false );
Thread.sleep ( 500 );
setVisible ( true );
Thread.sleep ( 500 );
}
catch ( InterruptedException e1 )
{
//
}
}
}
private void setVisible ( final boolean visible )
{
SwingUtilities.invokeLater ( new Runnable ()
{
public void run ()
{
label.setVisible ( visible );
}
} );
}
} ).start ();
}

Swing - Update Label

I have a message label and a submit button. The submit button will be pressed multiple times, and the action for the each press can take up to a minute.
When the button is pressed, I want to set the message to empty, and after the task is complete, I want to set the message to "Complete".
private void submitActionPerformed(java.awt.event.ActionEvent evt) {
message = "";
updateMessageLabel();
doTheTask();
/* this update is apply to the label after completion */
message = "Complete";
}
Is it possible to update that message label before the submitActionPerformed() method is run (or in the method), but after the the button is clicked?
Although the Swing concurrency tutorial already contains some very good samples on how to deal with concurrency in Swing, find below an example which
contains a checkbox to prove the UI is still alive
has a progress bar, which gets updated from the SwingWorker
has a label, which gets updated once the SwingWorker is finished
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class SwingWorkerExample {
private static JProgressBar PROGRESS_BAR;
private static JLabel OUTPUT_LABEL;
private static JFrame createGUI(){
JFrame testFrame = new JFrame( "TestFrame" );
PROGRESS_BAR = new JProgressBar( );
PROGRESS_BAR.setMinimum( 0 );
PROGRESS_BAR.setMaximum( 100 );
OUTPUT_LABEL = new JLabel( "Processing" );
testFrame.getContentPane().add( PROGRESS_BAR, BorderLayout.CENTER );
testFrame.getContentPane().add( OUTPUT_LABEL, BorderLayout.SOUTH );
//add a checkbox as well to proof the UI is still responsive
testFrame.getContentPane().add( new JCheckBox( "Click me to proof UI is responsive" ), BorderLayout.NORTH );
testFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
return testFrame;
}
public static void main( String[] args ) throws InvocationTargetException, InterruptedException {
EventQueue.invokeAndWait( new Runnable() {
#Override
public void run() {
JFrame frame = createGUI();
frame.pack();
frame.setVisible( true );
}
} );
//start the SwingWorker outside the EDT
MySwingWorker worker = new MySwingWorker( PROGRESS_BAR, OUTPUT_LABEL );
worker.execute();
}
private static class MySwingWorker extends SwingWorker<String, Double>{
private final JProgressBar fProgressBar;
private final JLabel fLabel;
private MySwingWorker( JProgressBar aProgressBar, JLabel aLabel ) {
fProgressBar = aProgressBar;
fLabel = aLabel;
}
#Override
protected String doInBackground() throws Exception {
int maxNumber = 10;
for( int i = 0; i < maxNumber; i++ ){
Thread.sleep( 2000 );//simulate long running process
double factor = ((double)(i+1) / maxNumber);
System.out.println("Intermediate results ready");
publish( factor );//publish the progress
}
return "Finished";
}
#Override
protected void process( List<Double> aDoubles ) {
//update the percentage of the progress bar that is done
int amount = fProgressBar.getMaximum() - fProgressBar.getMinimum();
fProgressBar.setValue( ( int ) (fProgressBar.getMinimum() + ( amount * aDoubles.get( aDoubles.size() - 1 ))) );
}
#Override
protected void done() {
try {
fLabel.setText( get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
}
}
Yes you can do this using SwingWorker thread, do all the pre submitActionPerformed() activities like updating the label, in the execute() method of the currentThread and call doTheTask() as a background job using worker Thread.
I suggest you to go through this documentation for reference about SwingWorker Thread

paintComponent not being called at the right time

I'm trying to write an app that goes something like this:
- Display a dialog
- When user clicks OK, close dialog, go to main app
Here are the relevant code snippets:
public class Owari extends JPanel implements ActionListener, MouseListener, Runnable {
// FIELDS
JFrame frame;
JTextField IP;
String IPAddress;
static final int SERVER_MODE = 0;
static final int CLIENT_MODE = 1;
int mode;
OwariBoard board;
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Owari() );
}
Owari() {
setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
board = new OwariBoard();
}
void main() {
this.addMouseListener( this );
frame.dispose();
frame = new JFrame( "Owari" );
frame.setContentPane( this );
frame.pack();
frame.setVisible(true);
if ( mode == SERVER_MODE ) {
server();
}
if ( mode == CLIENT_MODE ) {
client();
}
}
public void run() {
frame = new JFrame( "Owari" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel init = new JPanel( new GridBagLayout() );
frame.setContentPane( init );
add some components to the init panel including a button with
this as its actionListener and OK as its command.
frame.pack();
frame.setVisible( true );
}
public void actionPerformed( ActionEvent e ) {
if ( e.getActionCommand().equals( "Client" ) ) {
mode = CLIENT_MODE;
IP.setVisible( true );
}
else if ( e.getActionCommand().equals( "Server" ) ) {
mode = SERVER_MODE;
IP.setVisible( false );
}
else {
IPAddress = IP.getText();
main();
}
}
public void paintComponent( Graphics g ) {
super.paintComponent( g );
System.out.println( "painting" );
do some paintin
}
void server() {
frame.setTitle( "Owari Server" );
try {
server = new ServerSocket( 666 );
socket = server.accept();
initIO();
} catch ( IOException e ) {}
yourTurn = true;
System.out.println( "Got to end of server()" ); // At this point, the window
DOES get painted
What happens is the following:
The initial dialog displays:
I click the OK button.
The main window gets resized to the preferred size of the main app but it doesn't get painted, it's just transparent (shown here with this page as the background, heh):
http://imgur.com/6Ssij.jpg
I can tell the paintComponent method hasn't been called because "painting" isn't printed to the console.
However, "got to this point in the program" DOES get printed, so the program isn't hanging, it's just not calling paintComponent.
Then when I launch a client and connect, the app finally gets painted, and "painting" and "got a client" get printed to the console.
Also later on in the app, calls to repaint() are delayed (ie paintComponent is actually called later in the program than when the call to repaint() is made).
I also tried replacing the initial dialog using sthing along the lines of
public void main
frame.getRootPane.removeAll()
frame.setContentPane(this)
frame.getRootPane().revalidate()
frame.pack()
Exact same result.
tl;dr paintcomponent isn't being called when i want it to, what do?
Bumping for some more info: the call to repaint() is done before the call to sever.accept() So why does it not repaint() before hanging at the server.accept() call?
openasocketandwaitforaclient
Your code is executing in the Event Dispatch Thread so the blocking socket is preventing the GUI from repainting itself.
YOu need to use a separate Thread for the socket. Read the section from the Swing tutorial on Concurrency for an explanation and a solution.
your code seems to work so, maybe you should try to invoke the repaint() methode of you frame after resizing this frame.
Anhuin

Categories

Resources