0

我有一个场景,我需要我的 swing UI 在两个不同的线程上运行。我有一台笔记本电脑,我将在其中运行我的应用程序。在连接到我的笔记本电脑的另一个屏幕上单击应该开始演示文稿有一个按钮。

现在我做了一个扩展 SwingWorker 的课堂演示,并从文件夹中读取图像并将其显示在屏幕上。

class Presenatation extends SwingWorker<Integer, Integer> {

    @Override
    protected Integer doInBackground() throws Exception {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    start(outputFolder, screenVO);/*Creates a JFrame to be displayed
on new screen and sets a JPanel to it. Reads the file images sets it into 
JLabels every 2 seconds and updates it to Japnel*/
                }
            });
            return null;
        }

在我的 start 方法中,我有读取图像并在 UI 上显示它们的代码

我觉得这种方法是错误的,因为我的 SwingWorker 不应该在 doInBackground() 中调用 invokeLater

据我所知,它应该是这样的:

@Override
protected Void doInBackground() throws Exception
{

    return null;
}

@Override
protected void process(List<Integer> chunks
{

} 

我无法决定哪个部分应该放在哪里?

我有以下事情要做:

  • 开始一个新的框架以在新屏幕上显示
  • 每 2 秒将图像加载到框架中,从文件夹中读取图像
  • 将 Presentation 类扩展到 SwingWorker,这种方法正确吗?因为在外部我有一个 Executor 对象,我在其 exec() 中传递了 Presentation 对象

请帮我 !

4

4 回答 4

0

实际上,您不应该从 doInbackground 调用 invokeLater,也不应该产生一个新线程。注意 SwingWorker 是一个 Runnable,所以它可以提交给一个 Executor。

但是每次准备好要显示的新图像时,都必须调用 publish 方法。在幕后,SwingWorker 将在 Event Dispatch 线程上调用其 process 方法。

因此,您必须覆盖流程才能进行实际的小部件更新。

于 2014-01-28T13:35:54.860 回答
0

对这个答案持保留态度,因为许多人会不同意并说这是一种可怕的做法。然而,据我所知,没有人能准确地说出为什么这很糟糕。所以,在那之前,我保持我的观点:

我认为调用invokeLater()and是完全可以invokeAndWait()doInBackground()

在那里,我说了算。

上个月我问了基本相同的问题,从那以后我一直在尝试process()publish(). invokeLater()在我的实验中,我没有遇到任何线程或同步问题。而且这种方法比publish()and容易得多process()

我为什么这么说?

首先,通过调用invokeLater,您将向 EDT 抛出运行的任何代码。因此,该代码应该中断没有合乎逻辑的理由。

其次,process()publish()编写时考虑了非常非常具体的目标(即,为您提供一种发送表格结果的方式,以便您可以实时更新JTable或更新)。JList我从来没有,也没有一次,使用过process()或者publish()以它显然是为了使用而编写的(我使用大量的表格数据)。为了得到publish()process()做我想做的事(90% 的时间更新一个不确定的JProgressBar,9% 的时间更新一个确定的JProgressBar),我通常最终会写一些骇人听闻的方式来发布和处理我的数据。它令人困惑、难以阅读并且难以管理变更请求。

但是,invokeLater()从内部doInBackground()很容易阅读,而且,我还没有听到任何人说为什么这样做不安全。而且,就像我在上面链接的问题中提出的情况一样,如果您需要暂停执行以获取用户反馈,我认为除了 to 之外没有其他选择invokeAndWait()

我的诚实意见是,publish()写得process()不是很好。SwingWorker一旦你了解了细微差别,那就太棒了,但这些方法并不能满足大多数人的需求,也不能满足SwingWorker他们向用户更新进度的需求。至少,在我的经验中不是。

编辑:特别是关于你的情况,我会做的是:

  1. 为您创建一个构造函数SwingWorker并在那里初始化一个对话框,但使其成为 的类成员,SwingWorker以便您可以更新它。例如:

    类任务扩展 SwingWorker {

    IndeterminateLoadingDialog ild;
    
    public Task _Task() {
        JDialog dialog = new JDialog(// parent frame);
        ild = new IndeterminateLoadingDialog(this);
        dialog.add(ild);
        dialog.pack();
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.setLocationRelativeTo(dialog.getParent());
        dialog.setVisible(true);
    }
    

我的IndeterminateLoadingDialog班级是一个JPanel只有一个JProgressBar和一个JLabel。我可以通过与JLabel.

SwingWorker无论如何,我总是在构造函数中初始化我需要的任何 GUI 组件。只需确保new Task().execute()从 EDT 调用。

如果我需要更新我的IndeterminateLoadingDialog,我将直接使用invokeLater()来更改文本。

此外,我关闭了方法中的对话框done()

希望这对您有所帮助。

于 2014-01-29T15:59:36.023 回答
0

一些背景

Swing(和所有其他 GUI 框架)不是多线程的(因为一长串的正当理由)。

因此,一个好的经验法则是仅从 EDT 线程创建和操作任何 GUI组件。有一些边缘情况(例如从线程创建新的 JFrame)可能会起作用,但基本上从与 EDT 不同的线程对 GUI 执行任何操作都是一个坏主意。

您不仅应该而且必须从任何非 EDT 线程调用 invokeLater 或 invokeAndWait。这是确保您的线程被挂起并且提交的 Runnable 是从 EDT 线程的上下文中执行的唯一方法。

一些解决方案

基本上,您绝对不需要两个 EDT 线程。事实上这是一个坏主意,你有一个键盘和一个鼠标来创建一组 UI 事件,因此两个 EDT 线程没有用。

事实上,你甚至不需要一个swingworker就可以在第二个显示器上切换图片。SwingWorkers 非常适合从 EDT 线程卸载长时间运行的非 GUI 操作(即执行需要 20 秒才能完成的数据库操作)。就您而言,加载新图片不是火箭科学:)

执行以下操作:

  • 丢弃所有摇摆工人和其他东西
  • 当您按下按钮时,打开新的演示窗口
  • 在演示窗口中创建一个每 2 秒触发一次的计时器
  • 当计时器触发时,加载新图片并将其扔到窗口

这在现实中非常简单。考虑这个例子:

package a;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class PresentationStart extends JFrame {

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

    public PresentationStart() {
        super("Start here");
        final JButton button=new JButton("Start");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                new PresentationView();
            }
        });
        add(button);
        pack();
        setVisible(true);
    }
}

和观众:

package a;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

public class PresentationView extends JFrame {

    public PresentationView() {
        super("View");
        final JLabel picture=new JLabel("Picture comes here");
        add(picture);
        pack();
        setVisible(true);

        final List<String> pictures=new ArrayList<String>();
        pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
        pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
        pictures.add("http://www.alragdkw.com/wp-content/uploads/2016/01/fruit-Banans.jpg");

        final Timer timer=new Timer(2000,new ActionListener() {
            int index=0;

            @Override
            public void actionPerformed(final ActionEvent e) {
                // Load a new picture
                try {
                    picture.setIcon(new ImageIcon(ImageIO.read(new URL(pictures.get(index)))));
                } catch (final Exception ex) {
                    ex.printStackTrace();
                }
                index++;
                if (index>=pictures.size()) {
                    index=0;
                }
            }
        });
        timer.start();
    }
}
于 2016-02-24T17:01:00.243 回答
-1

将您的演示文稿作为一个独立的 java 程序,并使用 Runtime.exec() 启动它。它将创建一个单独的窗口。

于 2014-01-28T13:38:19.093 回答