外部 ProgressBar 未与按钮操作侦听器同时运行 [英] External ProgressBar is not running in concurrent with Button Action Listener

查看:28
本文介绍了外部 ProgressBar 未与按钮操作侦听器同时运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用运行进度条的 Swing worker 创建了一个外部类.这是代码,

I created one external class with Swing worker that runs the progress bar. This is the code,

public class ExtProgressMonitor extends JFrame {

private static final long serialVersionUID = 1L;
private static final String s = "Database Statistics Report is exectuing in the Background";
private JProgressBar progressBar = new JProgressBar(0, 100);
private JLabel label = new JLabel(s, JLabel.CENTER);

public ExtProgressMonitor() {
    this.setLayout(new GridLayout(0, 1));
    this.setTitle("Database Utility Execution");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.add(progressBar);
    this.add(label);
    this.setSize(100, 100);
    this.setLocationRelativeTo(null);
    this.setVisible(true);
    pack();
}

public void runCalc() {
    progressBar.setIndeterminate(false);
    progressBar.setStringPainted(false);
    TwoWorker task = new TwoWorker();
    task.addPropertyChangeListener(new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent e) {
            if ("progress".equals(e.getPropertyName())) {
                progressBar.setIndeterminate(false);
                progressBar.setValue((Integer) e.getNewValue());
            }
        }
    });
    task.execute();
}

private class TwoWorker extends SwingWorker<Integer, Integer> {

    private static final int N = 500;
    private final DecimalFormat df = new DecimalFormat(s);
    Integer x = 1;

    @Override
    protected Integer doInBackground() throws Exception {
        if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
            System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
        }
        for (int i = 1; i <= N; i++) {
            x = x - (((x * x - 2) / (2 * x)));
            setProgress(i * (100 / N));
            publish(Integer.valueOf(x));
            Thread.sleep(1000); // simulate latency
        }
        return Integer.valueOf(x);
    }

    @Override
    protected void process(List<Integer> chunks) {
        for (Integer percent : chunks ) {
           progressBar.setValue(progressBar.getValue() + percent);
        }
    }
}

当我在如下所示的主类中调用它时,上面的代码有效.

The above code works when I call it in main class like below .

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            ExtProgress t = new ExtProgress();
            t.runCalc();
        }
    });
}

但是,当我尝试在从数据库中获取大量行的操作按钮中调用相同的按钮时,大约需要 15-20 分钟.进度条启动,一旦db进程启动,progressbar就会被冻结,同时获取db统计信息.一旦漫长的进程结束,进度条会再次继续运行.

However When I try to call the same in my Action button that fetches a lot of row from database, takes around 15-20 minutes. progressbar gets launched, once the db process starts, the progressbar is frozen, while the db statistics is fetched.once the long process is over, the progress bar continues to run again.

private void jButton1ActionPerformed(java.awt.event.ActionEvent e) throws Exception {                                         
         
    ExtProgressMonitor t = new ExtProgressMonitor();
    t.runCalc();
    CheckStorage.DBVaultCheck(Host, port, instance, workbook, Schema_Password, schema);
    //.. Rest of the process, check storage comes from another class. 
    });

你能帮我解决这个问题吗?

Can you please help me fix this issue?

推荐答案

代表您的应用程序的 MRE 可能如下所示:

An MRE representing your application could look like the following:

import java.awt.GridLayout;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class SwingMain {

    SwingMain() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.add(new TestPanel());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new SwingMain();
    }
}

class TestPanel extends JPanel {

    public TestPanel() {

        JButton btn = new JButton("Run long process");
        btn.addActionListener(evt -> runLongProcess());
        add(btn);
    }

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        CheckStorage.DBVaultCheck();
    }
}

//For a second frame it is recommended to use JDialog instead of JFRame
class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "Database Statistics Report is exectuing in the Background";
    private final JProgressBar progressBar = new JProgressBar(0, 100);
    private final JLabel label = new JLabel(s, JLabel.CENTER);

    public ExtProgressMonitor() {
        this.setLayout(new GridLayout(0, 1));
        this.setTitle("Database Utility Execution");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);
        this.setSize(100, 100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        pack();
    }

    public void runCalc() {
        progressBar.setIndeterminate(false);
        progressBar.setStringPainted(false);
        new TwoWorker().execute();
    }

    private class TwoWorker extends SwingWorker<Integer, Integer> {

        private static final int N = 500;
        private final DecimalFormat df = new DecimalFormat(s);
        Integer x = 1;

        @Override
        protected Integer doInBackground() throws Exception {
            for (int i = 1; i <= N; i++) {
                x = x - (x * x - 2) / (2 * x);
                publish(Integer.valueOf(x));
                Thread.sleep(1000); // simulate latency
            }
            return Integer.valueOf(x);
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (Integer percent : chunks ) {
                progressBar.setValue(progressBar.getValue() + percent);
            }
        }
    }
}

class CheckStorage{

    private static final int  LIMIT = 1000;
    public static void DBVaultCheck() {
        int counter = 0;
        while(counter ++ < LIMIT)   { //simulate long process
            try {
                Thread.sleep(1000);
                System.out.println(counter);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

此代码的问题在于 CheckStorage.DBVaultCheck()EDT.
Swing 是一个单线程库.所有绘制任务都在事件调度线程 (EDT) 中执行.在 EDT 上运行长进程(例如睡眠)使该线程保持忙碌,因此它不会做其他事情喜欢更新gui.gui 变得无响应(冻结).
假设 CheckStorage.DBVaultCheck() 不会更新 gui,您所要做的就是通过更改代码中的一行来在不同的线程上运行长进程:

The problem with this code is that CheckStorage.DBVaultCheck() starts a long process on the EDT.
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT). Running long processes (such as sleep) on the EDT makes keeps this thread busy, so it does not do other things like updating the gui. The gui becomes unresponsive (freezes).
Assuming CheckStorage.DBVaultCheck() does not update the gui, all you have to do is run the long process on a different thread by changing a single line in the code:

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        new Thread(()->CheckStorage.DBVaultCheck()).start();
    }

如果 CheckStorage.DBVaultCheck() 确实更新了 gui,您必须采取措施确保这些更新发生在 EDT 上.Swing gui 更新只能由 EDT 完成.

In case CheckStorage.DBVaultCheck() does update the gui, you'l have to take measures to make sure that those updates occur on the EDT. Swing gui updates should only be done by the EDT.

这篇关于外部 ProgressBar 未与按钮操作侦听器同时运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆