We are trying to show the user a milky JGlassPane to indicate that events are currently not accepted, due to a progress-bar or something else currently being shown. While this would usually automatically work, since we are showing a modal dialog, we also want the JGlassPane to be usable without a JDialog, meaning just a JGlassPane and a waiting cursor.
So far, we did this by using a JGlassPane that ignores events and paints a translucent color. Initially we call requestFocus on the JGlassPane. The problem with this is, that we lose the previously focused components when the glasspane goes away. The solution for this was to simply remember the previously focused component. However, in cases where the Action, that triggered the JGlassPane, was invoked via a menu, the focused component was the windows JRootPane. Usually swing correctly restores the focus after closing a menu, however, in this case, we are breaking this functionallity. Is there a way to achieve what we want without breaking the focus behaviour?
In this demo you can see, that the focus is correctly returned when hitting ESC after using one of the two buttons, but not with the context menu on the table.
public class FocusDemo
{
//Temporary static veriable for demo purposes.
private static final AtomicReference<Component> previouslyFocused = new AtomicReference<>();
private static class ConsumingGlassPane extends JPanel
{
public ConsumingGlassPane()
{
setOpaque( false );
setFocusable( true );
addMouseListener( new MouseListener()
{
#Override
public void mouseClicked( #NonNull final MouseEvent e )
{
e.consume();
}
#Override
public void mousePressed( #NonNull final MouseEvent e )
{
e.consume();
}
#Override
public void mouseReleased( #NonNull final MouseEvent e )
{
e.consume();
}
#Override
public void mouseEntered( #NonNull final MouseEvent e )
{
e.consume();
}
#Override
public void mouseExited( #NonNull final MouseEvent e )
{
e.consume();
}
} );
addKeyListener( new KeyListener()
{
#Override
public void keyTyped( final KeyEvent e )
{
e.consume();
}
#Override
public void keyReleased( final KeyEvent e )
{
e.consume();
}
#Override
public void keyPressed( final KeyEvent e )
{
if ( e.getKeyCode() == KeyEvent.VK_ESCAPE )
{
setVisible( false );
final Component component = previouslyFocused.get();
if ( component != null )
{
component.requestFocusInWindow();
}
}
e.consume();
}
} );
// This component keeps the focus until is made hidden
setInputVerifier( new InputVerifier()
{
#Override
public boolean verify( final JComponent input )
{
return !isVisible();
}
} );
}
#Override
protected void paintComponent( final Graphics g )
{
final Graphics2D g2 = (Graphics2D) g.create();
g2.setColor( new Color( 240, 230, 230, 128 ) );
g2.fillRect( 0, 0, getWidth(), getHeight() );
g2.dispose();
}
}
public static void main( final String[] args )
{
final JFrame frame = new JFrame();
frame.setGlassPane( new ConsumingGlassPane() );
final ActionListener showglasspane = __ ->
{
previouslyFocused.set( frame.getFocusOwner() );
frame.getRootPane().getGlassPane().setVisible( true );
frame.getRootPane().getGlassPane().requestFocus();
};
final JButton button1 = new JButton( "Show GlassPane" );
final JButton button2 = new JButton( "Show GlassPane" );
button1.addActionListener( showglasspane );
button2.addActionListener( showglasspane );
final Object[][] objects = { { 1, 2, 3 }, { 1, 2, 3 } };
final String[] col = { "Lel", "Lol", "Lul" };
final JTable table = new JTable( objects, col );
final JMenuItem menuItem = new JMenuItem( "Show GlassPane" );
menuItem.addActionListener( showglasspane );
final JPopupMenu popup = new JPopupMenu();
popup.add( menuItem );
table.setComponentPopupMenu( popup );
frame.setLayout( new GridBagLayout() );
frame.add( button1 );
frame.add( button2 );
frame.add( table );
frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
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.
I want to set the most common JTabbedPane mouse event behavior, but I cannot find appropriate options to set:
Left mouse button click - Select tab.
Right mouse button click - Open current tab' dropdown menu.
Wheel mouse button click - Close the tab.
Question: Is there any way to implement them?
PS: Any example from here could be an SSCCE.
Tab selection is performed with left mouse button by default, so you don't need to add that feature. Everything else you can find in this small example:
public static void main ( String[] args )
{
final JFrame frame = new JFrame ();
final JTabbedPane tabbedPane = new JTabbedPane ();
tabbedPane.addTab ( "tab1", new JLabel ( "" ) );
tabbedPane.addTab ( "tab2", new JLabel ( "" ) );
tabbedPane.addTab ( "tab3", new JLabel ( "" ) );
tabbedPane.addTab ( "tab4", new JLabel ( "" ) );
frame.add ( tabbedPane );
tabbedPane.setUI ( new MetalTabbedPaneUI ()
{
protected MouseListener createMouseListener ()
{
return new CustomAdapter ( tabbedPane );
}
} );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
private static class CustomAdapter extends MouseAdapter
{
private JTabbedPane tabbedPane;
public CustomAdapter ( JTabbedPane tabbedPane )
{
super ();
this.tabbedPane = tabbedPane;
}
public void mousePressed ( MouseEvent e )
{
final int index = tabbedPane.getUI ().tabForCoordinate ( tabbedPane, e.getX (), e.getY () );
if ( index != -1 )
{
if ( SwingUtilities.isLeftMouseButton ( e ) )
{
if ( tabbedPane.getSelectedIndex () != index )
{
tabbedPane.setSelectedIndex ( index );
}
else if ( tabbedPane.isRequestFocusEnabled () )
{
tabbedPane.requestFocusInWindow ();
}
}
else if ( SwingUtilities.isMiddleMouseButton ( e ) )
{
tabbedPane.removeTabAt ( index );
}
else if ( SwingUtilities.isRightMouseButton ( e ) )
{
final JPopupMenu popupMenu = new JPopupMenu ();
final JMenuItem addNew = new JMenuItem ( "Add new" );
addNew.addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
tabbedPane.addTab ( "tab", new JLabel ( "" ) );
}
} );
popupMenu.add ( addNew );
final JMenuItem close = new JMenuItem ( "Close" );
close.addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
tabbedPane.removeTabAt ( index );
}
} );
popupMenu.add ( close );
final JMenuItem closeAll = new JMenuItem ( "Close all" );
closeAll.addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
tabbedPane.removeAll ();
}
} );
popupMenu.add ( closeAll );
final Rectangle tabBounds = tabbedPane.getBoundsAt ( index );
popupMenu.show ( tabbedPane, tabBounds.x, tabBounds.y + tabBounds.height );
}
}
}
}
Ofcourse you'd better save the displayed menu somewhere so it won't be recreated every time user opens it. You can also move the mouse listener to a separate class to use it every time you need menu and other features.
But my goal was to show you how those things can be done and not making a perfect example, so i guess it is more than enough to start working with tabbed pane :)
Ok, I have the following code.
public class MyProgressBar extends JPanel implements MyData, Serializable {
/**
*
*/
public static final int MAX = 10000;
public static final int WIDTH = 400;
public static final int HEIGHT = 75;
private JProgressBar MyBar = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
private JFrame MyFrame = new JFrame();
private int MyValue = 0;
private Thread MyThread = new Thread( new ProgressThread() );
public MyProgressBar() {
add(MyBar);
int x = ( MyData.SCREEN.width / 2 ) - ( WIDTH / 2);
int y = ( MyData.SCREEN.height / 2 ) - ( HEIGHT / 2);
this.setBounds( x, y, WIDTH, HEIGHT );
MyFrame.setBounds( x, y, WIDTH, HEIGHT );
MyFrame.setUndecorated(true);
MyFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
MyFrame.setVisible(false);
MyFrame.getContentPane().setLayout(null);
MyBar.setStringPainted( true );
MyBar.setBorderPainted( true );
MyBar.setValue( 0 );
MyBar.setBounds( 0, 0, WIDTH, HEIGHT );
MyFrame.add( MyBar );
MyFrame.pack();
MyFrame.repaint();
}
public void MyUpdateBar() {
MyBar.setValue( MyValue );
MyBar.repaint();
MyFrame.repaint();
this.repaint();
//dbug.Message( "MYPROGRESSBAR", "MyUpdateBar", "Value is %3.2f %d", MyBar.getPercentComplete(), MyValue );
}
public void MySetValue( int percent ) {
MyValue = (int)( MAX * ( (double)percent / 100.0 ) );
MyUpdateBar();
//dbug.Message( "MYPROGRESSBAR", "MySetValue", "Value is %3.2f %d percent was %d", MyBar.getPercentComplete(), MyValue, percent );
}
public void CreateAndShow () {
MyFrame.setVisible(true);
MyThread.start();
}
public void HideAndClear () {
MyThread.stop();
//frame.setVisible(false);
}
class ProgressThread implements Runnable {
public void run() {
EventQueue.invokeLater(new Runnable() {
public void run() {
while( MyValue < MyBar.getMaximum() ) {
MyBar.setValue( MyValue );
MyBar.repaint();
MyFrame.repaint();
dbug.Message( "MYPROGRESSBAR", "THREAD", "Value is %3.2f %d", MyBar.getPercentComplete(), MyValue );
}
}
});
}
}
}
As you can see, I have created a class that I want to have show the progress. What happens is I instantiate the class. Load my XML file, then as I am parsing data, I am calling to update the MyValue which I see when I let my dbug messages come out. However, the bar itself does not even show until it is 100% complete. I have read about threading and following someone else's example and if I left it as his example it worked. If I made a few tweaks (changing a loop in the thread to populate the setvalue of the progress bar to read a value) it does not even show until it is 100.
What did I do wrong?
Thanks!
You thread executes SwingUtilities.invokeLater. You're effectively running on Swing's Event Dispatch Thread. Not sure what are you trying to achieve. But it looks like you are blocking EDT and your while loop is not updated as MySetValue is not executed.
Consider using SwingWorker for lengthy operations. How to Use Progress Bars demonstrates use of SwingWorker with JProgressBar.
Make sure you call setValue method from the Event Dispatch Thread. You can use SwingUtilities.invokeLater for that. Read more about Threads and Swing.
Consider this simplified sample:
public static void main(String[] arguments) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
final JProgressBar bar = new JProgressBar(0, 100);
Thread t = new Thread(){
public void run(){
for(int i = 0 ; i < 100 ; i++){
final int percent = i;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
bar.setValue(percent);
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
};
frame.add(bar);
frame.pack();
frame.setVisible(true);
t.start();
}
The problem is that you use a loop in the EDT that updates the progress. Until that loop exits, the EDT cannot dispatch events (like repaint, revalidate, invokeLater, mouse events, key events, etc...) preventing it from refreshing the progress bar.
You should try to find a way to let the EDT dispatch its events between each update of the progress bar. Ideally, you move your "work" outside the EDT with a SwingWorker, and meanwhile the progressbar get updated through property change listeners in the EDT.
For your information, in Java, methods and variables starts with a lower case letter. Your code is really hard to read for others.
So, I tried to follow the tutorial and here is where I am at.
Ok, I have tried following tutorials but I keep getting lost somewhere. What I need is a class that creates and displays a progress bar (JProgressBar) that I can set the value of as I iterate over data loaded from a file and place into the database memory. My problems come that every example I have found has some kind of counter that fills the progress bar and executes from a "main" function. Every time I alter that tutorial to be a class that I can call at will and display the bar, I do not get the bar showing (ie the frame comes up but the bar does not even look like it is added to the frame until after the iteration is done). I have tried using SwingUtilities.invokeLater and SwingWorker (lastest attempt at class below) all having the same issue. To make matters worse, I can do a dbug.myMessage (basically sends to System.out) and see a message that shows that the bar is changing in memory just not showing. I am obviously missing something probably simple but I can't think of what it is.
Oh, one other thing, if I leave the tutorial as is (http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/components/ProgressBarDemo2Project/src/components/ProgressBarDemo2.java) and just change the main to a createAndShow method, it works but of course it does not do what I need it to do.
I did post another question about this but have altered the class so much I thought it best to post a new question.
So, here is my altered code that does not seem to work:
public class MyProgressBar extends JPanel implements PropertyChangeListener,
MyData,
Serializable {
/**
*
*/
private static final long serialVersionUID = -1632492668549544408L;
private MyDebug dbug = new MyDebug( MyData.MYDEBUGCHECK.MYPROGRESSBAR.getOn() );
public static final int MAX = 100;
public static final int WIDTH = 400;
public static final int HEIGHT = 75;
private JProgressBar myBar = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
private JFrame myFrame = new JFrame();
public Task task;
class Task extends SwingWorker<Void, Void> {
public int myValue = 0;
#Override
public Void doInBackground() {
//Initialize progress property.
setProgress(0);
while (myValue < 100) {
//Make random progress.
//myValue += random.nextInt(10);
setProgress( Math.min( myValue, 100 ) );
dbug.myMessage( "MYPROGRESSBAR", "doInBackground", "Value is %3.2f %d", myBar.getPercentComplete(), myValue );
myBar.repaint();
}
return null;
}
public void done() {
}
public void mySetValue( int percent ) {
myValue = (int)( MAX * ( (double)percent / 100.0 ) );
dbug.myMessage( "MYPROGRESSBAR", "mySetValue", "Value is %3.2f %d percent was %d", myBar.getPercentComplete(), myValue, percent );
}
}
public MyProgressBar() {
add(myBar);
int x = ( MyData.SCREEN.width / 2 ) - ( WIDTH / 2);
int y = ( MyData.SCREEN.height / 2 ) - ( HEIGHT / 2);
this.setBounds( x, y, WIDTH, HEIGHT );
myFrame.setBounds( x, y, WIDTH, HEIGHT );
myFrame.setUndecorated(true);
myFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
myFrame.setVisible(false);
myFrame.getContentPane().setLayout(null);
myFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
myBar.setStringPainted( true );
myBar.setBorderPainted( true );
myBar.setValue( 0 );
myBar.setBounds( 0, 0, WIDTH, HEIGHT );
myBar.addPropertyChangeListener( this );
myFrame.add( myBar );
//Create and set up the content pane.
//JComponent newContentPane = new MyProgressBar();
JComponent newContentPane = myBar;
newContentPane.setOpaque(true); //content panes must be opaque
myFrame.setContentPane(newContentPane);
myFrame.pack();
}
public void createAndShow () {
//Display the window.
myFrame.setVisible(true);
myFrame.repaint();
}
public void hideAndClear () {
//myFrame.setVisible(false);
}
#Override
public void propertyChange(PropertyChangeEvent args) {
dbug.myMessage( "MYPROGRESSBAR", "propertyChange", "Value is %s", args.getPropertyName() );
if ( "progress" == args.getPropertyName() ) {
int progress = (Integer) args.getNewValue();
//myBar.setValue(progress);
}
}
public void start () {
//Instances of javax.swing.SwingWorker are not reusuable, so
//we create new instances as needed.
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
}
The below snippet updates the progress bar while in progress
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setValue((int)percentage);
//below code to update progress bar while running on thread
progressBar.update(progressBar.getGraphics());}
});
Dynamic progress update of the progress bar is achieved via the below code:
int progress = Math.round(((float)finished/(float)(total)) * 100);
uploadPrgressBar.setIndeterminate(progress == 0);
uploadPrgressBar.setValue(progress);
uploadPrgressBar.update(uploadPrgressBar.getGraphics());
Call the method containing the above code in the loop(code logic) and it will dynamically update progress bar after each iteration
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