1

I have a Java - Eclipse RCP application. The Editor has several pages, some of which contain nebula.XYGraph graphs. The toolbar has an Action, which loops over these pages to take a snapshot of each graph. In the loop, I retrieve the page, use editor.setActivePage(index of the page) so the page should be displayed/refreshed, then call page.doSnapshot().

However, several problems happen:

  • setActivePage() does not display the page, ie the user does not see the switching of pages one after another. It is like the run of the Action is locking/freezing the Editor UI, and when reaching the end of the loop, only the last page is finally displayed.
  • As the graphs have not been displayed, some of their components (Annotations for example), are not correctly displayed/positioned. So the produced snapshots are incorrect. Graphs are correct only if the user himself has already opened the page one time before doing the Action.

I tried:

  • modifying the order of functions calls during the loop
  • using Thread.sleep() to let the pages have more time to display
  • Action using a Job, so one page is managed at a time
  • using the editor.setActivePage() when doing the editor.addPage(), so pages should be displayed one time at their creation (but as the pages are added in a loop again, there is no refresh/switching of pages in the UI)
  • set the graph's Annotation position, and other manipulation on the XYGraph. Nothing worked...
Sujith Kumar
  • 872
  • 6
  • 19
Felix
  • 31
  • 2
  • 2
    You can't do things like that in a loop because the loop blocks the SWT event dispatching until it completes. You have to switch to a page and then use something like `Display.asyncExec` or `Display.timerExec` to schedule the next switch after pending events have been run so that the page displays. – greg-449 May 15 '23 at 10:28
  • 1
    See [this question and answer](https://stackoverflow.com/q/64433534/2670892) for an example – greg-449 May 15 '23 at 10:37
  • Thank you for the explanation, Display.asyncExec worked. – Felix May 15 '23 at 15:11

1 Answers1

0

To add greg-449's comment:

Display.asyncExec() queues the runnable to be run on the display's user-interface thread at the next reasonable opportunity. This does not cause the current thread to wait for the runnable to complete.

In the context of the Eclipse UI, user interface operations (like drawing, updating, and taking snapshots of the UI) must be performed in the UI thread. This is because the UI toolkit (SWT in this case) is not thread-safe, meaning it does not support concurrent modification from multiple threads.

When you call Display.asyncExec(), the provided Runnable will be executed asynchronously on the UI thread, allowing it to safely perform UI operations. This asynchronous execution also has the advantage of not blocking the current thread.

In your case, when you are setting the active page and taking a snapshot, these operations need to be performed on the UI thread.
By using Display.asyncExec(), you are ensuring that these operations are performed on the UI thread, which can safely modify the UI. Additionally, since Display.asyncExec() does not block, it allows the UI to update in between each invocation, which gives the UI a chance to display the page before the snapshot is taken.

In contrast, when you were just looping through and calling setActivePage() and doSnapshot() directly, all those operations were being queued up on the UI thread, but the UI did not have a chance to update and display the new active page before the next operation was performed. This is why you were only seeing the last page displayed and why the snapshots were not correct.


See also "Difference between syncExec() and asyncExec() of Display class".
Both asyncExec() and syncExec() are used to execute code on the UI thread, but there is a significant difference in their behavior:

  • syncExec(Runnable) schedules the runnable to be run on the UI thread and then waits for it to finish before returning. This means that the calling thread is blocked until the runnable has completed its execution. This is useful when you need the result of the runnable's operation immediately, but it can cause a deadlock if used improperly.

  • asyncExec(Runnable) also schedules the runnable to be run on the UI thread, but it returns immediately without waiting for the runnable to finish. This means that the calling thread can continue doing other work without waiting for the UI operations to complete.

In your case, you are looping over pages and taking a snapshot of each one. If you used syncExec(), it would block on each iteration of the loop, waiting for the page to be displayed and the snapshot to be taken before moving on to the next iteration. This could potentially cause the UI to freeze, because the UI thread would be constantly busy and would not have time to process other events, like user input.

On the other hand, asyncExec() schedules the operations to be performed and then immediately moves on to the next iteration. This allows the UI thread to interleave the execution of the scheduled operations with other tasks, such as processing user input and updating the display. This can result in a more responsive user interface, because it allows the UI to update in between the execution of your operations.

So asyncExec() is more suitable for your use case, because it allows the UI to update in between the execution of your operations, which can lead to a more responsive and accurate user interface.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250