问题
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
回答1:
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.
回答2:
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);
}
}
回答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,
one for toggling with Cursor - manage only Cursor can be invoked from PropertyChangeListener or from ButtonModel from JMenuItem
another Swing Action or any SwingListener to call rest of/or expected code
you can to chaining those two Swing Actions (my a.m.), 1st one to call second
回答4:
Loading the file already keeps the EDT busy, so it doesn't get the chance to change the cursor.
回答5:
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));
回答6:
You should never run a long process on the Swing Event Dispatch thread (EDT), often however you like to show a wait cursor in the frame while loading some quick data (example when switching panel).
To achieve this I attach the cursor change to the root pane of the JFrame
Example
public class TestFrame extends JFrame {
private static final long serialVersionUID = 5671798241966272024L;
/**
* In this example static to show how they can be
* centralized in application with multiple frames
*/
public static void setWaitCursor(JFrame frame) {
if (frame != null) {
RootPaneContainer root = (RootPaneContainer) frame.getRootPane().getTopLevelAncestor();
root.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
root.getGlassPane().setVisible(true);
}
}
public static void setDefaultCursor(JFrame frame) {
if (frame != null) {
RootPaneContainer root = (RootPaneContainer) frame.getRootPane().getTopLevelAncestor();
root.getGlassPane().setCursor(Cursor.getDefaultCursor());
}
}
public TestFrame() {
super("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
}
private void init() {
getContentPane().setLayout(new BorderLayout());
JButton btnTest = new JButton("Load some quick stuff");
btnTest.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TestFrame.setWaitCursor(TestFrame.this);
doSomeShortProccessing();
TestFrame.setDefaultCursor(TestFrame.this);
}
});
getContentPane().add(btnTest);
pack();
}
protected void doSomeShortProccessing() {
try {
//You should never do Thread.sleep on the EDT is just to display function
//Normaly process would be create a new panel and load some quick data
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
TestFrame frame = new TestFrame();
frame.setLocationRelativeTo(null); //Middle of screen
frame.setVisible(true);
}
}
来源:https://stackoverflow.com/questions/21014861/swing-change-cursor-to-wait-cursor