MVC 进度条线程

发布于 2024-10-29 15:41:39 字数 1454 浏览 1 评论 0原文

我在设计中使用 MVC 模式,当用户按下搜索按钮时,我会在模型中调用搜索,但我还想使用从该模型返回的信息更新进度栏。

我尝试过使用 Swingworker,但进度条没有更新。我怀疑我的线程做错了什么。

我在控制器中定义的按钮是:

 class SearchBtnListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            _view.displayProgress();  
        }    
}

这调用模型中的搜索,并在视图中进行以下调用:

public void displayProgress() {

    TwoWorker task = new TwoWorker();
    task.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent e) {
             if ("progress".equals(e.getPropertyName())) {
                _progressBar.setValue((Integer) e.getNewValue());
             }
         }

     });
     task.execute();             
}      


private class TwoWorker extends SwingWorker<Void, Void> {        
    @Override
    protected Void doInBackground() throws Exception {
        _model.startSearch(getTerm());                  // time intensive code
        File file = new File("lock");           
        while (file.exists()){
            setProgress(_model.getStatus());
            System.out.println(_model.getStatus()); // never called
        }           
        return null;
    }  

    protected void done(){
        updateMain();
    }
}

模型中定义的虚拟函数用于测试:

public int getStatus(){
    Random r = new Random();
    return r.nextInt();
}

I am using an MVC pattern for my design, when a user presses the search button, I call a search in the model, but I also want to update a progress bar with information returned from that model.

I have tried using a swingworker, but the progress bar does not update. I suspect I am doing something wrong with my threading.

My button as defined in the controller is:

 class SearchBtnListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            _view.displayProgress();  
        }    
}

This calls the search in the model and has the following call in the view:

public void displayProgress() {

    TwoWorker task = new TwoWorker();
    task.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent e) {
             if ("progress".equals(e.getPropertyName())) {
                _progressBar.setValue((Integer) e.getNewValue());
             }
         }

     });
     task.execute();             
}      


private class TwoWorker extends SwingWorker<Void, Void> {        
    @Override
    protected Void doInBackground() throws Exception {
        _model.startSearch(getTerm());                  // time intensive code
        File file = new File("lock");           
        while (file.exists()){
            setProgress(_model.getStatus());
            System.out.println(_model.getStatus()); // never called
        }           
        return null;
    }  

    protected void done(){
        updateMain();
    }
}

Dummy function defined in Model for testing:

public int getStatus(){
    Random r = new Random();
    return r.nextInt();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

╰沐子 2024-11-05 15:41:39

不要

_progressBar.setValue(_model.getStatus());

从 SwingWorker 内部调用,因为这是从后台线程调用 Swing 代码,而且这也是 PropertyChangeListener 的用途。相反,只需设置进度属性即可。

另外,不要从 doInBackground 方法中调用 did(),因为这需要由 SwingWorker 从 EDT 调用。因此,让 SwingWorker 本身在实际完成时调用此方法。

另外,Done()应该是done()——第一个字母不应该大写,并且你应该在这段代码中使用@Override注释,这样你就可以确保你正确地重写了方法。

另外,这个有什么作用呢?

 _model.startSearch(_view.getTerm());

它是否调用需要一段时间才能完成的代码?是否应该从 SwingWorker doInBackground 本身内部进行初始化?

编辑:
另一种选择是为模型提供一个绑定的 int 属性,例如称为进度,然后向其中添加一个 PropertyChangeListener 直接让它更新 JProgressBar。例如,

import java.awt.BorderLayout;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javax.swing.*;

public class MVC_ProgressBarThread {
   private static void createAndShowUI() {
      MVC_View view = new MVC_View();
      MVC_Model model = new MVC_Model();
      MVC_Control control = new MVC_Control(view, model);
      view.setControl(control);

      JFrame frame = new JFrame("MVC_ProgressBarThread");
      frame.getContentPane().add(view);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

@SuppressWarnings("serial")
class MVC_View extends JPanel {
   private MVC_Control control;
   private JProgressBar progressBar = new JProgressBar();
   private JButton startActionButton = new JButton("Start Action");

   public MVC_View() {
      startActionButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            buttonActionPerformed();
         }
      });

      JPanel buttonPanel = new JPanel();
      buttonPanel.add(startActionButton);
      setLayout(new BorderLayout());
      add(buttonPanel, BorderLayout.NORTH);
      add(progressBar, BorderLayout.CENTER);
   }

   public void setControl(MVC_Control control) {
      this.control = control;
   }

   private void buttonActionPerformed() {
      if (control != null) {
         control.doButtonAction();
      }
   }

   public void setProgress(int progress) {
      progressBar.setValue(progress);
   }

   public void start() {
      startActionButton.setEnabled(false);
   }

   public void done() {
      startActionButton.setEnabled(true);
      setProgress(100);
   }
}

class MVC_Control {
   private MVC_View view;
   private MVC_Model model;

   public MVC_Control(final MVC_View view, final MVC_Model model) {
      this.view = view;
      this.model = model;
      model.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent pce) {
            if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) {
               view.setProgress((Integer)pce.getNewValue());
            }
         }
      });
   }

   public void doButtonAction() {
      view.start();
      SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {
         @Override
         protected Void doInBackground() throws Exception {
            model.reset();
            model.startSearch();
            return null;
         }

         @Override
         protected void done() {
            view.done();
         }
      };
      swingworker.execute();
   }

}

class MVC_Model {
   public static final String PROGRESS = "progress";
   private static final int MAX = 100;
   private static final long SLEEP_DELAY = 100;
   private int progress = 0;
   private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

   public void setProgress(int progress) {
      int oldProgress = this.progress;
      this.progress = progress;

      PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress);
      pcs.firePropertyChange(evt);
   }

   public void reset() {
      setProgress(0);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      pcs.addPropertyChangeListener(listener);
   }

   public void startSearch() {
      for (int i = 0; i < MAX; i++) {
         int newValue = (100 * i) / MAX;
         setProgress(newValue);
         try {
            Thread.sleep(SLEEP_DELAY);
         } catch (InterruptedException e) {}
      }
   }
}

Don't call

_progressBar.setValue(_model.getStatus());

from within your SwingWorker as this is calling Swing code from a background thread and is what the PropertyChangeListener is for anyway. Instead, just set the progress property, that's all.

Also, don't call done() from within the doInBackground method as this needs to be called from the EDT by the SwingWorker. So let the SwingWorker itself call this method when it is in fact done.

Also, Done() should be done() -- the first letter shouldn't be capitalized, and you should use @Override annotations in this code so you can be sure that you're overriding methods correctly.

Also, what does this do?

 _model.startSearch(_view.getTerm());

Does it call code that takes a while to complete? Should this be initialized from within the SwingWorker doInBackground itself?

Edit:
Another option is to give the Model a bound int property, say called progress, and then add a PropertyChangeListener to it directly letting it update the JProgressBar. For example,

import java.awt.BorderLayout;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javax.swing.*;

public class MVC_ProgressBarThread {
   private static void createAndShowUI() {
      MVC_View view = new MVC_View();
      MVC_Model model = new MVC_Model();
      MVC_Control control = new MVC_Control(view, model);
      view.setControl(control);

      JFrame frame = new JFrame("MVC_ProgressBarThread");
      frame.getContentPane().add(view);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

@SuppressWarnings("serial")
class MVC_View extends JPanel {
   private MVC_Control control;
   private JProgressBar progressBar = new JProgressBar();
   private JButton startActionButton = new JButton("Start Action");

   public MVC_View() {
      startActionButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            buttonActionPerformed();
         }
      });

      JPanel buttonPanel = new JPanel();
      buttonPanel.add(startActionButton);
      setLayout(new BorderLayout());
      add(buttonPanel, BorderLayout.NORTH);
      add(progressBar, BorderLayout.CENTER);
   }

   public void setControl(MVC_Control control) {
      this.control = control;
   }

   private void buttonActionPerformed() {
      if (control != null) {
         control.doButtonAction();
      }
   }

   public void setProgress(int progress) {
      progressBar.setValue(progress);
   }

   public void start() {
      startActionButton.setEnabled(false);
   }

   public void done() {
      startActionButton.setEnabled(true);
      setProgress(100);
   }
}

class MVC_Control {
   private MVC_View view;
   private MVC_Model model;

   public MVC_Control(final MVC_View view, final MVC_Model model) {
      this.view = view;
      this.model = model;
      model.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent pce) {
            if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) {
               view.setProgress((Integer)pce.getNewValue());
            }
         }
      });
   }

   public void doButtonAction() {
      view.start();
      SwingWorker<Void, Void> swingworker = new SwingWorker<Void, Void>() {
         @Override
         protected Void doInBackground() throws Exception {
            model.reset();
            model.startSearch();
            return null;
         }

         @Override
         protected void done() {
            view.done();
         }
      };
      swingworker.execute();
   }

}

class MVC_Model {
   public static final String PROGRESS = "progress";
   private static final int MAX = 100;
   private static final long SLEEP_DELAY = 100;
   private int progress = 0;
   private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

   public void setProgress(int progress) {
      int oldProgress = this.progress;
      this.progress = progress;

      PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress);
      pcs.firePropertyChange(evt);
   }

   public void reset() {
      setProgress(0);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      pcs.addPropertyChangeListener(listener);
   }

   public void startSearch() {
      for (int i = 0; i < MAX; i++) {
         int newValue = (100 * i) / MAX;
         setProgress(newValue);
         try {
            Thread.sleep(SLEEP_DELAY);
         } catch (InterruptedException e) {}
      }
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文