为了账号安全,请及时绑定邮箱和手机立即绑定

Swing Worker 无法更新模态 jdialog 中的 GUI 组件

Swing Worker 无法更新模态 jdialog 中的 GUI 组件

翻过高山走不出你 2022-06-04 09:21:23
我有一个带有进度条和文本区域的模态 Jdialog。我正在启动一个 Swing Worker 来执行一些后台任务并使用发布过程更新 jdialog 中的文本区域。但是,在运行程序时,我观察到即使调用了 process 方法,它仍然没有更新 jdialog 中的文本区域。另外,请注意,我在 swing worker 的 done 方法中关闭了 jdialog,它工作正常。谁能告诉我为什么没有从(SwingWorker 的)流程方法进行 gui 更新?JDialog 类 -public class ProgressDialog extends JDialog {private static final long serialVersionUID = 1L;private GridBagLayout gridBag;private GridBagConstraints constraints;private ProgressDialog.ProgressBar progressBar;private JScrollPane scrollPane;private JTextArea textArea;ProgressDialog(Frame owner, String title, boolean modal, int numTasks) {    super(owner,title,modal);    gridBag = new GridBagLayout();    constraints = new GridBagConstraints();    this.progressBar = new ProgressDialog.ProgressBar();    this.progressBar.init(numTasks);    this.textArea = new JTextArea(10,30);    this.textArea.setEditable(false);    this.scrollPane = new JScrollPane(this.textArea);    this.scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);    this.scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);    this.setLayout(gridBag);    constraints.gridx = 0;    constraints.gridy = 0;    gridBag.setConstraints(progressBar, constraints);    constraints.gridx = 0;    constraints.gridy = 1;    constraints.insets = new Insets(10,0,10,0);    gridBag.setConstraints(scrollPane, constraints);    this.add(progressBar);    this.add(scrollPane);    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);    this.setSize(400, 300);    this.setLocationRelativeTo(null);}class ProgressBar extends JProgressBar {    ProgressBar() {    }    void init(int numTasks) {        setMaximum(numTasks);        setStringPainted(true);        setValue(0);    }    void setProgress(int taskCompleted) {        int totalTasks = getMaximum();        setValue(taskCompleted);        setString(taskCompleted+"/"+totalTasks);    }}
查看完整描述

2 回答

?
达令说

TA贡献1821条经验 获得超6个赞

一个巨大的问题就在这里:


protected void done() {

    try {

        Thread.sleep(5000);

    } catch (InterruptedException e) {

        e.printStackTrace();

    }

    pDialog.dispose();

}

在 EDT 或事件调度线程上调用worker 的done()方法,并且您知道当您休眠该线程时会发生什么——整个 GUI 进入休眠状态并变得完全没有响应。如果您想延迟对话框的处理并保持您的 GUI 正常运行,请不要这样做,实际上永远不要调用Thread.sleepEDT。


选项1:

相反,在大多数其他 Swing 延迟策略中,使用 Swing Timer,例如,类似这样的东西(代码未测试):


protected void done() {

    int timerDelay = 5000;

    new Timer(timerDelay, new ActionListener() {

        public void actionPerformed(ActionEvent e) {

            pDialog.dispose();

            ((Timer) e.getSource).stop();

        }

    }).start();

}

如果您不熟悉 Swing Timer 的使用,请查看Swing Timer 教程


选项 2:

把你放在Thread.sleep(5000)你方法的最后doInBackground(),就在之前return null;


另一个问题:您正在使用发布/处理方法对,但只使用了一次,这违背了它的目的。也许您打算在轮询 while 循环中调用发布?当然你只能调用get()一次,但是有没有另一种方法可以从你正在调用的正在运行的进程中提取信息?如果是这样,则需要间歇性地获取信息,无论是在轮询 while 循环中,还是通过任何允许您提取临时结果的过程。


例如:


import java.awt.BorderLayout;

import java.awt.Dialog.ModalityType;

import java.awt.Dimension;

import java.awt.Window;

import java.awt.event.ActionEvent;

import java.beans.*;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.*;

import javax.swing.*;



public class TestProgressDialog extends JPanel {

    private static final long serialVersionUID = 1L;

    private ProgressDialog pDialog;

    private JSpinner taskNumberSpinner = new JSpinner(new SpinnerNumberModel(10, 1, 20, 1));


    public TestProgressDialog() {

        setPreferredSize(new Dimension(800, 650));

        add(new JButton(new AbstractAction("Launch Dialog") {

            private static final long serialVersionUID = 1L;


            @Override

            public void actionPerformed(ActionEvent e) {

                Window owner = SwingUtilities.windowForComponent(TestProgressDialog.this);

                String title = "Dialog";

                ModalityType modal = ModalityType.MODELESS;

                int numTasks = (int) taskNumberSpinner.getValue();

                pDialog = new ProgressDialog(owner, title, modal, numTasks);

                pDialog.pack();

                pDialog.setLocationByPlatform(true);

                pDialog.append("Initializing background tasks\n");

                MyWorker myWorker = new MyWorker(numTasks, pDialog);

                myWorker.addPropertyChangeListener(new WorkerListener(pDialog));

                myWorker.execute();

                pDialog.setVisible(true);

            }

        }));

        add(new JLabel("Number of Tasks:"));

        add(taskNumberSpinner);

    }


    private static void createAndShowGui() {

        TestProgressDialog mainPanel = new TestProgressDialog();


        JFrame frame = new JFrame("Test Progress Dialog");

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(mainPanel);

        frame.pack();

        frame.setLocationByPlatform(true);

        frame.setVisible(true);

    }


    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> createAndShowGui());

    }

}

interface Progressable {

    void setProgress(int progress);

    void append(String text);

}

class ProgressDialog extends JDialog implements Progressable {

    private static final long serialVersionUID = 1L;

    private JProgressBar progressBar = new JProgressBar(0, 100);

    private JTextArea textArea = new JTextArea(10, 30);

    private JScrollPane scrollPane = new JScrollPane(textArea);


    ProgressDialog(Window owner, String title, ModalityType modal, int nunTasks) {

        super(owner, title, modal);

        progressBar.setStringPainted(true);


        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        add(progressBar, BorderLayout.PAGE_START);

        add(scrollPane);

    }


    @Override

    public void append(String text) {

        textArea.append(text);

    }


    @Override

    public void setProgress(int progress) {

        progressBar.setValue(progress);

    }


}

class MyCallable implements Callable<String> {

    private static final long MAX_DUR = 6 * 1000;

    private static final long MIN_DUR = 1000;

    private String text;


    public MyCallable(String text) {

        this.text = text;

    }


    public String getText() {

        return text;

    }


    @Override

    public String call() throws Exception {

        // just wait some random delay and then return a String

        long timeout = (long) (Math.random() * MAX_DUR + MIN_DUR);

        TimeUnit.MILLISECONDS.sleep(timeout);

        return text + " time out: " + timeout;

    }


}

class WorkerListener implements PropertyChangeListener {

    private Progressable progressable;


    public WorkerListener(Progressable progressable) {

        this.progressable = progressable;

    }


    @Override

    public void propertyChange(PropertyChangeEvent evt) {

        if (evt.getPropertyName() != null && evt.getPropertyName().equals("progress")) { 

            int progress = (int)evt.getNewValue();

            progressable.setProgress(progress);

        }

    }

}

class MyWorker extends SwingWorker<Void, String> {


    private Progressable progressable;

    private List<Future<String>> futures = new ArrayList<>();

    private CompletionService<String> completionService;

    private int numTasks;

    // private BlockingQueue<Future<String>> completionQueue;



    public MyWorker(int numTasks, Progressable progressable) {

        this.numTasks = numTasks;

        this.progressable = progressable;

        ExecutorService service = Executors.newFixedThreadPool(numTasks);

        completionService = new ExecutorCompletionService<>(service);


        for (int i = 0; i < numTasks; i++) {

            futures.add(completionService.submit(new MyCallable("My Callable " + i)));

        }

        service.shutdown();

    }


    @Override

    protected Void doInBackground() throws Exception {

        while (futures.size() > 0) {

            Future<String> future = completionService.take();

            futures.remove(future);

            int progress = (100 * (numTasks - futures.size())) / numTasks;

            progress = Math.min(100, progress);

            progress = Math.max(0, progress);

            setProgress(progress);

            if (future != null) {

                publish(future.get());

            }

        }

        return null;

    }


    @Override

    protected void process(List<String> chunks) {

        for (String chunk : chunks) {

            progressable.append(chunk + "\n");

        }

    }


    public Progressable getpDialog() {

        return progressable;

    }


}


查看完整回答
反对 回复 2022-06-04
?
郎朗坤

TA贡献1921条经验 获得超9个赞

这里的问题是您的 Jdialog 框是模态的。每当弹出(或 JDialog)是setModal(true)EDT 线程被阻塞。这就是当使用 modal 弹出时用户将无法执行任何操作的方式true

您必须制作它setmodal(false),然后在弹出窗口中更新内容,然后再制作它setmodal(true)


查看完整回答
反对 回复 2022-06-04
  • 2 回答
  • 0 关注
  • 98 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号