MPS 2019.3ヘルプ

進捗インジケーター

デフォルトでは、アクションは実行中はUIをブロックします。すぐに終了しないアクションの場合は、アクションがアクティブであることをユーザーに示し、進行状況と時間の引用に関するフィードバックをユーザーに提供する進行状況バーを表示することをお勧めします。このセクションでは、進行状況インジケーターを適切に表示および更新する方法、ユーザーがアクションを手動でキャンセルできるようにする方法、およびアクションをバックグラウンドに送信する方法について説明します。

非同期タスク

UIがフリーズしないように、すべてのアクションを迅速に行うことを目指してください。長期にわたるアクティビティは、アクションから抽出し、1つ以上の非同期的に実行されるタスクに配置する必要があります。タスクはcom.intellij.openapi.progress@java_stubからタスククラスを拡張し、モーダルダイアログを表示する(Task.Modalクラス)か、バックグラウンドに送信する( Task.Backgroundableクラス)ことができます。タスクは、ApplicationManager.getApplication()。invokeLater()メソッドを介してEDTで呼び出す必要があります。

action ModalProgressAction {   mnemonic: <no mnemonic>   execute outside command: false   also available in: << ... >>   caption: BackgroundableProgressAction   description: <no description>   icon: <no icon>     construction parameters     << ... >>   action context parameters ( always visible = false )     Project project key: PROJECT required     MPSProject mpsProject key: MPS_PROJECT required   <update block>     execute(event)->void {   boolean canBeCanceled = true;   Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {     public void run(@NotNull() final ProgressIndicator indicator) {      doWorkChunk1(); if (checkForCancellation()) return; doWorkChunk2();   if (checkForCancellation()) return; ...     }      @Override      public void onCancel() {        super.onCancel();      }    };    ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(modalTask); });  } }

タスクは通常、タスクを実行するrun()メソッドと、ユーザーが呼び出したキャンセルを処理するonCancel()メソッドを提供します。実際のキャンセルロジックは、タスクの実装者が実装する必要があります。run()での作業は、それらの間に保留中のキャンセルのチェックを含むチャンクに編成する必要があります。onCancel()メソッドは、キャンセルによってrun()が終了した後にのみ起動されるため、タスクによって行われた部分的な作業をクリーンアップするために主に役立ちます。

キャンセルできないモーダルタスク( canBeCancelledパラメーターをfalseに設定)は、ユーザーにアクションが完全に終了するまで待機するよう強制するため、おそらくクリティカルなアクションに対してのみ使用する必要があり、キャンセルは適切に処理することが困難です。

進捗状況の監視

タスクのrun()メソッドは、IntelliJの進行状況インジケーターのインスタンスをパラメーターとして取得します。jetbrains.mps.progress@java_stubからのProgressMonitorAdapterでラップすることをお勧めします。ProgressMonitorAdapterは視覚的な進行状況ダイアログを表し、実際の進行状況バーの値とユーザーに表示されるラベルを設定するメソッドを提供します。また、キャンセルに関する情報も保持しています。

Task.Modal modalTask = new Task.Modal(ModalProgressAction.this.project, "Modal cancelable task", canBeCanceled) {  public void run(@NotNull() final ProgressIndicator indicator) {    final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator);        adapter.start("Progress in progress...", 8);    int stepValue = 1;      adapter.step("Do work 1 ...");    do work chunk 1   adapter.advance(stepValue);    if (adapter.isCanceled()) { return; }        adapter.step("Do work 2 ...");    do work chunk 2   adapter.advance(stepValue);    if (adapter.isCanceled()) { return; }      ...           adapter.done();  }

いくつかのメモ:

  • タスクのコンストラクターには、進行状況ダイアログのタイトルとして使用されるテキストが含まれます

  • start()メソッドは、進行状況バーの上に表示するテキストと、タスクを完了するためのいくつかのステップ/ポイントを提供する

  • step()メソッドは、進捗ダイアログの進捗バーに表示されるテキストラベルを変更する

  • advance()メソッドは、指定されたステップ数/ポイント数だけプログレスバーを新しい値に移動する

  • ユーザーエクスペリエンスを向上させるために、進行手順をできる限り小さくします。ステップを小さくすることで、ユーザーはよりスムーズに操作できます

バックグラウンドで実行

Task.Backgroundableクラスは、バックグラウンドで処理できるタスクに使用する必要があります。

action BackgroundableProgressAction {   mnemonic: <no mnemonic>   execute outside command: false   also available in: << ... >>   caption: BackgroundableProgressAction   description: <no description>   icon: <no icon>     construction parameters     << ... >>   action context parameters ( always visible = false )     Project project key: PROJECT required     MPSProject mpsProject key: MPS_PROJECT required   <update block>     execute(event)->void {   boolean canBeCanceled = true;   PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF;   Task.Backgroundable backgroundable = new Task.Backgroundable(BackgroundableProgressAction.this.project, "Backgroundable cancelable task", canBeCanceled, showProgress) {     public void run(@NotNull() final ProgressIndicator indicator) {  ...     }      @Override      public void onCancel() {        super.onCancel();      }    };    ApplicationManager.getApplication().invokeLater({ => ProgressManager.getInstance().run(backgroundable); });  }   additional methods   private void doWork() {      ...   } }

いくつかのメモ:

  • バックグラウンド可能なタスクはキャンセルを許可する場合と許可しない場合があります

  • PerformInBackgroundOptionインターフェースを使用すると、フォアグラウンドだけでなくバックグラウンドでも開始するタスクを作成できます

  • ユーザーは、バックグラウンド可能なタスクをバックグラウンドだけでなくフォアグラウンドにも移動できます

  • PerformInBackgroundOptionインターフェースの定義済み定数はDEAF(フォアグラウンドで開始)およびALWAYS_BACKGROUND(バックグラウンドで開始)で、ユーザーが注意を払う必要のない重要ではないアクションに便利です。ユーザーを混乱させるダイアログは表示されないため、UIは常に完全に使用可能です_)。

リソースにアクセスするときの適切なロック

タスク内でMPSリポジトリへの読み取りおよび書き込みロックを取得し、コマンドを実行しても問題ありません。

// ReadAction in step is ok for display state ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runReadAction(new Runnable() {    public void run() {      adapter.step("Do some work with Read Lock...");      ModalProgressAction.this.doWork();    }  }); adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }    // WriteAction in step is ok for display state  ModalProgressAction.this.mpsProject.getRepository().getModelAccess().runWriteAction(new Runnable() {    public void run() {      adapter.step("Do some work with Write Lock...");      ModalProgressAction.this.doWork();    }  });  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }    // Command in step is ok for display state  ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommand(new Runnable() {    public void run() {      adapter.step("Do some work in command...");      ModalProgressAction.this.doWork();    }  });  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }

いくつかのメモ:

  • アクション内でロックする必要がある場合は、すべての変更を単一のロックブロックにグループ化することをお勧めする

  • 他の潜在的なユーザーアクションのブロックを回避するために、不要になったらすぐにロックを解除する

  • EDTスレッドでR / Wアクションまたはコマンドを使用しないでください-これにより、進行状況の予測不可能な更新が発生し、UIがフリーズする可能性があります

取り消し不可能なアクション

モデルの変更には、executeCommandInEDT()メソッドを介して実行できる取り消し可能なアクションが必要です。ただし、ProgressIndicatorのメソッドはコマンド内から呼び出さないでください。ProgressIndicatorの自体はEDTで実行されており、進行状況バーの変更に対するすべての要求はコマンドが終了するまで遅延するためです。推奨されるアプローチは、executeCommandInEDT()でコマンドを呼び出す前にメインアクションのスレッドからプログレスバーに指示し、コマンドが終了するまで(おそらくCyclicBarrierまたは他の同期プリミティブで)メインアクションのスレッドをブロックすることです。

adapter.step("Do some work in command ...");  final CyclicBarrier barrier = new CyclicBarrier(2);    ModalProgressAction.this.mpsProject.getRepository().getModelAccess().executeCommandInEDT(new Runnable() {    public void run() {     try {         model m = model/myModel/;        m.new root node(MyRootConceptDeclaration);      } finally {        try {          barrier.await();        } catch (BrokenBarrierException e) {          <no statements>        } catch (InterruptedException e) {          <no statements>        }      }    }  });  try {    barrier.await();  } catch (InterruptedException e) {    <no statements>  } catch (BrokenBarrierException e) {    <no statements>  }  adapter.advance(stepValue);  if (adapter.isCanceled()) { return; }

追加の方法セクションを活用して、アクションの本体からロック実装コードを抽出できます。

private void block(CyclicBarrier barrier) {    try {      barrier.await();    } catch (BrokenBarrierException e) {      <no statements>    } catch (InterruptedException e) {      <no statements>    }  }
最終更新日: 2020年2月4日