6

更新をコントローラーに送り返す必要がある長時間実行プロセスで SwingWorker を使用する場合、モデルからビューを分離するにはどうすればよいですか?

  • を使用して、たとえばそこからSwingWorkers doInBackground()呼び出すことで、EDT の応答性を維持できます。model.doLongProcess()

  • 私が抱えている問題は、プロセスが終了する前にデータを取得して、進行状況でビューを更新しようとすることです..

  • メソッドを使用してデータを取得できることはわかっていますが、これにより、内にメソッド SwingWorkers publish()のコードを記述する必要があると思います。doLongProcess()doInBackground()


参考までに、MVC の実装は次のようになります。

http://www.leepoint.net/notes-java/GUI/structure/40mvc.html

/ structure/calc-mvc/CalcMVC.java -- Calculator in MVC pattern.
// Fred Swartz -- December 2004

import javax.swing.*;

public class CalcMVC {
    //... Create model, view, and controller.  They are
    //    created once here and passed to the parts that
    //    need them so there is only one copy of each.
    public static void main(String[] args) {

        CalcModel      model      = new CalcModel();
        CalcView       view       = new CalcView(model);
        CalcController controller = new CalcController(model, view);

        view.setVisible(true);
    }
}

他の多くのクラスを一緒にラップして、コントローラー用の単純なインターフェイスを形成する 1 つのモデル クラスがあります。

これらのクラスのコードのすべて/一部/いずれかをコントローラーに移動する必要はありません-そこに属していません。


アップデート:

これが私が取っているアプローチです-それは最もクリーンなソリューションではなくPropertyChangeSupport、セマンティックレベルで..の乱用として認識される可能性があります。

基本的に、長時間実行されるメソッドを持つすべての低レベル クラスにはpropertyChangeSupportフィールドがあります。長時間実行されるメソッドはfirePropertyChange()、メソッドのステータスを更新するために定期的に を呼び出しますが、必ずしもプロパティの変更を報告するわけではありません。

次に、低レベル クラスをラップする Model クラスがこれらのイベントをキャッチし、独自の高レベルfirePropertyChange.. をcontrollerリッスンできます..

編集:

明確にするために、firePropertyChange(propertyName, oldValue, newValue); を呼び出すと、

  • propertyName ---> propertyName を乱用してトピック名を表す
  • oldValue =ヌル
  • newValue = ブロードキャストしたいメッセージ

次に、モデル内の PropertyChangeListener またはトピック名に基づいてメッセージを識別できる場所。

したがって、Iv は基本的にシステムを曲げて、パブリッシュ/サブスクライブのように使用します ....


上記のメソッドの代わりに、更新される低レベルのクラスに進行状況フィールドを追加し、それに基づいて firePropertyChange を追加できると思います..これは、本来の使用方法と一致します。

4

3 に答える 3

4

パブリッシュ/プロセスのペアは、SwingWorker から GUI にデータをプッシュするものと考えています。情報を渡すもう 1 つの方法は、PropertyChangeSupport および PropertyChangeListeners を使用して、GUI またはコントロールに SwingWorker から情報を引き出すことです。検討

  • モデルに PropertyChangeSupport フィールドを与え、
  • PropertyChangeListener メソッドを追加および削除する
  • 状態の変化をサポート オブジェクトに通知する。
  • SwingWorker に PropertyChangeListener をモデルに追加させます。
  • 次に、モデルの状態の変化をコントロールまたはビューに通知する SwingWorker を用意します。
  • SwingWorker は、モデルから変更された情報でパブリッシュ/プロセスを使用することもできます。

編集
更新について:

基本的に、長時間実行されるメソッドを持つすべての低レベル クラスには、propertyChangeSupport フィールドがあります。長時間実行されるメソッドは定期的に firePropertyChange() を呼び出してメソッドのステータスを更新しますが、必ずしもプロパティの変更を報告するわけではありません。

これを行うことはお勧めしません。リッスンされているバインドされたプロパティが変更されない場合、firePC() が呼び出されても、PropertyChangeListeners (PCL) は通知されないことを理解してください。プロパティをポーリングする必要がある場合、PCL を使用してこれを行うことはありません。おそらくポーリングされているクラスの外から、単純にポーリングします。

于 2012-10-06T12:15:47.560 に答える
1

個人的には、mySwingWorkerでパブリックpublishメソッドを作成し、my のインスタンスをSwingWorker実行時間の長い Model メソッドに渡します。このようにして、モデルは更新をコントロール ( SwingWorker) にプッシュし、それからビューにプッシュします。

以下に例を示します。(実行を簡単にするために) すべてを 1 つのファイルにまとめましたが、通常はこれらのファイル/パッケージを個別に作成することを想像します。

編集

モデルをコントロールから切り離すには、モデルのオブザーバーが必要です。ProgressListener継承を実装しActionListenerます。ProgressListenerモデルは、登録されているすべての人に進捗があったことを通知するだけです。

import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class MVCSwingWorkerExample {

    public static void main(String[] args) {
        CalcModel      model      = new CalcModel();
        CalcView       view       = new CalcView();
        CalcController controller = new CalcController(model, view);
    }

    //Model class - contains long running methods ;)
    public static class CalcModel{

        //Contains registered progress listeners
        ArrayList<ActionListener> progressListeners = new ArrayList<ActionListener>();
        //Contains model's current progress
        public int status;

        //Takes in an instance of my control's Swing Worker
        public boolean longRunningProcess(MVCSwingWorkerExample.CalcController.Worker w){
            for(int i = 0; i < 60; i++){
                try {
                    //Silly calculation to publish some values
                    reportProgress( i==0 ? 0 : i*100/60);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Whowsa!");
                    e.printStackTrace();
                }
            }
            return true;
        }

        //Notify all listeners that progress was made
        private void reportProgress(int i){
            status = i;
            ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_FIRST, null);
            for(ActionListener l : progressListeners){
                l.actionPerformed(e);
            }
        }

        //Standard registering of the listeners
        public void addProgressListener(ActionListener l){
            progressListeners.add(l);
        }

        //Standard de-registering of the listeners
        public void removeProgressListener(ActionListener l){
            progressListeners.remove(l);
        }
    }

    //View Class - pretty bare bones (only contains view stuff)
    public static class CalcView{
        Box display;
        JButton actionButton;
        JLabel progress;

        public void buildDisplay(){
            display = Box.createVerticalBox();
            actionButton = new JButton("Press me!");
            display.add(actionButton);

            progress = new JLabel("Progress:");
            display.add(progress);
        }

        public void start(){
            final JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(display);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }

    public static class CalcController{
        CalcModel model;
        CalcView view;

        public CalcController(CalcModel model, CalcView view){
            this.model = model;
            this.view = view;

            //Build the view
            view.buildDisplay();

            //Create an action to add to our view's button (running the swing worker)
            ActionListener buttonAction = new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent e) {
                    Worker w = new Worker();
                    w.execute();
                }
            };
            view.actionButton.addActionListener(buttonAction);

            //Start up the view
            view.start();

        }

        //Notified when the Model updates it's status
        public class ProgressListener implements ActionListener{
            Worker w;

            public ProgressListener(Worker w){
                this.w = w;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                CalcModel model = (CalcModel)e.getSource();
                w.publishValue(model.status);
            }

        }


        //The worker - usually part of the control
        public class Worker extends SwingWorker<Boolean, Integer>{

            public Worker(){
                //Register a listener to pay attention to the model's status
                CalcController.this.model.addProgressListener(new ProgressListener(this));
            }

            @Override
            protected Boolean doInBackground() throws Exception {
                //Call the model, and pass in this swing worker (so the model can publish updates)
                return model.longRunningProcess(this);
            }

            //Expose a method to publish results
            public void publishValue(int i){
                publish(i);
            }

              @Override
              protected void process(java.util.List<Integer> chunks){
                  view.progress.setText("Progress:" + chunks.get(chunks.size()-1) + "%");
              }

             @Override
               protected void done() {
                   try {
                       view.progress.setText("Done");
                   } catch (Exception ignore) {
                   }
               }
        }
    }

}
于 2012-10-08T22:20:28.147 に答える
0

Swing で長時間実行されるプロセスでは、その目的のために新しいスレッドを作成する必要があります。そのため、このプロセスが完了したら、「Swing スレッド」内の MVC を更新する必要があります。アプリケーションごとに 1 つしかないことに注意してください。

アプリケーションが処理していることをユーザーに知らせる方法を見つけてください。完了するまで、ユーザーが再び「乗算」できないようにしてください。

public class CalcController {

////////////////////////////////////////// inner class MultiplyListener
/**
 * When a mulitplication is requested. 1. Get the user input number from the
 * View. 2. Call the model to mulitply by this number. 3. Get the result
 * from the Model. 4. Tell the View to display the result. If there was an
 * error, tell the View to display it.
 */
class MultiplyListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {
        final String userInput = m_view.getUserInput();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    m_model.multiplyBy(userInput);
                } catch (NumberFormatException nfex) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            m_view.showError("Bad input: '" + userInput + "'");
                        }
                    });
                }
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        m_view.setTotal(m_model.getValue());
                    }
                });
            }
        }).start();

    }
}//end inner class MultiplyListener

}
于 2012-10-15T16:07:34.147 に答える