7

See also Java Swing GUI hour glass. However the provided answer does not seem to work.

I have following code:

private void loadFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) {                                                

    int returnVal = fileChoser.showOpenDialog(this);
    if (returnVal == JFileChooser.APPROVE_OPTION) {

        try {
            this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            // do stuff
        } finally {
            this.setCursor(Cursor.getDefaultCursor());
        }
    } 
}

This is called when user selects the according entry in the menu bar. However the cursor never changes. Note that loading the file takes a file and hence a change in the cursor should be visible.

What am I doing wrong?

EDIT:

this is the top level JFrame.

EDIT 2: moved solution to separate answer

Community
  • 1
  • 1
beginner_
  • 7,230
  • 18
  • 70
  • 127
  • For better help sooner, post an [SSCCE](http://sscce.org/). Note the document is being reviewed and discussed on [this question](http://meta.stackexchange.com/q/214955/155831), contributions welcome. – Andrew Thompson Jan 09 '14 at 08:17
  • What does in //do stuff happen? The code that is implemented there should be in a separated thread. Otherwise your application won't see any changes made to the cursor. – oopbase Jan 09 '14 at 08:17
  • 3
    Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling `Thread.sleep(n)` implement a Swing `Timer` for repeating tasks or a `SwingWorker` for long running tasks. See [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. – Andrew Thompson Jan 09 '14 at 08:24
  • 1
    @beginner_ Edit looks good to me, **except** the `worker.get()` probably defeats the purpose of the worker thread... Don't block and wait, instead handle exceptions in the worker itself, and report back if a problem occurs. Consider also passing `this` to the `MyWorker` constructor, and make your inner class `static`, instead of the `Outer.this.set...`-mess. :-) – Harald K Jan 09 '14 at 09:41
  • Oh, and you probably want to put this in an answer, rather than in the question. :-) – Harald K Jan 09 '14 at 09:41

5 Answers5

7

First make a SwingWorker class:

private class FileLoader extends SwingWorker<String, Void> {

    private final JFrame frame;
    private final File file;        

    public SdfLoader(JFrame frame, File file) {            
        frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        this.frame = frame;
        this.file = file;
    }

    @Override
    public String doInBackground() throws IOException {

            String result = null; 
            // read file and set result;
        return result;
    }

    @Override
    public void done() {
        try {
            String result = get();
            //do stuff 
        } catch (ExecutionException | InterruptedException ex) {
            // display error
            JOptionPane.showMessageDialog(SdfViewer.this,
                    ioException.getMessage(),
                    "Error opening file",
                    JOptionPane.ERROR_MESSAGE);
        } finally {
            frame.setCursor(Cursor.getDefaultCursor());
        }
    }

}

Then call it like this:

private void loadFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) { 
    int returnVal = fileChoser.showOpenDialog(this);
    if (returnVal == JFileChooser.APPROVE_OPTION) {
        File file = fileChoser.getSelectedFile();
        logger.debug("Opening SD-File '{}'.", file.getAbsoluteFile());
        FileLoader loader = new FileLoader(this, file);
        loader.execute();
    } 
}  

EDIT made by @mKorbel, please apologize me for this hi_jack

  • I can't found my posts with this code here (something happends with underlaing database),

  • use this logics,

  • rest is in my comment here

virtual -1k wrong, wrong, wrong, your doInBackground() missing publish()-process(), done() is extremly wrong designed, read comments in answer by @trashgod, if is possible to use Runnable#Thread for FileIO, Socket or any XxxStreams instead of black hole based on Future and SwingWorker

  • please to delete this code in your thread as per edit

.

import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Random;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.table.*;

public class TableWithTimer implements ActionListener, Runnable {

    private static final long serialVersionUID = 1L;
    private JFrame frame = new JFrame();
    private JScrollPane scroll = new JScrollPane();
    private JTable myTable;
    private JPanel buttonPanel = new JPanel();
    private JButton startButton = new JButton("Start Thread to Update Table");
    private JButton stopButton = new JButton("Stop Thread for Update Table");
    private JButton newButton = new JButton("Load new Data to Table");
    private int count = 0;
    private int delay = 3;
    private javax.swing.Timer timer = null;
    private boolean runProcess;
    private int row = 0;
    private int column = 0;
    private String value = "Amnd";
    private int amndValue = 0;
    private String valueAt = "";
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private String[] head = {"One", "Two", "Three", "Four", "Five", "Six"};
    private String[][] data = new String[25][6];

    public TableWithTimer() {
        myTable = new TableBackroundPaint0(data, head);
        myTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        myTable.setRowSelectionAllowed(false);
        myTable.setColumnSelectionAllowed(true);
        //myTable.setCellSelectionEnabled(true);

        myTable.setGridColor(Color.gray);
        myTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        final TableCellRenderer cellRendener = myTable.getTableHeader().getDefaultRenderer();
        myTable.getTableHeader().setDefaultRenderer(new TableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                    boolean isSelected, boolean hasFocus, int row, int column) {
                JLabel label = (JLabel) cellRendener.getTableCellRendererComponent(
                        table, value, isSelected, hasFocus, row, column);
                label.setBackground(Color.orange);
                label.setForeground(Color.darkGray);
                label.setFont(new Font("SansSerif", Font.BOLD, 12));
                label.setBorder(BorderFactory.createCompoundBorder(label.getBorder(),
                        BorderFactory.createEmptyBorder(0, 5, 0, 0)));
                label.setHorizontalAlignment(SwingConstants.LEFT);
                label.setHorizontalAlignment(SwingConstants.CENTER);
                if ((label.getText().equals("First")) || (label.getText().equals("Second"))) {
                    label.setForeground(Color.red);
                }
                if ((label.getText().equals("Day")) || (label.getText().equals("Month")) || (label.getText().equals("Year"))) {
                    label.setForeground(Color.blue);
                }
                if ((label.getText().equals("Time"))) {
                    label.setForeground(Color.green);
                }
                return label;
            }
        });
        TableColumnModel cm = myTable.getColumnModel();
        for (int column1 = 0; column1 < cm.getColumnCount(); column1++) {
            TableColumn colLeft1 = cm.getColumn(column1);
            cm.getColumn(column1).setWidth(140);
            cm.getColumn(column1).setPreferredWidth(140);
        }
        //myTable.setFillsViewportHeight(true); // apply paintComponent for whole Viewport
        JButton cornerButtonTop = new JButton();
        cornerButtonTop.setBackground(scroll.getViewport().getBackground());
        JButton cornerButtonBottom = new JButton();
        cornerButtonBottom.setOpaque(false);
        scroll.setCorner(JScrollPane.UPPER_RIGHT_CORNER, cornerButtonTop);
        scroll.setCorner(JScrollPane.LOWER_RIGHT_CORNER, cornerButtonBottom);
        scroll.setViewportView(myTable);
        scroll.setMinimumSize(new Dimension(600, 400));
        scroll.setMaximumSize(new Dimension(900, 600));
        scroll.setPreferredSize(new Dimension(850, 430));
        frame.add(scroll, BorderLayout.CENTER);
        buttonPanel.setLayout(new GridLayout(1, 4, 10, 10));
        startButton.addActionListener(this);
        startButton.setEnabled(false);
        stopButton.addActionListener(this);
        stopButton.setEnabled(false);
        JButton hideButton = new JButton();
        newButton.addActionListener(this);
        newButton.setEnabled(false);
        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);
        buttonPanel.add(hideButton);
        buttonPanel.add(newButton);
        hideButton.setVisible(false);
        frame.add(buttonPanel, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(100, 100);
        frame.pack();
        frame.setVisible(true);
        start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == startButton) {
            runProcess = true;
            new Thread(this).start();
            myTable.requestFocus();
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
        } else if (e.getSource() == stopButton) {
            scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            runProcess = false;
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            newButton.setEnabled(true);
        } else if (e.getSource() == newButton) {
            runProcess = false;
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            newButton.setEnabled(false);
            addNewData();
        }
    }

    public void addNewData() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                TableModel model = myTable.getModel();
                for (int j = 0; j < model.getRowCount(); j++) {
                    int column = model.getColumnCount();
                    for (int i = 0; i < column; i++) {
                        model.setValueAt("Deleted", j, i);
                    }
                }
                startNewData();
            }
        });
    }

    private void start() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        timer = new javax.swing.Timer(delay * 100, updateCol());
        timer.start();
    }

    private void startNewData() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        count = 0;
        timer = new javax.swing.Timer(1500, updateCol());
        timer.start();
    }

    @Override
    public void run() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        count = 0;
        Random random = new Random();
        while (runProcess) {
            row = random.nextInt(myTable.getRowCount());
            column = random.nextInt(myTable.getColumnCount());
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    try {
                        amndValue++;
                        valueAt = ((myTable.getValueAt(row, column)).toString());
                        if (!(valueAt.startsWith("A"))) {
                            count++;
                            if (count == ((25 * 6))) {
                                JOptionPane.showMessageDialog(myTable, " Update done ");
                                scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                                runProcess = false;
                            }
                            java.util.Date date = new java.util.Date();
                            String dateTime = sdf.format(date.getTime());
                            myTable.setValueAt((value + " " + String.valueOf(amndValue) + " at: " + dateTime), row, column);
                            //myTable.setValueAt(new Integer(1), row, column); // please uncoment for generate misstype error on EDT
                            myTable.changeSelection(row, column, false, false);
                            System.out.println("update cycle with value :"
                                    + (value + " " + String.valueOf(amndValue) + " at: " + dateTime) + ", table row :" + row
                                    + ", table column " + column);
                        }
                    } catch (Exception e) {
                        runProcess = false;
                        System.out.println("Error for update JTable cell");
                        e.printStackTrace();
                    }
                }
            });
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Action updateCol() {
        return new AbstractAction("text load action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {

                System.out.println("updating row " + (count + 1));
                TableModel model = myTable.getModel();
                int cols = model.getColumnCount();
                int row = 0;
                for (int j = 0; j < cols; j++) {
                    row = count;
                    myTable.changeSelection(row, 0, false, false);
                    timer.setDelay(200);
                    Object value = "row " + (count + 1) + " item " + (j + 1);
                    model.setValueAt(value, count, j);
                }
                count++;
                if (count >= myTable.getRowCount()) {
                    myTable.changeSelection(0, 0, false, false);
                    timer.stop();
                    System.out.println("update cycle completed");
                    myTable.clearSelection();
                    startButton.setEnabled(true);
                    newButton.setEnabled(true);
                    scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                }
            }
        };
    }

    public static void main(String args[]) {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                System.out.println(info.getName());
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (UnsupportedLookAndFeelException e) {
            // handle exception
        } catch (ClassNotFoundException e) {
            // handle exception
        } catch (InstantiationException e) {
            // handle exception
        } catch (IllegalAccessException e) {
            // handle exception
        }
        TableWithTimer tableWithTimer = new TableWithTimer();
    }
}

class TableBackroundPaint0 extends JTable {

    private static final long serialVersionUID = 1L;

    TableBackroundPaint0(Object[][] data, Object[] head) {
        super(data, head);
        setOpaque(false);
        ((JComponent) getDefaultRenderer(Object.class)).setOpaque(false);
    }

    @Override
    public void paintComponent(Graphics g) {
        Color background = new Color(168, 210, 241);
        Color controlColor = new Color(230, 240, 230);
        int width = getWidth();
        int height = getHeight();
        Graphics2D g2 = (Graphics2D) g;
        Paint oldPaint = g2.getPaint();
        g2.setPaint(new GradientPaint(0, 0, background, width, 0, controlColor));
        g2.fillRect(0, 0, width, height);
        g2.setPaint(oldPaint);
        for (int row : getSelectedRows()) {
            Rectangle start = getCellRect(row, 0, true);
            Rectangle end = getCellRect(row, getColumnCount() - 1, true);
            g2.setPaint(new GradientPaint(start.x, 0, controlColor, (int) ((end.x + end.width - start.x) * 1.25), 0, Color.orange));
            g2.fillRect(start.x, start.y, end.x + end.width - start.x, start.height);
        }
        super.paintComponent(g);
    }
}
Community
  • 1
  • 1
beginner_
  • 7,230
  • 18
  • 70
  • 127
  • virtual -1k wrong, wrong, wrong, your doInBackground() missing publish()-process(), [done() is extremly wrong designed](http://stackoverflow.com/questions/7053865/cant-get-arrayindexoutofboundsexception-from-future-and-swingworker-if-threa), read comments in answer by @trashgod, if is possible to use Runnable#Thread for FileIO, Socket or any XxxStreams instead of black hole based on Future and SwingWorker – mKorbel Jan 09 '14 at 10:36
  • I don't need publish and process methods and done is fixed now. Runnable has an issue as you can't report exceptions to users...it will just fail and exception will go unnoticed: I think thats rather dumb especially with FileIO. – beginner_ Jan 22 '14 at 11:42
  • this isn't true, Runnable hasn't any issue, nor described in your comment, this description talking about why don't to use SwingWorker (has long and large buggy history, and quite often repeating), nor for production code, sure everything is up to you, Runnable is best of choices for FileIO, XxxStream, Socket, JDBC – mKorbel Jan 22 '14 at 11:59
5

The reason why you don't see the changes, is most likely that you are doing all the work on the EDT. Don't ever do that. Even if you // do stuff on a separate thread, the finally block will execute before the UI has had a chance to repaint itself.

Instead, you should spawn a SwingWorker that sets the wait cursor, then does the heavy work (file loading) in the background, and finally resets to the normal cursor when done.

This might show that the wait cursor wasn't really needed in the first place, and that using a progress bar would be more appropriate in this case.

Harald K
  • 26,314
  • 7
  • 65
  • 111
3
  • common issue for EventDispatchThread is that every events are done in one moment, then every code, methods, classes are repainted in one moments, after/when all code in ActionListener is executed

  • then you have to use split this logics to the two separate events wrapped to the (best of options) two Swing Actions,

    1. one for toggling with Cursor - manage only Cursor can be invoked from PropertyChangeListener or from ButtonModel from JMenuItem

    2. another Swing Action or any SwingListener to call rest of/or expected code

    3. you can to chaining those two Swing Actions (my a.m.), 1st one to call second

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • @Andrew Thompson not neccessary I have this thread in my radar :-), – mKorbel Jan 09 '14 at 08:32
  • Much cool. Hopefully we'll see a new improved version 'real soon now'. :) – Andrew Thompson Jan 09 '14 at 08:34
  • 1
    @Andrew Thompson sorry for my previous quite empty comment, important for me is that this option exist as standard here, nothing else, I'm ignoring every blablblba, every words for/against or whatever .... – mKorbel Jan 09 '14 at 08:35
  • 1
    No problem at all. :) We each give as much time as we can spare/stand. ;) – Andrew Thompson Jan 09 '14 at 08:36
  • As an aside, occasionally I spare a little time to go 'outside the box', but I prefer to come to a question, see an answer like you might post, up-vote it and move on along. :) – Andrew Thompson Jan 09 '14 at 08:39
  • 1
    @Andrew Thompson please do not read the following word, not thanks for your effor, here is Java and C ... , here is Swing and here is SSCCE, better ... :-) – mKorbel Jan 09 '14 at 08:40
3

Wow - whats with all the code. Its easy:

final JScrollPane jsp = new JScrollPane(jt);

jsp.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

                //DO SOMETHING
jsp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
Pang
  • 9,564
  • 146
  • 81
  • 122
smac2020
  • 9,637
  • 4
  • 24
  • 38
  • If the `DO SOMETHING` block happens on the EDT, then the cursor probably won't have a chance to be redrawn until after control is returned to the EDT (i.e. after you've reset the cursor back to the default) – Rangi Keen Sep 02 '22 at 18:50
2

Loading the file already keeps the EDT busy, so it doesn't get the chance to change the cursor.

Dan D.
  • 32,246
  • 5
  • 63
  • 79