Java 在程序运行时保持JPanel中的计时器处于活动状态

Java 在程序运行时保持JPanel中的计时器处于活动状态,java,multithreading,timer,jpanel,Java,Multithreading,Timer,Jpanel,我在一个名为MainFrame的类中创建了一个GUI。GUI的一个JPanel以秒为单位显示当前时间和日期。当用户决定使用GUI分析数据时,它会调用一个处理数据的类。当数据处理发生时,计时器暂停,然后在数据处理结束时恢复。即使程序正在运行,如何让计时器持续运行?计时器是它自己的线程,但我不知道从哪里开始JPanel的线程。 这里有一些代码剪辑 java(启动整个GUI的应用程序) 大型机(处理JPanels和dataprocess impl的类) 状态面板(显示计时器等的面板) java(Sta

我在一个名为MainFrame的类中创建了一个GUI。GUI的一个JPanel以秒为单位显示当前时间和日期。当用户决定使用GUI分析数据时,它会调用一个处理数据的类。当数据处理发生时,计时器暂停,然后在数据处理结束时恢复。即使程序正在运行,如何让计时器持续运行?计时器是它自己的线程,但我不知道从哪里开始JPanel的线程。 这里有一些代码剪辑

java(启动整个GUI的应用程序)

大型机(处理JPanels和dataprocess impl的类)

状态面板(显示计时器等的面板)

java(StatusPanel中使用的计时器)

数据处理是在dataControlsPanel中使用actionlisteners完成的

当用户决定使用GUI分析数据时,它会调用一个处理数据的类。当数据处理发生时,计时器暂停,然后在数据处理结束时恢复。即使程序正在运行,如何让计时器持续运行

首先,您的计时器应该是
javax.swing.timer
或“swing”计时器。这是专门针对Swing事件线程构建的,因此应该避免当前代码显示的许多Swing线程问题——例如,这里:
timeLabel.setText(timeFormat.format(currentTime))--这从后台线程发出Swing调用,是危险的代码。下一个
处理代码应该进入SwingWorker。当worker执行时,您可以通过调用计时器上的
stop()
暂停Swing计时器,或者简单地让计时器继续运行。当SwingWorker完成其操作时——我通常会在SwingWorker中添加PropertyChangeListener,监听其
state
属性是否更改为
SwingWorker.StateValue.DONE
,调用
get()
在worker上提取其持有的任何数据,更重要的是捕获可能引发的任何异常

例如:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;

@SuppressWarnings("serial")
public class MyApp extends JPanel {
    // display the date/time
    private static final String DATE_FORMAT = "HH:mm:ss  dd-MM-yyyy";
    private static final DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

    // timer updates measures seconds, but updates every 0.2 sec's to be sure
    private static final int TIMER_DELAY = 200;

    // JLabel that shows the date/time
    private JLabel timeLabel = new JLabel("", SwingConstants.CENTER);

    // JButton's Action / listener. This starts long-running data processing
    private Action dataProcessAction = new DataProcessAction("Process Data");

    // the SwingWorker that the above Action executes:
    private LongRunningSwProcess longRunningProcess;

    // label to display the count coming from the process above
    private JLabel countLabel = new JLabel("00");

    public MyApp() {
        // create a simple GUI
        JPanel dataProcessingPanel = new JPanel();
        dataProcessingPanel.add(new JButton(dataProcessAction)); // button that starts process
        dataProcessingPanel.add(new JLabel("Count:"));
        dataProcessingPanel.add(countLabel);        

        setLayout(new BorderLayout());
        add(timeLabel, BorderLayout.PAGE_START);
        add(dataProcessingPanel);
        showTimeLabelCurrentTime();
        // create and start Swing Timer
        new Timer(TIMER_DELAY, new TimerListener()).start(); 
    }

    // display count from swing worker
    public void setCount(int newValue) {
        countLabel.setText(String.format("%02d", newValue));
    }

    // clean up code after SwingWorker finishes
    public void longRunningProcessDone() {
        // re-enable JButton's action
        dataProcessAction.setEnabled(true);
        if (longRunningProcess != null) {
            try {
                // handle any exceptions that might get thrown from the SW
                longRunningProcess.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    // display the current time in our timeLabel JLabel
    private void showTimeLabelCurrentTime() {
        long currentTime = System.currentTimeMillis();
        Date date = new Date(currentTime);
        timeLabel.setText(dateFormat.format(date));
    }

    // Timer's ActionListener is simple -- display the current time in the timeLabel
    private class TimerListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            showTimeLabelCurrentTime();
        }
    }

    // JButton's action. This starts the long-running SwingWorker
    private class DataProcessAction extends AbstractAction {

        public DataProcessAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setEnabled(false); // first disable the button's action
            countLabel.setText("00"); // reset count label

            // then create SwingWorker and listen to its changes
            longRunningProcess = new LongRunningSwProcess();
            longRunningProcess.addPropertyChangeListener(new DataProcessListener());

            // execute the swingworker
            longRunningProcess.execute();
        }
    }

    // listen for state changes in our SwingWorker
    private class DataProcessListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(LongRunningSwProcess.COUNT)) {
                setCount((int)evt.getNewValue());
            } else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                longRunningProcessDone();
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("My App");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new MyApp());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

//SwingWorker长期运行操作的模型
类LongRunningSwProcess扩展SwingWorker{
公共静态最终字符串计数=“计数”;
专用静态最终整数最小值超时值=5;
专用静态最终整数最大超时=10;
私有整数计数=0;
@凌驾
受保护的Void doInBackground()引发异常{
//这个模型所做的只是增加一个计数字段
//每秒钟,直到达到超时
int timeOut=MIN_TIME_OUT+(int)(Math.random()*(MAX_TIME_OUT-MIN_TIME_OUT));
for(int i=0;i
使用摆动计时器——这种情况正是它应该使用的。谢谢,但我对你的答案有几个问题。为什么是“危险代码”?为什么我要停止计时器,而我正试图完成的是让计时器前进,即使数据处理正在发生。如果我有一个很长的数据进程,我不希望计时器停止。@mkunkel:您正在一个脱离事件分派线程的线程中进行Swing变异调用。这可能导致线程异常间歇性发生,使错误很难调试。如果要创建使用线程的Swing代码,您将需要阅读并理解Swing线程规则。请从这里开始:@mkunkel:对不起,我误解了你的要求。如果你想让计时器继续运行,那么不要停止它,如果你按照我的其他建议,它将继续运行。我读了很多关于swingworker的文章,我希望避免使用swingworker,因为我还不了解它。我猜这是不可避免的。@mkunkel:为什么要避开它们?当它是工作的最佳工具时,它有什么用途?
public class MainFrame extends JFrame {

    private DataProcess dataProcess = null;
...
...
    private StatusPanel statusPanel;
...
...
public MainFrame() {
...
        setJMenuBar(createFrameMenu());

        initializeVariables();
        constructLayout();
        createFileChooser();
        constructAppWindow();
}

    private void initializeVariables() {

        this.dataProcess = new DataProcess();
...
        this.statusPanel = new StatusPanel();
...
}
    private void constructLayout() {
        JPanel layoutPanel = new JPanel();
        layoutPanel.setLayout(new GridLayout(0, 3));
        layoutPanel.add(dataControlsPanel());

        setLayout(new BorderLayout());
        add(layoutPanel, BorderLayout.CENTER);
        add(statusPanel, BorderLayout.PAGE_END);
    }
public class StatusPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    private JLabel statusLabel;
    private JLabel timeLabel;

    private Timer timer;

    public StatusPanel() {
        initializeVariables();
        constructLayout();
        startTimer();
    }

    private void constructLayout() {
        setLayout(new FlowLayout(FlowLayout.CENTER));
        add(statusLabel);// , FlowLayout.CENTER
        add(timeLabel);
    }

    public void startTimer() {
        this.timer.start();
    }

    public void stopTimer() {
        this.timer.setRunning(false);
    }

    private void initializeVariables() {
        this.statusLabel = new JLabel();
        this.timeLabel = new JLabel();
        this.statusLabel.setText(StringConstants.STATUS_PANEL_TEXT);
        this.timer = new Timer(timeLabel);
    }

}
public class Timer extends Thread {

    private boolean isRunning;
    private JLabel timeLabel;
    private SimpleDateFormat timeFormat;

    public Timer(JLabel timeLabel) {
        initializeVariables(timeLabel);
    }

    private void initializeVariables(JLabel timeLabel) {
        this.timeLabel = timeLabel;
        this.timeFormat = new SimpleDateFormat("HH:mm:ss  dd-MM-yyyy");
        this.isRunning = true;
    }

    @Override
    public void run() {
        while (isRunning) {
            Calendar calendar = Calendar.getInstance();
            Date currentTime = calendar.getTime();
            timeLabel.setText(timeFormat.format(currentTime));

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

}
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;

@SuppressWarnings("serial")
public class MyApp extends JPanel {
    // display the date/time
    private static final String DATE_FORMAT = "HH:mm:ss  dd-MM-yyyy";
    private static final DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

    // timer updates measures seconds, but updates every 0.2 sec's to be sure
    private static final int TIMER_DELAY = 200;

    // JLabel that shows the date/time
    private JLabel timeLabel = new JLabel("", SwingConstants.CENTER);

    // JButton's Action / listener. This starts long-running data processing
    private Action dataProcessAction = new DataProcessAction("Process Data");

    // the SwingWorker that the above Action executes:
    private LongRunningSwProcess longRunningProcess;

    // label to display the count coming from the process above
    private JLabel countLabel = new JLabel("00");

    public MyApp() {
        // create a simple GUI
        JPanel dataProcessingPanel = new JPanel();
        dataProcessingPanel.add(new JButton(dataProcessAction)); // button that starts process
        dataProcessingPanel.add(new JLabel("Count:"));
        dataProcessingPanel.add(countLabel);        

        setLayout(new BorderLayout());
        add(timeLabel, BorderLayout.PAGE_START);
        add(dataProcessingPanel);
        showTimeLabelCurrentTime();
        // create and start Swing Timer
        new Timer(TIMER_DELAY, new TimerListener()).start(); 
    }

    // display count from swing worker
    public void setCount(int newValue) {
        countLabel.setText(String.format("%02d", newValue));
    }

    // clean up code after SwingWorker finishes
    public void longRunningProcessDone() {
        // re-enable JButton's action
        dataProcessAction.setEnabled(true);
        if (longRunningProcess != null) {
            try {
                // handle any exceptions that might get thrown from the SW
                longRunningProcess.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    // display the current time in our timeLabel JLabel
    private void showTimeLabelCurrentTime() {
        long currentTime = System.currentTimeMillis();
        Date date = new Date(currentTime);
        timeLabel.setText(dateFormat.format(date));
    }

    // Timer's ActionListener is simple -- display the current time in the timeLabel
    private class TimerListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            showTimeLabelCurrentTime();
        }
    }

    // JButton's action. This starts the long-running SwingWorker
    private class DataProcessAction extends AbstractAction {

        public DataProcessAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            setEnabled(false); // first disable the button's action
            countLabel.setText("00"); // reset count label

            // then create SwingWorker and listen to its changes
            longRunningProcess = new LongRunningSwProcess();
            longRunningProcess.addPropertyChangeListener(new DataProcessListener());

            // execute the swingworker
            longRunningProcess.execute();
        }
    }

    // listen for state changes in our SwingWorker
    private class DataProcessListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(LongRunningSwProcess.COUNT)) {
                setCount((int)evt.getNewValue());
            } else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                longRunningProcessDone();
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("My App");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new MyApp());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
// mock up of SwingWorker for long-running action
class LongRunningSwProcess extends SwingWorker<Void, Integer> {
    public static final String COUNT = "count";
    private static final int MIN_TIME_OUT = 5;
    private static final int MAX_TIME_OUT = 10;
    private int count = 0;

    @Override
    protected Void doInBackground() throws Exception {
        // all this mock up does is increment a count field 
        // every second until timeOut reached
        int timeOut = MIN_TIME_OUT + (int) (Math.random() * (MAX_TIME_OUT - MIN_TIME_OUT));
        for (int i = 0; i < timeOut; i++) {
            setCount(i);
            TimeUnit.SECONDS.sleep(1);
        }
        return null;
    }

    // make count a "bounded" property -- one that will notify listeners if changed
    public void setCount(int count) {
        int oldValue = this.count;
        int newValue = count;
        this.count = newValue;
        firePropertyChange(COUNT, oldValue, newValue);
    }

    public int getCount() {
        return count;
    }
}